123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- #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 );
- }
- }
- }
- }
|