diff --git a/VBA2008.vcproj b/VBA2008.vcproj index b21bb87a..dcd7ba99 100644 --- a/VBA2008.vcproj +++ b/VBA2008.vcproj @@ -901,6 +901,10 @@ RelativePath=".\src\win32\OpenGL.cpp" > + + - - - - - - - - + + + + + + + + + + + + diff --git a/doc/DevInfo.txt b/doc/DevInfo.txt index d0f729c4..65c379f6 100644 --- a/doc/DevInfo.txt +++ b/doc/DevInfo.txt @@ -15,6 +15,7 @@ Known preprocessor switches: - NO_OGL: Exclude OpenGL code - NO_D3D: Exclude Direct3D code - NO_OAL: Exclude OpenAL code +- NO_XAUDIO2: Exclude XAudio2 code (supersedes DirectSound) diff --git a/doc/Known Bugs.txt b/doc/Known Bugs.txt index 0a7ff33f..456aabaa 100644 --- a/doc/Known Bugs.txt +++ b/doc/Known Bugs.txt @@ -11,7 +11,6 @@ Known Bugs: - Audio core: assertation error occurs when disabling GB sound - I think its best we mute sound instead, since some games rely on audio for timing. Plus, blargg's GB_Snd_Emu is extremely optimized stuff. (Mudlord) -- x64: Needs optimizations (x64 native code, whatever) - Wrong bit depth image is displayed for 2 frames when switching from/to HQ3x/4x ASM - This is caused by the 16bit hack which does not re-process the emulated image. It results in the display devices treating the image at pix with the wrong bit depth. \ No newline at end of file diff --git a/src/win32/MainWnd.cpp b/src/win32/MainWnd.cpp index 36816dfa..20fdc80f 100644 --- a/src/win32/MainWnd.cpp +++ b/src/win32/MainWnd.cpp @@ -434,6 +434,9 @@ BEGIN_MESSAGE_MAP(MainWnd, CWnd) ON_WM_WINDOWPOSCHANGING() ON_COMMAND(ID_EMULATOR_BIOSFILES, &MainWnd::OnEmulatorBiosfiles) ON_COMMAND(ID_FILE_OPEN_GBC, &MainWnd::OnFileOpenGbc) + ON_WM_NCRBUTTONDOWN() + ON_COMMAND(ID_OUTPUTAPI_XAUDIO2, &MainWnd::OnOutputapiXaudio2) + ON_UPDATE_COMMAND_UI(ID_OUTPUTAPI_XAUDIO2, &MainWnd::OnUpdateOutputapiXaudio2) END_MESSAGE_MAP() @@ -1303,3 +1306,13 @@ void MainWnd::OnWindowPosChanging(WINDOWPOS* lpwndpos) soundPause(); } } + +void MainWnd::OnNcRButtonDown(UINT nHitTest, CPoint point) +{ + // pause sound before process is halted + if( emulating ) { + soundPause(); + } + + CWnd::OnNcRButtonDown(nHitTest, point); +} diff --git a/src/win32/MainWnd.h b/src/win32/MainWnd.h index 16a5874a..4c56e68c 100644 --- a/src/win32/MainWnd.h +++ b/src/win32/MainWnd.h @@ -422,6 +422,9 @@ public: afx_msg void OnWindowPosChanging(WINDOWPOS* lpwndpos); afx_msg void OnEmulatorBiosfiles(); afx_msg void OnFileOpenGbc(); + afx_msg void OnNcRButtonDown(UINT nHitTest, CPoint point); + afx_msg void OnOutputapiXaudio2(); + afx_msg void OnUpdateOutputapiXaudio2(CCmdUI *pCmdUI); }; ///////////////////////////////////////////////////////////////////////////// diff --git a/src/win32/MainWndOptions.cpp b/src/win32/MainWndOptions.cpp index 5efe3479..ef8842f0 100644 --- a/src/win32/MainWndOptions.cpp +++ b/src/win32/MainWndOptions.cpp @@ -1740,6 +1740,27 @@ void MainWnd::OnUpdateOutputapiDirectsound(CCmdUI *pCmdUI) pCmdUI->Enable(!theApp.aviRecording && !theApp.soundRecording); } +void MainWnd::OnOutputapiXaudio2() +{ +#ifndef NO_XAUDIO2 + if( theApp.audioAPI != XAUDIO2 ) { + theApp.audioAPI = XAUDIO2; + systemSoundShutdown(); + systemSoundInit(); + } +#endif +} + +void MainWnd::OnUpdateOutputapiXaudio2(CCmdUI *pCmdUI) +{ +#ifndef NO_XAUDIO2 + pCmdUI->SetCheck( ( theApp.audioAPI == XAUDIO2 ) ? 1 : 0 ); + pCmdUI->Enable(!theApp.aviRecording && !theApp.soundRecording); +#else + pCmdUI->Enable( FALSE ); +#endif +} + void MainWnd::OnOutputapiOpenal() { #ifndef NO_OAL diff --git a/src/win32/Sound.h b/src/win32/Sound.h index 773a44b2..dd1cebd1 100644 --- a/src/win32/Sound.h +++ b/src/win32/Sound.h @@ -21,9 +21,12 @@ #define VBA_WIN32_SOUND_H enum AUDIO_API { - DIRECTSOUND + DIRECTSOUND = 0 #ifndef NO_OAL - , OPENAL_SOUND + , OPENAL_SOUND = 1 +#endif +#ifndef NO_XAUDIO2 + , XAUDIO2 = 2 #endif }; @@ -37,6 +40,8 @@ class ISound virtual void reset() = 0; virtual void resume() = 0; virtual void write() = 0; + + virtual void setThrottle( unsigned short throttle ) {}; }; #endif diff --git a/src/win32/VBA.cpp b/src/win32/VBA.cpp index aa71e85a..11ce90cb 100644 --- a/src/win32/VBA.cpp +++ b/src/win32/VBA.cpp @@ -111,6 +111,9 @@ extern ISound *newDirectSound(); #ifndef NO_OAL extern ISound *newOpenAL(); #endif +#ifndef NO_XAUDIO2 +extern ISound *newXAudio2_Output(); +#endif extern void remoteStubSignal(int, int); extern void remoteOutput(char *, u32); @@ -902,7 +905,6 @@ void VBA::updateThrottle( unsigned short throttle ) if( throttle == 0 ) { autoFrameSkip = false; - return; } else { Sm60FPS::K_fCpuSpeed = (float)throttle; Sm60FPS::K_fTargetFps = 60.0f * Sm60FPS::K_fCpuSpeed / 100; @@ -910,7 +912,10 @@ void VBA::updateThrottle( unsigned short throttle ) autoFrameSkip = true; frameSkip = 0; systemFrameSkip = 0; - return; + } + + if( theApp.sound ) { + theApp.sound->setThrottle( throttle ); } } @@ -1197,10 +1202,21 @@ bool systemSoundInit() case OPENAL_SOUND: theApp.sound = newOpenAL(); break; +#endif +#ifndef NO_XAUDIO2 + case XAUDIO2: + theApp.sound = newXAudio2_Output(); + break; #endif } - return theApp.sound->init(); + bool retVal = theApp.sound->init(); + + if( retVal ) { + theApp.sound->setThrottle( theApp.throttle ); + } + + return retVal; } @@ -1448,6 +1464,9 @@ void VBA::loadSettings() if( ( audioAPI != DIRECTSOUND ) #ifndef NO_OAL && ( audioAPI != OPENAL_SOUND ) +#endif +#ifndef NO_XAUDIO2 + && ( audioAPI != XAUDIO2 ) #endif ) { audioAPI = DIRECTSOUND; diff --git a/src/win32/VBA.rc b/src/win32/VBA.rc index 28548f18..61be4139 100644 --- a/src/win32/VBA.rc +++ b/src/win32/VBA.rc @@ -9,39 +9,10 @@ // #include "afxres.h" + ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// German (Germany) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU) -#ifdef _WIN32 -LANGUAGE LANG_GERMAN, SUBLANG_GERMAN -#pragma code_page(1252) -#endif //_WIN32 - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\0" -END - -#endif // APSTUDIO_INVOKED - -#endif // German (Germany) resources -///////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////////// // English (U.S.) resources @@ -1698,6 +1669,8 @@ BEGIN BEGIN MENUITEM "DirectSound", ID_OUTPUTAPI_DIRECTSOUND MENUITEM SEPARATOR + MENUITEM "XAudio2", ID_OUTPUTAPI_XAUDIO2 + MENUITEM SEPARATOR MENUITEM "OpenAL", ID_OUTPUTAPI_OPENAL MENUITEM " Configuration...", ID_OUTPUTAPI_OALCONFIGURATION MENUITEM SEPARATOR @@ -2219,6 +2192,12 @@ BEGIN IDS_AVI_CANNOT_WRITE_VIDEO "Cannot write video frame to AVI file." IDS_AVI_CANNOT_WRITE_AUDIO "Cannot write audio frame to AVI file." IDS_FILTER_GBCROM "Game Boy Color ROMs_*.GBC;*.CGB;*.ZIP;*.7Z;*.Z;*.GZ__" + IDS_COM_FAILURE "The COM (Component Object Model) failed to initialize!" + IDS_XAUDIO2_FAILURE "The XAudio2 interface failed to initialize!" + IDS_XAUDIO2_CANNOT_CREATE_MASTERINGVOICE + "XAudio2: Creating mastering voice failed!" + IDS_XAUDIO2_CANNOT_CREATE_SOURCEVOICE + "XAudio2: Creating source voice failed!" END #endif // English (U.S.) resources @@ -2242,7 +2221,7 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS 2 TEXTINCLUDE BEGIN - "#include ""afxres.h""\r\0" + "#include ""afxres.h""\r\r\0" END #endif // APSTUDIO_INVOKED @@ -2251,12 +2230,3 @@ END ///////////////////////////////////////////////////////////////////////////// - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - diff --git a/src/win32/XAudio2.cpp b/src/win32/XAudio2.cpp new file mode 100644 index 00000000..67b64a80 --- /dev/null +++ b/src/win32/XAudio2.cpp @@ -0,0 +1,295 @@ +// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. +// Copyright (C) 1999-2003 Forgotten +// Copyright (C) 2004 Forgotten and the VBA development team +// Copyright (C) 2005-2006 VBA development team +// Copyright (C) 2007-2008 VBA-M development team + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2, or(at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + +#ifndef NO_XAUDIO2 + + +#define NBUFFERS 4 + +// MFC +#include "stdafx.h" + +// Interface +#include "Sound.h" + +// XAudio2 +#include + +// Internals +#include "../Sound.h" // for soundBufferLen, soundFinalWave and soundQuality +#include "../System.h" // for systemMessage() +#include "../Globals.h" // for 'speedup' and 'synchronize' + + +// Class Declaration +class XAudio2_Output + : public ISound +{ +public: + XAudio2_Output(); + ~XAudio2_Output(); + + // Initialization + bool init(); + + // Sound Data Feed + void write(); + + // Play Control + void pause(); + void resume(); + void reset(); + + // Configuration Changes + void setThrottle( unsigned short throttle ); + + +private: + bool failed; + bool initialized; + bool playing; + UINT32 freq; + BYTE *buffers; + int currentBuffer; + + IXAudio2 *xaud; + IXAudio2MasteringVoice *mVoice; // dest + IXAudio2SourceVoice *sVoice; // source + XAUDIO2_BUFFER buf; + XAUDIO2_VOICE_STATE vState; +}; + + +// Class Implementation +XAudio2_Output::XAudio2_Output() +{ + failed = false; + initialized = false; + playing = false; + freq = 0; + buffers = 0; + currentBuffer = 0; + + xaud = NULL; + mVoice = NULL; + sVoice = NULL; + ZeroMemory( &buf, sizeof( buf ) ); + ZeroMemory( &vState, sizeof( vState ) ); + + if( S_OK != CoInitializeEx( NULL, COINIT_MULTITHREADED ) ) { + systemMessage( IDS_COM_FAILURE, NULL ); + failed = true; + return; + } +} + + +XAudio2_Output::~XAudio2_Output() +{ + initialized = false; + + if( sVoice ) { + if( playing ) { + sVoice->Stop( 0 ); + } + sVoice->DestroyVoice(); + } + + if( buffers ) { + free( buffers ); + } + + if( mVoice ) { + mVoice->DestroyVoice(); + } + + if( xaud ) { + xaud->Release(); + } + + CoUninitialize(); +} + + +bool XAudio2_Output::init() +{ + if( failed || initialized ) return false; + + HRESULT hr; + UINT32 flags = 0; + +#ifdef _DEBUG + flags |= XAUDIO2_DEBUG_ENGINE; +#endif + + hr = XAudio2Create( &xaud, flags ); + + if( FAILED( hr ) ) { + systemMessage( IDS_XAUDIO2_FAILURE, NULL ); + failed = true; + return false; + } + + + freq = 44100 / (UINT32)soundQuality; + + // 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 because sound data must not be manipulated + // by the audio emulation core while it is still being played back + buffers = (BYTE *)malloc( ( NBUFFERS + 1 ) * soundBufferLen ); + // + 1 because we need one temporary buffer when all others are still playing + + 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, 2, freq ); + + if( FAILED( hr ) ) { + systemMessage( IDS_XAUDIO2_CANNOT_CREATE_MASTERINGVOICE, NULL ); + failed = true; + return false; + } + + + // Create Sound Emitter + hr = xaud->CreateSourceVoice( &sVoice, &wfx, 0, XAUDIO2_MAX_FREQ_RATIO ); + + if( FAILED( hr ) ) { + systemMessage( IDS_XAUDIO2_CANNOT_CREATE_SOURCEVOICE, NULL ); + failed = true; + return false; + } + + sVoice->Start( 0 ); + playing = true; + + + setsystemSoundOn( true ); + initialized = true; + return true; +} + + +void XAudio2_Output::write() +{ + if( !initialized || failed ) return; + + bool drop = false; + + // copy & protect the audio data in own memory area while playing it + CopyMemory( &buffers[ currentBuffer * soundBufferLen ], soundFinalWave, soundBufferLen ); + + buf.AudioBytes = soundBufferLen; + buf.pAudioData = &buffers[ currentBuffer * soundBufferLen ]; + + currentBuffer++; + currentBuffer %= ( NBUFFERS + 1 ); + + while( true ) { + sVoice->GetState( &vState ); + ASSERT( vState.BuffersQueued <= NBUFFERS ); + + if( vState.BuffersQueued == NBUFFERS ) { + // all buffers filled + if( speedup || !synchronize || theApp.throttle ) { + drop = true; + break; + } + + if( synchronize ) { + // wait for about half the time one buffer needs to finish + // unoptimized: ( sourceBufferLen * 1000 ) / ( freq * 2 * 2 ) * 1/2 + Sleep( soundBufferLen / ( freq >> 7 ) ); + } + } else { + // still room for new audio + break; + } + } + + if( !drop ) { + // add the sound buffer to the queue + sVoice->SubmitSourceBuffer( &buf ); + } +} + + +void XAudio2_Output::pause() +{ + if( !initialized || failed ) return; + + if( playing ) { + sVoice->Stop( 0 ); + playing = false; + } +} + + +void XAudio2_Output::resume() +{ + if( !initialized || failed ) return; + + if( !playing ) { + sVoice->Start( 0 ); + playing = true; + } +} + + +void XAudio2_Output::reset() +{ + if( !initialized || failed ) return; + + if( playing ) { + sVoice->Stop( 0 ); + } + + sVoice->FlushSourceBuffers(); + sVoice->Start( 0 ); + playing = true; +} + + +void XAudio2_Output::setThrottle( unsigned short throttle ) +{ + if( throttle == 0 ) throttle = 100; + sVoice->SetFrequencyRatio( (float)throttle / 100.0f ); +} + + +ISound *newXAudio2_Output() +{ + return new XAudio2_Output(); +} + + +#endif // #ifndef NO_XAUDIO2 diff --git a/src/win32/resource.h b/src/win32/resource.h index ba81ee1a..a9b4a259 100644 --- a/src/win32/resource.h +++ b/src/win32/resource.h @@ -538,6 +538,10 @@ #define IDS_AVI_CANNOT_WRITE_VIDEO 2005 #define IDS_AVI_CANNOT_WRITE_AUDIO 2006 #define IDS_FILTER_GBCROM 2007 +#define IDS_COM_FAILURE 2008 +#define IDS_XAUDIO2_FAILURE 2009 +#define IDS_XAUDIO2_CANNOT_CREATE_MASTERINGVOICE 2010 +#define IDS_XAUDIO2_CANNOT_CREATE_SOURCEVOICE 2011 #define ID_HELP_ABOUT 40001 #define ID_FILE_EXIT 40002 #define ID_OPTIONS_VIDEO_FRAMESKIP_0 40003 @@ -845,13 +849,14 @@ #define ID_EMULATOR_BIOSFILES 40356 #define ID_FILE_OPENGBC 40357 #define ID_FILE_OPEN_GBC 40358 +#define ID_OUTPUTAPI_XAUDIO2 40359 // Next default values for new objects -// +// #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 163 -#define _APS_NEXT_COMMAND_VALUE 40359 +#define _APS_NEXT_COMMAND_VALUE 40360 #define _APS_NEXT_CONTROL_VALUE 1284 #define _APS_NEXT_SYMED_VALUE 103 #endif diff --git a/src/win32/stdafx.h b/src/win32/stdafx.h index 29146ff2..77c15e38 100644 --- a/src/win32/stdafx.h +++ b/src/win32/stdafx.h @@ -26,7 +26,6 @@ // Target for Windows 2000 (Required by GetMenuBar and other functions) #define NTDDI_VERSION NTDDI_WIN2K // new #define _WIN32_WINNT 0x0500 // old -//#define WINVER 0x0500 // Enable STRICT type checking #define STRICT @@ -36,3 +35,4 @@ #include #include #include "VBA.h" +#include "resource.h"