Initial community commit
This commit is contained in:
parent
537bcbc862
commit
fc06254474
16440 changed files with 4239995 additions and 2 deletions
330
Src/Plugins/Input/in_wave/AudioThread.cpp
Normal file
330
Src/Plugins/Input/in_wave/AudioThread.cpp
Normal file
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue