{ delphi interface to macdll.dll 3.97 by thomas la cour 30-04-2003 http://www.top-house.dk/~nr161/delphi/ still missing: StartEx for TAPECompress Slightly modified by Andrei Borovsky for better ACS complience. } { $Log: macdll.pas,v $ Revision 1.1 2005/12/19 18:36:49 z0m3ie *** empty log message *** Revision 1.1 2005/09/12 22:04:53 z0m3ie modified structure again, fileformats are now in an sperat folder. all File In/Out classes are capsulated from TFileIn and TFileOut Revision 1.1 2005/08/25 20:18:00 z0m3ie Version 2.4 restructure TCDPlayer removed (fits not in component structure) TMP3ToWavConverter removed (fits not in component structure) Revision 1.2 2005/08/22 20:17:02 z0m3ie changed Headers to log changed mail adress } unit macdll; interface uses classes, sysutils, windows, mmsystem; const MACPath = 'MACDll.dll'; var MACLoaded : Boolean = False; type {$IFDEF FPC} TWaveFormatEx = MMSystem.WaveFormatEx; {$ELSE} TWaveFormatEx = MMSystem.TWaveFormatEx; {$ENDIF} //////////////////////////////////////////////////////////////////////////////// // MAC Error Codes //////////////////////////////////////////////////////////////////////////////// const // file and i/o errors (1000's) MAC_ERROR_IO_READ = 1000; MAC_ERROR_IO_WRITE = 1001; MAC_ERROR_INVALID_INPUT_FILE = 1002; MAC_ERROR_INVALID_OUTPUT_FILE = 1003; MAC_ERROR_INPUT_FILE_TOO_LARGE = 1004; MAC_ERROR_INPUT_FILE_UNSUPPORTED_BIT_DEPTH = 1005; MAC_ERROR_INPUT_FILE_UNSUPPORTED_SAMPLE_RATE = 1006; MAC_ERROR_INPUT_FILE_UNSUPPORTED_CHANNEL_COUNT = 1007; MAC_ERROR_INPUT_FILE_TOO_SMALL = 1008; MAC_ERROR_INVALID_CHECKSUM = 1009; MAC_ERROR_DECOMPRESSING_FRAME = 1010; MAC_ERROR_INITIALIZING_UNMAC = 1011; MAC_ERROR_INVALID_FUNCTION_PARAMETER = 1012; MAC_ERROR_UNSUPPORTED_FILE_TYPE = 1013; MAC_ERROR_UPSUPPORTED_FILE_VERSION = 1014; // memory errors (2000's) MAC_ERROR_INSUFFICIENT_MEMORY = 2000; // dll errors (3000's) MAC_ERROR_LOADINGAPE_DLL = 3000; MAC_ERROR_LOADINGAPE_INFO_DLL = 3001; MAC_ERROR_LOADING_UNMAC_DLL = 3002; // general and misc errors MAC_ERROR_USER_STOPPED_PROCESSING = 4000; MAC_ERROR_SKIPPED = 4001; // programmer errors MAC_ERROR_BAD_PARAMETER = 5000; // IAPECompress errors MAC_ERROR_APE_COMPRESS_TOO_MUCH_DATA = 6000; // unknown error MAC_ERROR_UNDEFINED = -1; //////////////////////////////////////////////////////////////////////////////// // MAC compression codes //////////////////////////////////////////////////////////////////////////////// const COMPRESSION_LEVEL_FAST = 1000; COMPRESSION_LEVEL_NORMAL = 2000; COMPRESSION_LEVEL_HIGH = 3000; COMPRESSION_LEVEL_EXTRA_HIGH = 4000; MAC_FORMAT_FLAG_8_BIT = 1; // is 8-bit mac_format_flag_crc = 2; // uses the new crc32 error detection mac_format_flag_has_peak_level = 4; // unsigned __int32 peak_level after the header mac_format_flag_24_bit = 8; // is 24-bit mac_format_flag_has_seek_elements = 16; // has the number of seek elements after the peak level mac_format_flag_create_wav_header = 32; // create the wave header on decompression (not stored) CREATE_WAV_HEADER_ON_DECOMPRESSION = -1; MAX_AUDIO_BYTES_UNKNOWN = -1; type macProgressCallback_t = procedure(PercentageDone: LongInt); stdcall; var macProgressCallback : macProgressCallback_t; //////////////////////////////////////////////////////////////////////////////// // WAV header structure //////////////////////////////////////////////////////////////////////////////// type TWaveHeader = packed record // RIFF header cRIFFHeader: array[0..3] of char; //unsigned int nRIFFBytes; nRIFFBytes: Longword; // data type cDataTypeID: array[0..3] of char; //char cDataTypeID[4]; // wave format cFormatHeader: array[0..3] of char; //char cFormatHeader[4]; nFormatBytes: Longword; //unsigned int nFormatBytes; nFormatTag: Word; //unsigned short nFormatTag; nChannels: Word; //unsigned short nChannels; nSamplesPerSec: Longword; //unsigned int nSamplesPerSec; nAvgBytesPerSec: Longword; //unsigned int nAvgBytesPerSec; nBlockAlign: Word; //unsigned short nBlockAlign; nBitsPerSample: Word; //unsigned short nBitsPerSample; // data chunk header cDataHeader: array[0..3] of char; //char cDataHeader[4]; nDataBytes: Longword; //unsigned int nDataBytes; end; PWaveHeader = ^TWaveHeader; {***************************************************************************************** APE header structure (what's at the front of an APE file) *****************************************************************************************} type TAPEHeader = packed record cID: array[0..3] of char; // should equal 'MAC ' nVersion: SmallInt; // version number * 1000 (3.81 = 3810) nCompressionLevel: SmallInt; // the compression level nFormatFlags: SmallInt; // any format flags (for future use) nChannels: SmallInt; // the number of channels (1 or 2) nSampleRate: LongInt; // the sample rate (typically 44100) nHeaderBytes: LongInt; // the bytes after the MAC header that compose the WAV header nTerminatingBytes: LongInt; // the bytes after that raw data (for extended info) nTotalFrames: LongInt; // the number of frames in the file nFinalFrameBlocks: LongInt; // the number of samples in the final frame end; PAPEHeader = ^TAPEHeader; {******************************************************************************* TAPEDecompress 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) *******************************************************************************} type APE_DECOMPRESS_FIELDS = Integer; const 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] //////////////////////////////////////////////////////////////////////////////// // misc types //////////////////////////////////////////////////////////////////////////////// type ID3_TAG = record TagHeader: array[1..3] of Char; // should equal 'TAG' Title: array[1..30] of Char; // title Artist: array[1..30] of Char; // artist Album: array[1..30] of Char; // album Year: array[1..4] of Char; // year Comment: array[1..30] of Char; // comment Track: Byte; // track Genre: Byte; // genre end; pID3_TAG = ^ID3_TAG; type APE_DECOMPRESS_HANDLE = LongWord;//Pointer; APE_COMPRESS_HANDLE = LongWord;//Pointer; //////////////////////////////////////////////////////////////////////////////// // Simple functions - see the SDK sample projects for usage examples //////////////////////////////////////////////////////////////////////////////// type macRemoveTag_t = function(pFilename: PChar): LongInt; stdcall; macCompressFile_t = function ( InputFile: PChar; OutputFile: PChar; CompressionLevel: LongInt; PercentageDone: PInteger; ProgressCalllback: macProgressCallback_t; KillFlag: PInteger ): LongInt; stdcall; macConvertFile_t = function( InputFile: PChar; OutputFile: PChar; CompressionLevel: LongInt; var PercentageDone: Integer; ProgressCalllback: macProgressCallback_t; var KillFlag: Integer ): LongInt; stdcall; macDecompressFile_t = function( InputFile: PChar; OutputFile: PChar; var PercentageDone: Integer; //Pointer; ProgressCalllback: macProgressCallback_t; var KillFlag: Integer ): LongInt; stdcall; macFillWaveFormatEx_t = function( var WaveFormatEx: TWaveFormatEx; SampleRate: Integer = 44100; BitsPerSample: Integer = 16; Channels: Integer = 2 ): LongInt; stdcall; macFillWaveHeader_t = function( var WaveHeader: TWaveHeader; AudioBytes: Integer; var WaveFormatEx: TWaveFormatEx; TerminatingBytes: Integer = 0 ): LongInt; stdcall; macGetID3Tag_t = function( pFilename: PChar; pID3Tag: pID3_TAG ): LongInt; stdcall; macGetInterfaceCompatibility_t = function( nVersion: Integer; bDisplayWarningsOnFailure: Boolean; hwndParent: HWND ): LongInt; stdcall; macGetVersionNumber_t = function(): LongInt; stdcall; macShowFileInfoDialog_t = function( Filename: PChar; HWndWindow: HWND ): LongInt; stdcall; macTagFileSimple_t = function( pFilename: PChar; pArtist: PChar; pAlbum: PChar; pTitle: PChar; pComment: PChar; pGenre: PChar; pYear: PChar; pTrack: PChar; bClearFirst: boolean; bUseOldID3: boolean ): LongInt; stdcall; macVerifyFile_t = function( InputFile: PChar; var PercentageDone: Integer; ProgressCalllback: macProgressCallback_t; var KillFlag: Integer ): LongInt; stdcall; // // Utility functions // function macErrorExplanation(MacErrorCode: LongInt): string; function macMSToTime(MilliSeconds: LongInt): Double; var macRemoveTag : macRemoveTag_t; macCompressFile : macCompressFile_t; macConvertFile : macConvertFile_t; macDecompressFile : macDecompressFile_t; macFillWaveFormatEx : macFillWaveFormatEx_t; macFillWaveHeader : macFillWaveHeader_t; macGetID3Tag : macGetID3Tag_t; macGetInterfaceCompatibility : macGetInterfaceCompatibility_t; macGetVersionNumber : macGetVersionNumber_t; macShowFileInfoDialog : macShowFileInfoDialog_t; macTagFileSimple : macTagFileSimple_t; macVerifyFile : macVerifyFile_t; // // EAPEException // type EAPEException = class(Exception) public constructor Create(MacErrorCode: LongInt); overload; end; {************************************************************************************************* TAPECompress - class for creating APE files Usage: To create an APE file, you Start(...), then add data (in a variety of ways), then Finish(...) *************************************************************************************************} TAPECompress = class private FHandle: APE_COMPRESS_HANDLE; //FFilename: string; public ErrorCode: Integer; constructor Create; destructor Destroy; override; {********************************************************************************************* * 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;} function Start( pOutputFilename: PChar; pwfeInput: PWaveFormatEx; nMaxAudioBytes: Integer = MAX_AUDIO_BYTES_UNKNOWN; nCompressionLevel: Integer = COMPRESSION_LEVEL_NORMAL; pHeaderData: Pointer = nil; nHeaderBytes: Integer = CREATE_WAV_HEADER_ON_DECOMPRESSION ): LongInt; {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; function AddData( pData: Pointer; nBytes: Integer ): LongInt; ////////////////////////////////////////////////////////////////////////////////////////////// // GetBufferBytesAvailable(...) - returns the number of bytes available in the buffer // (helpful when locking) ////////////////////////////////////////////////////////////////////////////////////////////// //virtual int GetBufferBytesAvailable() = 0; function GetBufferBytesAvailable( ): LongInt; stdcall; ////////////////////////////////////////////////////////////////////////////////////////////// // 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; function LockBuffer( var pBytesAvailable: Integer ): Pointer; ////////////////////////////////////////////////////////////////////////////////////////////// // 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; function UnlockBuffer( nBytesAdded: Integer; bProcess: Boolean = True ): LongInt; ////////////////////////////////////////////////////////////////////////////////////////////// // 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; function Finish( pTerminatingData: Pointer; nTerminatingBytes: Integer; nWAVTerminatingBytes: Integer ): LongInt; ////////////////////////////////////////////////////////////////////////////////////////////// // Kill(...) - stops encoding and deletes the output file // --- NOT CURRENTLY IMPLEMENTED --- ////////////////////////////////////////////////////////////////////////////////////////////// //virtual int Kill() = 0; function Kill( ): LongInt; published property Handle: APE_DECOMPRESS_HANDLE read FHandle; end; {************************************************************************************************* TAPEDecompress - class for working with existing APE files (decoding, seeking, analyzing, etc.) *************************************************************************************************} TAPEDecompress = class private FHandle: APE_DECOMPRESS_HANDLE; FFilename: string; public ErrorCode: Integer; constructor Create(Filename: string); destructor Destroy; override; ////////////////////////////////////////////////////////////////////////////////////////////// // 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; function GetData(pBuffer: Pointer; nBlocks: Integer; var pBlocksRetrieved: Integer): LongInt; ////////////////////////////////////////////////////////////////////////////////////////////// // Seek(...) - seeks // // Parameters: // int nBlockOffset // the block to seek to (see note at intro about blocks vs. samples) ////////////////////////////////////////////////////////////////////////////////////////////// //virtual int Seek(int nBlockOffset) = 0; function Seek(nBlockOffset: Integer): LongInt; ////////////////////////////////////////////////////////////////////////////////////////////// // 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; function GetInfo(Field: APE_DECOMPRESS_FIELDS; nParam1: Integer = 0; nParam2: Integer = 0): LongInt; function AverageBitrate: LongWord; function CurrentBitrate: LongWord; function CurrentBlock: LongWord; function CurrentMS: LongWord; function LengthMS: LongWord; function TotalBlocks: LongWord; function InfoApeTotalBytes: LongInt; function InfoAverageBitrage: LongInt; function InfoBitsPerSample: LongInt; function InfoBlockAlign: LongInt; function InfoBlocksPerFrame: LongInt; function InfoBytesPerSample: LongInt; function InfoChannels: LongInt; function InfoCompressionLevel: LongInt; function InfoDecompressedBitrate: LongInt; function InfoFileVersion: LongInt; function InfoFinalFrameBlocks: LongInt; function InfoFormatFlags: LongInt; function InfoFrameBitrate(FrameIndex: Integer): LongInt; function InfoFrameBlocks(FrameIndex: Integer): LongInt; function InfoFrameBytes(FrameIndex: Integer): LongInt; function InfoIOSource: LongInt; function InfoLengthMS: LongInt; function InfoPeakLevel: LongInt; function InfoSampleRate: LongInt; function InfoSeekBit(FrameIndex: Integer): LongInt; function InfoSeekByte(FrameIndex: Integer): LongInt; function InfoTag: Pointer; function InfoTerminatingData(Buffer: Pointer; MaxBytes: Integer): LongInt; function InfoTotalBlocks: LongInt; function InfoTotalFrames: LongInt; function InfoWavDataBytes: LongInt; function InfoWaveFormatEx(var WaveFormatEx: TWaveFormatEx): LongInt; function InfoWavHeaderBytes: LongInt; function InfoWavHeaderData(Buffer: Pointer; MaxBytes: Integer): LongInt; function InfoWavTerminatingBytes: LongInt; function InfoWavTotalBytes: LongInt; published property Handle: APE_DECOMPRESS_HANDLE read FHandle; property Filename: string read FFilename; end; implementation function macErrorExplanation(MACErrorCode: LongInt): string; begin case MACErrorCode of 0: Result := 'OK'; MAC_ERROR_IO_READ: Result := 'I/O read error'; MAC_ERROR_IO_WRITE: Result := 'I/O write error'; MAC_ERROR_INVALID_INPUT_FILE: Result := 'invalid input file'; MAC_ERROR_INVALID_OUTPUT_FILE: Result := 'invalid output file'; MAC_ERROR_INPUT_FILE_TOO_LARGE: Result := 'input file file too large'; MAC_ERROR_INPUT_FILE_UNSUPPORTED_BIT_DEPTH: Result := 'input file unsupported bit depth'; MAC_ERROR_INPUT_FILE_UNSUPPORTED_SAMPLE_RATE: Result := 'input file unsupported sample rate'; MAC_ERROR_INPUT_FILE_UNSUPPORTED_CHANNEL_COUNT: Result := 'input file unsupported channel count'; MAC_ERROR_INPUT_FILE_TOO_SMALL: Result := 'input file too small'; MAC_ERROR_INVALID_CHECKSUM: Result := 'invalid checksum'; MAC_ERROR_DECOMPRESSING_FRAME: Result := 'decompressing frame'; MAC_ERROR_INITIALIZING_UNMAC: Result := 'initializing unmac'; MAC_ERROR_INVALID_FUNCTION_PARAMETER: Result := 'invalid function parameter'; MAC_ERROR_UNSUPPORTED_FILE_TYPE: Result := 'unsupported file type'; MAC_ERROR_INSUFFICIENT_MEMORY: Result := 'insufficient memory'; MAC_ERROR_LOADINGAPE_DLL: Result := 'loading MAC.dll'; MAC_ERROR_LOADINGAPE_INFO_DLL: Result := 'loading MACinfo.dll'; MAC_ERROR_LOADING_UNMAC_DLL: Result := 'loading UnMAC.dll'; MAC_ERROR_USER_STOPPED_PROCESSING: Result := 'user stopped processing'; MAC_ERROR_SKIPPED: Result := 'skipped...'; MAC_ERROR_BAD_PARAMETER: Result := 'bad parameter'; MAC_ERROR_APE_COMPRESS_TOO_MUCH_DATA: Result := 'APE compress too much data'; else Result := 'undefined (' + inttostr(MACErrorCode) + ')'; //MAC_ERROR_UNDEFINED end; end; function macMSToTime(MilliSeconds: LongInt): Double; const MS_PER_DAY = 24 * 60 * 60 * 1000; begin Result := MilliSeconds / MS_PER_DAY; end; { EAPEException } constructor EAPEException.Create(MacErrorCode: Integer); begin inherited Create('MACDll error: '+macErrorExplanation(MacErrorCode)); end; //////////////////////////////////////////////////////////////////////////////// // TAPECompress wrapper(s) //////////////////////////////////////////////////////////////////////////////// {int __stdcall c_APECompress_AddData( APE_COMPRESS_HANDLE hAPECompress, unsigned char * pData, int nBytes);} type c_APECompress_AddData_t = function( hAPECompress: APE_COMPRESS_HANDLE; pData: Pointer; nBytes: Integer ): LongInt; stdcall; // c_APECompress_Create; Index 14; Information not available {APE_COMPRESS_HANDLE __stdcall c_APECompress_Create( int * pErrorCode = NULL);} c_APECompress_Create_t = function( var pErrorCode: Integer ): APE_COMPRESS_HANDLE; stdcall; // c_APECompress_Destroy; Index 15; Information not available {void __stdcall c_APECompress_Destroy( APE_COMPRESS_HANDLE hAPECompress);} c_APECompress_Destroy_t = procedure( hAPECompress: APE_COMPRESS_HANDLE ); stdcall; // c_APECompress_Finish; Index 16; Information not available {int __stdcall c_APECompress_Finish( APE_COMPRESS_HANDLE hAPECompress, unsigned char * pTerminatingData, int nTerminatingBytes, int nWAVTerminatingBytes);} c_APECompress_Finish_t = function( hAPECompress: APE_COMPRESS_HANDLE; pTerminatingData: Pointer; nTerminatingBytes: Integer; nWAVTerminatingBytes: Integer ): LongInt; stdcall; // c_APECompress_GetBufferBytesAvailable; Index 17; Information not available {int __stdcall c_APECompress_GetBufferBytesAvailable( APE_COMPRESS_HANDLE hAPECompress);} c_APECompress_GetBufferBytesAvailable_t = function( hAPECompress: APE_COMPRESS_HANDLE ): LongInt; stdcall; // c_APECompress_Kill; Index 18; Information not available {int __stdcall c_APECompress_Kill(APE_COMPRESS_HANDLE hAPECompress);} c_APECompress_Kill_t = function( hAPECompress: APE_COMPRESS_HANDLE ): LongInt; stdcall; // c_APECompress_LockBuffer; Index 19; Information not available {unsigned char * __stdcall c_APECompress_LockBuffer( APE_COMPRESS_HANDLE hAPECompress, int * pBytesAvailable);} c_APECompress_LockBuffer_t = function( hAPECompress: APE_COMPRESS_HANDLE; var BytesAvailable: Integer ): Pointer; stdcall; // c_APECompress_Start; Index 20; Information not available {int __stdcall c_APECompress_Start( APE_COMPRESS_HANDLE hAPECompress, 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);} c_APECompress_Start_t = function( hAPECompress: APE_COMPRESS_HANDLE; pOutputFilename: PChar; pwfeInput: PWaveFormatEx; nMaxAudioBytes: Integer = MAX_AUDIO_BYTES_UNKNOWN; nCompressionLevel: Integer = COMPRESSION_LEVEL_NORMAL; pHeaderData: Pointer = nil; nHeaderBytes: Integer = CREATE_WAV_HEADER_ON_DECOMPRESSION ): LongInt; stdcall; // c_APECompress_UnlockBuffer; Index 21; Information not available {int __stdcall c_APECompress_UnlockBuffer( APE_COMPRESS_HANDLE hAPECompress, int nBytesAdded, BOOL bProcess = TRUE);} c_APECompress_UnlockBuffer_t = function( hAPECompress: APE_COMPRESS_HANDLE; nBytesAdded: Integer; bProcess: Boolean = True ): LongInt; stdcall; var c_APECompress_AddData : c_APECompress_AddData_t; c_APECompress_Create : c_APECompress_Create_t; c_APECompress_Destroy : c_APECompress_Destroy_t; c_APECompress_Finish : c_APECompress_Finish_t; c_APECompress_GetBufferBytesAvailable : c_APECompress_GetBufferBytesAvailable_t; c_APECompress_Kill : c_APECompress_Kill_t; c_APECompress_LockBuffer : c_APECompress_LockBuffer_t; c_APECompress_Start : c_APECompress_Start_t; c_APECompress_UnlockBuffer : c_APECompress_UnlockBuffer_t; //////////////////////////////////////////////////////////////////////////////// // TAPECompress //////////////////////////////////////////////////////////////////////////////// function TAPECompress.AddData(pData: Pointer; nBytes: Integer): LongInt; begin Result := c_APECompress_AddData(FHandle, pData, nBytes); end; function TAPECompress.Kill: LongInt; begin Result := c_APECompress_Kill(FHandle); end; function TAPECompress.Start(pOutputFilename: PChar; pwfeInput: PWaveFormatEx; nMaxAudioBytes, nCompressionLevel: Integer; pHeaderData: Pointer; nHeaderBytes: Integer): LongInt; begin Result := c_APECompress_Start(FHandle, pOutputFilename, pwfeInput, nMaxAudioBytes, nCompressionLevel, pHeaderData, nHeaderBytes); end; constructor TAPECompress.Create; begin FHandle := c_APECompress_Create(ErrorCode); if ErrorCode<>0 then raise EAPEException.Create(ErrorCode); end; destructor TAPECompress.Destroy; begin if FHandle<>0 then c_APECompress_Destroy(FHandle); inherited; end; function TAPECompress.Finish(pTerminatingData: Pointer; nTerminatingBytes, nWAVTerminatingBytes: Integer): LongInt; begin Result := c_APECompress_Finish(FHandle, pTerminatingData, nTerminatingBytes, nWAVTerminatingBytes); end; function TAPECompress.GetBufferBytesAvailable: LongInt; begin Result := c_APECompress_GetBufferBytesAvailable(FHandle); end; function TAPECompress.LockBuffer(var pBytesAvailable: Integer): Pointer; begin Result := c_APECompress_LockBuffer(FHandle, pBytesAvailable); end; function TAPECompress.UnlockBuffer(nBytesAdded: Integer; bProcess: Boolean): LongInt; begin Result := c_APECompress_UnlockBuffer(FHandle, nBytesAdded); end; //////////////////////////////////////////////////////////////////////////////// // TAPEDecompress wrapper(s) //////////////////////////////////////////////////////////////////////////////// // c_APEDecompress_Create; Index 22; Information not available type c_APEDecompress_Create_t = function( pFilename: PChar; var pErrorCode: Integer ): APE_DECOMPRESS_HANDLE; stdcall; // c_APEDecompress_Destroy; Index 23; Information not available c_APEDecompress_Destroy_t = procedure( hAPEDecompress: APE_DECOMPRESS_HANDLE ); stdcall; // c_APEDecompress_GetData; Index 24; Information not available { __declspec( dllexport ) int __stdcall c_APEDecompress_GetData( APE_DECOMPRESS_HANDLE hAPEDecompress, char * pBuffer, int nBlocks, int * pBlocksRetrieved);} c_APEDecompress_GetData_t = function( hAPEDecompress: APE_DECOMPRESS_HANDLE; pBuffer: Pointer; nBlocks: Integer; var pBlocksRetrieved: Integer ): LongInt; stdcall; // c_APEDecompress_GetInfo; Index 25; Information not available c_APEDecompress_GetInfo_t = function( hAPEDecompress: APE_DECOMPRESS_HANDLE; Field: APE_DECOMPRESS_FIELDS; nParam1: Integer = 0; nParam2: Integer = 0 ): LongInt; stdcall; // c_APEDecompress_Seek; Index 26; Information not available {__declspec(dllexport)int __stdcall c_APEDecompress_Seek( APE_DECOMPRESS_HANDLE hAPEDecompress, int nBlockOffset);} c_APEDecompress_Seek_t = function( hAPEDecompress: APE_DECOMPRESS_HANDLE; nBlockOffset: Integer ): LongInt; stdcall; var c_APEDecompress_Create : c_APEDecompress_Create_t; c_APEDecompress_Destroy : c_APEDecompress_Destroy_t; c_APEDecompress_GetData : c_APEDecompress_GetData_t; c_APEDecompress_GetInfo : c_APEDecompress_GetInfo_t; c_APEDecompress_Seek : c_APEDecompress_Seek_t; //////////////////////////////////////////////////////////////////////////////// // TAPEDecompress //////////////////////////////////////////////////////////////////////////////// constructor TAPEDecompress.Create(Filename: string); begin FFilename := Filename; FHandle := c_APEDecompress_Create(PChar(FFilename), ErrorCode); if ErrorCode<>0 then raise EAPEException.Create(ErrorCode); end; destructor TAPEDecompress.Destroy; begin if FHandle<>0 then c_APEDecompress_Destroy(FHandle); inherited; end; function TAPEDecompress.GetData(pBuffer: Pointer; nBlocks: LongInt; var pBlocksRetrieved: Integer): LongInt; begin Result := c_APEDecompress_GetData(FHandle, pBuffer, nBlocks, pBlocksRetrieved); end; function TAPEDecompress.GetInfo(Field: APE_DECOMPRESS_FIELDS; nParam1, nParam2: Integer): LongInt; begin Result := c_APEDecompress_GetInfo(FHandle, Field, nParam1, nParam2); end; function TAPEDecompress.Seek(nBlockOffset: Integer): LongInt; begin Result := c_APEDecompress_Seek(FHandle, nBlockOffset); end; // querying the information engine function TAPEDecompress.InfoFileVersion: LongInt; // version of the APE file * 1000 (3.93 = 3930) [ignored, ignored] begin // APE_INFO_FILE_VERSION = 1000; Result := GetInfo(APE_INFO_FILE_VERSION); end; function TAPEDecompress.InfoCompressionLevel: LongInt; // compression level of the APE file [ignored, ignored] begin // APE_INFO_COMPRESSION_LEVEL = 1001; Result := GetInfo(APE_INFO_COMPRESSION_LEVEL); end; function TAPEDecompress.InfoFormatFlags: LongInt; // format flags of the APE file [ignored, ignored] begin // APE_INFO_FORMAT_FLAGS = 1002; Result := GetInfo(APE_INFO_FORMAT_FLAGS); end; function TAPEDecompress.InfoSampleRate: LongInt; // sample rate (Hz) [ignored, ignored] begin // APE_INFO_SAMPLE_RATE = 1003; Result := GetInfo(APE_INFO_SAMPLE_RATE); end; function TAPEDecompress.InfoBitsPerSample: LongInt; // bits per sample [ignored, ignored] begin // APE_INFO_BITS_PER_SAMPLE = 1004; Result := GetInfo(APE_INFO_BITS_PER_SAMPLE); end; function TAPEDecompress.InfoBytesPerSample: LongInt; // number of bytes per sample [ignored, ignored] begin // APE_INFO_BYTES_PER_SAMPLE = 1005; Result := GetInfo(APE_INFO_BYTES_PER_SAMPLE); end; function TAPEDecompress.InfoChannels: LongInt; // channels [ignored, ignored] begin // APE_INFO_CHANNELS = 1006; Result := GetInfo(APE_INFO_CHANNELS); end; function TAPEDecompress.InfoBlockAlign: LongInt; // block alignment [ignored, ignored] begin // APE_INFO_BLOCK_ALIGN = 1007; Result := GetInfo(APE_INFO_BLOCK_ALIGN); end; function TAPEDecompress.InfoBlocksPerFrame: LongInt; // number of blocks in a frame (frames are used internally) [ignored, ignored] begin // APE_INFO_BLOCKS_PER_FRAME = 1008; Result := GetInfo(APE_INFO_BLOCKS_PER_FRAME); end; function TAPEDecompress.InfoFinalFrameBlocks: LongInt; // blocks in the final frame (frames are used internally) [ignored, ignored] begin // APE_INFO_FINAL_FRAME_BLOCKS = 1009; Result := GetInfo(APE_INFO_FINAL_FRAME_BLOCKS); end; function TAPEDecompress.InfoTotalFrames: LongInt; begin // APE_INFO_TOTAL_FRAMES = 1010; // total number frames (frames are used internally) [ignored, ignored] Result := GetInfo(APE_INFO_TOTAL_FRAMES); end; function TAPEDecompress.InfoWavHeaderBytes: LongInt; // header bytes of the decompressed WAV [ignored, ignored] begin // APE_INFO_WAV_HEADER_BYTES = 1011; Result := GetInfo(APE_INFO_WAV_HEADER_BYTES); end; function TAPEDecompress.InfoWavTerminatingBytes: LongInt; // terminating bytes of the decompressed WAV [ignored, ignored] begin // APE_INFO_WAV_TERMINATING_BYTES = 1012; Result := GetInfo(APE_INFO_WAV_TERMINATING_BYTES); end; function TAPEDecompress.InfoWavDataBytes: LongInt; // data bytes of the decompressed WAV [ignored, ignored] begin // APE_INFO_WAV_DATA_BYTES = 1013; Result := GetInfo(APE_INFO_WAV_DATA_BYTES); end; function TAPEDecompress.InfoWavTotalBytes: LongInt; // total bytes of the decompressed WAV [ignored, ignored] begin // APE_INFO_WAV_TOTAL_BYTES = 1014; Result := GetInfo(APE_INFO_WAV_TOTAL_BYTES); end; function TAPEDecompress.InfoApeTotalBytes: LongInt; // total bytes of the APE file [ignored, ignored] begin // APE_INFO_APE_TOTAL_BYTES = 1015; Result := GetInfo(APE_INFO_APE_TOTAL_BYTES); end; function TAPEDecompress.InfoTotalBlocks: LongInt; // total blocks of audio data [ignored, ignored] begin // APE_INFO_TOTAL_BLOCKS = 1016; Result := GetInfo(APE_INFO_TOTAL_BLOCKS); end; function TAPEDecompress.InfoLengthMS: LongInt; // length in ms (1 sec = 1000 ms) [ignored, ignored] begin // APE_INFO_LENGTH_MS = 1017; Result := GetInfo(APE_INFO_LENGTH_MS); end; function TAPEDecompress.InfoAverageBitrage: LongInt; // average bitrate of the APE [ignored, ignored] begin // APE_INFO_AVERAGE_BITRATE = 1018; Result := GetInfo(APE_INFO_AVERAGE_BITRATE); end; function TAPEDecompress.InfoFrameBitrate(FrameIndex: Integer): LongInt; // bitrate of specified APE frame [frame index, ignored] begin // APE_INFO_FRAME_BITRATE = 1019; Result := GetInfo(APE_INFO_FRAME_BITRATE, FrameIndex); end; function TAPEDecompress.InfoDecompressedBitrate: LongInt; // bitrate of the decompressed WAV [ignored, ignored] begin // APE_INFO_DECOMPRESSED_BITRATE = 1020; Result := GetInfo(APE_INFO_DECOMPRESSED_BITRATE); end; function TAPEDecompress.InfoPeakLevel: LongInt; // peak audio level (-1 is unknown) [ignored, ignored] begin // APE_INFO_PEAK_LEVEL = 1021; Result := GetInfo(APE_INFO_PEAK_LEVEL); end; function TAPEDecompress.InfoSeekBit(FrameIndex: Integer): LongInt; // bit offset [frame index, ignored] begin // APE_INFO_SEEK_BIT = 1022; Result := GetInfo(APE_INFO_SEEK_BIT, FrameIndex); end; function TAPEDecompress.InfoSeekByte(FrameIndex: Integer): LongInt; // byte offset [frame index, ignored] begin // APE_INFO_SEEK_BYTE = 1023; Result := GetInfo(APE_INFO_SEEK_BYTE, FrameIndex); end; function TAPEDecompress.InfoWavHeaderData(Buffer: Pointer; MaxBytes: Integer): LongInt; // error code [buffer *, max bytes] begin // APE_INFO_WAV_HEADER_DATA = 1024; Result := GetInfo(APE_INFO_WAV_HEADER_DATA, Integer(Buffer), MaxBytes); end; function TAPEDecompress.InfoTerminatingData(Buffer: Pointer; MaxBytes: Integer): LongInt; // error code [buffer *, max bytes] begin // APE_INFO_WAV_TERMINATING_DATA = 1025; Result := GetInfo(APE_INFO_WAV_TERMINATING_DATA, Integer(Buffer), MaxBytes); end; function TAPEDecompress.InfoWaveFormatEx(var WaveFormatEx: TWaveFormatEx): LongInt; // error code [waveformatex *, ignored] begin // APE_INFO_WAVEFORMATEX = 1026; Result := GetInfo(APE_INFO_WAVEFORMATEX, Integer(@WaveFormatEx)); end; function TAPEDecompress.InfoIOSource: LongInt; // I/O source (CIO *) [ignored, ignored] begin // APE_INFO_IO_SOURCE = 1027; Result := GetInfo(APE_INFO_IO_SOURCE); end; function TAPEDecompress.InfoFrameBytes(FrameIndex: Integer): LongInt; // bytes (compressed) of the frame [frame index, ignored] begin // APE_INFO_FRAME_BYTES = 1028; Result := GetInfo(APE_INFO_SEEK_BYTE, FrameIndex); end; function TAPEDecompress.InfoFrameBlocks(FrameIndex: Integer): LongInt; // blocks in a given frame [frame index, ignored] begin // APE_INFO_FRAME_BLOCKS = 1029; Result := GetInfo(APE_INFO_FRAME_BLOCKS, FrameIndex); end; function TAPEDecompress.InfoTag: Pointer; // point to tag (CAPETag *) [ignored, ignored] begin // APE_INFO_TAG = 1030; Result := Pointer(GetInfo(APE_INFO_TAG)); end; // querying the decompressor function TAPEDecompress.CurrentBlock: LongWord; // current block location [ignored, ignored] begin Result := GetInfo(APE_DECOMPRESS_CURRENT_BLOCK); end; function TAPEDecompress.CurrentMS: LongWord; // current millisecond location [ignored, ignored] begin Result := GetInfo(APE_DECOMPRESS_CURRENT_MS); end; function TAPEDecompress.TotalBlocks: LongWord; // total blocks in the decompressors range [ignored, ignored] begin Result := GetInfo(APE_DECOMPRESS_TOTAL_BLOCKS); end; function TAPEDecompress.LengthMS: LongWord; // total blocks in the decompressors range [ignored, ignored] begin Result := GetInfo(APE_DECOMPRESS_LENGTH_MS); end; function TAPEDecompress.CurrentBitrate: LongWord; // current bitrate [ignored, ignored] begin Result := GetInfo(APE_DECOMPRESS_CURRENT_BITRATE); end; function TAPEDecompress.AverageBitrate: LongWord; // average bitrate (works with ranges) [ignored, ignored] begin Result := GetInfo(APE_DECOMPRESS_AVERAGE_BITRATE); end; var Libhandle : HMODULE; initialization Libhandle := LoadLibraryEx(MACPath, 0, 0); if Libhandle <> 0 then begin MACLoaded := True; macProgressCallback := GetProcAddress(Libhandle, 'ProgressCallback'); macRemoveTag := GetProcAddress(Libhandle, 'RemoveTag'); macCompressFile := GetProcAddress(Libhandle, 'CompressFile'); macConvertFile := GetProcAddress(Libhandle, 'ConvertFile'); macDecompressFile := GetProcAddress(Libhandle, 'DecompressFile'); macFillWaveFormatEx := GetProcAddress(Libhandle, 'FillWaveFormatEx'); macFillWaveHeader := GetProcAddress(Libhandle, 'FillWaveHeader'); macGetID3Tag := GetProcAddress(Libhandle, 'GetID3Tag'); macGetInterfaceCompatibility := GetProcAddress(Libhandle, 'GetInterfaceCompatibility'); macGetVersionNumber := GetProcAddress(Libhandle, 'GetVersionNumber'); macShowFileInfoDialog := GetProcAddress(Libhandle, 'ShowFileInfoDialog'); macTagFileSimple := GetProcAddress(Libhandle, 'TagFileSimple'); macVerifyFile := GetProcAddress(Libhandle, 'VerifyFile'); c_APECompress_AddData := GetProcAddress(Libhandle, 'c_APECompress_AddData'); c_APECompress_Create := GetProcAddress(Libhandle, 'c_APECompress_Create'); c_APECompress_Destroy := GetProcAddress(Libhandle, 'c_APECompress_Destroy'); c_APECompress_Finish := GetProcAddress(Libhandle, 'c_APECompress_Finish'); c_APECompress_GetBufferBytesAvailable := GetProcAddress(Libhandle, 'c_APECompress_GetBufferBytesAvailable'); c_APECompress_Kill := GetProcAddress(Libhandle, 'c_APECompress_Kill'); c_APECompress_LockBuffer := GetProcAddress(Libhandle, 'c_APECompress_LockBuffer'); c_APECompress_Start := GetProcAddress(Libhandle, 'c_APECompress_Start'); c_APECompress_UnlockBuffer := GetProcAddress(Libhandle, 'c_APECompress_UnlockBuffer'); c_APEDecompress_Create := GetProcAddress(Libhandle, 'c_APEDecompress_Create'); c_APEDecompress_Destroy := GetProcAddress(Libhandle, 'c_APEDecompress_Destroy'); c_APEDecompress_GetData := GetProcAddress(Libhandle, 'c_APEDecompress_GetData'); c_APEDecompress_GetInfo := GetProcAddress(Libhandle, 'c_APEDecompress_GetInfo'); c_APEDecompress_Seek := GetProcAddress(Libhandle, 'c_APEDecompress_Seek'); end; finalization if Libhandle <> 0 then FreeLibrary(Libhandle); end.