diff options
Diffstat (limited to 'Src/Plugins/Input/in_mp3/ExtendedRead.cpp')
-rw-r--r-- | Src/Plugins/Input/in_mp3/ExtendedRead.cpp | 323 |
1 files changed, 323 insertions, 0 deletions
diff --git a/Src/Plugins/Input/in_mp3/ExtendedRead.cpp b/Src/Plugins/Input/in_mp3/ExtendedRead.cpp new file mode 100644 index 00000000..7196aad9 --- /dev/null +++ b/Src/Plugins/Input/in_mp3/ExtendedRead.cpp @@ -0,0 +1,323 @@ +#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; + } +}
\ No newline at end of file |