/*****************************************************************************************
Monkey's Audio MACLib.h (include for using MACLib.lib in your projects)
Copyright (C) 2000-2002 by Matthew T. Ashland   All Rights Reserved.

Overview:

There are two main interfaces... create one (using CreateIAPExxx) and go to town:

	IAPECompress - for creating APE files
	IAPEDecompress - for decompressing and analyzing APE files

Note(s):

-unless otherwise specified, functions return ERROR_SUCCESS (0) on success and an 
error code on failure.

-the terminology "Sample" refers to a single sample value, and "Block" refers 
to a collection	of "Channel" samples.  For simplicity, MAC typically uses blocks
everywhere so that channel mis-alignment cannot happen. (i.e. on a CD, a sample is
2 bytes and a block is 4 bytes ([2 bytes / sample] * [2 channels] = 4 bytes))

Questions / Suggestions:

Please direct questions or comments to the Monkey's Audio developers board:
http://www.monkeysaudio.com/cgi-bin/YaBB/YaBB.cgi -> Developers
or, if necessary, email @ monkeysaudio.com
*****************************************************************************************/

#ifndef APE_MACLIB_H
#define APE_MACLIB_H

/*****************************************************************************************
Defines
*****************************************************************************************/
#define COMPRESSION_LEVEL_FAST			1000
#define COMPRESSION_LEVEL_NORMAL		2000
#define COMPRESSION_LEVEL_HIGH			3000
#define COMPRESSION_LEVEL_EXTRA_HIGH	4000

#define MAC_FORMAT_FLAG_8_BIT				1	// is 8-bit
#define MAC_FORMAT_FLAG_CRC					2	// uses the new CRC32 error detection
#define MAC_FORMAT_FLAG_HAS_PEAK_LEVEL		4	// unsigned __int32 Peak_Level after the header
#define MAC_FORMAT_FLAG_24_BIT				8	// is 24-bit
#define MAC_FORMAT_FLAG_HAS_SEEK_ELEMENTS	16	// has the number of seek elements after the peak level
#define MAC_FORMAT_FLAG_CREATE_WAV_HEADER	32  // create the wave header on decompression (not stored)

#define CREATE_WAV_HEADER_ON_DECOMPRESSION	-1
#define MAX_AUDIO_BYTES_UNKNOWN -1

typedef void (__stdcall * APE_PROGRESS_CALLBACK) (int);

/*****************************************************************************************
WAV header structure
*****************************************************************************************/
struct WAVE_HEADER
{
	// RIFF header
	char cRIFFHeader[4];
	unsigned int nRIFFBytes;

	// data type
	char cDataTypeID[4];

	// wave format
	char cFormatHeader[4];
	unsigned int nFormatBytes;

	unsigned short nFormatTag;
    unsigned short nChannels;
    unsigned int nSamplesPerSec;
    unsigned int nAvgBytesPerSec;
    unsigned short nBlockAlign;
    unsigned short nBitsPerSample;
	
	// data chunk header
	char cDataHeader[4];
	unsigned int nDataBytes;
};

/*****************************************************************************************
APE header structure (what's at the front of an APE file)
*****************************************************************************************/
#define APE_HEADER_BYTES	32
struct APE_HEADER 
{
	char cID[4];							// should equal 'MAC '
	unsigned __int16 nVersion;				// version number * 1000 (3.81 = 3810)
	unsigned __int16 nCompressionLevel;		// the compression level
	unsigned __int16 nFormatFlags;			// any format flags (for future use)
	unsigned __int16 nChannels;				// the number of channels (1 or 2)
	unsigned __int32 nSampleRate;			// the sample rate (typically 44100)
	unsigned __int32 nHeaderBytes;			// the bytes after the MAC header that compose the WAV header
	unsigned __int32 nTerminatingBytes;		// the bytes after that raw data (for extended info)
	unsigned __int32 nTotalFrames;			// the number of frames in the file
	unsigned __int32 nFinalFrameBlocks;		// the number of samples in the final frame
};

/*************************************************************************************************
Classes (fully defined elsewhere)
*************************************************************************************************/
class CIO;
class CInputSource;
class CAPEInfo;

/*************************************************************************************************
IAPEDecompress fields - used when querying for information

Note(s):
-the distinction between APE_INFO_XXXX and APE_DECOMPRESS_XXXX is that the first is querying the APE
information engine, and the other is querying the decompressor, and since the decompressor can be
a range of an APE file (for APL), differences will arise.  Typically, use the APE_DECOMPRESS_XXXX
fields when querying for info about the length, etc. so APL will work properly. 
(i.e. (APE_INFO_TOTAL_BLOCKS != APE_DECOMPRESS_TOTAL_BLOCKS) for APL files)
*************************************************************************************************/
enum APE_DECOMPRESS_FIELDS
{
	APE_INFO_FILE_VERSION = 1000,			// version of the APE file * 1000 (3.93 = 3930) [ignored, ignored]
	APE_INFO_COMPRESSION_LEVEL = 1001,		// compression level of the APE file [ignored, ignored]
	APE_INFO_FORMAT_FLAGS = 1002,			// format flags of the APE file [ignored, ignored]
	APE_INFO_SAMPLE_RATE = 1003,			// sample rate (Hz) [ignored, ignored]
	APE_INFO_BITS_PER_SAMPLE = 1004,		// bits per sample [ignored, ignored]
	APE_INFO_BYTES_PER_SAMPLE = 1005,		// number of bytes per sample [ignored, ignored]
	APE_INFO_CHANNELS = 1006,				// channels [ignored, ignored]
	APE_INFO_BLOCK_ALIGN = 1007,			// block alignment [ignored, ignored]
	APE_INFO_BLOCKS_PER_FRAME = 1008,		// number of blocks in a frame (frames are used internally)  [ignored, ignored]
	APE_INFO_FINAL_FRAME_BLOCKS = 1009,		// blocks in the final frame (frames are used internally) [ignored, ignored]
	APE_INFO_TOTAL_FRAMES = 1010,			// total number frames (frames are used internally) [ignored, ignored]
	APE_INFO_WAV_HEADER_BYTES = 1011,		// header bytes of the decompressed WAV [ignored, ignored]
	APE_INFO_WAV_TERMINATING_BYTES = 1012,	// terminating bytes of the decompressed WAV [ignored, ignored]
	APE_INFO_WAV_DATA_BYTES = 1013,			// data bytes of the decompressed WAV [ignored, ignored]
	APE_INFO_WAV_TOTAL_BYTES = 1014,		// total bytes of the decompressed WAV [ignored, ignored]
	APE_INFO_APE_TOTAL_BYTES = 1015,		// total bytes of the APE file [ignored, ignored]
	APE_INFO_TOTAL_BLOCKS = 1016,			// total blocks of audio data [ignored, ignored]
	APE_INFO_LENGTH_MS = 1017,				// length in ms (1 sec = 1000 ms) [ignored, ignored]
	APE_INFO_AVERAGE_BITRATE = 1018,		// average bitrate of the APE [ignored, ignored]
	APE_INFO_FRAME_BITRATE = 1019,			// bitrate of specified APE frame [frame index, ignored]
	APE_INFO_DECOMPRESSED_BITRATE = 1020,	// bitrate of the decompressed WAV [ignored, ignored]
	APE_INFO_PEAK_LEVEL = 1021,				// peak audio level (-1 is unknown) [ignored, ignored]
	APE_INFO_SEEK_BIT = 1022,				// bit offset [frame index, ignored]
	APE_INFO_SEEK_BYTE = 1023,				// byte offset [frame index, ignored]
	APE_INFO_WAV_HEADER_DATA = 1024,		// error code [buffer *, max bytes]
	APE_INFO_WAV_TERMINATING_DATA = 1025,	// error code [buffer *, max bytes]
	APE_INFO_WAVEFORMATEX = 1026,			// error code [waveformatex *, ignored]
	APE_INFO_IO_SOURCE = 1027,				// I/O source (CIO *) [ignored, ignored]
	APE_INFO_FRAME_BYTES = 1028,			// bytes (compressed) of the frame [frame index, ignored]
	APE_INFO_FRAME_BLOCKS = 1029,			// blocks in a given frame [frame index, ignored]
	APE_INFO_TAG = 1030,					// point to tag (CAPETag *) [ignored, ignored]

	APE_DECOMPRESS_CURRENT_BLOCK = 2000,	// current block location [ignored, ignored]
	APE_DECOMPRESS_CURRENT_MS = 2001,		// current millisecond location [ignored, ignored]
	APE_DECOMPRESS_TOTAL_BLOCKS = 2002,		// total blocks in the decompressors range [ignored, ignored]
	APE_DECOMPRESS_LENGTH_MS = 2003,		// total blocks in the decompressors range [ignored, ignored]
	APE_DECOMPRESS_CURRENT_BITRATE = 2004,	// current bitrate [ignored, ignored]
	APE_DECOMPRESS_AVERAGE_BITRATE = 2005,	// average bitrate (works with ranges) [ignored, ignored]
};

/*************************************************************************************************
IAPEDecompress - interface for working with existing APE files (decoding, seeking, analyzing, etc.)
*************************************************************************************************/
class IAPEDecompress
{
public:

	// destructor (needed so implementation's destructor will be called)
	virtual ~IAPEDecompress() {}
	
	/*********************************************************************************************
	* Decompress / Seek
	*********************************************************************************************/
	
	//////////////////////////////////////////////////////////////////////////////////////////////
	// GetData(...) - gets raw decompressed audio
	// 
	// Parameters:
	//	char * pBuffer
	//		a pointer to a buffer to put the data into
	//	int nBlocks
	//		the number of audio blocks desired (see note at intro about blocks vs. samples)
	//	int * pBlocksRetrieved
	//		the number of blocks actually retrieved (could be less at end of file or on critical failure)
	//////////////////////////////////////////////////////////////////////////////////////////////
	virtual int GetData(char * pBuffer, int nBlocks, int * pBlocksRetrieved) = 0;

	//////////////////////////////////////////////////////////////////////////////////////////////
	// Seek(...) - seeks
	// 
	// Parameters:
	//	int nBlockOffset
	//		the block to seek to (see note at intro about blocks vs. samples)
	//////////////////////////////////////////////////////////////////////////////////////////////
	virtual int Seek(int nBlockOffset) = 0;

	/*********************************************************************************************
	* Get Information
	*********************************************************************************************/

	//////////////////////////////////////////////////////////////////////////////////////////////
	// GetInfo(...) - get information about the APE file or the state of the decompressor
	// 
	// Parameters:
	//	APE_DECOMPRESS_FIELDS Field
	//		the field we're querying (see APE_DECOMPRESS_FIELDS above for more info)
	//	int nParam1
	//		generic parameter... usage is listed in APE_DECOMPRESS_FIELDS
	//	int nParam2
	//		generic parameter... usage is listed in APE_DECOMPRESS_FIELDS
	//////////////////////////////////////////////////////////////////////////////////////////////
	virtual int GetInfo(APE_DECOMPRESS_FIELDS Field, int nParam1 = 0, int nParam2 = 0) = 0;
};

/*************************************************************************************************
IAPECompress - interface for creating APE files

Usage:

	To create an APE file, you Start(...), then add data (in a variety of ways), then Finish(...)
*************************************************************************************************/
class IAPECompress
{
public:

	// destructor (needed so implementation's destructor will be called)
	virtual ~IAPECompress() {}
	
	/*********************************************************************************************
	* Start
	*********************************************************************************************/
	
	//////////////////////////////////////////////////////////////////////////////////////////////
	// Start(...) / StartEx(...) - starts encoding
	// 
	// Parameters:
	//	CIO * pioOutput / const char * pFilename
	//		the output... either a filename or an I/O source
	//	WAVEFORMATEX * pwfeInput
	//		format of the audio to encode (use FillWaveFormatEx() if necessary)
	//	int nMaxAudioBytes
	//		the absolute maximum audio bytes that will be encoded... encoding fails with a
	//		ERROR_APE_COMPRESS_TOO_MUCH_DATA if you attempt to encode more than specified here
	//		(if unknown, use MAX_AUDIO_BYTES_UNKNOWN to allocate as much storage in the seek table as
	//		possible... limit is then 2 GB of data (~4 hours of CD music)... this wastes around
	//		30kb, so only do it if completely necessary)
	//	int nCompressionLevel
	//		the compression level for the APE file (fast - extra high)
	//		(note: extra-high is much slower for little gain)
	//	const unsigned char * pHeaderData
	//		a pointer to a buffer containing the WAV header (data before the data block in the WAV)
	//		(note: use NULL for on-the-fly encoding... see next parameter)
	//	int nHeaderBytes
	//		number of bytes in the header data buffer (use CREATE_WAV_HEADER_ON_DECOMPRESSION and
	//		NULL for the pHeaderData and MAC will automatically create the appropriate WAV header
	//		on decompression)
	//////////////////////////////////////////////////////////////////////////////////////////////

	virtual int Start(const char * pOutputFilename, const WAVEFORMATEX * pwfeInput, 
		int nMaxAudioBytes = MAX_AUDIO_BYTES_UNKNOWN, int nCompressionLevel = COMPRESSION_LEVEL_NORMAL, 
		const unsigned char * pHeaderData = NULL, int nHeaderBytes = CREATE_WAV_HEADER_ON_DECOMPRESSION) = 0;

	virtual int StartEx(CIO * pioOutput, const WAVEFORMATEX * pwfeInput, 
		int nMaxAudioBytes = MAX_AUDIO_BYTES_UNKNOWN, int nCompressionLevel = COMPRESSION_LEVEL_NORMAL, 
		const unsigned char * pHeaderData = NULL, int nHeaderBytes = CREATE_WAV_HEADER_ON_DECOMPRESSION) = 0;
	
	/*********************************************************************************************
	* Add / Compress Data
	*	- there are 3 ways to add data:
	*		1) simple call AddData(...)
	*		2) lock MAC's buffer, copy into it, and unlock (LockBuffer(...) / UnlockBuffer(...))
	*		3) from an I/O source (AddDataFromInputSource(...))
	*********************************************************************************************/

	//////////////////////////////////////////////////////////////////////////////////////////////
	// AddData(...) - adds data to the encoder
	// 
	// Parameters:
	//	unsigned char * pData
	//		a pointer to a buffer containing the raw audio data
	//	int nBytes
	//		the number of bytes in the buffer
	//////////////////////////////////////////////////////////////////////////////////////////////
	virtual int AddData(unsigned char * pData, int nBytes) = 0;
	
	
	
	//////////////////////////////////////////////////////////////////////////////////////////////
	// GetBufferBytesAvailable(...) - returns the number of bytes available in the buffer
	//	(helpful when locking)
	//////////////////////////////////////////////////////////////////////////////////////////////
	virtual int GetBufferBytesAvailable() = 0;

	//////////////////////////////////////////////////////////////////////////////////////////////
	// LockBuffer(...) - locks MAC's buffer so we can copy into it
	// 
	// Parameters:
	//	int * pBytesAvailable
	//		returns the number of bytes available in the buffer (DO NOT COPY MORE THAN THIS IN)
	//
	// Return:
	//	pointer to the buffer (add at that location)
	//////////////////////////////////////////////////////////////////////////////////////////////
	virtual unsigned char * LockBuffer(int * pBytesAvailable) = 0;

	//////////////////////////////////////////////////////////////////////////////////////////////
	// UnlockBuffer(...) - releases the buffer
	// 
	// Parameters:
	//	int nBytesAdded
	//		the number of bytes copied into the buffer
	//	BOOL bProcess
	//		whether MAC should process as much as possible of the buffer
	//////////////////////////////////////////////////////////////////////////////////////////////
	virtual int UnlockBuffer(int nBytesAdded, BOOL bProcess = TRUE) = 0;
	

	//////////////////////////////////////////////////////////////////////////////////////////////
	// AddDataFromInputSource(...) - use a CInputSource (input source) to add data
	//
	// Parameters:
	//	CInputSource * pInputSource
	//		a pointer to the input source
	//	int nMaxBytes
	//		the maximum number of bytes to let MAC add (-1 if MAC can add any amount)
	//	int * pBytesAdded
	//		returns the number of bytes added from the I/O source
	//////////////////////////////////////////////////////////////////////////////////////////////
	virtual int AddDataFromInputSource(CInputSource * pInputSource, int nMaxBytes = -1, int * pBytesAdded = NULL) = 0;
	
	/*********************************************************************************************
	* Finish / Kill
	*********************************************************************************************/

	//////////////////////////////////////////////////////////////////////////////////////////////
	// Finish(...) - ends encoding and finalizes the file
	// 
	// Parameters:
	//	unsigned char * pTerminatingData
	//		a pointer to a buffer containing the information to place at the end of the APE file
	//		(comprised of the WAV terminating data (data after the data block in the WAV) followed
	//		by any tag information)
	//	int nTerminatingBytes
	//		number of bytes in the terminating data buffer
	//	int nWAVTerminatingBytes
	//		the number of bytes of the terminating data buffer that should be appended to a decoded
	//		WAV file (it's basically nTerminatingBytes - the bytes that make up the tag)
	//////////////////////////////////////////////////////////////////////////////////////////////
	virtual int Finish(unsigned char * pTerminatingData, int nTerminatingBytes, int nWAVTerminatingBytes) = 0;

	//////////////////////////////////////////////////////////////////////////////////////////////
	// Kill(...) - stops encoding and deletes the output file
	// --- NOT CURRENTLY IMPLEMENTED ---
	//////////////////////////////////////////////////////////////////////////////////////////////
	virtual int Kill() = 0;
};

/*************************************************************************************************
Functions to create the interfaces

Usage:
	Interface creation returns a NULL pointer on failure (and fills error code if it was passed in)

Usage example:
	int nErrorCode;
	IAPEDecompress * pAPEDecompress = CreateIAPEDecompress("c:\\1.ape", &nErrorCode);
	if (pAPEDecompress == NULL)
	{
		// failure... nErrorCode will have specific code
	}

*************************************************************************************************/
extern "C"
{
	IAPEDecompress * __stdcall CreateIAPEDecompress(const char * pFilename, int * pErrorCode = NULL);
	IAPEDecompress * __stdcall CreateIAPEDecompressEx(CIO * pIO, int * pErrorCode = NULL);
	IAPEDecompress * __stdcall CreateIAPEDecompressEx2(CAPEInfo * pAPEInfo, int nStartBlock = -1, int nFinishBlock = -1, int * pErrorCode = NULL);
	IAPECompress * __stdcall CreateIAPECompress(int * pErrorCode = NULL);
}

/*************************************************************************************************
Simple functions - see the SDK sample projects for usage examples
*************************************************************************************************/
extern "C"
{
	// process whole files
	DLLEXPORT int __stdcall CompressFile(const char * pInputFile, const char * pOutputFile, int nCompressionLevel = COMPRESSION_LEVEL_NORMAL, int * pPercentageDone = NULL, APE_PROGRESS_CALLBACK ProgressCallback = 0, int * pKillFlag = NULL);
	DLLEXPORT int __stdcall DecompressFile(const char * pInputFilename, const char * pOutputFilename, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag);
	DLLEXPORT int __stdcall ConvertFile(const char * pInputFilename, const char * pOutputFilename, int nCompressionLevel, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag);
	DLLEXPORT int __stdcall VerifyFile(const char * pInputFilename, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag); 

	// helper functions
	DLLEXPORT int __stdcall FillWaveFormatEx(WAVEFORMATEX * pWaveFormatEx, int nSampleRate = 44100, int nBitsPerSample = 16, int nChannels = 2);
	DLLEXPORT int __stdcall FillWaveHeader(WAVE_HEADER * pWAVHeader, int nAudioBytes, WAVEFORMATEX * pWaveFormatEx, int nTerminatingBytes = 0);
}

#endif // #ifndef APE_MACLIB_H
