/*****************************************************************************************
Monkey's Audio MACLib.h (include for using MACLib.lib in your projects)
Copyright (C) 2000-2003 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 per sample] * [2 channels] = 4 bytes))

Questions / Suggestions:

Please direct questions or comments to the Monkey's Audio developers board:
http:
or, if necessary, matt @ monkeysaudio.com
*****************************************************************************************/

#ifndef APE_MACLIB_H
#define APE_MACLIB_H

/*************************************************************************************************
APE File Format Overview: (pieces in order -- only valid for the latest version APE files)

    JUNK - any amount of "junk" before the APE_DESCRIPTOR (so people that put ID3v2 tags on the files aren't hosed)
    APE_DESCRIPTOR - defines the sizes (and offsets) of all the pieces, as well as the MD5 checksum
    APE_HEADER - describes all of the necessary information about the APE file
    SEEK TABLE - the table that represents seek offsets [optional]
    HEADER DATA - the pre-audio data from the original file [optional]
    APE FRAMES - the actual compressed audio (broken into frames for seekability)
    TERMINATING DATA - the post-audio data from the original file [optional]
    TAG - describes all the properties of the file [optional]

Notes:

    Junk:

    This block may not be supported in the future, so don't write any software that adds meta data
    before the APE_DESCRIPTOR.  Please use the APE Tag for any meta data.

    Seek Table:

    A 32-bit unsigned integer array of offsets from the header to the frame data.  May become "delta" 
    values someday to better suit huge files.

    MD5 Hash:

    Since the header is the last part written to an APE file, you must calculate the MD5 checksum out of order.
    So, you first calculate from the tail of the seek table to the end of the terminating data.
    Then, go back and do from the end of the descriptor to the tail of the seek table.
    You may wish to just cache the header data when starting and run it last, so you don't 
    need to seek back in the I/O.
*************************************************************************************************/

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

#define MAC_FORMAT_FLAG_8_BIT                 1    
#define MAC_FORMAT_FLAG_CRC                   2    
#define MAC_FORMAT_FLAG_HAS_PEAK_LEVEL        4    
#define MAC_FORMAT_FLAG_24_BIT                8    
#define MAC_FORMAT_FLAG_HAS_SEEK_ELEMENTS    16    
#define MAC_FORMAT_FLAG_CREATE_WAV_HEADER    32    

#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
{
    char cRIFFHeader[4];
    unsigned int nRIFFBytes;

    char cDataTypeID[4];

    char cFormatHeader[4];
    unsigned int nFormatBytes;

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

/*****************************************************************************************
APE_DESCRIPTOR structure (file header that describes lengths, offsets, etc.)
*****************************************************************************************/
struct APE_DESCRIPTOR
{
    char    cID[4];                             
    uint16  nVersion;                           

    uint32  nDescriptorBytes;                   
    uint32  nHeaderBytes;                       
    uint32  nSeekTableBytes;                    
    uint32  nHeaderDataBytes;                   
    uint32  nAPEFrameDataBytes;                 
    uint32  nAPEFrameDataBytesHigh;             
    uint32  nTerminatingDataBytes;              

    uint8   cFileMD5[16];                       
};

/*****************************************************************************************
APE_HEADER structure (describes the format, duration, etc. of the APE file)
*****************************************************************************************/
struct APE_HEADER
{
    uint16    nCompressionLevel;                 
    uint16    nFormatFlags;                      

    uint32    nBlocksPerFrame;                   
    uint32    nFinalFrameBlocks;                 
    uint32    nTotalFrames;                      

    uint16    nBitsPerSample;                    
    uint16    nChannels;                         
    uint32    nSampleRate;                       
};

/*************************************************************************************************
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,               
    APE_INFO_COMPRESSION_LEVEL = 1001,          
    APE_INFO_FORMAT_FLAGS = 1002,               
    APE_INFO_SAMPLE_RATE = 1003,                
    APE_INFO_BITS_PER_SAMPLE = 1004,            
    APE_INFO_BYTES_PER_SAMPLE = 1005,           
    APE_INFO_CHANNELS = 1006,                   
    APE_INFO_BLOCK_ALIGN = 1007,                
    APE_INFO_BLOCKS_PER_FRAME = 1008,           
    APE_INFO_FINAL_FRAME_BLOCKS = 1009,         
    APE_INFO_TOTAL_FRAMES = 1010,               
    APE_INFO_WAV_HEADER_BYTES = 1011,           
    APE_INFO_WAV_TERMINATING_BYTES = 1012,      
    APE_INFO_WAV_DATA_BYTES = 1013,             
    APE_INFO_WAV_TOTAL_BYTES = 1014,            
    APE_INFO_APE_TOTAL_BYTES = 1015,            
    APE_INFO_TOTAL_BLOCKS = 1016,               
    APE_INFO_LENGTH_MS = 1017,                  
    APE_INFO_AVERAGE_BITRATE = 1018,            
    APE_INFO_FRAME_BITRATE = 1019,              
    APE_INFO_DECOMPRESSED_BITRATE = 1020,       
    APE_INFO_PEAK_LEVEL = 1021,                 
    APE_INFO_SEEK_BIT = 1022,                   
    APE_INFO_SEEK_BYTE = 1023,                  
    APE_INFO_WAV_HEADER_DATA = 1024,            
    APE_INFO_WAV_TERMINATING_DATA = 1025,       
    APE_INFO_WAVEFORMATEX = 1026,               
    APE_INFO_IO_SOURCE = 1027,                  
    APE_INFO_FRAME_BYTES = 1028,                
    APE_INFO_FRAME_BLOCKS = 1029,               
    APE_INFO_TAG = 1030,                        
    
    APE_DECOMPRESS_CURRENT_BLOCK = 2000,        
    APE_DECOMPRESS_CURRENT_MS = 2001,           
    APE_DECOMPRESS_TOTAL_BLOCKS = 2002,         
    APE_DECOMPRESS_LENGTH_MS = 2003,            
    APE_DECOMPRESS_CURRENT_BITRATE = 2004,      
    APE_DECOMPRESS_AVERAGE_BITRATE = 2005,      

    APE_INTERNAL_INFO = 3000,                   
};

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

    virtual ~IAPEDecompress() {}
    
    /*********************************************************************************************
    * Decompress / Seek
    *********************************************************************************************/
    
    virtual int GetData(char * pBuffer, int nBlocks, int * pBlocksRetrieved) = 0;

    virtual int Seek(int nBlockOffset) = 0;

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

    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:

    virtual ~IAPECompress() {}
    
    /*********************************************************************************************
    * Start
    *********************************************************************************************/
    

    virtual int Start(const str_utf16 * pOutputFilename, const WAVEFORMATEX * pwfeInput, 
        int nMaxAudioBytes = MAX_AUDIO_BYTES_UNKNOWN, int nCompressionLevel = COMPRESSION_LEVEL_NORMAL, 
        const void * 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 void * 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(...))
    *********************************************************************************************/

    virtual int AddData(unsigned char * pData, int nBytes) = 0;
    
    virtual int GetBufferBytesAvailable() = 0;

    virtual unsigned char * LockBuffer(int * pBytesAvailable) = 0;

    virtual int UnlockBuffer(int nBytesAdded, BOOL bProcess = TRUE) = 0;
    

    virtual int AddDataFromInputSource(CInputSource * pInputSource, int nMaxBytes = -1, int * pBytesAdded = NULL) = 0;
    
    /*********************************************************************************************
    * Finish / Kill
    *********************************************************************************************/

    virtual int Finish(unsigned char * pTerminatingData, int nTerminatingBytes, int nWAVTerminatingBytes) = 0;

    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)
    {
    }

*************************************************************************************************/
extern "C"
{
    IAPEDecompress * __stdcall CreateIAPEDecompress(const str_utf16 * 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"
{
    DLLEXPORT int __stdcall CompressFile(const str_ansi * pInputFilename, const str_ansi * pOutputFilename, int nCompressionLevel = COMPRESSION_LEVEL_NORMAL, int * pPercentageDone = NULL, APE_PROGRESS_CALLBACK ProgressCallback = 0, int * pKillFlag = NULL);
    DLLEXPORT int __stdcall DecompressFile(const str_ansi * pInputFilename, const str_ansi * pOutputFilename, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag);
    DLLEXPORT int __stdcall ConvertFile(const str_ansi * pInputFilename, const str_ansi * pOutputFilename, int nCompressionLevel, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag);
    DLLEXPORT int __stdcall VerifyFile(const str_ansi * pInputFilename, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag); 

    DLLEXPORT int __stdcall CompressFileW(const str_utf16 * pInputFilename, const str_utf16 * pOutputFilename, int nCompressionLevel = COMPRESSION_LEVEL_NORMAL, int * pPercentageDone = NULL, APE_PROGRESS_CALLBACK ProgressCallback = 0, int * pKillFlag = NULL);
    DLLEXPORT int __stdcall DecompressFileW(const str_utf16 * pInputFilename, const str_utf16 * pOutputFilename, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag);
    DLLEXPORT int __stdcall ConvertFileW(const str_utf16 * pInputFilename, const str_utf16 * pOutputFilename, int nCompressionLevel, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag);
    DLLEXPORT int __stdcall VerifyFileW(const str_utf16 * pInputFilename, int * pPercentageDone, APE_PROGRESS_CALLBACK ProgressCallback, int * pKillFlag, BOOL bQuickVerifyIfPossible = FALSE); 

    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 
