/*
** AIFF_CD.h - header file defining both chunks of the AIFF-CD standard in C
**             and the standard itself
**
** $Id: AIFF_CD.h 1.6 1998/11/02 11:52:47 patrick Rel patrick $
**
** $Log: AIFF_CD.h $
** Revision 1.6  1998/11/02 11:52:47  patrick
** added the authors mail address
**
** Revision 1.5  1998/11/02 11:46:02  patrick
** External filenames are explicitely required to follow the Amiga convations.
** Added explanation of this convention.
**
** Revision 1.4  1998/07/02 11:45:56  patrick
** added RCS logging
**
**
** Revision 1.3:
**   added example
** Revision 1.2:
**   added macros to step between ACD_TRInfos
** Revision 1.1:
**   initial revision
**
** ©1998 Patrick Ohly, Patrick.Ohly@gmx.de
*/

#ifndef AIFF_CD_H
#define AIFF_CD_H

/*
** CD-ROM extensions to AIFF[-C] standard
** ======================================
** 
** 1. Introduction
** ---------------
**
** The intention of this document is to define an extension to the AIFF and
** AIFC data file format that allows such files to represent all informations
** relevant to an audio and mixed-mode CD (data track + audio tracks).
** 
** Knowledge about these two standards and IFF are expected. This document uses
** the same data type notation as in those standards. AIFF is used as a synonym
** for both AIFF and AIFC from now on.
** 
** This documents's main focus are definitely audio CDs. It does not try to
** represent all possible types of CDs, only those that consist of one session,
** consecutive audio tracks plus one optional data track at the beginning.
** ".aiff-cd" and ".aifc-cd" should be used as suffices for this new file
** format.
** 
** The audio tracks are stored in the usual AIFF way and can have any format
** valid for AIFF. It is up to applications to cope with different number of
** channels, bits per sample, and frequency correctly.
** 
** This approach has the advantage that existing audio applications can access
** the audio data without any modifications in the program. It has the
** disadvantage that the audio data has to be copied, too.
** 
** Therefore the audio data for each track may alternatively be stored in
** another file that is only referenced by the AIFF-CD file. The contents of
** this other file is not defined by this standard.
** 
** Applications aware of the CD-ROM extensions may store the following
** informations:
** - International Standard Recording Code (ISRC) and other
**   advanced information for some or all tracks
** - Medium Catalog Number for the CD
** - Index markers within each track
** - one data track
** 
** These extensions are stored in local chunks within the AIFF
** form. They are defined in the following chapter. They are illustrated
** by an example at the end of this document.
** 
** 2. Chunk Definitions
** --------------------
** 
** All of these chunks are optional. It is up to an application to use
** reasonable default values, if necessary. The following data types
** are used in this standard:
*/

typedef unsigned long ACD_Block;      /* logical CD block,
                                         1min = 60s, 1s = 75 blocks,
                                         block #0 == 00:02:00 min:sec:frame */
typedef unsigned long ACD_AudioBlock; /* as above, but now relative to beginning
                                         of the audio sample:
                                         block #0 == 0.0 sec of audio sample */
typedef unsigned long ACD_SampleNum;  /* position within an audio sample or
                                         amount of audio data;
                                         this value is independent from the
                                         number of channels, i.e. multiple
                                         samples played in parallel only count
                                         as one sample */
typedef unsigned long ACD_ULONG;      /* 32 bit integer, unsigned */
typedef   signed long ACD_LONG;       /* 32 bit integer, signed */
typedef     ACD_ULONG ACD_ID;

/*
** Some more types as defined in the AIFF standard are used in the
** definition of chunks. Any definition not allowed in C (variable sized
** arrays, pstrings, ... ) are excluded using
** #ifdef ACD_NON_C_DATATYPES
** ...
** #endif.
** Take care to handle those entries in the chunks correctly in your code.
**
** These are chunks required for AIFF and AIFC. They are included for
** your convenience only and are explained in the corresponding standards.
*/

typedef struct {
	unsigned short	exponent;			/* Exponent, bit #15 is sign bit for mantissa */
	unsigned long	mantissa[2];		/* 64 bit mantissa */
} extended;

typedef struct
{
	SHORT numChannels;
	ULONG numSampleFrames;
	SHORT sampleSize;
	extended sampleRate;
} AIFF_CommonChunk;

typedef struct
{
	SHORT numChannels;
	ULONG numSampleFrames;
	SHORT sampleSize;
	extended sampleRate;
	ULONG compression_ID;
}  AIFC_CommonChunk;

typedef struct
{
	ULONG offset;
	ULONG blocksize;
} AIF_SoundData;


/*
** 2.1 Track Information
** ^^^^^^^^^^^^^^^^^^^^^
** 
** For each track its source and type can be defined independently. The data can either
** be contained in the SSND chunk of the AIFF-CD file itself or in the file refered to
** in the track entry. If an external file is used then it is up to the application to
** recognize the format. In both cases the relevant data may be some subpart of the
** source defined by start and length.
*/


#define ACD_TRI_PREAMPHASIZE    (1<<0)    /* audio data was sampled with preamphasize */
#define ACD_TRI_COPYRIGHTED     (1<<1)    /* data has a copyright */
#define ACD_TRI_EXTERNAL        (1<<2)    /* data is stored in an external file */
#define ACD_TRI_PAUSE           (1<<3)    /* last entry in index list marks a pause */

typedef struct {
	ACD_ULONG        pause;            /* This number of empty blocks shall be generated
	                                      before the track when writing to CD-R; usually 0.
	                                      This data will be part of the pregap.             */
	ACD_SampleNum    start;            /* beginning and                                     */
	ACD_SampleNum    length;           /* length of the audio data taken from the source(s) */
	ACD_ULONG        flags;            /* ACD_TRI_#? as defined above */

	ACD_ULONG        numIndices;       /* Indices are given relative to the beginning of   
	                                      the real audio data of this track (i.e. 'start').
	                                      Index #1 always starts at 0 and has to be omitted.
	                                      The last entry is the beginning of the index pause
	                                      if ACD_TRI_PAUSE is set, otherwise just another index. */

	/* Entry may be extended in the future by a variable number of bytes;
	   this is the number of bytes appended to this structure at the end in
	   reserved[].                                                          */
	ACD_ULONG        numReservedBytes;    /* 0 at the moment */


	#ifdef ACD_NON_C_DATATYPES
	ACD_AudioBlock indices[];

	/* only used with ACD_TRI_EXTERNAL, but always present;
	   filename can be absolute or relativ to the
	   directory the AIFF-CD file is stored in.
	
	   AIFF-CD follows the Amiga filename conventions:
	   [[»volume«]:][[»dir«]/]*»filename«
	
	   »volume«, »dir« and »filename« are 8 bit characters
	   without colon (":") and slash ("/"). Absolute paths
	   are specified with the optional volume name followed
	   by a colon. The colon alone refers to the root directory
	   of the filesystem that contains the AIFF-CD file. The
	   usage of the absolute part of the filename on non-Amiga
	   systems is not specified.
	
	   The path specification is similar to Unix, with only one
	   exception: a slash without a preceeding directory name
	   refers to the parent directory.
	   E.g. "dir1/dir2//file" = "dir1/file"
	        "/" = parent directory of current directory
	*/

	pstring       externalFile[];

	byte          reserved[];          /* just skip these bytes if the contents is unknown */
	#endif
} ACD_TRInfo;

/*
** Going from one ACD_TRInfo to the next is a bit tricky because of the
** variable sized entries at the end. These macros can be used in C to get
** pointers to the first ACD_TRInfo, all following ones and indices[]/
** externalFile[]/reserved[].
**
** ACD_FIRST_TRINFO() expects a pointer to the ACD_TRInfoChunk structures
** as defined below, including all the data of the chunk. The chunk header
** has to be part of this data if and only if ACD_CHUNK_HEADER was defined
** to include it in the C structure, too.
**
** The same applies to the other macros which expect a pointer to
** ACD_TRInfo.
*/

#define ACD_FIRST_TRINFO(tr_infos) \
	((ACD_TRInfo *) &((ACD_TRInfoChunk *)tr_infos)[1])  /* skip general infos */

#define ACD_TRINFO_INDICES(tr_info) \
	((ULONG *) &((ACD_TRInfo *)tr_info)[1])             /* skip track specific infos of constant length */

#define ACD_TRINFO_EXTERNAL(tr_info) \
	((UBYTE *) &ACD_TRINFO_INDICES(tr_info)[((ACD_TRInfo *)tr_info)->numIndices])
	                                                    /* start at indices and skip all of them */

#define ACD_TRINFO_NEXT(tr_info) \
	((ACD_TRInfo *) &ACD_TRINFO_EXTERNAL(tr_info)[ACD_TRINFO_EXTERNAL(tr_info)[0] + \
	                                              ((ACD_TRINFO_EXTERNAL(tr_info)[0] % 2) ? 1 : 2) + \
	                                              ((ACD_TRInfo *)tr_info)->numReservedBytes])
	                                                    /* start at external file name,
	                                                       skip the name itself,
	                                                       the length byte and the optional pad byte
	                                                       and finally the reserved bytes */


/*
** The different TRInfos are gathered in one obligatory chunk:
*/

#define ACD_TRINFOID 'TRIF'

typedef struct {
#ifdef ACD_CHUNK_HEADER
	ACD_ID           ckID;             /* 'TRIF' */
	ACD_LONG         ckDataSize;
#endif

	ULONG            version;          /* 1 at the moment - don't accept versions you don't know,
	                                      any other chunk might have changed */
	ACD_ULONG        firstTrackNum;    /* the first track number can be chosen, the following
	                                      once increase in steps of one */
	ACD_ULONG        numTRInfos;
	#ifdef ACD_NON_C_DATATYPES
	TRInfo         TRInfos[];
	#endif
} ACD_TRInfoChunk;


/*
** 2.2 ISRC
** ^^^^^^^^
** 
** For each track at most one ISRC can be specified. Tracks are referenced by
** their index in the track info array, not by what they would have on CD.
** Only some characters are allowed for the four components of an ISRC. Each
** component has to consist of exactly the right number of characters.
*/

typedef char ACD_AZASCII;    /* ASCII value of characters A-Z */
typedef char ACD_AZ09ASCII;  /* ASCII value of characters 0-9 and A-Z */
typedef char ACD_09ASCII;    /* ASCII value of characters 0-9 */

typedef struct {
	ACD_ULONG        track;            /* the same as in the CDMarkers */
	ACD_AZASCII      country[2];       /* country code */
	ACD_AZ09ASCII    owner[3];         /* owner code */
	ACD_09ASCII      year[2];          /* year of recording */
	ACD_09ASCII      serial[5];        /* serial number assigned by owner */
} ACD_ISRC;

/*
** The different ISRCs are gathered in one optional chunk.
** Not every track must have an ISRC, but entries have to be
** sorted on the track number in increasing order.
*/

#define ACD_ISRCID 'ISRC'

typedef struct {
#ifdef ACD_CHUNK_HEADER
	ACD_ID           ckID;             /* 'ISRC' */
	ACD_LONG         ckDataSize;
#endif

	ACD_ULONG           numISRCs;
	#ifdef ACD_NON_C_DATATYPES 
	ISRC           ISRCs[];
	#endif
} ACD_ISRCChunk;


/*
** 2.3 Medium Catalog Number
** ^^^^^^^^^^^^^^^^^^^^^^^^^
** 
** This optional chunk stores the UPC/EAN number of the CD. The same rules
** for valid characters and length as for ISRC apply.
*/

#define ACD_MCNID 'MCNU'

typedef struct {
#ifdef ACD_CHUNK_HEADER
	ACD_ID           ckID;             /* 'MCNU' */
	ACD_LONG         ckDataSize;
#endif

	ACD_09ASCII      MCNum[13];        /* Medium Catalog Number */
	ACD_09ASCII      pad;
} ACD_MCNChunk;


/*
** 2.4 Data Track
** ^^^^^^^^^^^^^^
** 
** This optional chunk is not relevant for audio applications. It is used
** by CD writer software to allow the user to store the image of a
** complete CD in one file. An audio application should copy this chunk
** unchanged when writing back a AIFF-CD file or warn the user if it is
** not able to do so.
** 
** Different types of data can be stored and different data formats are
** allowed. More types may be defined latter. An applications should be
** prepared to issue a meaningful warning (perhaps using the data type and
** format names specified in the chunk) if unknown or unsupported data is
** found.
*/

typedef enum {
	ACD_MODE1,       /* CD-ROM mode 1 (block size 2048 bytes) */
	ACD_MODE2,       /* CD-ROM mode 2 (2336 bytes) */
	ACD_MODE2_FORM1, /* XA mode 2, form 1 (2048 bytes) */
	ACD_MODE2_FORM2  /* XA mode 2, form 2 (2324 bytes) */
} ACD_DataType;

typedef enum {
	ACD_DF_COOKED,   /* only user data with block size as specified above */
	ACD_DF_RAW       /* complete block of 2352 bytes, containing user data,
	                   sync, (sub) header and EDC/EDD as specified in the MMC
	                   standard; not scrambled */
} ACD_DataFormat;

/*
** These enumerations have the following English names:
*/

#ifdef ACD_NAMES
char *ACD_TypeNames[] = {
	"Mode 1",
	"Mode 2",
	"Mode 2, Form 1",
	"Mode 2, Form 2"
};

char *ACD_FormatNames[] = {
	"cooked",
	"raw"
};
#endif

/*
** The data track contains data with the block size specified by type and
** format. This data is placed in a chunk on its own to allow seperate
** writing of the data and the control information. The number of blocks
** is deduced from the length of this chunk and the block size.
** 
** The track starts on the CD at a specific logical CD
** block ("startBlock") and must not be relocated without appropriate warnings or
** transformations. The data may contain some blocks ("preGapLen") that are
** already part of the pregap between the data track and the following audio
** track. This pregap is described by the logical CD block number where the
** the second, audio part starts ("audioPregapStart") and the logical CD block
** number where the audio data is placed ("audioStart"):
** 
** # trackType/Format data in the data chunk
** +     "      "       "  that has to be generated
** * audio data as defined by the track infos chunk
**   (a pause requested for the first audio track is valid and
**    moves the audioStart)
** - audio data that has to be generated
** 
** #####################################+++------------------****************
** |                   |               |   |                 |
** startBlock           <- preGapLen ->    audioPregapStart  audioStart
** <--- data track ---> <---       pregap (index 0)      ---> <-- audio track
** 
** All these values are placed in this chunk:
*/

#define ACD_DATATRACKINFOID 'CDTI'

typedef struct {
#ifdef ACD_CHUNK_HEADER
	ACD_ID           ckID;             /* 'CDTI' */
	ACD_LONG         ckDataSize;
#endif

	ACD_DataType     trackType;
	ACD_DataFormat   trackFormat;
	ACD_Block        startBlock;       /* usually #0 */
	ACD_ULONG        preGapLen;          
	ACD_Block        audioPregapStart; /* >= startBlock + number of blocks */
	ACD_Block        audioStart;       /* > audioPregapStart */

	ACD_ULONG        flags;            /* none defined at the moment */
	ACD_ULONG        reserved[4];      /* has to be set to 0 */

	#ifdef ACD_NON_C_DATATYPES
	pstring       trackTypeName;       /* from TypeNames   */
	pstring       dataFormatName;      /* from FormatNames */
	#endif

} ACD_DataTrackInfoChunk;


/*
** This is the chunk for the track data itself. It has to be present if
** the data track is present.
*/

#define ACD_DATATRACKDATAID 'CDDT'

#ifdef ACD_NON_C_DATATYPES
typedef struct
{
#ifdef ACD_CHUNK_HEADER
	ACD_ID           ckID;             /* 'CDDT' */
	ACD_LONG         ckDataSize;
#endif

	/* pure data - number of blocks can be calculated from
	               ckDataSize and block size               */
	char          trackData[];
} ACD_DataTrackDataChunk;
#endif

/*
** Future CD alike media (e.g. DVD) might contain too much blocks to count the
** number of bytes in ckDataSize. This document does not try to solve this
** problem, because the audio data storable in AIFF itself is already limited
** the same way.
*/


/*
** Appendix I: example AIFF-CD file
**
** This hex dump describes an audio CD with
** a medium catalog number ("9876543219876") and
** two tracks:
** 1. audio data without preamphasize,
**    starting at CD block #32, length 10 seconds,
**    no ISRC, indices relativ to track start:
**    #1: 0 seconds (by definition of a track)
**    #2: 2 seconds
**    #3: 5 seconds
**    no index pause
** 2. audio data with preamphasize,
**    starting at CD block #782, length 10 seconds,
**    ISRC "UK POH 98 12345", indices:
**    #1: 0 seconds (s.o.)
**    index pause: 8 seconds
**
** AIFF form chunk:
**    464F524D 0035D5F4 41494646
**    "FORM"   length   "AIFF"
** common chunk:
**    434F4D4D 00000012
**    "COMM"   length
**    0002         - 2 channels
**    000D7550     - 882000 sample frames = 20 seconds
**    0010         - 16 bit sampling size
**    400EAC44 00000000 0000 - frequency (see AIFF standard)
** track infos chunk:
**    54524946 0000004C
**    "TRIF"   length
**    00000001     - version 1 of AIFF-CD
**    00000001     - number of first track on CD
**    00000002     - two track in the file
**    track infos for track #1:
**    00000020     - prepend pause of 32 blocks
**    00000000     - start at sample frame #0 in SSND chunk
**    0006BAA8     - 441000 sample frames long = 10 seconds
**    00000002     - flags: ACD_TRI_COPYRIGHTED
**    00000002     - two indices in addition to the obligatory one at the beginning
**    00000000     - number of reserved bytes: none
**    00000096     - indices at track block #150 = 2 seconds
**    00000177     - and at track block #375 = 5 seconds
**                 - _no_ index pause (ACD_TRI_PAUSE not set)
**    0000         - empty filename (length 0 + pad byte)
**    no reserved bytes, track infos for track #2:
**    00000000     - no pause
**    0006BAA8     - start at sample frame #441000 = 10 seconds
**    0006BAA8     - 441000 sample frames long = 10 seconds
**    0000000B     - flags: ACD_TRI_PREAMPHASIZE, ACD_TRI_COPYRIGHTED, ACD_TRI_PAUSE
**    00000001     - one index marker
**    00000000     - number of reserved bytes: none
**    00000258     - index _pause_ (ACD_TRI_PAUSE is set) at #600 = 8 seconds
**    0000         - empty filename
** medium catalog number chunk:
**    4D434E55 0000000E 39383736353433323139383736 00
**    "MCNU"   length   "9876543219876"            pad byte
** ISRC chunk:
**    49535243 00000014
**    "ISRC"   length
**    00000001     - one ISRC:
**    00000001     - track _index_ in infos array, i.e. ISRC is for track #2
**    554B 4F484C 3938 3132333435
**    "UK  OHL    98   12345"
** sound chunk:
**     53534E44 0035D548
**     "SSND"   length
**     [3528008 bytes of audio data follow]
*/

#endif /* AIFF_CD_H */
