ADDED sound output using Microsoft's new XAudio2 API

This commit is contained in:
spacy51 2008-03-09 14:01:34 +00:00
parent 8359ac8351
commit 8c9a679eca
12 changed files with 410 additions and 67 deletions

View File

@ -901,6 +901,10 @@
RelativePath=".\src\win32\OpenGL.cpp" RelativePath=".\src\win32\OpenGL.cpp"
> >
</File> </File>
<File
RelativePath=".\src\win32\XAudio2.cpp"
>
</File>
</Filter> </Filter>
<Filter <Filter
Name="Linking" Name="Linking"
@ -1438,36 +1442,44 @@
</Filter> </Filter>
</Filter> </Filter>
<Filter <Filter
Name="Res" Name="Resources"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx" Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
> >
<File
RelativePath=".\res\DevInfo.txt"
>
</File>
<File
RelativePath=".\res\gpl.txt"
>
</File>
<File
RelativePath=".\res\Known Bugs.txt"
>
</File>
<File
RelativePath=".\res\ReadMe.txt"
>
</File>
<File <File
RelativePath=".\src\win32\resource.h" RelativePath=".\src\win32\resource.h"
> >
</File> </File>
<File
RelativePath=".\src\win32\VBA-M.ico"
>
</File>
<File <File
RelativePath=".\src\win32\VBA.rc" RelativePath=".\src\win32\VBA.rc"
> >
</File> </File>
</Filter>
<Filter
Name="Documentation"
>
<File <File
RelativePath=".\src\win32\vba.rc2" RelativePath=".\doc\authors.txt"
>
</File>
<File
RelativePath=".\doc\DevInfo.txt"
>
</File>
<File
RelativePath=".\doc\Known Bugs.txt"
>
</File>
<File
RelativePath=".\doc\ReadMe.txt"
>
</File>
<File
RelativePath=".\doc\todo.txt"
> >
</File> </File>
</Filter> </Filter>

View File

@ -15,6 +15,7 @@ Known preprocessor switches:
- NO_OGL: Exclude OpenGL code - NO_OGL: Exclude OpenGL code
- NO_D3D: Exclude Direct3D code - NO_D3D: Exclude Direct3D code
- NO_OAL: Exclude OpenAL code - NO_OAL: Exclude OpenAL code
- NO_XAUDIO2: Exclude XAudio2 code (supersedes DirectSound)

View File

@ -11,7 +11,6 @@ Known Bugs:
- Audio core: assertation error occurs when disabling GB sound - 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. - 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) 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 - 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. - 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. It results in the display devices treating the image at pix with the wrong bit depth.

View File

@ -434,6 +434,9 @@ BEGIN_MESSAGE_MAP(MainWnd, CWnd)
ON_WM_WINDOWPOSCHANGING() ON_WM_WINDOWPOSCHANGING()
ON_COMMAND(ID_EMULATOR_BIOSFILES, &MainWnd::OnEmulatorBiosfiles) ON_COMMAND(ID_EMULATOR_BIOSFILES, &MainWnd::OnEmulatorBiosfiles)
ON_COMMAND(ID_FILE_OPEN_GBC, &MainWnd::OnFileOpenGbc) 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() END_MESSAGE_MAP()
@ -1303,3 +1306,13 @@ void MainWnd::OnWindowPosChanging(WINDOWPOS* lpwndpos)
soundPause(); soundPause();
} }
} }
void MainWnd::OnNcRButtonDown(UINT nHitTest, CPoint point)
{
// pause sound before process is halted
if( emulating ) {
soundPause();
}
CWnd::OnNcRButtonDown(nHitTest, point);
}

View File

@ -422,6 +422,9 @@ public:
afx_msg void OnWindowPosChanging(WINDOWPOS* lpwndpos); afx_msg void OnWindowPosChanging(WINDOWPOS* lpwndpos);
afx_msg void OnEmulatorBiosfiles(); afx_msg void OnEmulatorBiosfiles();
afx_msg void OnFileOpenGbc(); afx_msg void OnFileOpenGbc();
afx_msg void OnNcRButtonDown(UINT nHitTest, CPoint point);
afx_msg void OnOutputapiXaudio2();
afx_msg void OnUpdateOutputapiXaudio2(CCmdUI *pCmdUI);
}; };
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////

View File

@ -1740,6 +1740,27 @@ void MainWnd::OnUpdateOutputapiDirectsound(CCmdUI *pCmdUI)
pCmdUI->Enable(!theApp.aviRecording && !theApp.soundRecording); 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() void MainWnd::OnOutputapiOpenal()
{ {
#ifndef NO_OAL #ifndef NO_OAL

View File

@ -21,9 +21,12 @@
#define VBA_WIN32_SOUND_H #define VBA_WIN32_SOUND_H
enum AUDIO_API { enum AUDIO_API {
DIRECTSOUND DIRECTSOUND = 0
#ifndef NO_OAL #ifndef NO_OAL
, OPENAL_SOUND , OPENAL_SOUND = 1
#endif
#ifndef NO_XAUDIO2
, XAUDIO2 = 2
#endif #endif
}; };
@ -37,6 +40,8 @@ class ISound
virtual void reset() = 0; virtual void reset() = 0;
virtual void resume() = 0; virtual void resume() = 0;
virtual void write() = 0; virtual void write() = 0;
virtual void setThrottle( unsigned short throttle ) {};
}; };
#endif #endif

View File

@ -111,6 +111,9 @@ extern ISound *newDirectSound();
#ifndef NO_OAL #ifndef NO_OAL
extern ISound *newOpenAL(); extern ISound *newOpenAL();
#endif #endif
#ifndef NO_XAUDIO2
extern ISound *newXAudio2_Output();
#endif
extern void remoteStubSignal(int, int); extern void remoteStubSignal(int, int);
extern void remoteOutput(char *, u32); extern void remoteOutput(char *, u32);
@ -902,7 +905,6 @@ void VBA::updateThrottle( unsigned short throttle )
if( throttle == 0 ) { if( throttle == 0 ) {
autoFrameSkip = false; autoFrameSkip = false;
return;
} else { } else {
Sm60FPS::K_fCpuSpeed = (float)throttle; Sm60FPS::K_fCpuSpeed = (float)throttle;
Sm60FPS::K_fTargetFps = 60.0f * Sm60FPS::K_fCpuSpeed / 100; Sm60FPS::K_fTargetFps = 60.0f * Sm60FPS::K_fCpuSpeed / 100;
@ -910,7 +912,10 @@ void VBA::updateThrottle( unsigned short throttle )
autoFrameSkip = true; autoFrameSkip = true;
frameSkip = 0; frameSkip = 0;
systemFrameSkip = 0; systemFrameSkip = 0;
return; }
if( theApp.sound ) {
theApp.sound->setThrottle( throttle );
} }
} }
@ -1197,10 +1202,21 @@ bool systemSoundInit()
case OPENAL_SOUND: case OPENAL_SOUND:
theApp.sound = newOpenAL(); theApp.sound = newOpenAL();
break; break;
#endif
#ifndef NO_XAUDIO2
case XAUDIO2:
theApp.sound = newXAudio2_Output();
break;
#endif #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 ) if( ( audioAPI != DIRECTSOUND )
#ifndef NO_OAL #ifndef NO_OAL
&& ( audioAPI != OPENAL_SOUND ) && ( audioAPI != OPENAL_SOUND )
#endif
#ifndef NO_XAUDIO2
&& ( audioAPI != XAUDIO2 )
#endif #endif
) { ) {
audioAPI = DIRECTSOUND; audioAPI = DIRECTSOUND;

View File

@ -9,39 +9,10 @@
// //
#include "afxres.h" #include "afxres.h"
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS #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 // English (U.S.) resources
@ -1698,6 +1669,8 @@ BEGIN
BEGIN BEGIN
MENUITEM "DirectSound", ID_OUTPUTAPI_DIRECTSOUND MENUITEM "DirectSound", ID_OUTPUTAPI_DIRECTSOUND
MENUITEM SEPARATOR MENUITEM SEPARATOR
MENUITEM "XAudio2", ID_OUTPUTAPI_XAUDIO2
MENUITEM SEPARATOR
MENUITEM "OpenAL", ID_OUTPUTAPI_OPENAL MENUITEM "OpenAL", ID_OUTPUTAPI_OPENAL
MENUITEM " Configuration...", ID_OUTPUTAPI_OALCONFIGURATION MENUITEM " Configuration...", ID_OUTPUTAPI_OALCONFIGURATION
MENUITEM SEPARATOR MENUITEM SEPARATOR
@ -2219,6 +2192,12 @@ BEGIN
IDS_AVI_CANNOT_WRITE_VIDEO "Cannot write video frame to AVI file." 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_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_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 END
#endif // English (U.S.) resources #endif // English (U.S.) resources
@ -2242,7 +2221,7 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS
2 TEXTINCLUDE 2 TEXTINCLUDE
BEGIN BEGIN
"#include ""afxres.h""\r\0" "#include ""afxres.h""\r\r\0"
END END
#endif // APSTUDIO_INVOKED #endif // APSTUDIO_INVOKED
@ -2251,12 +2230,3 @@ END
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

295
src/win32/XAudio2.cpp Normal file
View File

@ -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 <Xaudio2.h>
// 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

View File

@ -538,6 +538,10 @@
#define IDS_AVI_CANNOT_WRITE_VIDEO 2005 #define IDS_AVI_CANNOT_WRITE_VIDEO 2005
#define IDS_AVI_CANNOT_WRITE_AUDIO 2006 #define IDS_AVI_CANNOT_WRITE_AUDIO 2006
#define IDS_FILTER_GBCROM 2007 #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_HELP_ABOUT 40001
#define ID_FILE_EXIT 40002 #define ID_FILE_EXIT 40002
#define ID_OPTIONS_VIDEO_FRAMESKIP_0 40003 #define ID_OPTIONS_VIDEO_FRAMESKIP_0 40003
@ -845,13 +849,14 @@
#define ID_EMULATOR_BIOSFILES 40356 #define ID_EMULATOR_BIOSFILES 40356
#define ID_FILE_OPENGBC 40357 #define ID_FILE_OPENGBC 40357
#define ID_FILE_OPEN_GBC 40358 #define ID_FILE_OPEN_GBC 40358
#define ID_OUTPUTAPI_XAUDIO2 40359
// Next default values for new objects // Next default values for new objects
// //
#ifdef APSTUDIO_INVOKED #ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS #ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 163 #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_CONTROL_VALUE 1284
#define _APS_NEXT_SYMED_VALUE 103 #define _APS_NEXT_SYMED_VALUE 103
#endif #endif

View File

@ -26,7 +26,6 @@
// Target for Windows 2000 (Required by GetMenuBar and other functions) // Target for Windows 2000 (Required by GetMenuBar and other functions)
#define NTDDI_VERSION NTDDI_WIN2K // new #define NTDDI_VERSION NTDDI_WIN2K // new
#define _WIN32_WINNT 0x0500 // old #define _WIN32_WINNT 0x0500 // old
//#define WINVER 0x0500
// Enable STRICT type checking // Enable STRICT type checking
#define STRICT #define STRICT
@ -36,3 +35,4 @@
#include <afxcmn.h> #include <afxcmn.h>
#include <afxdlgs.h> #include <afxdlgs.h>
#include "VBA.h" #include "VBA.h"
#include "resource.h"