Added XAudio2 backend.
Windows audio backend with lower latency. Audio never glitches on my machine but the number of buffers may be set too aggressively. If you run into problems try turning up NUM_BUFFERS in the h file and leave some feedback. From my tests games seem to prefer filling the buffer with smaller chunks. For this to work the callback that fills the buffer needs to either spin or run async so I went with async. git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@6371 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
parent
6ece147568
commit
1991a4f3a5
|
@ -413,14 +413,6 @@
|
|||
<Filter
|
||||
Name="SoundStreams"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\Src\NullSoundStream.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\NullSoundStream.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\AOSoundStream.cpp"
|
||||
>
|
||||
|
@ -437,6 +429,14 @@
|
|||
RelativePath=".\Src\DSoundStream.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\NullSoundStream.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\NullSoundStream.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\OpenALStream.cpp"
|
||||
>
|
||||
|
@ -449,6 +449,14 @@
|
|||
RelativePath=".\Src\SoundStream.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\XAudio2Stream.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Src\XAudio2Stream.h"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<File
|
||||
RelativePath=".\Src\aldlist.cpp"
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "Mixer.h"
|
||||
#include "NullSoundStream.h"
|
||||
#include "DSoundStream.h"
|
||||
#include "XAudio2Stream.h"
|
||||
#include "AOSoundStream.h"
|
||||
#include "AlsaSoundStream.h"
|
||||
#ifdef __APPLE__
|
||||
|
@ -42,6 +43,8 @@ namespace AudioCommon
|
|||
soundStream = new NullSound(mixer, g_dspInitialize.hWnd);
|
||||
else if (backend == BACKEND_DIRECTSOUND && DSound::isValid())
|
||||
soundStream = new DSound(mixer, g_dspInitialize.hWnd);
|
||||
else if (backend == BACKEND_XAUDIO2 && XAudio2::isValid())
|
||||
soundStream = new XAudio2(mixer);
|
||||
else if (backend == BACKEND_AOSOUND && AOSound::isValid())
|
||||
soundStream = new AOSound(mixer);
|
||||
else if (backend == BACKEND_ALSA && AlsaSound::isValid())
|
||||
|
@ -98,6 +101,8 @@ namespace AudioCommon
|
|||
backends.push_back(BACKEND_NULLSOUND);
|
||||
if (DSound::isValid())
|
||||
backends.push_back(BACKEND_DIRECTSOUND);
|
||||
if (XAudio2::isValid())
|
||||
backends.push_back(BACKEND_XAUDIO2);
|
||||
if (OpenALStream::isValid())
|
||||
backends.push_back(BACKEND_OPENAL);
|
||||
if (AOSound::isValid())
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#define BACKEND_OPENAL "OpenAL"
|
||||
#define BACKEND_ALSA "ALSA"
|
||||
#define BACKEND_PULSEAUDIO "Pulse"
|
||||
#define BACKEND_XAUDIO2 "XAudio2"
|
||||
|
||||
struct AudioCommonConfig
|
||||
{
|
||||
|
|
|
@ -24,5 +24,7 @@ else:
|
|||
files += [ 'PulseAudioStream.cpp' ]
|
||||
if sys.platform == 'win32':
|
||||
files += [ 'DSoundStream.cpp' ]
|
||||
if sys.platform == 'win32':
|
||||
files += [ 'XAudio2Stream.cpp' ]
|
||||
|
||||
env.StaticLibrary(env['local_libs'] + 'audiocommon', files)
|
||||
|
|
|
@ -0,0 +1,211 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// 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, version 2.0.
|
||||
|
||||
// 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 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "AudioCommon.h"
|
||||
#include "XAudio2Stream.h"
|
||||
|
||||
struct StreamingVoiceContext : public IXAudio2VoiceCallback
|
||||
{
|
||||
IXAudio2SourceVoice* pSourceVoice;
|
||||
CMixer *m_mixer;
|
||||
Common::EventEx *soundSyncEvent;
|
||||
short *xaBuffer;
|
||||
|
||||
StreamingVoiceContext(IXAudio2 *pXAudio2, CMixer *pMixer, Common::EventEx *pSyncEvent)
|
||||
{
|
||||
|
||||
m_mixer = pMixer;
|
||||
soundSyncEvent = pSyncEvent;
|
||||
|
||||
WAVEFORMATEXTENSIBLE wfx;
|
||||
|
||||
memset(&wfx, 0, sizeof(WAVEFORMATEXTENSIBLE));
|
||||
wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
wfx.Format.nSamplesPerSec = m_mixer->GetSampleRate();
|
||||
wfx.Format.nChannels = 2;
|
||||
wfx.Format.wBitsPerSample = 16;
|
||||
wfx.Format.nBlockAlign = wfx.Format.nChannels*wfx.Format.wBitsPerSample/8;
|
||||
wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
|
||||
wfx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
|
||||
wfx.Samples.wValidBitsPerSample = 16;
|
||||
wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
|
||||
wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
|
||||
// create source voice
|
||||
HRESULT hr;
|
||||
if(FAILED(hr = pXAudio2->CreateSourceVoice(&pSourceVoice, (WAVEFORMATEX*)&wfx, XAUDIO2_VOICE_NOSRC, 1.0f, this)))
|
||||
PanicAlert("XAudio2 CreateSourceVoice failed: %#X", hr);
|
||||
|
||||
pSourceVoice->FlushSourceBuffers();
|
||||
pSourceVoice->Start();
|
||||
|
||||
xaBuffer = new s16[NUM_BUFFERS * BUFFER_SIZE];
|
||||
memset(xaBuffer, 0, NUM_BUFFERS * BUFFER_SIZE_BYTES);
|
||||
|
||||
//start buffers with silence
|
||||
for(int i=0; i < NUM_BUFFERS; i++)
|
||||
{
|
||||
XAUDIO2_BUFFER buf = {0};
|
||||
buf.AudioBytes = BUFFER_SIZE_BYTES;
|
||||
buf.pAudioData = (BYTE *) &xaBuffer[i * BUFFER_SIZE];
|
||||
buf.pContext = (void *) buf.pAudioData;
|
||||
|
||||
pSourceVoice->SubmitSourceBuffer(&buf);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
~StreamingVoiceContext()
|
||||
{
|
||||
IXAudio2SourceVoice* temp = pSourceVoice;
|
||||
pSourceVoice = NULL;
|
||||
temp->FlushSourceBuffers();
|
||||
temp->DestroyVoice();
|
||||
safe_delete_array(xaBuffer);
|
||||
}
|
||||
|
||||
void StreamingVoiceContext::Stop() {
|
||||
if (pSourceVoice)
|
||||
pSourceVoice->Stop();
|
||||
}
|
||||
|
||||
void StreamingVoiceContext::Play() {
|
||||
if (pSourceVoice)
|
||||
pSourceVoice->Start();
|
||||
}
|
||||
|
||||
STDMETHOD_(void, OnVoiceError) (THIS_ void* pBufferContext, HRESULT Error) {}
|
||||
STDMETHOD_(void, OnVoiceProcessingPassStart) (UINT32) {}
|
||||
STDMETHOD_(void, OnVoiceProcessingPassEnd) () {}
|
||||
STDMETHOD_(void, OnBufferStart) (void*) {}
|
||||
STDMETHOD_(void, OnLoopEnd) (void*) {}
|
||||
STDMETHOD_(void, OnStreamEnd) () {}
|
||||
STDMETHOD_(void, OnBufferEnd) (void* context)
|
||||
{ //
|
||||
// buffer end callback; gets SAMPLES_PER_BUFFER samples for a new buffer
|
||||
//
|
||||
if( !pSourceVoice || !context) return;
|
||||
|
||||
//soundSyncEvent->Init();
|
||||
//soundSyncEvent->Wait(); //sync
|
||||
//soundSyncEvent->Spin(); //or tight sync
|
||||
|
||||
//if (!pSourceVoice) return;
|
||||
|
||||
m_mixer->Mix((short *)context, SAMPLES_PER_BUFFER);
|
||||
|
||||
|
||||
XAUDIO2_BUFFER buf = {0};
|
||||
buf.AudioBytes = BUFFER_SIZE_BYTES;
|
||||
buf.pAudioData = (byte*)context;
|
||||
buf.pContext = context;
|
||||
|
||||
pSourceVoice->SubmitSourceBuffer(&buf);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
StreamingVoiceContext* pVoiceContext = 0;
|
||||
|
||||
bool XAudio2::Start()
|
||||
{
|
||||
//soundSyncEvent.Init();
|
||||
|
||||
// XAudio2 init
|
||||
CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
HRESULT hr;
|
||||
if(FAILED(hr = XAudio2Create(&pXAudio2, 0, XAUDIO2_ANY_PROCESSOR))) //callback dosent seem to run on a speecific cpu anyways
|
||||
{
|
||||
PanicAlert("XAudio2 init failed: %#X", hr);
|
||||
CoUninitialize();
|
||||
return false;
|
||||
}
|
||||
|
||||
// XAudio2 master voice
|
||||
// XAUDIO2_DEFAULT_CHANNELS instead of 2 for expansion?
|
||||
if(FAILED(hr = pXAudio2->CreateMasteringVoice(&pMasteringVoice, 2, m_mixer->GetSampleRate())))
|
||||
{
|
||||
PanicAlert("XAudio2 master voice creation failed: %#X", hr);
|
||||
safe_release(pXAudio2);
|
||||
CoUninitialize();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Volume
|
||||
if (pMasteringVoice)
|
||||
pMasteringVoice->SetVolume(m_volume);
|
||||
|
||||
if (pXAudio2)
|
||||
pVoiceContext = new StreamingVoiceContext(pXAudio2, m_mixer, &soundSyncEvent);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void XAudio2::SetVolume(int volume)
|
||||
{
|
||||
//linear 1- .01
|
||||
m_volume = (float)volume / 100.0;
|
||||
|
||||
if (pMasteringVoice)
|
||||
pMasteringVoice->SetVolume(m_volume);
|
||||
|
||||
}
|
||||
|
||||
|
||||
//XAUDIO2_PERFORMANCE_DATA perfData;
|
||||
//int xi = 0;
|
||||
void XAudio2::Update()
|
||||
{
|
||||
//soundSyncEvent.Set();
|
||||
|
||||
//xi++;
|
||||
//if (xi == 100000) {
|
||||
// xi = 0;
|
||||
// pXAudio2->GetPerformanceData(&perfData);
|
||||
// NOTICE_LOG(DSPHLE, "XAudio2 latency (samples): %i",perfData.CurrentLatencyInSamples);
|
||||
// NOTICE_LOG(DSPHLE, "XAudio2 total glitches: %i",perfData.GlitchesSinceEngineStarted);
|
||||
//}
|
||||
}
|
||||
|
||||
void XAudio2::Clear(bool mute)
|
||||
{
|
||||
m_muted = mute;
|
||||
|
||||
if (pVoiceContext)
|
||||
{
|
||||
if (m_muted)
|
||||
pVoiceContext->Stop();
|
||||
else
|
||||
pVoiceContext->Play();
|
||||
}
|
||||
}
|
||||
|
||||
void XAudio2::Stop()
|
||||
{
|
||||
//soundSyncEvent.Set();
|
||||
|
||||
safe_delete(pVoiceContext);
|
||||
pVoiceContext = NULL;
|
||||
|
||||
if(pMasteringVoice)
|
||||
pMasteringVoice->DestroyVoice();
|
||||
|
||||
safe_release(pXAudio2);
|
||||
pMasteringVoice = NULL;
|
||||
CoUninitialize();
|
||||
//soundSyncEvent.Shutdown();
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// 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, version 2.0.
|
||||
|
||||
// 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 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _XAUDIO2STREAM_H_
|
||||
#define _XAUDIO2STREAM_H_
|
||||
|
||||
#include "SoundStream.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Thread.h"
|
||||
#include <xaudio2.h>
|
||||
|
||||
const int NUM_BUFFERS = 3;
|
||||
const int SAMPLES_PER_BUFFER = 96;
|
||||
|
||||
const int NUM_CHANNELS = 2;
|
||||
const int BUFFER_SIZE = SAMPLES_PER_BUFFER * NUM_CHANNELS;
|
||||
const int BUFFER_SIZE_BYTES = BUFFER_SIZE * sizeof(s16);
|
||||
|
||||
|
||||
#ifndef safe_delete_array
|
||||
#define safe_delete_array(p) { if(p) { delete[] (p); (p)=NULL; } }
|
||||
#endif
|
||||
#ifndef safe_delete
|
||||
#define safe_delete(a) if( (a) != NULL ) delete (a); (a) = NULL;
|
||||
#endif
|
||||
#ifndef safe_release
|
||||
#define safe_release(p) { if(p) { (p)->Release(); (p)=NULL; } }
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
class XAudio2 : public SoundStream
|
||||
{
|
||||
#ifdef _WIN32
|
||||
IXAudio2 *pXAudio2;
|
||||
IXAudio2MasteringVoice *pMasteringVoice;
|
||||
IXAudio2SourceVoice *pSourceVoice;
|
||||
|
||||
Common::EventEx soundSyncEvent;
|
||||
float m_volume;
|
||||
|
||||
|
||||
bool Init();
|
||||
public:
|
||||
XAudio2(CMixer *mixer)
|
||||
: SoundStream(mixer),
|
||||
pXAudio2(0),
|
||||
pMasteringVoice(0),
|
||||
pSourceVoice(0),
|
||||
m_volume(1.0f) {}
|
||||
|
||||
virtual ~XAudio2() {}
|
||||
|
||||
virtual bool Start();
|
||||
virtual void SetVolume(int volume);
|
||||
virtual void Stop();
|
||||
virtual void Clear(bool mute);
|
||||
static bool isValid() { return true; }
|
||||
virtual bool usesMixer() const { return true; }
|
||||
virtual void Update();
|
||||
|
||||
#else
|
||||
public:
|
||||
XAudio2(CMixer *mixer, void *hWnd = NULL)
|
||||
: SoundStream(mixer)
|
||||
{}
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif //_XAUDIO2STREAM_H_
|
|
@ -155,6 +155,7 @@ bool DSPConfigDialogHLE::SupportsVolumeChanges(std::string backend)
|
|||
// too much just to enable/disable a stupid slider...
|
||||
return (backend == BACKEND_DIRECTSOUND ||
|
||||
backend == BACKEND_OPENAL ||
|
||||
backend == BACKEND_XAUDIO2 ||
|
||||
backend == BACKEND_PULSEAUDIO);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue