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://www.monkeysaudio.com/cgi-bin/YaBB/YaBB.cgi -> Developers
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.
*************************************************************************************************/
#include "NoWindows.h"
#include "All.h"
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 // is 8-bit [OBSOLETE]
#define MAC_FORMAT_FLAG_CRC 2 // uses the new CRC32 error detection [OBSOLETE]
#define MAC_FORMAT_FLAG_HAS_PEAK_LEVEL 4 // uint32 nPeakLevel after the header [OBSOLETE]
#define MAC_FORMAT_FLAG_24_BIT 8 // is 24-bit [OBSOLETE]
#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
{
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)
{
// failure... nErrorCode will have specific code
}
*************************************************************************************************/
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, BOOL bQuickVerifyIfPossible);
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