// This is the main DLL file. using namespace System; using namespace System::Text; using namespace System::Collections::Generic; using namespace System::Collections::Specialized; using namespace System::Runtime::InteropServices; #ifndef _WAVEFORMATEX_ #define _WAVEFORMATEX_ #define BOOL int #define TRUE 1 #define FALSE 0 #define HWND long /* * extended waveform format structure used for all non-PCM formats. this * structure is common to all non-PCM formats. */ typedef struct tWAVEFORMATEX { Int16 wFormatTag; /* format type */ Int16 nChannels; /* number of channels (i.e. mono, stereo...) */ Int32 nSamplesPerSec; /* sample rate */ Int32 nAvgBytesPerSec; /* for buffer estimation */ Int16 nBlockAlign; /* block size of data */ Int16 wBitsPerSample; /* number of bits per sample of mono data */ Int16 cbSize; /* the count in bytes of the size of */ /* extra information (after cbSize) */ } WAVEFORMATEX, *PWAVEFORMATEX, *NPWAVEFORMATEX, *LPWAVEFORMATEX; #endif /* _WAVEFORMATEX_ */ #include "All.h" #include "MACLib.h" #include "APETag.h" namespace APEDotNet { public ref class APEReader { public: APEReader(String^ path) { IntPtr pathChars; pAPEDecompress = NULL; _sampleOffset = 0; _samplesWaiting = false; _path = NULL; pBuffer = NULL; int nRetVal = 0; pathChars = Marshal::StringToHGlobalUni(path); size_t pathLen = wcslen ((const wchar_t*)pathChars.ToPointer())+1; _path = new wchar_t[pathLen]; memcpy ((void*) _path, (const wchar_t*)pathChars.ToPointer(), pathLen*sizeof(wchar_t)); Marshal::FreeHGlobal(pathChars); pAPEDecompress = CreateIAPEDecompress (_path, &nRetVal); if (!pAPEDecompress) { throw gcnew Exception("Unable to open file."); } _sampleRate = pAPEDecompress->GetInfo (APE_INFO_SAMPLE_RATE, 0, 0); _bitsPerSample = pAPEDecompress->GetInfo (APE_INFO_BITS_PER_SAMPLE, 0, 0); _channelCount = pAPEDecompress->GetInfo (APE_INFO_CHANNELS, 0, 0); // make a buffer to hold 16384 blocks of audio data nBlockAlign = pAPEDecompress->GetInfo (APE_INFO_BLOCK_ALIGN, 0, 0); pBuffer = new unsigned char [16384 * nBlockAlign]; // loop through the whole file _sampleCount = pAPEDecompress->GetInfo (APE_DECOMPRESS_TOTAL_BLOCKS, 0, 0); // * ? } ~APEReader () { if (pBuffer) delete [] pBuffer; if (_path) delete [] _path; } property Int32 BitsPerSample { Int32 get() { return _bitsPerSample; } } property Int32 ChannelCount { Int32 get() { return _channelCount; } } property Int32 SampleRate { Int32 get() { return _sampleRate; } } property Int64 Length { Int64 get() { return _sampleCount; } } property Int64 Position { Int64 get() { return _sampleOffset; } void set(Int64 offset) { _sampleOffset = offset; _samplesWaiting = false; if (pAPEDecompress->Seek ((int) offset /*? */)) throw gcnew Exception("Unable to seek."); } } property Int64 Remaining { Int64 get() { return _sampleCount - _sampleOffset; } } void Close() { if (pAPEDecompress) delete pAPEDecompress; pAPEDecompress = NULL; } property NameValueCollection^ Tags { NameValueCollection^ get () { if (!_tags) GetTags (); return _tags; } void set (NameValueCollection ^tags) { _tags = tags; } } Int32 Read([Out] array^% sampleBuffer) { int sampleCount; int nBlocksRetrieved; if (pAPEDecompress->GetData ((char *) pBuffer, 16384, &nBlocksRetrieved)) throw gcnew Exception("An error occurred while decoding."); sampleCount = nBlocksRetrieved; array^ _sampleBuffer = gcnew array (nBlocksRetrieved, 2); { interior_ptr pMyBuffer = &_sampleBuffer[0, 0]; unsigned short * pAPEBuffer = (unsigned short *) pBuffer; unsigned short * pAPEBufferEnd = (unsigned short *) pBuffer + 2 * nBlocksRetrieved; while (pAPEBuffer < pAPEBufferEnd) { *(pMyBuffer++) = *(pAPEBuffer++); *(pMyBuffer++) = *(pAPEBuffer++); } } #if 0 for (int i = 0; i < nBlocksRetrieved; i++) { _sampleBuffer[i,0] = pBuffer[i*4] + (pBuffer[i*4+1] << 8); _sampleBuffer[i,1] = pBuffer[i*4+2] + (pBuffer[i*4+3] << 8); } #endif sampleBuffer = _sampleBuffer; _sampleOffset += nBlocksRetrieved; _samplesWaiting = false; return sampleCount; } private: IAPEDecompress * pAPEDecompress; NameValueCollection^ _tags; Int64 _sampleCount, _sampleOffset; Int32 _bitsPerSample, _channelCount, _sampleRate; int nBlockAlign; bool _samplesWaiting; unsigned char * pBuffer; const wchar_t * _path; void GetTags (void) { _tags = gcnew NameValueCollection(); CAPETag apeTag (_path, TRUE); for (int i = 0; ; i++) { CAPETagField * field = apeTag.GetTagField (i); if (!field) break; if (field->GetIsUTF8Text()) { int valueSize = field->GetFieldValueSize(); while (valueSize && field->GetFieldValue()[valueSize-1]=='\0') valueSize --; const wchar_t * fieldName = field->GetFieldName (); if (!wcsicmp (fieldName, L"YEAR")) fieldName = L"DATE"; if (!wcsicmp (fieldName, L"TRACK")) fieldName = L"TRACKNUMBER"; _tags->Add (gcnew String (fieldName), gcnew String (field->GetFieldValue(), 0, valueSize, System::Text::Encoding::UTF8)); } } } #if 0 APE__StreamDecoderWriteStatus WriteCallback(const APE__StreamDecoder *decoder, const APE__Frame *frame, const APE__int32 * const buffer[], void *client_data) { if ((_sampleBuffer == nullptr) || (_sampleBuffer->GetLength(0) != sampleCount)) { _sampleBuffer = gcnew array(sampleCount, _channelCount); } for (Int32 iChan = 0; iChan < _channelCount; iChan++) { interior_ptr pMyBuffer = &_sampleBuffer[0, iChan]; const APE__int32 *pAPEBuffer = buffer[iChan]; const APE__int32 *pAPEBufferEnd = pAPEBuffer + sampleCount; while (pAPEBuffer < pAPEBufferEnd) { *pMyBuffer = *pAPEBuffer; pMyBuffer += _channelCount; pAPEBuffer++; } } } #endif }; }