123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323 |
- #include "main.h"
- #include "adts.h"
- #include <memory.h>
- #include <malloc.h>
- #include <xutility>
- #include <assert.h>
- #include <shlwapi.h>
- #include <foundation/error.h>
- #include "../nu/RingBuffer.h"
- #include <api/service/waservicefactory.h>
- // {19450308-90D7-4E45-8A9D-DC71E67123E2}
- static const GUID adts_aac_guid =
- { 0x19450308, 0x90d7, 0x4e45, { 0x8a, 0x9d, 0xdc, 0x71, 0xe6, 0x71, 0x23, 0xe2 } };
- // {4192FE3F-E843-445c-8D62-51BE5EE5E68C}
- static const GUID adts_mp2_guid =
- { 0x4192fe3f, 0xe843, 0x445c, { 0x8d, 0x62, 0x51, 0xbe, 0x5e, 0xe5, 0xe6, 0x8c } };
- class GapCutter
- {
- public:
- GapCutter() {}
- void SetEndSize( int postSize );
- void SetSize( int preSize, int postSize );
- void Flush( int time_in_ms );
- int Write( void *dest, void *input, size_t inputBytes );
- private:
- RingBuffer ringBuffer;
-
- int preCut = 0;
- int preCutSize = 0;
- };
- void GapCutter::SetEndSize(int postSize)
- {
- if (postSize < 0)
- postSize = 0;
- if (postSize)
- {
- ringBuffer.Reset();
- ringBuffer.reserve(postSize);
- }
- }
- void GapCutter::SetSize( int preSize, int postSize )
- {
- if ( preSize < 0 )
- preSize = 0;
- if ( postSize < 0 )
- postSize = 0;
- SetEndSize( postSize );
- preCutSize = preSize;
- preCut = preSize;
- }
- void GapCutter::Flush( int time_in_ms )
- {
- // if (time_in_ms == 0) // TODO: calculate actual delay if we seek within the encoder delay area
- preCut = preCutSize; // reset precut size if we seek to the start
- ringBuffer.clear();
- }
- int GapCutter::Write( void *dest, void *input, size_t inputBytes ) // returns # of bytes written
- {
- int bytesWritten = 0;
- unsigned __int8 *in = (unsigned __int8 *)input;
- unsigned __int8 *out = (unsigned __int8 *)dest;
- // cut pre samples, if necessary
- intptr_t pre = min( preCut, (intptr_t)inputBytes );
- in += pre;
- inputBytes -= pre;
- preCut -= (int)pre;
- if ( !inputBytes )
- return bytesWritten;
- size_t remainingFill = ringBuffer.avail();
- intptr_t fillWrite = min( (intptr_t)( inputBytes - remainingFill ), (intptr_t)ringBuffer.size() ); // only write fill buffer if we've got enough left to fill it up
- if ( fillWrite > 0 )
- {
- size_t written = ringBuffer.read( out, fillWrite );
- bytesWritten += (int)written;
- out += written;
- }
- remainingFill = ringBuffer.avail();
-
- int outWrite = (int)max( 0, (intptr_t)( inputBytes - remainingFill ) );
- if ( outWrite )
- memcpy( out, in, outWrite );
- bytesWritten += outWrite;
- in += outWrite;
- inputBytes -= outWrite;
- if ( inputBytes )
- ringBuffer.write( in, inputBytes );
- return bytesWritten;
- }
- struct ExtendedRead
- {
- ExtendedRead() { memset(&data, 0, sizeof(data)); }
- ~ExtendedRead()
- {
- file.Close();
- if ( decoder )
- {
- decoder->Close();
- decoder->Release();
- }
- }
-
- bool Open( const wchar_t *fn, int *size, int *bps, int *nch, int *srate, bool useFloat );
- adts *decoder = NULL;
- int bits = 0;
- size_t initialData = 0;
- int frameSize = 0;
- GapCutter cutter;
- CGioFile file;
- #define DATA_SIZE (6*4*2*2*1152)
- unsigned char data[DATA_SIZE];
- };
- bool ExtendedRead::Open(const wchar_t *fn, int *size, int *bps, int *nch, int *srate, bool useFloat)
- {
- if (file.Open(fn, config_max_bufsize_k) != NErr_Success)
- return false;
- int downmix = 0;
- bool allowsurround = 1;
- if (*nch == 1)
- {
- downmix = 1;
- allowsurround = 0;
- }
- else if (*nch == 2)
- {
- allowsurround = 0;
- }
- if (useFloat)
- bits=32;
- else if (*bps == 24)
- bits = 24;
- else
- {
- bits = 16;
- *bps = 16;
- }
- wchar_t *ext = PathFindExtensionW(fn);
- if (!_wcsicmp(ext, L".vlb"))
- {
- return false;
- }
- else if (!_wcsicmp(ext, L".aac") || !_wcsicmp(ext, L".apl"))
- {
- waServiceFactory *factory = mod.service->service_getServiceByGuid(adts_aac_guid);
- if (factory)
- decoder = (adts *)factory->getInterface();
- }
- else
- {
- waServiceFactory *factory = mod.service->service_getServiceByGuid(adts_mp2_guid);
- if (factory)
- decoder = (adts *)factory->getInterface();
- }
- if (!decoder)
- return false;
- decoder->Initialize(!!downmix, 0, allowsurround, bits, false, useFloat);
- decoder->Open(&file);
- size_t bitrate;
- bool done=false;
- while (!done)
- {
- switch (decoder->Sync(&file, data, sizeof(data), &initialData, &bitrate))
- {
- case adts::SUCCESS:
- done=true;
- break;
- case adts::FAILURE:
- case adts::ENDOFFILE:
- return false;
- case adts::NEEDMOREDATA:
- break;
- }
- }
- size_t numBits = 0;
- decoder->GetOutputParameters(&numBits, nch, srate);
- *bps = bits = (int)numBits;
- frameSize = bits / 8 * *nch;
- if (config_gapless)
- cutter.SetSize((file.prepad + (int)decoder->GetDecoderDelay())*frameSize, (file.postpad - (int)decoder->GetDecoderDelay())*frameSize);
- if (file.m_vbr_samples) // exact number of samples in the LAME header, how nice :)
- *size = (int)file.m_vbr_samples*frameSize;
- else if (file.m_vbr_ms) // if we know the milliseconds accurately
- *size = MulDiv(*srate * frameSize, file.m_vbr_ms, 1000); // our size should be mostly accurate
- else // no helpful info to go on
- {
- // just guess based on bitrate and content length
- bitrate=decoder->GetCurrentBitrate();
- int len_ms = MulDiv(file.GetContentLength(), 8, (int)bitrate);
- *size = MulDiv(*srate * frameSize, len_ms, 1000);
- }
- return true;
- }
- extern "C"
- {
- //returns handle!=0 if successful, 0 if error
- //size will return the final nb of bytes written to the output, -1 if unknown
- __declspec(dllexport) intptr_t winampGetExtendedRead_openW(const wchar_t *fn, int *size, int *bps, int *nch, int *srate)
- {
- ExtendedRead *ext = new ExtendedRead;
- if (ext)
- {
- if (ext->Open(fn, size, bps, nch, srate, false))
- return reinterpret_cast<intptr_t>(ext);
- delete ext;
- }
- return 0;
- }
- __declspec(dllexport) intptr_t winampGetExtendedRead_openW_float(const wchar_t *fn, int *size, int *bps, int *nch, int *srate)
- {
- ExtendedRead *ext = new ExtendedRead;
- if (ext)
- {
- if (ext->Open(fn, size, bps, nch, srate, true))
- return reinterpret_cast<intptr_t>(ext);
- delete ext;
- }
- return 0;
- }
- //returns nb of bytes read. -1 if read error (like CD ejected). if (ret==0), EOF is assumed
- __declspec(dllexport) size_t winampGetExtendedRead_getData(intptr_t handle, char *dest, size_t len, int *killswitch)
- {
- ExtendedRead *ext = (ExtendedRead *)handle;
- int copied = 0;
- if (ext)
- {
- len -= (len % ext->frameSize); // only do whole frames
- while (len)
- {
- size_t toMove = min(len, ext->initialData);
- int toCopy = ext->cutter.Write(dest, ext->data, toMove);
- if (ext->initialData != toMove)
- memmove(ext->data, ext->data + toMove, ext->initialData - toMove);
- ext->initialData -= toMove;
- len -= toCopy;
- copied += toCopy;
- dest += toCopy;
- if (!ext->initialData)
- {
- size_t written = 0, bitrate, endCut = 0;
- int ret = ext->decoder->Decode(&ext->file, ext->data, DATA_SIZE, &written, &bitrate, &endCut);
- if (config_gapless && endCut)
- ext->cutter.SetEndSize((int)(endCut - ext->decoder->GetDecoderDelay())*ext->frameSize);
- ext->initialData = written;
- if (/*ret != adts::SUCCESS && */!ext->initialData && (copied || ret == adts::ENDOFFILE))
- return copied;
- if (ret == adts::FAILURE)
- return -1;
- }
- }
- }
- return copied;
- }
- // return nonzero on success, zero on failure.
- __declspec(dllexport) int winampGetExtendedRead_setTime(intptr_t handle, int millisecs)
- {
- ExtendedRead *ext = (ExtendedRead *)handle;
- if (ext)
- {
- if (!ext->file.IsSeekable()) return 0; // not seekable
- int br = ext->file.GetAvgVBRBitrate();
- if (!br) br = (int)ext->decoder->GetCurrentBitrate();
- if (!br) return 0; // can't find a valid bitrate
- ext->cutter.Flush(millisecs); // fucko?
- ext->decoder->Flush(&ext->file);
- ext->file.Seek(millisecs,br);
- return 1;
- }
- return 0;
- }
- __declspec(dllexport) void winampGetExtendedRead_close(intptr_t handle)
- {
- ExtendedRead *ext = (ExtendedRead *)handle;
- if (ext) delete ext;
- }
- }
|