Initial community commit
This commit is contained in:
parent
537bcbc862
commit
fc06254474
16440 changed files with 4239995 additions and 2 deletions
719
Src/Plugins/Input/in_flac/DecodeThread.cpp
Normal file
719
Src/Plugins/Input/in_flac/DecodeThread.cpp
Normal file
|
@ -0,0 +1,719 @@
|
|||
/*
|
||||
** 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 benski@winamp.com
|
||||
** 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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue