/* $Id */
/* $Log */
/*********************************************************************

This software module was originally developed by

Eric D. Scheirer (MIT Media Laboratory)

in the course of development of the MPEG-2 NBC/MPEG-4 Audio standard
ISO/IEC 13818-7, 14496-1,2 and 3. This software module is an
implementation of a part of one or more MPEG-2 NBC/MPEG-4 Audio tools
as specified by the MPEG-2 NBC/MPEG-4 Audio standard.  ISO/IEC gives
users of the MPEG-2 NBC/MPEG-4 Audio standards free license to this
software module or modifications thereof for use in hardware or
software products claiming conformance to the MPEG-2 NBC/ MPEG-4 Audio
standards. Those intending to use this software module in hardware or
software products are advised that this use may infringe existing
patents. The original developer of this software module and his/her
company, the subsequent editors and their companies, and ISO/IEC have
no liability for use of this software module or modifications thereof
in an implementation.

This software module is hereby released into the public domain.

***********************************************************************/

/***************************************************************\
*   aifif.h                                                     *
*   Detailed parsing of AIFF and AIFC files for read/write      *
*   Header file                                                 *
*   dpwe@media.mit.edu 1993mar01, 1994feb21                     *
\***************************************************************/

/* std libs */
#ifndef _AIFIF_H

#define _AIFIF_H

#include "dpwelib.h"
#include <stdlib.h>
#include <string.h>

/* personal libs */
#include "dpweaiff.h"
#include "byteswap.h"
#define BM_INORDER      INORDER     /* as defined in byteswap.h */
#include "IEEE80.h"

/* constants */
#define UNK_LEN     (-1L)   /* indicates an unknown length */
#define BITS_PER_BYTE   8

/* typedefs */
#ifndef _UCHAR_DEFINED_
typedef unsigned char uchar;
#define _UCHAR_DEFINED_
#endif /* _UCHAR_DEFINED_ */

#ifndef BYTE
#define BYTE    uchar
#endif /* BYTE */

/* static data */
enum {  /* bitmask for chunks */
    AIF_M_NONE = 0,
    AIF_M_COMM = 1,
    AIF_M_FVER = 2,
    AIF_M_SSND = 4,
    AIF_M_MARK = 8, 
    AIF_M_INST = 16, 
    AIF_M_COMT = 32, 

    AIF_M_NAME = 64,
    AIF_M_AUTH = 128, 
    AIF_M_COPY = 256,
    AIF_M_ANNO = 512, 
    AIF_M_APPL = 1024, 
    AIF_M_AESD = 2048, 
    AIF_M_MIDI = 4096, 
    AIF_M_SAXL = 8192,

    AIF_M_TEXT = 65536, 
    AIF_M_DATA = 131072

};

/* which chunks MUST be present */
#define AIFC_MASK (AIF_M_COMM | AIF_M_FVER | AIF_M_SSND)
#define AIFF_MASK (AIF_M_COMM | AIF_M_SSND)

/*----------------------- basic structure management -------------------*/
typedef struct _marker {
    struct _marker *next;   /* put markers in linked list */
    char    *markerName;    /* ptr to the name as a Cstr, not inline pstr */
    Marker  marker;
} MarkerNode;   /* 'augmented' marker structure due to alloc problems */

typedef struct _comment {
    struct _comment *next;  /* put comments in linked list */
    char    *text;      /* ptr to the cmt as a Cstr, not inline pstr */
    Comment comment;
} CommentNode;  /* 'augmented' comment structure due to alloc problems */

typedef struct _data_chunk  {       /* used for two lists: data and text cks */
    struct _data_chunk *next;
    BYTE        *data;  /* pointer to binary data.  text is null terminated */
    long        pos;    /* file position of this text/data chunk */
    CkHdr       ckHdr;  /* chunk header includes size and chunk ID */
    long        len;    /* bytes of data (excluding added null on text) */
} DataChunkNode;

typedef struct _anynode {   /* gen. node allows chase down list for freeing */
    struct _anynode *next;      /* in same place on all node types */
    char            *data;      /* second element always allocated data */
} AnyNode;

typedef struct  {       /* collect all data for AIFC soundfile */
    FILE        *file;
    char        *fileName;
    long        fileSize;       /* for simulation of ansi file functions */
    long        filePos;
    long        sndBsize;       /* how many data bytes in SSND chunk */
    long        sndBpos;        /* seek position of first SSND data byte */
    int         isRead;         /* we are reading this file (or writing?) */
    int         isAifc;         /* AIFF/AIFC flag */
    int         isSeekable;     /* (gulp) flag to (not) seek */
    long        chunkMask;      /* bitfield of which chunks are read */
    int         bytemode;       /* how to swap bytes (e.g. on DEC) */
    
    int         frameSize;      /* bytes per 'frame' for Read/WriteFrames */
    
    FormHdr             form;
    long                commPos;        /* ftell() of comm */
    AIFCCommonChunk     comm;           /* common chunk (AIFFcc is subset) */
    char                *compressionName;       /* pstring at end of comm */
    long                fverPos;        /* ftell() of fver */
    FormatVersionChunk  fver;           /* AIFC version chunk */
    long                markPos;        /* ftell() of mark */
    MarkerChunk         mark;
    MarkerNode          *markers;       /* root of linked list of markers */
    long                comtPos;        /* ftell() of comt */
    CommentsChunk       comt;
    CommentNode         *comments;      /* root of linked list of comments */
    long                instPos;        /* ftell() of inst */
    InstrumentChunk     inst;
    long                ssndPos;        /* ftell() of ssnd */
    SoundDataHdr        ssnd;
    DataChunkNode       *textChunks;    /* linked list for AUTH, NAME .. */
    DataChunkNode       *dataChunks;    /* linked list for MIDI, AESD .. */
} AIF_STRUCT;

#define FLOATofLONG(x)      (*(float *)(&(x)))
#define LONGofFLOAT(x)      (*(long *)(&(x)))

enum {          /* filetypes returned for P_FILETYPE */
    AIF_FT_NONE,
    AIF_FT_AIFF,
    AIF_FT_AIFC
};

enum {          /* parameter names for aifGet/SetParams */
    AIF_P_FILETYPE = 1,
    AIF_P_SAMPRATE,     /* float in a long using FLOATofLONG() etc */
    AIF_P_SAMPSIZE,
    AIF_P_CHANNELS,
    AIF_P_NFRAMES,
    AIF_P_COMPID,       /* char[4] in a long is the compression ID string */
    /* above six correspond EXACTLY with awcfif.h Q_ types */
    AIF_P_COMPNAME,     /* char * in a long is Cstr of comp.name (new alloc) */
    AIF_P_FVERTIME,     /* time stamp in Format Version chunk */
    AIF_P_SNDBPOS,      /* file position of first valid SSND data byte */
    AIF_P_SNDBSIZE,     /* number of valid bytes in SSND data */
    AIF_P_CHUNKMASK,    /* bitfield of chunks read (or to write?) */
    AIF_P_FILEPTR       /* read-only, opacity violation: return FILE * */
};

enum {  /* error codes from top level functions - same as awcfif.h */
    AIF_E_NO_ERR = 0, 
    AIF_E_CANT_OPEN,
    AIF_E_NOT_AIFFC,
    AIF_E_BAD_TYPE, 
    AIF_E_BAD_CHUNK,
    AIF_E_MISSING_CHUNK
};

AIF_STRUCT *aifNew(void);
    /* allocate & initialize the basic object, prior to aifOpenRead 
       or setting up parameters for write */
void aifFree(AIF_STRUCT *aifs);
    /* destroy AIF object */

void aifSetParams(AIF_STRUCT *aifs, long *pvBuf, int pvLen);
    /* set parameters of AIF object.  <pvBuf> is an array of
       <pvLen> longs; the even-numbered elements are taken from
       the AIF_P_.. enum above; the odd-numbered are the values for
       the parameters so referred */
void aifGetParams(AIF_STRUCT *aifs, long *pvBuf, int pvLen);
    /* read parameters of AIF object.  Even-numbered elements
       of <pvLen> longs in <pvBuf> specify AIF_P_.. parameter;
       their values are written into the odd locations on return. */

int aifOpenRead(AIF_STRUCT *aifs, char *path);
    /* open the file whose name is in <path> and attach it to the
       pre-allocated <aifs>; parse all the chunks; return at start of SSND
       Return AIF_E_.. error code: 0 means success */
int aifOpenWrite(AIF_STRUCT *aifs, char *path, long sndSize);
    /* create the file whose name is in <path> ready to write
       an AIF file described by the pre-initialized <aifs>.
       Specifying <sndSize> as the total number of bytes of sample
       data that will be written in the SSND chunk will allow the
       code to write the file sizes right first time; passing UNK_LEN
       will make it go back and rewrite those sizes on aifClose().
       Returns AIF_E_ code (0 on success). */
void aifClose(AIF_STRUCT *aifs);
    /* close the file associated with <aifs>.  If file was open
       for write, will attempt to rewrite header sizes according
       to the actual number of bytes written to the file. 
       (current fpos assumed at end of SSND chunk to figure sizes) */

long aifReadFrames(AIF_STRUCT *aifs, void *buf, long nFrames);
    /* read next <nFrames> sample frames from the <aifs> (presumed OpenRead)
       into <buf>.  If file is 16bit stereo, the total number of
       bytes will be 4*nFrames (assuming file is large enough).
       If file is compressed data, nFrames is interpreted as byte count.
       Returns the actual number of frames written into buf, which
       may be less than nFrames if the end-of-chunk is hit */
long aifWriteFrames(AIF_STRUCT *aifs, void *buf, long nFrames);
    /* write <nFrames> sample frames out to file attached to <aifs>.
       nFrames is byte count for compressed files.  Returns number
       of frames successfully written. */
long aifFrameFtell(AIF_STRUCT *aifs);
    /* return the *frame index* of current read/write point in
       file. */
long aifFrameSeek(AIF_STRUCT *aifs, long frame);
    /* seek to a certain *frame index* in a read file.  Return is achieved
       frame index */
long aifFrameFeof(AIF_STRUCT *aifs);
    /* do an 'feof' relative to the ssnd region */
char *aifErrMsg(int err);
      /* return appropriate error message */

    /* low-level functions for direct access to chunks */
#ifdef DEC /* DEC's cc can't handle the idea of 4chr literals */
typedef void *AiffIDPtr;
/* no need to take address of AiffIDs on DEC - use this for '&' */
#define ADR
/* this compiler doesn't like const */
#ifndef __alpha	/* actually, the alpha doesn't mind */
#define const
#endif /* !__alpha */
#else  /* !DEC - ansi C perhaps */
typedef AiffID *AiffIDPtr;
#define ADR &
#endif /* DEC */

int aifIdCmp(AiffIDPtr a, AiffIDPtr b);     /* manage 4-chr AIFF ids */
    /* compare two IDs; return 1 if they are the same */
void aifIdSet(AiffIDPtr d, AiffIDPtr s);
    /* copy an ID to a specified location */
char *aifIdStr(AiffIDPtr id);
    /* return a printf-able string for an ID */
void aifLongSet(LONG *dst, long val);   /* read/wr packed longs in structs */
    /* copy a long onto 2 shorts, preserve binary image */
long aifLongGet(LONG *src);
    /* read 2 shorts as a long, preserve binary image */

long aifRawRead(AIF_STRUCT *aifs, BYTE *buf, long n);   /* bottom level   */
long aifRawWrite(AIF_STRUCT *aifs, BYTE *buf, long n);  /*   file access. */
long aifSeekAbs(AIF_STRUCT *aifs, long pos);
long aifSeekRel(AIF_STRUCT *aifs, long pos);
long aifFsize(AIF_STRUCT *aifs);    /* only works for read? */
int aifFeof(AIF_STRUCT *aifs);
    /* are we at the end of the file (as we know it)?  Return 1 if so.  */
long aifFtell(AIF_STRUCT *aifs);

    /* There may be zero or more mark/comment/text-data chunks.
       These functions access them one by one. */
MarkerNode *aifSetMark(AIF_STRUCT *aifs, int markID, long pos, char *name);
    /* change or add a mark of the specified ID to the specified value */
MarkerNode *aifGetMark(AIF_STRUCT *aifs, int markID, long *ppos, char **pname);
    /* return a pointer to the MarkerNode and its position if it is found, 
       else NULL and 0L */
CommentNode *aifSetComment(AIF_STRUCT *aifs, int markID, long time, 
                           char *text, long len);
    /* add a comment to the specified markID with timestamp and len of text.
       At present, can have several comments per mark.  Should perhaps limit 
       to one, then comment replaces previous if same markID. */
CommentNode *aifGetComment(AIF_STRUCT *aifs, int markID, long *time, 
                           char **text, long *len);
    /* return a pointer to the first CommentNode and its contents attached 
       to markID if it is found, else NULL and zeros */
DataChunkNode *aifSetTextOrData(AIF_STRUCT *aifs, AiffIDPtr id, BYTE *data, 
                                long datalen, int textq);
    /* Set up a data chunk node in our linked list with the 
       specified chunk id <id>, consisiting of <datalen> bytes found 
       at <data> (which is copied) <textq> set means this is stored 
       in the text list, else data list. */
DataChunkNode *aifGetTextOrData(AIF_STRUCT *aifs, AiffIDPtr id, BYTE **pdata, 
                                long *pdatalen, int textq);
    /* Search data or text linked lists for the specified nodes.
       Current architecture only allows one of each type, so 
       only needs to return first if any.  NULL if none found */

#endif
