123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719 |
- /*
- ** Copyright (C) 2007-2011 Nullsoft, Inc.
- **
- ** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held
- ** liable for any damages arising from the use of this software.
- **
- ** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to
- ** alter it and redistribute it freely, subject to the following restrictions:
- **
- ** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
- ** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
- **
- ** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
- **
- ** 3. This notice may not be removed or altered from any source distribution.
- **
- ** Author: Ben Allison [email protected]
- ** Created: March 1, 2007
- **
- */
- #ifndef WIN32_LEAN_AND_MEAN
- #define WIN32_LEAN_AND_MEAN
- #endif
- #include "main.h"
- #include <windows.h>
- #include <math.h>
- #include <assert.h>
- #include <locale.h>
- #include <FLAC/all.h>
- #include "StreamFileWin32.h"
- #include "../Winamp/wa_ipc.h"
- #include "QuickBuf.h"
- #include "api__in_flv.h"
- #include "../nu/AudioOutput.h"
- #include "../Agave/Language/api_language.h"
- #include "FLACFileCallbacks.h"
- #include "nx/nxpath.h"
- int m_force_seek = -1; // el hacko grande
- const DWORD PAUSE_TIMEOUT = 100; // number of milliseconds to sleep for when paused
- static bool paused = false;
- // could probably move this inside client_data
- int realbits;
- int bps;
- int samplerate;
- int channels;
- volatile int currentSongLength=-1000;
- int averageBitrate;
- // if input plugins weren't single-instance only, we could put this in thread-local-storage
- static FLAC__StreamDecoder *decoder = 0;
- static uint64_t fileSize = 0;
- static FLAC__uint64 decodePosition = 0;
- double gain = 1.0;
- static double buffer_scale=1.0;
- class FLACWait
- {
- public:
- int WaitOrAbort(int time_in_ms)
- {
- if (WaitForSingleObject(killswitch, time_in_ms) == WAIT_OBJECT_0)
- return 1;
- return 0;
- }
- };
- volatile int bufferCount=0;
- static void Buffering(int bufStatus, const wchar_t *displayString)
- {
- if (bufStatus < 0 || bufStatus > 100)
- return;
- char tempdata[75*2] = {0, };
- int csa = plugin.SAGetMode();
- if (csa & 1)
- {
- for (int x = 0; x < bufStatus*75 / 100; x ++)
- tempdata[x] = x * 16 / 75;
- }
- else if (csa&2)
- {
- int offs = (csa & 1) ? 75 : 0;
- int x = 0;
- while (x < bufStatus*75 / 100)
- {
- tempdata[offs + x++] = -6 + x * 14 / 75;
- }
- while (x < 75)
- {
- tempdata[offs + x++] = 0;
- }
- }
- else if (csa == 4)
- {
- tempdata[0] = tempdata[1] = (bufStatus * 127 / 100);
- }
- if (csa) plugin.SAAdd(tempdata, ++bufferCount, (csa == 3) ? 0x80000003 : csa);
- /*
- TODO
- wchar_t temp[64] = {0};
- StringCchPrintf(temp, 64, L"%s: %d%%",displayString, bufStatus);
- SetStatus(temp);
- */
- //SetVideoStatusText(temp); // TODO: find a way to set the old status back
- // videoOutput->notifyBufferState(static_cast<int>(bufStatus*2.55f));
- }
- static nu::AudioOutput<FLACWait> audio_output(&plugin);
- #pragma region Truncaters
- inline static void clip(double &x, double a, double b)
- {
- double x1 = fabs(x - a);
- double x2 = fabs(x - b);
- x = x1 + (a + b);
- x -= x2;
- x *= 0.5;
- }
- static void InterleaveAndTruncate32(const FLAC__int32 *const buffer[], void *_output, int bps, int channels, int blocksize, double gain)
- {
- // TODO: this can be sped up significantly
- FLAC__int32 *output = (FLAC__int32 *)_output;
- for (int b = 0;b < blocksize;b++)
- {
- for (int c = 0;c < channels;c++)
- {
- double appliedGain = gain * (double)buffer[c][b];
- // TODO: add dither
- clip(appliedGain, -2147483648., 2147483647.);
- *output = (FLAC__int32)appliedGain;
- output++;
- }
- }
- }
- static void InterleaveAndTruncate24(const FLAC__int32 *const buffer[], void *_output, int bps, int channels, int blocksize, double gain)
- {
- char *output = (char *)_output;
- for (int b = 0;b < blocksize;b++)
- {
- for (int c = 0;c < channels;c++)
- {
- double appliedGain = gain * (double)buffer[c][b];
- // TODO: add dither
- clip(appliedGain, -8388608., 8388607.);
- FLAC__int32 sample = (FLAC__int32)appliedGain;
- // little endian specific code
- output[0] = (unsigned char)(sample);
- output[1] = (unsigned char)(sample >> 8);
- output[2] = (unsigned char)(sample >> 16);
- output += 3;
- }
- }
- }
- static void InterleaveAndTruncate16(const FLAC__int32 *const buffer[], void *_output, int bps, int channels, int blocksize, double gain)
- {
- short *output = (short *)_output;
- for (int b = 0;b < blocksize;b++)
- {
- for (int c = 0;c < channels;c++)
- {
- double appliedGain = gain * (double)buffer[c][b];
- // TODO: add dither
- clip(appliedGain, -32768., 32767.);
- FLAC__int32 sample = (FLAC__int32)appliedGain;
- *output = (short) sample ;
- output ++;
- }
- }
- }
- static void InterleaveAndTruncate8(const FLAC__int32 *const buffer[], void *_output, int bps, int channels, int blocksize, double gain)
- {
- unsigned char *output = (unsigned char *)_output;
- for (int b = 0;b < blocksize;b++)
- {
- for (int c = 0;c < channels;c++)
- {
- double appliedGain = gain * (double)buffer[c][b];
- // TODO: add dither
- clip(appliedGain, -128., 127.);
- FLAC__int32 sample = (FLAC__int32)appliedGain;
- *output = (unsigned char)(sample + 128);
- output++;
- }
- }
- }
- /* --- Versions without gain adjustment --- */
- static void InterleaveAndTruncate32(const FLAC__int32 *const buffer[], void *_output, int bps, int channels, int blocksize)
- {
- FLAC__int32 *output = (FLAC__int32 *)_output;
- for (int b = 0;b < blocksize;b++)
- {
- for (int c = 0;c < channels;c++)
- {
- *output = buffer[c][b];
- output++;
- }
- }
- }
- static void InterleaveAndTruncate24(const FLAC__int32 *const buffer[], void *_output, int bps, int channels, int blocksize)
- {
- char *output = (char *)_output;
- for (int b = 0;b < blocksize;b++)
- {
- for (int c = 0;c < channels;c++)
- {
- FLAC__int32 sample = buffer[c][b];
- // little endian specific code
- output[0] = (unsigned char)(sample);
- output[1] = (unsigned char)(sample >> 8);
- output[2] = (unsigned char)(sample >> 16);
- output += 3;
- }
- }
- }
- static void InterleaveAndTruncate16(const FLAC__int32 *const buffer[], void *_output, int bps, int channels, int blocksize)
- {
- short *output = (short *)_output;
- for (int b = 0;b < blocksize;b++)
- {
- for (int c = 0;c < channels;c++)
- {
- *output = (short) buffer[c][b];
- output ++;
- }
- }
- }
- static void InterleaveAndTruncate8(const FLAC__int32 *const buffer[], void *_output, int bps, int channels, int blocksize)
- {
- unsigned char *output = (unsigned char *)_output;
- for (int b = 0;b < blocksize;b++)
- {
- for (int c = 0;c < channels;c++)
- {
- *output = (unsigned char)(buffer[c][b] + 128);
- output++;
- }
- }
- }
- void InterleaveAndTruncate(const FLAC__int32 *const buffer[], void *_output, int bps, int channels, int blocksize, double gain)
- {
- if (gain == 1.0) // if it's EXACTLY 1.0, i.e. from a hardcoded value. not meant to be a "RG happens to be 1.0" check
- {
- switch(bps)
- {
- case 8:
- InterleaveAndTruncate8(buffer, _output, bps, channels, blocksize);
- break;
- case 16:
- InterleaveAndTruncate16(buffer, _output, bps, channels, blocksize);
- break;
- case 24:
- InterleaveAndTruncate24(buffer, _output, bps, channels, blocksize);
- break;
- case 32:
- InterleaveAndTruncate32(buffer, _output, bps, channels, blocksize);
- break;
- }
- }
- else // apply replay gain
- {
- switch(bps)
- {
- case 8:
- InterleaveAndTruncate8(buffer, _output, bps, channels, blocksize, gain);
- break;
- case 16:
- InterleaveAndTruncate16(buffer, _output, bps, channels, blocksize, gain);
- break;
- case 24:
- InterleaveAndTruncate24(buffer, _output, bps, channels, blocksize, gain);
- break;
- case 32:
- InterleaveAndTruncate32(buffer, _output, bps, channels, blocksize, gain);
- break;
- }
- }
- }
- #pragma endregion
- QuickBuf output;
- FLAC__uint64 lastoutputtime;
- static FLAC__StreamDecoderWriteStatus OnAudio(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data)
- {
- // TODO: if frame bps/samplerate/channels doesn't equal our own, we'll probably have to close & re-open the audio output
- // mux buffer into interleaved samples
- FLAC__uint64 newPosition;
- FLAC__stream_decoder_get_decode_position(decoder, &newPosition);
- FLAC__uint64 delta = newPosition - decodePosition;
- decodePosition = newPosition;
- if (!config_average_bitrate)
- plugin.SetInfo((int)(delta / (125.*frame->header.blocksize / samplerate)), -1, -1, -1);
- else if (fixBitrate)
- {
- fixBitrate = false;
- plugin.SetInfo(averageBitrate, -1, -1, -1);
- }
- if (frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER)
- lastoutputtime = 1000ULL*frame->header.number.sample_number / (FLAC__uint64)samplerate;
- else
- lastoutputtime = 0;
- int byteLength = (bps / 8) * channels * frame->header.blocksize;
- output.Reserve(byteLength*2);
- InterleaveAndTruncate(buffer, output, bps, channels, frame->header.blocksize, gain);
- audio_output.Write(output, byteLength);
- return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
- }
- static int GetBits(int incoming)
- {
- int bits = AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"bits", 16);
- if (bits > 16 && AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain", false))
- return bits;
- return min(bits, incoming);
- }
- static double GetGain(const FLAC__StreamMetadata *metadata)
- {
- if (AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain", false))
- {
- if (metadata)
- {
- int gainPos = -1, peakPos = -1;
- switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_source", 0))
- {
- case 0: // track
- gainPos = FLAC__metadata_object_vorbiscomment_find_entry_from(metadata, 0, "REPLAYGAIN_TRACK_GAIN");
- if (gainPos < 0 && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
- gainPos = FLAC__metadata_object_vorbiscomment_find_entry_from(metadata, 0, "REPLAYGAIN_ALBUM_GAIN");
- peakPos = FLAC__metadata_object_vorbiscomment_find_entry_from(metadata, 0, "REPLAYGAIN_TRACK_PEAK");
- if (peakPos < 0 && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
- peakPos = FLAC__metadata_object_vorbiscomment_find_entry_from(metadata, 0, "REPLAYGAIN_ALBUM_PEAK");
- break;
- case 1:
- gainPos = FLAC__metadata_object_vorbiscomment_find_entry_from(metadata, 0, "REPLAYGAIN_ALBUM_GAIN");
- if (gainPos < 0 && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
- gainPos = FLAC__metadata_object_vorbiscomment_find_entry_from(metadata, 0, "REPLAYGAIN_TRACK_GAIN");
- peakPos = FLAC__metadata_object_vorbiscomment_find_entry_from(metadata, 0, "REPLAYGAIN_ALBUM_PEAK");
- if (peakPos < 0 && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false))
- peakPos = FLAC__metadata_object_vorbiscomment_find_entry_from(metadata, 0, "REPLAYGAIN_TRACK_PEAK");
- break;
- }
- double dB = 0, peak = 1.0;
- _locale_t C_locale = WASABI_API_LNG->Get_C_NumericLocale();
- if (gainPos >= 0)
- {
- const char *entry = (const char *)metadata->data.vorbis_comment.comments[gainPos].entry;
- const char *value = strchr(entry, '='); // find the first equal
- if (value++)
- {
- if (value[0] == '+')
- dB = _atof_l(&value[1], C_locale);
- else
- dB = _atof_l(value, C_locale);
- }
- }
- else
- {
- dB = AGAVE_API_CONFIG->GetFloat(playbackConfigGroupGUID, L"non_replaygain", -6.0);
- return pow(10.0, dB / 20.0);
- }
- if (peakPos >= 0)
- {
- const char *entry = (const char *)metadata->data.vorbis_comment.comments[peakPos].entry;
- const char *value = strchr(entry, '='); // find the first equal
- if (value++)
- {
- peak = _atof_l(value, C_locale);
- }
- }
- switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_mode", 1))
- {
- case 0: // apply gain
- return pow(10.0, dB / 20.0);
- break;
- case 1: // apply gain, but don't clip
- return min(pow(10.0, dB / 20.0), 1.0 / peak);
- break;
- case 2: // normalize
- return 1.0 / peak;
- break;
- case 3: // prevent clipping
- if (peak > 1.0)
- return 1.0 / peak;
- else
- return 1.0;
- }
- }
- else
- {
- double dB = AGAVE_API_CONFIG->GetFloat(playbackConfigGroupGUID, L"non_replaygain", -6.0);
- return pow(10.0, dB / 20.0);
- }
- }
- return 1.0;
- }
- static void OnMetadata(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
- {
- switch (metadata->type)
- {
- case FLAC__METADATA_TYPE_STREAMINFO:
- {
- realbits = metadata->data.stream_info.bits_per_sample;
- channels = metadata->data.stream_info.channels;
- samplerate = metadata->data.stream_info.sample_rate;
- bps = GetBits(metadata->data.stream_info.bits_per_sample);
- gain = GetGain(0) * pow(2., (double)(bps - realbits));
- if (metadata->data.stream_info.total_samples)
- {
- currentSongLength = (int)((metadata->data.stream_info.total_samples*1000ULL)/(uint64_t)samplerate);
- averageBitrate = (int)(fileSize / (125 * metadata->data.stream_info.total_samples / (__int64)samplerate));
- }
- else
- {
- currentSongLength=-1000;
- averageBitrate=0;
- }
- }
- break;
- case FLAC__METADATA_TYPE_VORBIS_COMMENT:
- gain = GetGain(metadata) * pow(2., (double)(bps - realbits));
- break;
- }
- }
- static void OnError(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
- {
- //client_data = client_data; // dummy line so i can set a breakpoint
- }
- void CALLBACK APCPause(ULONG_PTR data)
- {
- paused = !!data;
- plugin.outMod->Pause(!!paused);
- }
- void CALLBACK APCSeek(ULONG_PTR data)
- {
- // TODO: break out of end-of-file handling if necessary
- buffer_scale=1.0;
- int time_in_ms = (int)data;
- lastoutputtime=time_in_ms; // cheating a bit here :)
- audio_output.Flush(time_in_ms);
- __int64 frames = Int32x32To64(time_in_ms, samplerate) / 1000;
- FLAC__StreamDecoderState state = FLAC__stream_decoder_get_state(decoder);
- plugin.outMod->Pause(0);
- FLAC__stream_decoder_seek_absolute(decoder, frames);
- plugin.outMod->Pause(paused);
- if (FLAC__stream_decoder_get_state(decoder) == FLAC__STREAM_DECODER_SEEK_ERROR)
- {
- FLAC__stream_decoder_flush(decoder);
- }
- }
- static const double prebuffer_seconds = 0.5;
- static bool DoBuffering(nx_file_t file)
- {
- // TODO: check for disconnect, etc.
- // anything less than half-a-second and we'll rebuffer. but when we rebuffer, we'll get 2 seconds worth of audio
- // also, every time we're forced to re-buffer, we'll double the buffer amount
- uint64_t required;
- if (averageBitrate > 0)
- {
- required = (uint64_t)((double)averageBitrate * 1000.0 * buffer_scale * prebuffer_seconds / 8.0);
- }
- else /* no bitrate specified, let's assume 1000kbps */
- {
- required = (uint64_t)(1000.0 * 1000.0 * buffer_scale * prebuffer_seconds / 8.0);
- }
- uint64_t available;
- if (NXFileProgressiveDownloaderAvailable(file, required, &available) == NErr_True)
- return true;
-
- Buffering(0, 0);
- bufferCount=lastoutputtime;
- required *= 4;
- buffer_scale *= 2.0;
- while (NXFileProgressiveDownloaderAvailable(file, required, &available) == NErr_False)
- {
- int percent = (int)((double)available * 100.0 / (double)required);
- if (percent <= 0)
- percent=1;
- if (percent > 99)
- percent=99;
-
- Buffering(percent, 0);
- if (WaitForSingleObject(killswitch, 10) == WAIT_OBJECT_0)
- {
- return false;
- }
- }
- Buffering(100, 0);
- bufferCount=0;
- return true;
- }
- extern HANDLE threadStarted;
- DWORD CALLBACK FLACThread(LPVOID param)
- {
- buffer_scale=1.0;
- bool streaming=false;
- nx_file_t file;
- FLACClientData state;
- audio_output.Init(plugin.outMod);
- paused=false;
- SetEvent(threadStarted);
- nx_uri_t filename = (nx_uri_t)param;
- decodePosition = 0;
- gain = 1.0;
- bufferCount = 0;
- decoder = FLAC__stream_decoder_new();
- if (decoder == 0)
- {
- if (WaitForSingleObject(killswitch, 200) != WAIT_OBJECT_0)
- PostMessage(plugin.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
- free(filename);
- return 0;
- }
- FLAC__stream_decoder_set_md5_checking(decoder, false);
- FLAC__stream_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT);
- plugin.is_seekable = 1;
- int ret;
-
- if (NXPathIsURL(filename) == NErr_True)
- {
- // Display a window to request to download
- //
- MessageBox(NULL, L"Cannot stream flac file, please download it", NULL, 0);
- /*
- char agent[256] = { 0 };
- snprintf(agent, 256, "User-Agent: %S/%S", "Winamp", "5.9.1");
- ret = NXFileOpenProgressiveDownloader(&file, filename, nx_file_FILE_read_binary, 0, agent); // TODO: calculate real user agent
- */
- return NErr_Disabled;
- }
- else
- {
- ret = NXFileOpenFile(&file, filename, nx_file_FILE_read_binary);
- }
- if (ret != NErr_Success)
- {
- FLAC__stream_decoder_delete(decoder);
- if (WaitForSingleObject(killswitch, 200) != WAIT_OBJECT_0)
- PostMessage(plugin.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
- NXURIRelease(filename);
- return 0;
- }
- state.SetFile(file);
- NXFileLength(file, &fileSize);
- if (FLAC__stream_decoder_init_stream(
- decoder,
- FLAC_NXFile_Read,
- FLAC_NXFile_Seek,
- FLAC_NXFile_Tell,
- FLAC_NXFile_Length,
- FLAC_NXFile_EOF,
- OnAudio,
- OnMetadata, // or NULL
- OnError,
- &state
- ) != FLAC__STREAM_DECODER_INIT_STATUS_OK)
- {
- FLAC__stream_decoder_delete(decoder);
- NXFileRelease(file);
- if (WaitForSingleObject(killswitch, 200) != WAIT_OBJECT_0)
- PostMessage(plugin.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
- NXURIRelease(filename);
- return 0;
- }
- FLAC__stream_decoder_process_until_end_of_metadata(decoder);
- if (!audio_output.Open(0, channels, samplerate, bps))
- {
- FLAC__stream_decoder_finish(decoder);
- FLAC__stream_decoder_delete(decoder);
- NXFileRelease(file);
- if (WaitForSingleObject(killswitch, 200) != WAIT_OBJECT_0)
- PostMessage(plugin.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
- NXURIRelease(filename);
- return 0;
- }
- plugin.SetInfo(averageBitrate, -1, -1, -1);
- plugin.outMod->SetVolume(volume);
- plugin.outMod->SetPan(pan);
- if (streaming && !DoBuffering(file))
- {
- FLAC__stream_decoder_finish(decoder);
- FLAC__stream_decoder_delete(decoder);
- NXFileRelease(file);
- NXURIRelease(filename);
-
- return 0;
- }
- if (m_force_seek != -1)
- APCSeek((ULONG_PTR)m_force_seek);
- m_force_seek = -1;
- HANDLE events[] = {killswitch};
- DWORD timeout = 0;
- while (true)
- {
- DWORD result = WaitForMultipleObjectsEx(sizeof(events) / sizeof(*events), events, FALSE, timeout, TRUE);
- switch (result)
- {
- case WAIT_OBJECT_0:// kill thread
- FLAC__stream_decoder_finish(decoder);
- FLAC__stream_decoder_delete(decoder);
- NXFileRelease(file);
- NXURIRelease(filename);
- return 0;
- case WAIT_TIMEOUT:
- timeout = paused ? PAUSE_TIMEOUT : 0;
- if (streaming && !DoBuffering(file))
- {
- FLAC__stream_decoder_finish(decoder);
- FLAC__stream_decoder_delete(decoder);
- NXFileRelease(file);
- NXURIRelease(filename);
-
- return 0;
- }
- if (!paused)
- {
- FLAC__bool decode_successful = FLAC__stream_decoder_process_single(decoder);
- FLAC__StreamDecoderState FLACstate = FLAC__stream_decoder_get_state(decoder);
- if (FLACstate == FLAC__STREAM_DECODER_END_OF_STREAM)
- {
- audio_output.Write(0, 0);
- if (audio_output.WaitWhilePlaying())
- {
- FLAC__stream_decoder_finish(decoder);
- FLAC__stream_decoder_delete(decoder);
- NXFileRelease(file);
- NXURIRelease(filename);
- return 0;
- }
- PostMessage(plugin.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
- timeout = INFINITE; // sit and wait for killswitch
- }
- else if (!decode_successful)
- {
- // some other error - abort playback
- // if we can find FLAC files with errors, we might be able to gracefully handle some errors
- PostMessage(plugin.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
- timeout = INFINITE; // sit and wait for killswitch
- }
- }
- break;
- }
- }
- return 0;
- }
|