#ifndef NO_XAUDIO2 // Interface #include "../common/SoundDriver.h" // XAudio2 #include // MMDevice API #include #include // Internals #include "../System.h" // for systemMessage() #include "../gba/Globals.h" class XAudio2_Output; static void xaudio2_device_changed( XAudio2_Output * ); // Synchronization Event class XAudio2_BufferNotify : public IXAudio2VoiceCallback { public: HANDLE hBufferEndEvent; XAudio2_BufferNotify() { hBufferEndEvent = NULL; hBufferEndEvent = CreateEventEx( NULL, FALSE, FALSE, NULL ); } ~XAudio2_BufferNotify() { CloseHandle( hBufferEndEvent ); hBufferEndEvent = NULL; } STDMETHOD_( void, OnBufferEnd ) ( void *pBufferContext ) { SetEvent( hBufferEndEvent ); } // dummies: STDMETHOD_( void, OnVoiceProcessingPassStart ) ( UINT32 BytesRequired ) {} STDMETHOD_( void, OnVoiceProcessingPassEnd ) () {} STDMETHOD_( void, OnStreamEnd ) () {} STDMETHOD_( void, OnBufferStart ) ( void *pBufferContext ) {} STDMETHOD_( void, OnLoopEnd ) ( void *pBufferContext ) {} STDMETHOD_( void, OnVoiceError ) ( void *pBufferContext, HRESULT Error ) {}; }; // Class Declaration class XAudio2_Output : public SoundDriver { public: XAudio2_Output(); ~XAudio2_Output(); // Initialization bool init(long sampleRate); // Sound Data Feed void write(u16 * finalWave, int length); // Play Control void pause(); void resume(); void reset(); void close(); void device_change(); // Configuration Changes void setThrottle( unsigned short throttle ); private: bool failed; bool initialized; bool playing; UINT32 freq; UINT32 bufferCount; BYTE *buffers; int currentBuffer; int soundBufferLen; volatile bool device_changed; IXAudio2 *xaud; IXAudio2MasteringVoice *mVoice; // listener IXAudio2SourceVoice *sVoice; // sound source XAUDIO2_BUFFER buf; XAUDIO2_VOICE_STATE vState; XAudio2_BufferNotify notify; // buffer end notification }; // Class Implementation XAudio2_Output::XAudio2_Output() { failed = false; initialized = false; playing = false; freq = 0; bufferCount = 2;//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; if( sVoice ) { if( playing ) { HRESULT hr = sVoice->Stop( 0 ); //ASSERT( hr == S_OK ); } sVoice->DestroyVoice(); sVoice = NULL; } if( buffers ) { free( buffers ); buffers = NULL; } if( mVoice ) { mVoice->DestroyVoice(); mVoice = NULL; } if( xaud ) { xaud->Release(); xaud = NULL; } } void XAudio2_Output::device_change() { device_changed = true; } bool XAudio2_Output::init(long sampleRate) { if( failed || initialized ) return false; HRESULT hr; // Initialize XAudio2 UINT32 flags = 0; //#ifdef _DEBUG // flags = XAUDIO2_DEBUG_ENGINE; //#endif hr = XAudio2Create( &xaud, flags ); if( hr != S_OK ) { //systemMessage( IDS_XAUDIO2_FAILURE, NULL ); failed = true; return false; } freq = sampleRate; // calculate the number of samples per frame first // then multiply it with the size of a sample frame (16 bit * stereo) soundBufferLen = ( freq / 60 ) * 4; // create own buffers to store sound data because it must not be // manipulated while the voice plays from it buffers = (BYTE *)malloc( ( bufferCount + 1 ) * soundBufferLen ); // + 1 because we need one temporary buffer when all others are in use WAVEFORMATEX wfx; ZeroMemory( &wfx, sizeof( wfx ) ); wfx.wFormatTag = WAVE_FORMAT_PCM; wfx.nChannels = 2; wfx.nSamplesPerSec = freq; wfx.wBitsPerSample = 16; wfx.nBlockAlign = wfx.nChannels * ( wfx.wBitsPerSample / 8 ); wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; // create sound receiver hr = xaud->CreateMasteringVoice( &mVoice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, 0, NULL ); if( hr != S_OK ) { //systemMessage( IDS_XAUDIO2_CANNOT_CREATE_MASTERINGVOICE, NULL ); failed = true; return false; } // create sound emitter hr = xaud->CreateSourceVoice( &sVoice, &wfx, 0, 4.0f, ¬ify ); if( hr != S_OK ) { //systemMessage( IDS_XAUDIO2_CANNOT_CREATE_SOURCEVOICE, NULL ); failed = true; return false; } //if( theApp.xa2Upmixing ) { // // set up stereo upmixing // XAUDIO2_DEVICE_DETAILS dd; // ZeroMemory( &dd, sizeof( dd ) ); // hr = xaud->GetDeviceDetails( 0, &dd ); // ASSERT( hr == S_OK ); // float *matrix = NULL; // matrix = (float*)malloc( sizeof( float ) * 2 * dd.OutputFormat.Format.nChannels ); // if( matrix == NULL ) return false; // bool matrixAvailable = true; // switch( dd.OutputFormat.Format.nChannels ) { // case 4: // 4.0 ////Speaker \ Left Source Right Source ///*Front L*/ matrix[0] = 1.0000f; matrix[1] = 0.0000f; ///*Front R*/ matrix[2] = 0.0000f; matrix[3] = 1.0000f; ///*Back L*/ matrix[4] = 1.0000f; matrix[5] = 0.0000f; ///*Back R*/ matrix[6] = 0.0000f; matrix[7] = 1.0000f; // break; // case 5: // 5.0 ////Speaker \ Left Source Right Source ///*Front L*/ matrix[0] = 1.0000f; matrix[1] = 0.0000f; ///*Front R*/ matrix[2] = 0.0000f; matrix[3] = 1.0000f; ///*Front C*/ matrix[4] = 0.7071f; matrix[5] = 0.7071f; ///*Side L*/ matrix[6] = 1.0000f; matrix[7] = 0.0000f; ///*Side R*/ matrix[8] = 0.0000f; matrix[9] = 1.0000f; // break; // case 6: // 5.1 ////Speaker \ Left Source Right Source ///*Front L*/ matrix[0] = 1.0000f; matrix[1] = 0.0000f; ///*Front R*/ matrix[2] = 0.0000f; matrix[3] = 1.0000f; ///*Front C*/ matrix[4] = 0.7071f; matrix[5] = 0.7071f; ///*LFE */ matrix[6] = 0.0000f; matrix[7] = 0.0000f; ///*Side L*/ matrix[8] = 1.0000f; matrix[9] = 0.0000f; ///*Side R*/ matrix[10] = 0.0000f; matrix[11] = 1.0000f; // break; // case 7: // 6.1 ////Speaker \ Left Source Right Source ///*Front L*/ matrix[0] = 1.0000f; matrix[1] = 0.0000f; ///*Front R*/ matrix[2] = 0.0000f; matrix[3] = 1.0000f; ///*Front C*/ matrix[4] = 0.7071f; matrix[5] = 0.7071f; ///*LFE */ matrix[6] = 0.0000f; matrix[7] = 0.0000f; ///*Side L*/ matrix[8] = 1.0000f; matrix[9] = 0.0000f; ///*Side R*/ matrix[10] = 0.0000f; matrix[11] = 1.0000f; ///*Back C*/ matrix[12] = 0.7071f; matrix[13] = 0.7071f; // break; // case 8: // 7.1 ////Speaker \ Left Source Right Source ///*Front L*/ matrix[0] = 1.0000f; matrix[1] = 0.0000f; ///*Front R*/ matrix[2] = 0.0000f; matrix[3] = 1.0000f; ///*Front C*/ matrix[4] = 0.7071f; matrix[5] = 0.7071f; ///*LFE */ matrix[6] = 0.0000f; matrix[7] = 0.0000f; ///*Back L*/ matrix[8] = 1.0000f; matrix[9] = 0.0000f; ///*Back R*/ matrix[10] = 0.0000f; matrix[11] = 1.0000f; ///*Side L*/ matrix[12] = 1.0000f; matrix[13] = 0.0000f; ///*Side R*/ matrix[14] = 0.0000f; matrix[15] = 1.0000f; // break; // default: // matrixAvailable = false; // break; // } // if( matrixAvailable ) { // hr = sVoice->SetOutputMatrix( NULL, 2, dd.OutputFormat.Format.nChannels, matrix ); // ASSERT( hr == S_OK ); // } // free( matrix ); // matrix = NULL; //} hr = sVoice->Start( 0 ); //ASSERT( hr == S_OK ); playing = true; currentBuffer = 0; device_changed = false; initialized = true; return true; } 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 ); if( vState.BuffersQueued < bufferCount ) { if( vState.BuffersQueued == 0 ) { // buffers ran dry if( systemVerbose & VERBOSE_SOUNDOUTPUT ) { static unsigned int i = 0; log( "XAudio2: Buffers were not refilled fast enough (i=%i)\n", i++ ); } } // there is at least one free buffer break; } else { //synchronize = false; // the maximum number of buffers is currently queued if( synchronize && !speedup/* && !theApp.throttle*/ ) { // wait for one buffer to finish playing if (WaitForSingleObjectEx( notify.hBufferEndEvent, 10000, false ) == WAIT_TIMEOUT) { device_changed = true; } } else { // drop current audio frame return; } } } // copy & protect the audio data in own memory area while playing it CopyMemory( &buffers[ currentBuffer * soundBufferLen ], finalWave, soundBufferLen ); buf.AudioBytes = soundBufferLen; buf.pAudioData = &buffers[ currentBuffer * soundBufferLen ]; currentBuffer++; currentBuffer %= ( bufferCount + 1 ); // + 1 because we need one temporary buffer HRESULT hr = sVoice->SubmitSourceBuffer( &buf ); // send buffer to queue //ASSERT( hr == S_OK ); } void XAudio2_Output::pause() { if( !initialized || failed ) return; if( playing ) { HRESULT hr = sVoice->Stop( 0 ); //ASSERT( hr == S_OK ); playing = false; } } void XAudio2_Output::resume() { if( !initialized || failed ) return; if( !playing ) { HRESULT hr = sVoice->Start( 0 ); //ASSERT( hr == S_OK ); playing = true; } } void XAudio2_Output::reset() { if( !initialized || failed ) return; if( playing ) { HRESULT hr = sVoice->Stop( 0 ); //ASSERT( hr == S_OK ); } sVoice->FlushSourceBuffers(); sVoice->Start( 0 ); playing = true; } void XAudio2_Output::setThrottle( unsigned short throttle ) { if( !initialized || failed ) return; if( throttle == 0 ) throttle = 100; HRESULT hr = sVoice->SetFrequencyRatio( (float)throttle / 100.0f ); //ASSERT( hr == S_OK ); } void xaudio2_device_changed( XAudio2_Output * instance ) { instance->device_change(); } SoundDriver *newXAudio2_Output() { return new XAudio2_Output(); } #endif // #ifndef NO_XAUDIO2