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( &registered ) == 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( &registered ) == 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, &notify );
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