1 #ifndef NO_XAUDIO2
2
3 // MFC
4 #include "stdafx.h"
5
6 // Application
7 #include "VBA.h"
8
9 // Interface
10 #include "../common/SoundDriver.h"
11
12 // XAudio2
13 #include <xaudio2.h>
14
15 // MMDevice API
16 #include <mmdeviceapi.h>
17 #include <vector>
18 #include <string>
19
20 // Internals
21 #include "../System.h" // for systemMessage()
22 #include "../gba/Globals.h"
23
24
25 class XAudio2_Output;
26
27 static void xaudio2_device_changed( XAudio2_Output * );
28
29 class XAudio2_Device_Notifier : public IMMNotificationClient
30 {
31 volatile LONG registered;
32 IMMDeviceEnumerator *pEnumerator;
33
34 std::wstring last_device;
35
36 CRITICAL_SECTION lock;
37 std::vector<XAudio2_Output*> instances;
38
39 public:
XAudio2_Device_Notifier()40 XAudio2_Device_Notifier() : registered( 0 )
41 {
42 InitializeCriticalSection( &lock );
43 }
~XAudio2_Device_Notifier()44 ~XAudio2_Device_Notifier()
45 {
46 DeleteCriticalSection( &lock );
47 }
48
AddRef()49 ULONG STDMETHODCALLTYPE AddRef()
50 {
51 return 1;
52 }
53
Release()54 ULONG STDMETHODCALLTYPE Release()
55 {
56 return 1;
57 }
58
QueryInterface(REFIID riid,VOID ** ppvInterface)59 HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, VOID **ppvInterface )
60 {
61 if (IID_IUnknown == riid)
62 {
63 *ppvInterface = (IUnknown*)this;
64 }
65 else if (__uuidof(IMMNotificationClient) == riid)
66 {
67 *ppvInterface = (IMMNotificationClient*)this;
68 }
69 else
70 {
71 *ppvInterface = NULL;
72 return E_NOINTERFACE;
73 }
74 return S_OK;
75 }
76
OnDefaultDeviceChanged(EDataFlow flow,ERole role,LPCWSTR pwstrDeviceId)77 HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged( EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId )
78 {
79 if ( flow == eRender && last_device.compare( pwstrDeviceId ) != 0 )
80 {
81 last_device = pwstrDeviceId;
82
83 EnterCriticalSection( &lock );
84 for ( auto it = instances.begin(); it < instances.end(); ++it )
85 {
86 xaudio2_device_changed( *it );
87 }
88 LeaveCriticalSection( &lock );
89 }
90
91 return S_OK;
92 }
93
OnDeviceAdded(LPCWSTR pwstrDeviceId)94 HRESULT STDMETHODCALLTYPE OnDeviceAdded( LPCWSTR pwstrDeviceId ) { return S_OK; }
OnDeviceRemoved(LPCWSTR pwstrDeviceId)95 HRESULT STDMETHODCALLTYPE OnDeviceRemoved( LPCWSTR pwstrDeviceId ) { return S_OK; }
OnDeviceStateChanged(LPCWSTR pwstrDeviceId,DWORD dwNewState)96 HRESULT STDMETHODCALLTYPE OnDeviceStateChanged( LPCWSTR pwstrDeviceId, DWORD dwNewState ) { return S_OK; }
OnPropertyValueChanged(LPCWSTR pwstrDeviceId,const PROPERTYKEY key)97 HRESULT STDMETHODCALLTYPE OnPropertyValueChanged( LPCWSTR pwstrDeviceId, const PROPERTYKEY key ) { return S_OK; }
98
do_register(XAudio2_Output * p_instance)99 void do_register(XAudio2_Output * p_instance)
100 {
101 if ( InterlockedIncrement( ®istered ) == 1 )
102 {
103 pEnumerator = NULL;
104 HRESULT hr = CoCreateInstance( __uuidof( MMDeviceEnumerator ), NULL, CLSCTX_INPROC_SERVER, __uuidof( IMMDeviceEnumerator ), ( void** ) &pEnumerator );
105 if ( SUCCEEDED( hr ) )
106 {
107 pEnumerator->RegisterEndpointNotificationCallback( this );
108 }
109 }
110
111 EnterCriticalSection( &lock );
112 instances.push_back( p_instance );
113 LeaveCriticalSection( &lock );
114 }
115
do_unregister(XAudio2_Output * p_instance)116 void do_unregister( XAudio2_Output * p_instance )
117 {
118 if ( InterlockedDecrement( ®istered ) == 0 )
119 {
120 if (pEnumerator)
121 {
122 pEnumerator->UnregisterEndpointNotificationCallback( this );
123 pEnumerator->Release();
124 pEnumerator = NULL;
125 }
126 }
127
128 EnterCriticalSection( &lock );
129 for ( auto it = instances.begin(); it < instances.end(); ++it )
130 {
131 if ( *it == p_instance )
132 {
133 instances.erase( it );
134 break;
135 }
136 }
137 LeaveCriticalSection( &lock );
138 }
139 } g_notifier;
140
141 // Synchronization Event
142 class XAudio2_BufferNotify : public IXAudio2VoiceCallback
143 {
144 public:
145 HANDLE hBufferEndEvent;
146
XAudio2_BufferNotify()147 XAudio2_BufferNotify() {
148 hBufferEndEvent = NULL;
149 hBufferEndEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
150 ASSERT( hBufferEndEvent != NULL );
151 }
152
~XAudio2_BufferNotify()153 ~XAudio2_BufferNotify() {
154 CloseHandle( hBufferEndEvent );
155 hBufferEndEvent = NULL;
156 }
157
STDMETHOD_(void,OnBufferEnd)158 STDMETHOD_( void, OnBufferEnd ) ( void *pBufferContext ) {
159 ASSERT( hBufferEndEvent != NULL );
160 SetEvent( hBufferEndEvent );
161 }
162
163
164 // dummies:
STDMETHOD_(void,OnVoiceProcessingPassStart)165 STDMETHOD_( void, OnVoiceProcessingPassStart ) ( UINT32 BytesRequired ) {}
STDMETHOD_(void,OnVoiceProcessingPassEnd)166 STDMETHOD_( void, OnVoiceProcessingPassEnd ) () {}
STDMETHOD_(void,OnStreamEnd)167 STDMETHOD_( void, OnStreamEnd ) () {}
STDMETHOD_(void,OnBufferStart)168 STDMETHOD_( void, OnBufferStart ) ( void *pBufferContext ) {}
STDMETHOD_(void,OnLoopEnd)169 STDMETHOD_( void, OnLoopEnd ) ( void *pBufferContext ) {}
STDMETHOD_(void,OnVoiceError)170 STDMETHOD_( void, OnVoiceError ) ( void *pBufferContext, HRESULT Error ) {};
171 };
172
173
174 // Class Declaration
175 class XAudio2_Output
176 : public SoundDriver
177 {
178 public:
179 XAudio2_Output();
180 ~XAudio2_Output();
181
182 // Initialization
183 bool init(long sampleRate);
184
185 // Sound Data Feed
186 void write(u16 * finalWave, int length);
187
188 // Play Control
189 void pause();
190 void resume();
191 void reset();
192 void close();
193 void device_change();
194
195 // Configuration Changes
196 void setThrottle( unsigned short throttle );
197
198 private:
199 bool failed;
200 bool initialized;
201 bool playing;
202 UINT32 freq;
203 UINT32 bufferCount;
204 BYTE *buffers;
205 int currentBuffer;
206 int soundBufferLen;
207
208 volatile bool device_changed;
209
210 IXAudio2 *xaud;
211 IXAudio2MasteringVoice *mVoice; // listener
212 IXAudio2SourceVoice *sVoice; // sound source
213 XAUDIO2_BUFFER buf;
214 XAUDIO2_VOICE_STATE vState;
215 XAudio2_BufferNotify notify; // buffer end notification
216 };
217
218
219 // Class Implementation
XAudio2_Output()220 XAudio2_Output::XAudio2_Output()
221 {
222 failed = false;
223 initialized = false;
224 playing = false;
225 freq = 0;
226 bufferCount = theApp.xa2BufferCount;
227 buffers = NULL;
228 currentBuffer = 0;
229 device_changed = false;
230
231 xaud = NULL;
232 mVoice = NULL;
233 sVoice = NULL;
234 ZeroMemory( &buf, sizeof( buf ) );
235 ZeroMemory( &vState, sizeof( vState ) );
236
237 g_notifier.do_register( this );
238 }
239
240
~XAudio2_Output()241 XAudio2_Output::~XAudio2_Output()
242 {
243 g_notifier.do_unregister( this );
244 close();
245 }
246
close()247 void XAudio2_Output::close()
248 {
249 initialized = false;
250
251 if( sVoice ) {
252 if( playing ) {
253 HRESULT hr = sVoice->Stop( 0 );
254 ASSERT( hr == S_OK );
255 }
256 sVoice->DestroyVoice();
257 sVoice = NULL;
258 }
259
260 if( buffers ) {
261 free( buffers );
262 buffers = NULL;
263 }
264
265 if( mVoice ) {
266 mVoice->DestroyVoice();
267 mVoice = NULL;
268 }
269
270 if( xaud ) {
271 xaud->Release();
272 xaud = NULL;
273 }
274 }
275
device_change()276 void XAudio2_Output::device_change()
277 {
278 device_changed = true;
279 }
280
281
init(long sampleRate)282 bool XAudio2_Output::init(long sampleRate)
283 {
284 if( failed || initialized ) return false;
285
286 HRESULT hr;
287
288 // Initialize XAudio2
289 UINT32 flags = 0;
290 //#ifdef _DEBUG
291 // flags = XAUDIO2_DEBUG_ENGINE;
292 //#endif
293
294 hr = XAudio2Create( &xaud, flags );
295 if( hr != S_OK ) {
296 systemMessage( IDS_XAUDIO2_FAILURE, NULL );
297 failed = true;
298 return false;
299 }
300
301
302 freq = sampleRate;
303
304 // calculate the number of samples per frame first
305 // then multiply it with the size of a sample frame (16 bit * stereo)
306 soundBufferLen = ( freq / 60 ) * 4;
307
308 // create own buffers to store sound data because it must not be
309 // manipulated while the voice plays from it
310 buffers = (BYTE *)malloc( ( bufferCount + 1 ) * soundBufferLen );
311 // + 1 because we need one temporary buffer when all others are in use
312
313 WAVEFORMATEX wfx;
314 ZeroMemory( &wfx, sizeof( wfx ) );
315 wfx.wFormatTag = WAVE_FORMAT_PCM;
316 wfx.nChannels = 2;
317 wfx.nSamplesPerSec = freq;
318 wfx.wBitsPerSample = 16;
319 wfx.nBlockAlign = wfx.nChannels * ( wfx.wBitsPerSample / 8 );
320 wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
321
322
323 // create sound receiver
324 hr = xaud->CreateMasteringVoice(
325 &mVoice,
326 XAUDIO2_DEFAULT_CHANNELS,
327 XAUDIO2_DEFAULT_SAMPLERATE,
328 0,
329 theApp.xa2Device,
330 NULL );
331 if( hr != S_OK ) {
332 systemMessage( IDS_XAUDIO2_CANNOT_CREATE_MASTERINGVOICE, NULL );
333 failed = true;
334 return false;
335 }
336
337
338 // create sound emitter
339 hr = xaud->CreateSourceVoice( &sVoice, &wfx, 0, 4.0f, ¬ify );
340 if( hr != S_OK ) {
341 systemMessage( IDS_XAUDIO2_CANNOT_CREATE_SOURCEVOICE, NULL );
342 failed = true;
343 return false;
344 }
345
346
347 if( theApp.xa2Upmixing ) {
348 // set up stereo upmixing
349 XAUDIO2_DEVICE_DETAILS dd;
350 ZeroMemory( &dd, sizeof( dd ) );
351 hr = xaud->GetDeviceDetails( 0, &dd );
352 ASSERT( hr == S_OK );
353 float *matrix = NULL;
354 matrix = (float*)malloc( sizeof( float ) * 2 * dd.OutputFormat.Format.nChannels );
355 if( matrix == NULL ) return false;
356 bool matrixAvailable = true;
357 switch( dd.OutputFormat.Format.nChannels ) {
358 case 4: // 4.0
359 //Speaker \ Left Source Right Source
360 /*Front L*/ matrix[0] = 1.0000f; matrix[1] = 0.0000f;
361 /*Front R*/ matrix[2] = 0.0000f; matrix[3] = 1.0000f;
362 /*Back L*/ matrix[4] = 1.0000f; matrix[5] = 0.0000f;
363 /*Back R*/ matrix[6] = 0.0000f; matrix[7] = 1.0000f;
364 break;
365 case 5: // 5.0
366 //Speaker \ Left Source Right Source
367 /*Front L*/ matrix[0] = 1.0000f; matrix[1] = 0.0000f;
368 /*Front R*/ matrix[2] = 0.0000f; matrix[3] = 1.0000f;
369 /*Front C*/ matrix[4] = 0.7071f; matrix[5] = 0.7071f;
370 /*Side L*/ matrix[6] = 1.0000f; matrix[7] = 0.0000f;
371 /*Side R*/ matrix[8] = 0.0000f; matrix[9] = 1.0000f;
372 break;
373 case 6: // 5.1
374 //Speaker \ Left Source Right Source
375 /*Front L*/ matrix[0] = 1.0000f; matrix[1] = 0.0000f;
376 /*Front R*/ matrix[2] = 0.0000f; matrix[3] = 1.0000f;
377 /*Front C*/ matrix[4] = 0.7071f; matrix[5] = 0.7071f;
378 /*LFE */ matrix[6] = 0.0000f; matrix[7] = 0.0000f;
379 /*Side L*/ matrix[8] = 1.0000f; matrix[9] = 0.0000f;
380 /*Side R*/ matrix[10] = 0.0000f; matrix[11] = 1.0000f;
381 break;
382 case 7: // 6.1
383 //Speaker \ Left Source Right Source
384 /*Front L*/ matrix[0] = 1.0000f; matrix[1] = 0.0000f;
385 /*Front R*/ matrix[2] = 0.0000f; matrix[3] = 1.0000f;
386 /*Front C*/ matrix[4] = 0.7071f; matrix[5] = 0.7071f;
387 /*LFE */ matrix[6] = 0.0000f; matrix[7] = 0.0000f;
388 /*Side L*/ matrix[8] = 1.0000f; matrix[9] = 0.0000f;
389 /*Side R*/ matrix[10] = 0.0000f; matrix[11] = 1.0000f;
390 /*Back C*/ matrix[12] = 0.7071f; matrix[13] = 0.7071f;
391 break;
392 case 8: // 7.1
393 //Speaker \ Left Source Right Source
394 /*Front L*/ matrix[0] = 1.0000f; matrix[1] = 0.0000f;
395 /*Front R*/ matrix[2] = 0.0000f; matrix[3] = 1.0000f;
396 /*Front C*/ matrix[4] = 0.7071f; matrix[5] = 0.7071f;
397 /*LFE */ matrix[6] = 0.0000f; matrix[7] = 0.0000f;
398 /*Back L*/ matrix[8] = 1.0000f; matrix[9] = 0.0000f;
399 /*Back R*/ matrix[10] = 0.0000f; matrix[11] = 1.0000f;
400 /*Side L*/ matrix[12] = 1.0000f; matrix[13] = 0.0000f;
401 /*Side R*/ matrix[14] = 0.0000f; matrix[15] = 1.0000f;
402 break;
403 default:
404 matrixAvailable = false;
405 break;
406 }
407 if( matrixAvailable ) {
408 hr = sVoice->SetOutputMatrix( NULL, 2, dd.OutputFormat.Format.nChannels, matrix );
409 ASSERT( hr == S_OK );
410 }
411 free( matrix );
412 matrix = NULL;
413 }
414
415
416 hr = sVoice->Start( 0 );
417 ASSERT( hr == S_OK );
418 playing = true;
419
420 currentBuffer = 0;
421 device_changed = false;
422
423 initialized = true;
424 return true;
425 }
426
427
write(u16 * finalWave,int length)428 void XAudio2_Output::write(u16 * finalWave, int length)
429 {
430 if( !initialized || failed ) return;
431
432 while( true ) {
433 if ( device_changed ) {
434 close();
435 if (!init(freq)) return;
436 }
437
438 sVoice->GetState( &vState );
439
440 ASSERT( vState.BuffersQueued <= bufferCount );
441
442 if( vState.BuffersQueued < bufferCount ) {
443 if( vState.BuffersQueued == 0 ) {
444 // buffers ran dry
445 if( systemVerbose & VERBOSE_SOUNDOUTPUT ) {
446 static unsigned int i = 0;
447 log( "XAudio2: Buffers were not refilled fast enough (i=%i)\n", i++ );
448 }
449 }
450 // there is at least one free buffer
451 break;
452 } else {
453 // the maximum number of buffers is currently queued
454 if( !speedup && throttle && !gba_joybus_active ) {
455 // wait for one buffer to finish playing
456 if (WaitForSingleObject( notify.hBufferEndEvent, 10000 ) == WAIT_TIMEOUT) {
457 device_changed = true;
458 }
459 } else {
460 // drop current audio frame
461 return;
462 }
463 }
464 }
465
466 // copy & protect the audio data in own memory area while playing it
467 CopyMemory( &buffers[ currentBuffer * soundBufferLen ], finalWave, soundBufferLen );
468
469 buf.AudioBytes = soundBufferLen;
470 buf.pAudioData = &buffers[ currentBuffer * soundBufferLen ];
471
472 currentBuffer++;
473 currentBuffer %= ( bufferCount + 1 ); // + 1 because we need one temporary buffer
474
475 HRESULT hr = sVoice->SubmitSourceBuffer( &buf ); // send buffer to queue
476 ASSERT( hr == S_OK );
477 }
478
479
pause()480 void XAudio2_Output::pause()
481 {
482 if( !initialized || failed ) return;
483
484 if( playing ) {
485 HRESULT hr = sVoice->Stop( 0 );
486 ASSERT( hr == S_OK );
487 playing = false;
488 }
489 }
490
491
resume()492 void XAudio2_Output::resume()
493 {
494 if( !initialized || failed ) return;
495
496 if( !playing ) {
497 HRESULT hr = sVoice->Start( 0 );
498 ASSERT( hr == S_OK );
499 playing = true;
500 }
501 }
502
503
reset()504 void XAudio2_Output::reset()
505 {
506 if( !initialized || failed ) return;
507
508 if( playing ) {
509 HRESULT hr = sVoice->Stop( 0 );
510 ASSERT( hr == S_OK );
511 }
512
513 sVoice->FlushSourceBuffers();
514 sVoice->Start( 0 );
515 playing = true;
516 }
517
518
setThrottle(unsigned short throttle)519 void XAudio2_Output::setThrottle( unsigned short throttle )
520 {
521 if( !initialized || failed ) return;
522
523 if( throttle == 0 ) throttle = 100;
524 HRESULT hr = sVoice->SetFrequencyRatio( (float)throttle / 100.0f );
525 ASSERT( hr == S_OK );
526 }
527
xaudio2_device_changed(XAudio2_Output * instance)528 void xaudio2_device_changed( XAudio2_Output * instance )
529 {
530 instance->device_change();
531 }
532
newXAudio2_Output()533 SoundDriver *newXAudio2_Output()
534 {
535 return new XAudio2_Output();
536 }
537
538
539 #endif // #ifndef NO_XAUDIO2
540