Added device change notifier to XAudio2 outputs
This commit is contained in:
parent
4d174be28a
commit
d2e00a0085
|
@ -12,11 +12,130 @@
|
|||
// XAudio2
|
||||
#include <xaudio2.h>
|
||||
|
||||
// MMDevice API
|
||||
#include <mmdeviceapi.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
// Internals
|
||||
#include "../System.h" // for systemMessage()
|
||||
#include "../gba/Globals.h"
|
||||
|
||||
|
||||
class XAudio2_Output;
|
||||
|
||||
static void xaudio2_device_changed( XAudio2_Output * );
|
||||
|
||||
class XAudio2_Device_Notifier : public IMMNotificationClient
|
||||
{
|
||||
volatile LONG registered;
|
||||
IMMDeviceEnumerator *pEnumerator;
|
||||
|
||||
std::wstring last_device;
|
||||
|
||||
CRITICAL_SECTION lock;
|
||||
std::vector<XAudio2_Output*> instances;
|
||||
|
||||
public:
|
||||
XAudio2_Device_Notifier() : registered( 0 )
|
||||
{
|
||||
InitializeCriticalSection( &lock );
|
||||
}
|
||||
~XAudio2_Device_Notifier()
|
||||
{
|
||||
DeleteCriticalSection( &lock );
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE AddRef()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE Release()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, VOID **ppvInterface )
|
||||
{
|
||||
if (IID_IUnknown == riid)
|
||||
{
|
||||
*ppvInterface = (IUnknown*)this;
|
||||
}
|
||||
else if (__uuidof(IMMNotificationClient) == riid)
|
||||
{
|
||||
*ppvInterface = (IMMNotificationClient*)this;
|
||||
}
|
||||
else
|
||||
{
|
||||
*ppvInterface = NULL;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged( EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId )
|
||||
{
|
||||
if ( flow == eRender && last_device.compare( pwstrDeviceId ) != 0 )
|
||||
{
|
||||
last_device = pwstrDeviceId;
|
||||
|
||||
EnterCriticalSection( &lock );
|
||||
for ( auto it = instances.begin(); it < instances.end(); ++it )
|
||||
{
|
||||
xaudio2_device_changed( *it );
|
||||
}
|
||||
LeaveCriticalSection( &lock );
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceAdded( LPCWSTR pwstrDeviceId ) { return S_OK; }
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceRemoved( LPCWSTR pwstrDeviceId ) { return S_OK; }
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged( LPCWSTR pwstrDeviceId, DWORD dwNewState ) { return S_OK; }
|
||||
HRESULT STDMETHODCALLTYPE OnPropertyValueChanged( LPCWSTR pwstrDeviceId, const PROPERTYKEY key ) { return S_OK; }
|
||||
|
||||
void do_register(XAudio2_Output * p_instance)
|
||||
{
|
||||
if ( InterlockedIncrement( ®istered ) == 1 )
|
||||
{
|
||||
pEnumerator = NULL;
|
||||
HRESULT hr = CoCreateInstance( __uuidof( MMDeviceEnumerator ), NULL, CLSCTX_INPROC_SERVER, __uuidof( IMMDeviceEnumerator ), ( void** ) &pEnumerator );
|
||||
if ( SUCCEEDED( hr ) )
|
||||
{
|
||||
pEnumerator->RegisterEndpointNotificationCallback( this );
|
||||
registered = true;
|
||||
}
|
||||
}
|
||||
|
||||
EnterCriticalSection( &lock );
|
||||
instances.push_back( p_instance );
|
||||
LeaveCriticalSection( &lock );
|
||||
}
|
||||
|
||||
void do_unregister( XAudio2_Output * p_instance )
|
||||
{
|
||||
if ( InterlockedDecrement( ®istered ) == 0 )
|
||||
{
|
||||
pEnumerator->UnregisterEndpointNotificationCallback( this );
|
||||
pEnumerator->Release();
|
||||
registered = false;
|
||||
}
|
||||
|
||||
EnterCriticalSection( &lock );
|
||||
for ( auto it = instances.begin(); it < instances.end(); ++it )
|
||||
{
|
||||
if ( *it == p_instance )
|
||||
{
|
||||
instances.erase( it );
|
||||
break;
|
||||
}
|
||||
}
|
||||
LeaveCriticalSection( &lock );
|
||||
}
|
||||
} g_notifier;
|
||||
|
||||
// Synchronization Event
|
||||
class XAudio2_BufferNotify : public IXAudio2VoiceCallback
|
||||
{
|
||||
|
@ -68,6 +187,8 @@ public:
|
|||
void pause();
|
||||
void resume();
|
||||
void reset();
|
||||
void close();
|
||||
void device_change();
|
||||
|
||||
// Configuration Changes
|
||||
void setThrottle( unsigned short throttle );
|
||||
|
@ -82,6 +203,8 @@ private:
|
|||
int currentBuffer;
|
||||
int soundBufferLen;
|
||||
|
||||
volatile bool device_changed;
|
||||
|
||||
IXAudio2 *xaud;
|
||||
IXAudio2MasteringVoice *mVoice; // listener
|
||||
IXAudio2SourceVoice *sVoice; // sound source
|
||||
|
@ -101,16 +224,25 @@ XAudio2_Output::XAudio2_Output()
|
|||
bufferCount = theApp.xa2BufferCount;
|
||||
buffers = NULL;
|
||||
currentBuffer = 0;
|
||||
device_changed = false;
|
||||
|
||||
xaud = NULL;
|
||||
mVoice = NULL;
|
||||
sVoice = NULL;
|
||||
ZeroMemory( &buf, sizeof( buf ) );
|
||||
ZeroMemory( &vState, sizeof( vState ) );
|
||||
|
||||
g_notifier.do_register( this );
|
||||
}
|
||||
|
||||
|
||||
XAudio2_Output::~XAudio2_Output()
|
||||
{
|
||||
g_notifier.do_unregister( this );
|
||||
close();
|
||||
}
|
||||
|
||||
void XAudio2_Output::close()
|
||||
{
|
||||
initialized = false;
|
||||
|
||||
|
@ -120,6 +252,7 @@ XAudio2_Output::~XAudio2_Output()
|
|||
ASSERT( hr == S_OK );
|
||||
}
|
||||
sVoice->DestroyVoice();
|
||||
sVoice = NULL;
|
||||
}
|
||||
|
||||
if( buffers ) {
|
||||
|
@ -129,6 +262,7 @@ XAudio2_Output::~XAudio2_Output()
|
|||
|
||||
if( mVoice ) {
|
||||
mVoice->DestroyVoice();
|
||||
mVoice = NULL;
|
||||
}
|
||||
|
||||
if( xaud ) {
|
||||
|
@ -137,6 +271,11 @@ XAudio2_Output::~XAudio2_Output()
|
|||
}
|
||||
}
|
||||
|
||||
void XAudio2_Output::device_change()
|
||||
{
|
||||
device_changed = true;
|
||||
}
|
||||
|
||||
|
||||
bool XAudio2_Output::init(long sampleRate)
|
||||
{
|
||||
|
@ -276,6 +415,8 @@ bool XAudio2_Output::init(long sampleRate)
|
|||
ASSERT( hr == S_OK );
|
||||
playing = true;
|
||||
|
||||
currentBuffer = 0;
|
||||
device_changed = false;
|
||||
|
||||
initialized = true;
|
||||
return true;
|
||||
|
@ -287,6 +428,11 @@ void XAudio2_Output::write(u16 * finalWave, int length)
|
|||
if( !initialized || failed ) return;
|
||||
|
||||
while( true ) {
|
||||
if ( device_changed ) {
|
||||
close();
|
||||
if (!init(freq)) return;
|
||||
}
|
||||
|
||||
sVoice->GetState( &vState );
|
||||
|
||||
ASSERT( vState.BuffersQueued <= bufferCount );
|
||||
|
@ -305,7 +451,9 @@ void XAudio2_Output::write(u16 * finalWave, int length)
|
|||
// the maximum number of buffers is currently queued
|
||||
if( synchronize && !speedup && !theApp.throttle ) {
|
||||
// wait for one buffer to finish playing
|
||||
WaitForSingleObject( notify.hBufferEndEvent, INFINITE );
|
||||
if (WaitForSingleObject( notify.hBufferEndEvent, 10000 ) == WAIT_TIMEOUT) {
|
||||
device_changed = true;
|
||||
}
|
||||
} else {
|
||||
// drop current audio frame
|
||||
return;
|
||||
|
@ -375,6 +523,11 @@ void XAudio2_Output::setThrottle( unsigned short throttle )
|
|||
ASSERT( hr == S_OK );
|
||||
}
|
||||
|
||||
void xaudio2_device_changed( XAudio2_Output * instance )
|
||||
{
|
||||
instance->device_change();
|
||||
}
|
||||
|
||||
SoundDriver *newXAudio2_Output()
|
||||
{
|
||||
return new XAudio2_Output();
|
||||
|
|
|
@ -10,11 +10,130 @@
|
|||
// XAudio2
|
||||
#include <XAudio2.h>
|
||||
|
||||
// MMDevice API
|
||||
#include <mmdeviceapi.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
// Internals
|
||||
#include "../System.h" // for systemMessage()
|
||||
#include "../gba/Globals.h"
|
||||
|
||||
|
||||
class XAudio2_Output;
|
||||
|
||||
static void xaudio2_device_changed( XAudio2_Output * );
|
||||
|
||||
class XAudio2_Device_Notifier : public IMMNotificationClient
|
||||
{
|
||||
volatile LONG registered;
|
||||
IMMDeviceEnumerator *pEnumerator;
|
||||
|
||||
std::wstring last_device;
|
||||
|
||||
CRITICAL_SECTION lock;
|
||||
std::vector<XAudio2_Output*> instances;
|
||||
|
||||
public:
|
||||
XAudio2_Device_Notifier() : registered( 0 )
|
||||
{
|
||||
InitializeCriticalSection( &lock );
|
||||
}
|
||||
~XAudio2_Device_Notifier()
|
||||
{
|
||||
DeleteCriticalSection( &lock );
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE AddRef()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE Release()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, VOID **ppvInterface )
|
||||
{
|
||||
if (IID_IUnknown == riid)
|
||||
{
|
||||
*ppvInterface = (IUnknown*)this;
|
||||
}
|
||||
else if (__uuidof(IMMNotificationClient) == riid)
|
||||
{
|
||||
*ppvInterface = (IMMNotificationClient*)this;
|
||||
}
|
||||
else
|
||||
{
|
||||
*ppvInterface = NULL;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged( EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId )
|
||||
{
|
||||
if ( flow == eRender && last_device.compare( pwstrDeviceId ) != 0 )
|
||||
{
|
||||
last_device = pwstrDeviceId;
|
||||
|
||||
EnterCriticalSection( &lock );
|
||||
for ( auto it = instances.begin(); it < instances.end(); ++it )
|
||||
{
|
||||
xaudio2_device_changed( *it );
|
||||
}
|
||||
LeaveCriticalSection( &lock );
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceAdded( LPCWSTR pwstrDeviceId ) { return S_OK; }
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceRemoved( LPCWSTR pwstrDeviceId ) { return S_OK; }
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged( LPCWSTR pwstrDeviceId, DWORD dwNewState ) { return S_OK; }
|
||||
HRESULT STDMETHODCALLTYPE OnPropertyValueChanged( LPCWSTR pwstrDeviceId, const PROPERTYKEY key ) { return S_OK; }
|
||||
|
||||
void do_register(sound_out_i_xaudio2 * p_instance)
|
||||
{
|
||||
if ( InterlockedIncrement( ®istered ) == 1 )
|
||||
{
|
||||
pEnumerator = NULL;
|
||||
HRESULT hr = CoCreateInstance( __uuidof( MMDeviceEnumerator ), NULL, CLSCTX_INPROC_SERVER, __uuidof( IMMDeviceEnumerator ), ( void** ) &pEnumerator );
|
||||
if ( SUCCEEDED( hr ) )
|
||||
{
|
||||
pEnumerator->RegisterEndpointNotificationCallback( this );
|
||||
registered = true;
|
||||
}
|
||||
}
|
||||
|
||||
EnterCriticalSection( &lock );
|
||||
instances.push_back( p_instance );
|
||||
LeaveCriticalSection( &lock );
|
||||
}
|
||||
|
||||
void do_unregister( sound_out_i_xaudio2 * p_instance )
|
||||
{
|
||||
if ( InterlockedDecrement( ®istered ) == 0 )
|
||||
{
|
||||
pEnumerator->UnregisterEndpointNotificationCallback( this );
|
||||
pEnumerator->Release();
|
||||
registered = false;
|
||||
}
|
||||
|
||||
EnterCriticalSection( &lock );
|
||||
for ( auto it = instances.begin(); it < instances.end(); ++it )
|
||||
{
|
||||
if ( *it == p_instance )
|
||||
{
|
||||
instances.erase( it );
|
||||
break;
|
||||
}
|
||||
}
|
||||
LeaveCriticalSection( &lock );
|
||||
}
|
||||
} g_notifier;
|
||||
|
||||
// Synchronization Event
|
||||
class XAudio2_BufferNotify : public IXAudio2VoiceCallback
|
||||
{
|
||||
|
@ -66,6 +185,8 @@ public:
|
|||
void pause();
|
||||
void resume();
|
||||
void reset();
|
||||
void close();
|
||||
void device_change();
|
||||
|
||||
// Configuration Changes
|
||||
void setThrottle( unsigned short throttle );
|
||||
|
@ -80,6 +201,8 @@ private:
|
|||
int currentBuffer;
|
||||
int soundBufferLen;
|
||||
|
||||
volatile bool device_changed;
|
||||
|
||||
IXAudio2 *xaud;
|
||||
IXAudio2MasteringVoice *mVoice; // listener
|
||||
IXAudio2SourceVoice *sVoice; // sound source
|
||||
|
@ -99,16 +222,25 @@ XAudio2_Output::XAudio2_Output()
|
|||
bufferCount = gopts.audio_buffers;
|
||||
buffers = NULL;
|
||||
currentBuffer = 0;
|
||||
device_changed = false;
|
||||
|
||||
xaud = NULL;
|
||||
mVoice = NULL;
|
||||
sVoice = NULL;
|
||||
ZeroMemory( &buf, sizeof( buf ) );
|
||||
ZeroMemory( &vState, sizeof( vState ) );
|
||||
|
||||
g_notifier.do_register( this );
|
||||
}
|
||||
|
||||
|
||||
XAudio2_Output::~XAudio2_Output()
|
||||
{
|
||||
g_notifier.do_unregister( this );
|
||||
close();
|
||||
}
|
||||
|
||||
void XAudio2_Output::close()
|
||||
{
|
||||
initialized = false;
|
||||
|
||||
|
@ -118,6 +250,7 @@ XAudio2_Output::~XAudio2_Output()
|
|||
assert( hr == S_OK );
|
||||
}
|
||||
sVoice->DestroyVoice();
|
||||
sVoice = NULL;
|
||||
}
|
||||
|
||||
if( buffers ) {
|
||||
|
@ -127,6 +260,7 @@ XAudio2_Output::~XAudio2_Output()
|
|||
|
||||
if( mVoice ) {
|
||||
mVoice->DestroyVoice();
|
||||
mVoice = NULL;
|
||||
}
|
||||
|
||||
if( xaud ) {
|
||||
|
@ -135,6 +269,11 @@ XAudio2_Output::~XAudio2_Output()
|
|||
}
|
||||
}
|
||||
|
||||
void XAudio2_Output::device_change()
|
||||
{
|
||||
device_changed = true;
|
||||
}
|
||||
|
||||
|
||||
static int XA2GetDev(IXAudio2 *);
|
||||
|
||||
|
@ -277,6 +416,8 @@ bool XAudio2_Output::init(long sampleRate)
|
|||
assert( hr == S_OK );
|
||||
playing = true;
|
||||
|
||||
currentBuffer = 0;
|
||||
device_changed = false;
|
||||
|
||||
initialized = true;
|
||||
return true;
|
||||
|
@ -288,6 +429,11 @@ void XAudio2_Output::write(u16 * finalWave, int length)
|
|||
if( !initialized || failed ) return;
|
||||
|
||||
while( true ) {
|
||||
if ( device_changed ) {
|
||||
close();
|
||||
if (!init(freq)) return;
|
||||
}
|
||||
|
||||
sVoice->GetState( &vState );
|
||||
|
||||
assert( vState.BuffersQueued <= bufferCount );
|
||||
|
@ -306,7 +452,9 @@ void XAudio2_Output::write(u16 * finalWave, int length)
|
|||
// the maximum number of buffers is currently queued
|
||||
if( synchronize && !speedup && !gopts.throttle ) {
|
||||
// wait for one buffer to finish playing
|
||||
WaitForSingleObject( notify.hBufferEndEvent, INFINITE );
|
||||
if (WaitForSingleObject( notify.hBufferEndEvent, 10000 ) == WAIT_TIMEOUT) {
|
||||
device_changed = true;
|
||||
}
|
||||
} else {
|
||||
// drop current audio frame
|
||||
return;
|
||||
|
@ -376,6 +524,11 @@ void XAudio2_Output::setThrottle( unsigned short throttle )
|
|||
assert( hr == S_OK );
|
||||
}
|
||||
|
||||
void xaudio2_device_changed( XAudio2_Output * instance )
|
||||
{
|
||||
instance->device_change();
|
||||
}
|
||||
|
||||
SoundDriver *newXAudio2_Output()
|
||||
{
|
||||
return new XAudio2_Output();
|
||||
|
|
Loading…
Reference in New Issue