aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Input/in_wave/AudioThread.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Plugins/Input/in_wave/AudioThread.cpp')
-rw-r--r--Src/Plugins/Input/in_wave/AudioThread.cpp330
1 files changed, 330 insertions, 0 deletions
diff --git a/Src/Plugins/Input/in_wave/AudioThread.cpp b/Src/Plugins/Input/in_wave/AudioThread.cpp
new file mode 100644
index 00000000..b6e8676c
--- /dev/null
+++ b/Src/Plugins/Input/in_wave/AudioThread.cpp
@@ -0,0 +1,330 @@
+#include <windows.h>
+#include "main.h"
+#include "../Winamp/wa_ipc.h"
+#include "config.h"
+#include "api__in_wave.h"
+#include <shlwapi.h>
+#include "VirtualIO.h"
+
+// {B6CB4A7C-A8D0-4c55-8E60-9F7A7A23DA0F}
+static const GUID playbackConfigGroupGUID =
+ { 0xb6cb4a7c, 0xa8d0, 0x4c55, { 0x8e, 0x60, 0x9f, 0x7a, 0x7a, 0x23, 0xda, 0xf } };
+
+HANDLE audioThread = INVALID_HANDLE_VALUE;
+DWORD WINAPI ThreadProcedure( void *data );
+
+#define kill events[0]
+#define running events[1]
+
+HANDLE stopped = 0;
+HANDLE events[ 2 ] = { 0 };
+
+static size_t bufferSize = 0;
+static char *audioBuffer = 0;
+static int frameSize = 0; // in bytes
+static int endOfFile = 0;
+static int bits = 0;
+static SF_INFO info;
+static void *reader = 0;
+
+
+int CalcBits()
+{
+ switch (info.format & SF_FORMAT_SUBMASK)
+ {
+ case SF_FORMAT_DOUBLE:
+ return 64;
+
+ case SF_FORMAT_PCM_32:
+ case SF_FORMAT_FLOAT:
+ return 32;
+
+ case SF_FORMAT_DWVW_24:
+ case SF_FORMAT_PCM_24:
+ return 24;
+
+ case SF_FORMAT_DPCM_16:
+ case SF_FORMAT_DWVW_16:
+ case SF_FORMAT_PCM_16:
+ return 16;
+
+ //case SF_FORMAT_PCM_S8: // cut, because 8bits is assumed unsigned
+ case SF_FORMAT_PCM_U8:
+ case SF_FORMAT_DPCM_8:
+ return 8;
+
+ default: return 16;
+ }
+}
+
+int CalcBitRate( const SF_INFO *info )
+{
+ switch ( info->format & SF_FORMAT_SUBMASK )
+ {
+ case SF_FORMAT_PCM_S8:
+ case SF_FORMAT_PCM_U8:
+ case SF_FORMAT_DPCM_8:
+ return MulDiv( 8 * info->channels, info->samplerate, 1000 );
+ case SF_FORMAT_DWVW_12:
+ return MulDiv( 12 * info->channels, info->samplerate, 1000 );
+ case SF_FORMAT_DPCM_16:
+ case SF_FORMAT_DWVW_16:
+ case SF_FORMAT_PCM_16:
+ return MulDiv( 16 * info->channels, info->samplerate, 1000 );
+ case SF_FORMAT_DWVW_24:
+ case SF_FORMAT_PCM_24:
+ return MulDiv( 24 * info->channels, info->samplerate, 1000 );
+ case SF_FORMAT_PCM_32:
+ case SF_FORMAT_FLOAT:
+ return MulDiv( 32 * info->channels, info->samplerate, 1000 );
+ case SF_FORMAT_DOUBLE:
+ return MulDiv( 64 * info->channels, info->samplerate, 1000 );
+
+ case SF_FORMAT_G721_32:
+ return 32;
+
+ case SF_FORMAT_G723_24:
+ return 24;
+
+ case SF_FORMAT_G723_40:
+ return 40;
+ case SF_FORMAT_MS_ADPCM:
+ case SF_FORMAT_VOX_ADPCM:
+ case SF_FORMAT_IMA_ADPCM:
+ return MulDiv( 4 * info->channels, info->samplerate, 1000 );
+ default:
+ return MulDiv( 16 * info->channels, info->samplerate, 1000 );
+ }
+}
+
+
+void CALLBACK APCSeek( ULONG_PTR p_data )
+{
+ endOfFile = 0;
+
+ int time_in_ms = (int)p_data;
+ int frames = MulDiv( time_in_ms, info.samplerate, 1000 ); // TODO: verify calculation
+
+ sf_seek( sndFile, frames, SEEK_SET );
+
+ plugin.outMod->Flush( time_in_ms );
+}
+
+void CALLBACK APCPause( ULONG_PTR p_data )
+{
+ int pause = (int)p_data;
+ if ( pause )
+ ResetEvent( running );
+ else
+ SetEvent( running );
+
+ plugin.outMod->Pause( !!pause );
+}
+
+void CALLBACK APCStart( ULONG_PTR p_data )
+{
+ endOfFile = 0;
+ const wchar_t *file = (const wchar_t *)p_data;
+
+ info.format = 0;
+ if ( PathIsURLW( file ) )
+ {
+ reader = CreateReader( file );
+ if ( reader )
+ sndFile = sf_open_virtual( &httpIO, SFM_READ, &info, reader );
+ }
+ else // It's a local file
+ {
+ sndFile = sf_wchar_open( file, SFM_READ, &info );
+ }
+
+ if ( !sndFile )
+ {
+ if ( WaitForSingleObject( kill, 200 ) == WAIT_TIMEOUT )
+ PostMessage( plugin.hMainWindow, WM_WA_MPEG_EOF, 0, 0 );
+
+ return;
+ }
+
+ currentSongLength = MulDiv( (int)info.frames, 1000, info.samplerate ); // TODO: is this correct?
+ switch ( info.format & SF_FORMAT_SUBMASK )
+ {
+ case SF_FORMAT_FLOAT:
+ case SF_FORMAT_DOUBLE:
+ sf_command( sndFile, SFC_SET_SCALE_FLOAT_INT_READ, NULL, SF_TRUE );
+ break;
+ }
+
+ bits = CalcBits();
+
+ size_t config_bits = AGAVE_API_CONFIG->GetUnsigned( playbackConfigGroupGUID, L"bits", 16 );
+ if ( config_bits == 16 && config_bits < (size_t)bits )
+ bits = (int)config_bits;
+
+ if ( bits < 16 && config_upsample8bit )
+ bits = 16;
+
+ int latency = plugin.outMod->Open( info.samplerate, info.channels, bits, -1, -1 );
+ if ( latency < 0 )
+ {
+ sf_close( sndFile );
+ if ( reader )
+ DestroyReader( reader );
+
+ reader = 0;
+ sndFile = NULL;
+
+ if ( WaitForSingleObject( kill, 200 ) == WAIT_TIMEOUT )
+ PostMessage( plugin.hMainWindow, WM_WA_MPEG_EOF, 0, 0 );
+
+ return;
+ }
+
+ frameSize = ( bits / 8 ) * info.channels;
+
+ plugin.SAVSAInit( latency, info.samplerate );
+ plugin.VSASetInfo( info.samplerate, info.channels );
+
+ int bitrate = CalcBitRate( &info );
+
+ plugin.SetInfo( bitrate, info.samplerate / 1000, info.channels, 1 );
+ plugin.is_seekable = info.seekable;
+
+ plugin.outMod->SetVolume( volume );
+ plugin.outMod->SetPan( pan );
+
+ size_t requiredBufferSize = 576 * frameSize * 2; // * 2 for dsp bullshit
+ if ( requiredBufferSize > bufferSize )
+ {
+ free( audioBuffer );
+
+ audioBuffer = (char *)calloc( requiredBufferSize, sizeof( char ) );
+ bufferSize = requiredBufferSize;
+ }
+
+ SetEvent( running );
+}
+
+void CALLBACK APCStop( ULONG_PTR p_data )
+{
+ if ( sndFile )
+ {
+ sf_close( sndFile );
+ if ( reader )
+ DestroyReader( reader );
+
+ reader = 0;
+ sndFile = NULL;
+
+ }
+
+ ResetEvent( running );
+ SetEvent( stopped );
+}
+
+
+void Kill()
+{
+ SetEvent( kill );
+}
+
+void AudioThreadInit()
+{
+ DWORD id;
+
+ kill = CreateEvent( NULL, TRUE, FALSE, NULL );
+ running = CreateEvent( NULL, TRUE, FALSE, NULL );
+ stopped = CreateEvent( NULL, FALSE, FALSE, NULL );
+ audioThread = CreateThread( NULL, 0, ThreadProcedure, 0, 0, &id );
+
+ if ( audioThread )
+ SetThreadPriority( audioThread, (int)AGAVE_API_CONFIG->GetInt( playbackConfigGroupGUID, L"priority", THREAD_PRIORITY_HIGHEST ) );
+}
+
+void AudioThreadQuit()
+{
+ free( audioBuffer );
+ audioBuffer = 0;
+
+ bufferSize = 0;
+
+ CloseHandle( running );
+ running = 0;
+
+ CloseHandle( kill );
+ kill = 0;
+
+ CloseHandle( stopped );
+ stopped = 0;
+
+ CloseHandle( audioThread );
+ audioThread = 0;
+}
+
+
+DWORD WINAPI ThreadProcedure( void *data )
+{
+ DWORD result;
+ sf_count_t framesRead;
+ while ( true )
+ {
+ result = WaitForMultipleObjectsEx( 2, events, FALSE, INFINITE, TRUE );
+ if ( result == WAIT_OBJECT_0 ) // kill thread
+ return 0;
+
+ if ( result == ( WAIT_OBJECT_0 + 1 ) )
+ {
+ if ( endOfFile ) // if we hit the end of file previously ...
+ {
+ if ( plugin.outMod->IsPlaying() ) // see if we're still going
+ SleepEx( 10, TRUE ); // sleep for a bit
+ else // yay done playing
+ {
+ PostMessage( plugin.hMainWindow, WM_WA_MPEG_EOF, 0, 0 ); // tell winamp we're stopped
+ // don't shut down completely yet (mpegeof will trigger a call to stop)
+ ResetEvent( running ); // but we can at least sit in waitformultipleobjects ...
+ }
+ }
+ else if ( plugin.outMod->CanWrite() > ( 576 * frameSize ) )
+ {
+ switch ( bits )
+ {
+ case 16:
+ framesRead = sf_readf_short( sndFile, (short *)audioBuffer, 576 );
+ break;
+ case 32:
+ framesRead = sf_readf_int( sndFile, (int *)audioBuffer, 576 );
+ break;
+ default:
+ framesRead = sf_read_raw( sndFile, (int *)audioBuffer, 576 * frameSize ) / frameSize;
+ break;
+ }
+
+
+ if ( framesRead == 0 )
+ {
+ endOfFile = 1;
+
+ plugin.outMod->Write( NULL, 0 );
+ }
+ else
+ {
+ framesRead = plugin.dsp_dosamples( (short *)audioBuffer, (int)framesRead, bits, info.channels, info.samplerate );
+ if ( framesRead >= 576 )
+ {
+ int timestamp = plugin.outMod->GetWrittenTime();
+
+ plugin.SAAddPCMData( (char *)audioBuffer, info.channels, bits, timestamp );
+ plugin.VSAAddPCMData( (char *)audioBuffer, info.channels, bits, timestamp );
+ }
+
+ plugin.outMod->Write( audioBuffer, (int)framesRead * frameSize );
+ }
+ }
+ else
+ {
+ SleepEx( 10, TRUE );
+ }
+ }
+ }
+}