Audio system overhaul for support of multiple audio drivers.

This commit is contained in:
gibbed 2014-01-25 03:18:22 -08:00
parent bbe50cfe26
commit 47c71d702a
13 changed files with 418 additions and 231 deletions

View File

@ -0,0 +1,27 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/apu/audio_driver.h>
#include <xenia/emulator.h>
#include <xenia/cpu/processor.h>
#include <xenia/cpu/xenon_thread_state.h>
using namespace xe;
using namespace xe::apu;
using namespace xe::cpu;
AudioDriver::AudioDriver(Emulator* emulator) :
emulator_(emulator), memory_(emulator->memory()) {
}
AudioDriver::~AudioDriver() {
}

View File

@ -0,0 +1,44 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_APU_AUDIO_DRIVER_H_
#define XENIA_APU_AUDIO_DRIVER_H_
#include <xenia/core.h>
#include <xenia/xbox.h>
XEDECLARECLASS1(xe, Emulator);
XEDECLARECLASS2(xe, cpu, Processor);
XEDECLARECLASS2(xe, cpu, XenonThreadState);
namespace xe {
namespace apu {
class AudioDriver {
public:
AudioDriver(Emulator* emulator);
virtual ~AudioDriver();
virtual void SubmitFrame(uint32_t samples_ptr) = 0;
protected:
Emulator* emulator_;
Memory* memory_;
cpu::Processor* processor_;
};
} // namespace apu
} // namespace xe
#endif // XENIA_APU_AUDIO_DRIVER_H_

View File

@ -8,6 +8,7 @@
*/ */
#include <xenia/apu/audio_system.h> #include <xenia/apu/audio_system.h>
#include <xenia/apu/audio_driver.h>
#include <xenia/emulator.h> #include <xenia/emulator.h>
#include <xenia/cpu/processor.h> #include <xenia/cpu/processor.h>
@ -21,12 +22,19 @@ using namespace xe::cpu;
AudioSystem::AudioSystem(Emulator* emulator) : AudioSystem::AudioSystem(Emulator* emulator) :
emulator_(emulator), memory_(emulator->memory()), emulator_(emulator), memory_(emulator->memory()),
thread_(0), running_(false), thread_(0), running_(false) {
client_({ 0 }) {
lock_ = xe_mutex_alloc(); lock_ = xe_mutex_alloc();
memset(clients_, 0, sizeof(clients_));
for (size_t i = 0; i < maximum_client_count_; ++i) {
client_wait_handles_[i] = CreateEvent(NULL, TRUE, FALSE, NULL);
unused_clients_.push(i);
}
} }
AudioSystem::~AudioSystem() { AudioSystem::~AudioSystem() {
for (size_t i = 0; i < maximum_client_count_; ++i) {
CloseHandle(client_wait_handles_[i]);
}
xe_mutex_free(lock_); xe_mutex_free(lock_);
} }
@ -70,27 +78,34 @@ void AudioSystem::ThreadStart() {
// Main run loop. // Main run loop.
while (running_) { while (running_) {
// Pump worker. auto result = WaitForMultipleObjectsEx(maximum_client_count_, client_wait_handles_, FALSE, INFINITE, FALSE);
// This may block. if (result == WAIT_FAILED) {
Pump(); DWORD err = GetLastError();
XEASSERTALWAYS();
}
size_t pumped = 0;
if (result >= WAIT_OBJECT_0 && result <= WAIT_OBJECT_0 + (maximum_client_count_ - 1)) {
size_t index = result - WAIT_OBJECT_0;
do {
xe_mutex_lock(lock_); xe_mutex_lock(lock_);
uint32_t client_callback = client_.callback; uint32_t client_callback = clients_[index].callback;
uint32_t client_callback_arg = client_.wrapped_callback_arg; uint32_t client_callback_arg = clients_[index].wrapped_callback_arg;
xe_mutex_unlock(lock_); xe_mutex_unlock(lock_);
if (client_callback) { if (client_callback) {
processor->Execute( processor->Execute(thread_state_, client_callback, client_callback_arg, 0);
thread_state_, client_callback, client_callback_arg, 0); }
} else { pumped++;
Sleep(500); index++;
} while (index < maximum_client_count_ && WaitForSingleObject(client_wait_handles_[index], 0) == WAIT_OBJECT_0);
} }
if (!running_) { if (!running_) {
break; break;
} }
// Pump audio system. if (!pumped) {
Pump(); Sleep(500);
}
} }
running_ = false; running_ = false;
@ -109,23 +124,53 @@ void AudioSystem::Shutdown() {
memory()->HeapFree(thread_block_, 0); memory()->HeapFree(thread_block_, 0);
} }
void AudioSystem::RegisterClient( X_STATUS AudioSystem::RegisterClient(
uint32_t callback, uint32_t callback_arg) { uint32_t callback, uint32_t callback_arg, size_t* out_index) {
// Only support one client for now. XEASSERTTRUE(unused_clients_.size());
XEASSERTZERO(client_.callback); xe_mutex_lock(lock_);
auto index = unused_clients_.front();
auto wait_handle = client_wait_handles_[index];
ResetEvent(wait_handle);
AudioDriver* driver;
auto result = CreateDriver(index, wait_handle, &driver);
if (XFAILED(result)) {
return result;
}
XEASSERTNOTNULL(driver != NULL);
unused_clients_.pop();
uint32_t ptr = (uint32_t)memory()->HeapAlloc(0, 0x4, 0); uint32_t ptr = (uint32_t)memory()->HeapAlloc(0, 0x4, 0);
auto mem = memory()->membase(); auto mem = memory()->membase();
XESETUINT32BE(mem + ptr, callback_arg); XESETUINT32BE(mem + ptr, callback_arg);
clients_[index] = { driver, callback, callback_arg, ptr };
if (out_index) {
*out_index = index;
}
xe_mutex_unlock(lock_);
return X_STATUS_SUCCESS;
}
void AudioSystem::SubmitFrame(size_t index, uint32_t samples_ptr) {
xe_mutex_lock(lock_); xe_mutex_lock(lock_);
client_ = { callback, callback_arg, ptr }; XEASSERTTRUE(index < maximum_client_count_);
XEASSERTTRUE(clients_[index].driver != NULL);
(clients_[index].driver)->SubmitFrame(samples_ptr);
ResetEvent(client_wait_handles_[index]);
xe_mutex_unlock(lock_); xe_mutex_unlock(lock_);
} }
void AudioSystem::UnregisterClient() { void AudioSystem::UnregisterClient(size_t index) {
xe_mutex_lock(lock_); xe_mutex_lock(lock_);
client_ = { 0 }; XEASSERTTRUE(index < maximum_client_count_);
DestroyDriver(clients_[index].driver);
clients_[index] = { 0 };
unused_clients_.push(index);
xe_mutex_unlock(lock_); xe_mutex_unlock(lock_);
} }

View File

@ -13,11 +13,12 @@
#include <xenia/core.h> #include <xenia/core.h>
#include <xenia/xbox.h> #include <xenia/xbox.h>
#include <queue>
XEDECLARECLASS1(xe, Emulator); XEDECLARECLASS1(xe, Emulator);
XEDECLARECLASS2(xe, cpu, Processor); XEDECLARECLASS2(xe, cpu, Processor);
XEDECLARECLASS2(xe, cpu, XenonThreadState); XEDECLARECLASS2(xe, cpu, XenonThreadState);
XEDECLARECLASS2(xe, apu, AudioDriver);
namespace xe { namespace xe {
namespace apu { namespace apu {
@ -34,9 +35,12 @@ public:
virtual X_STATUS Setup(); virtual X_STATUS Setup();
virtual void Shutdown(); virtual void Shutdown();
void RegisterClient(uint32_t callback, uint32_t callback_arg); X_STATUS RegisterClient(uint32_t callback, uint32_t callback_arg, size_t* out_index);
void UnregisterClient(); void UnregisterClient(size_t index);
virtual void SubmitFrame(uint32_t samples_ptr) = 0; void SubmitFrame(size_t index, uint32_t samples_ptr);
virtual X_STATUS CreateDriver(size_t index, HANDLE wait_handle, AudioDriver** out_driver) = 0;
virtual void DestroyDriver(AudioDriver* driver) = 0;
bool HandlesRegister(uint64_t addr); bool HandlesRegister(uint64_t addr);
virtual uint64_t ReadRegister(uint64_t addr); virtual uint64_t ReadRegister(uint64_t addr);
@ -44,7 +48,6 @@ public:
protected: protected:
virtual void Initialize(); virtual void Initialize();
virtual void Pump() = 0;
private: private:
static void ThreadStartThunk(AudioSystem* this_ptr) { static void ThreadStartThunk(AudioSystem* this_ptr) {
@ -76,11 +79,17 @@ protected:
bool running_; bool running_;
xe_mutex_t* lock_; xe_mutex_t* lock_;
static const size_t maximum_client_count_ = 8;
struct { struct {
AudioDriver* driver;
uint32_t callback; uint32_t callback;
uint32_t callback_arg; uint32_t callback_arg;
uint32_t wrapped_callback_arg; uint32_t wrapped_callback_arg;
} client_; } clients_[maximum_client_count_];
HANDLE client_wait_handles_[maximum_client_count_];
std::queue<size_t> unused_clients_;
}; };

View File

@ -24,18 +24,10 @@ NopAudioSystem::NopAudioSystem(Emulator* emulator) :
NopAudioSystem::~NopAudioSystem() { NopAudioSystem::~NopAudioSystem() {
} }
void NopAudioSystem::Initialize() { X_STATUS NopAudioSystem::CreateDriver(size_t index, HANDLE wait_handle, AudioDriver** out_driver) {
AudioSystem::Initialize(); return X_STATUS_NOT_IMPLEMENTED;
} }
void NopAudioSystem::Pump() { void NopAudioSystem::DestroyDriver(AudioDriver* driver) {
// XEASSERTALWAYS();
}
void NopAudioSystem::SubmitFrame(uint32_t frame_ptr) {
// Process samples! They are big-endian floats.
}
void NopAudioSystem::Shutdown() {
AudioSystem::Shutdown();
} }

View File

@ -26,15 +26,8 @@ public:
NopAudioSystem(Emulator* emulator); NopAudioSystem(Emulator* emulator);
virtual ~NopAudioSystem(); virtual ~NopAudioSystem();
virtual void Shutdown(); virtual X_STATUS CreateDriver(size_t index, HANDLE wait_handle, AudioDriver** out_driver);
virtual void DestroyDriver(AudioDriver* driver);
virtual void SubmitFrame(uint32_t frame_ptr);
protected:
virtual void Initialize();
virtual void Pump();
private:
}; };

View File

@ -4,6 +4,8 @@
'apu-private.h', 'apu-private.h',
'apu.cc', 'apu.cc',
'apu.h', 'apu.h',
'audio_driver.cc',
'audio_driver.h',
'audio_system.cc', 'audio_system.cc',
'audio_system.h', 'audio_system.h',
], ],

View File

@ -4,6 +4,8 @@
'xaudio2_apu-private.h', 'xaudio2_apu-private.h',
'xaudio2_apu.cc', 'xaudio2_apu.cc',
'xaudio2_apu.h', 'xaudio2_apu.h',
'xaudio2_audio_driver.cc',
'xaudio2_audio_driver.h',
'xaudio2_audio_system.cc', 'xaudio2_audio_system.cc',
'xaudio2_audio_system.h', 'xaudio2_audio_system.h',
], ],

View File

@ -0,0 +1,171 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/apu/xaudio2/xaudio2_audio_driver.h>
#include <xenia/apu/apu-private.h>
#include <xenia/emulator.h>
using namespace xe;
using namespace xe::apu;
using namespace xe::apu::xaudio2;
class XAudio2AudioDriver::VoiceCallback : public IXAudio2VoiceCallback {
public:
VoiceCallback(HANDLE wait_handle) : wait_handle_(wait_handle) {}
~VoiceCallback() {}
void OnStreamEnd() {}
void OnVoiceProcessingPassEnd() {}
void OnVoiceProcessingPassStart(uint32_t samples_required) {}
void OnBufferEnd(void* context);
void OnBufferStart(void* context) {}
void OnLoopEnd(void* context) {}
void OnVoiceError(void* context, HRESULT result) {}
private:
HANDLE wait_handle_;
};
void XAudio2AudioDriver::VoiceCallback::OnBufferEnd(void* context) {
SetEvent(wait_handle_);
}
XAudio2AudioDriver::XAudio2AudioDriver(Emulator* emulator, HANDLE wait) :
audio_(0), mastering_voice_(0), pcm_voice_(0),
wait_handle_(wait), voice_callback_(0),
AudioDriver(emulator) {
}
XAudio2AudioDriver::~XAudio2AudioDriver() {
}
const DWORD ChannelMasks[] =
{
0, // TODO: fixme
0, // TODO: fixme
SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY,
0, // TODO: fixme
0, // TODO: fixme
0, // TODO: fixme
SPEAKER_FRONT_LEFT | SPEAKER_FRONT_CENTER | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
0, // TODO: fixme
};
void XAudio2AudioDriver::Initialize() {
HRESULT hr;
voice_callback_ = new VoiceCallback(wait_handle_);
hr = XAudio2Create(&audio_, 0, XAUDIO2_DEFAULT_PROCESSOR);
if (FAILED(hr)) {
XELOGE("XAudio2Create failed with %.8X", hr);
XEASSERTALWAYS();
return;
}
XAUDIO2_DEBUG_CONFIGURATION config;
config.TraceMask = XAUDIO2_LOG_ERRORS | XAUDIO2_LOG_WARNINGS;
config.BreakMask = 0;
config.LogThreadID = FALSE;
config.LogTiming = TRUE;
config.LogFunctionName = TRUE;
config.LogFileline = TRUE;
audio_->SetDebugConfiguration(&config);
hr = audio_->CreateMasteringVoice(&mastering_voice_, frame_channels_, 48000);
if (FAILED(hr)) {
XELOGE("CreateMasteringVoice failed with %.8X", hr);
XEASSERTALWAYS();
return;
}
WAVEFORMATIEEEFLOATEX waveformat;
waveformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
waveformat.Format.nChannels = frame_channels_;
waveformat.Format.nSamplesPerSec = 48000;
waveformat.Format.wBitsPerSample = 32;
waveformat.Format.nBlockAlign = (waveformat.Format.nChannels * waveformat.Format.wBitsPerSample) / 8;
waveformat.Format.nAvgBytesPerSec = waveformat.Format.nSamplesPerSec * waveformat.Format.nBlockAlign;
waveformat.Format.cbSize = sizeof(waveformat) - sizeof(WAVEFORMATEX);
waveformat.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
waveformat.Samples.wValidBitsPerSample = waveformat.Format.wBitsPerSample;
waveformat.dwChannelMask = ChannelMasks[waveformat.Format.nChannels];
hr = audio_->CreateSourceVoice(&pcm_voice_, &waveformat.Format,
XAUDIO2_VOICE_NOSRC, 1.0f,
voice_callback_);
if (FAILED(hr)) {
XELOGE("CreateSourceVoice failed with %.8X", hr);
XEASSERTALWAYS();
return;
}
hr = pcm_voice_->Start();
if (FAILED(hr)) {
XELOGE("Start failed with %.8X", hr);
XEASSERTALWAYS();
return;
}
SetEvent(wait_handle_);
}
void XAudio2AudioDriver::SubmitFrame(uint32_t frame_ptr) {
// Process samples! They are big-endian floats.
HRESULT hr;
auto input_frame = reinterpret_cast<float*>(emulator_->memory()->membase() + frame_ptr);
auto output_frame = reinterpret_cast<float*>(frame_);
auto interleave_channels = frame_channels_;
bool burp = false;
// interleave the data
for (int index = 0, o = 0; index < channel_samples_; ++index) {
for (int channel = 0, table = 0; channel < interleave_channels; ++channel, table += channel_samples_) {
output_frame[o++] = XESWAPF32BE(input_frame[table + index]);
}
}
XAUDIO2_BUFFER buffer;
buffer.Flags = 0;
buffer.pAudioData = reinterpret_cast<BYTE*>(&frame_[0]);
buffer.AudioBytes = sizeof(frame_);
buffer.PlayBegin = 0;
buffer.PlayLength = channel_samples_;
buffer.LoopBegin = XAUDIO2_NO_LOOP_REGION;
buffer.LoopLength = 0;
buffer.LoopCount = 0;
buffer.pContext = 0;
hr = pcm_voice_->SubmitSourceBuffer(&buffer);
if (FAILED(hr)) {
XELOGE("SubmitSourceBuffer failed with %.8X", hr);
XEASSERTALWAYS();
return;
}
}
void XAudio2AudioDriver::Shutdown() {
pcm_voice_->Stop();
pcm_voice_->DestroyVoice();
pcm_voice_ = NULL;
mastering_voice_->DestroyVoice();
mastering_voice_ = NULL;
audio_->StopEngine();
XESAFERELEASE(audio_);
delete voice_callback_;
CloseHandle(wait_handle_);
}

View File

@ -0,0 +1,54 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_APU_XAUDIO2_XAUDIO2_AUDIO_DRIVER_H_
#define XENIA_APU_XAUDIO2_XAUDIO2_AUDIO_DRIVER_H_
#include <xenia/core.h>
#include <xenia/apu/audio_driver.h>
#include <xenia/apu/xaudio2/xaudio2_apu-private.h>
#include <xaudio2.h>
namespace xe {
namespace apu {
namespace xaudio2 {
class XAudio2AudioDriver : public AudioDriver {
public:
XAudio2AudioDriver(Emulator* emulator, HANDLE wait);
virtual ~XAudio2AudioDriver();
virtual void Initialize();
virtual void SubmitFrame(uint32_t frame_ptr);
virtual void Shutdown();
private:
IXAudio2* audio_;
IXAudio2MasteringVoice* mastering_voice_;
IXAudio2SourceVoice* pcm_voice_;
static const int frame_channels_ = 6;
static const int channel_samples_ = 256;
float frame_[frame_channels_ * channel_samples_];
HANDLE wait_handle_;
class VoiceCallback;
VoiceCallback* voice_callback_;
};
} // namespace xaudio2
} // namespace apu
} // namespace xe
#endif // XENIA_APU_XAUDIO2_XAUDIO2_AUDIO_DRIVER_H_

View File

@ -8,183 +8,40 @@
*/ */
#include <xenia/apu/xaudio2/xaudio2_audio_system.h> #include <xenia/apu/xaudio2/xaudio2_audio_system.h>
#include <xenia/apu/xaudio2/xaudio2_audio_driver.h>
#include <xenia/apu/apu-private.h> #include <xenia/apu/apu-private.h>
#include <xenia/emulator.h> #include <xenia/emulator.h>
using namespace xe; using namespace xe;
using namespace xe::apu; using namespace xe::apu;
using namespace xe::apu::xaudio2; using namespace xe::apu::xaudio2;
class XAudio2AudioSystem::VoiceCallback : public IXAudio2VoiceCallback {
public:
VoiceCallback(HANDLE wait_handle) : wait_handle_(wait_handle) {}
~VoiceCallback() {}
void OnStreamEnd() {}
void OnVoiceProcessingPassEnd() {}
void OnVoiceProcessingPassStart(UINT32 SamplesRequired) {}
void OnBufferEnd(void * pBufferContext) {
SetEvent(wait_handle_);
}
void OnBufferStart(void * pBufferContext) {}
void OnLoopEnd(void * pBufferContext) {}
void OnVoiceError(void * pBufferContext, HRESULT Error) {}
private:
HANDLE wait_handle_;
};
XAudio2AudioSystem::XAudio2AudioSystem(Emulator* emulator) : XAudio2AudioSystem::XAudio2AudioSystem(Emulator* emulator) :
audio_(0), mastering_voice_(0), pcm_voice_(0),
active_channels_(0),
wait_handle_(NULL), voice_callback_(0),
AudioSystem(emulator) { AudioSystem(emulator) {
} }
XAudio2AudioSystem::~XAudio2AudioSystem() { XAudio2AudioSystem::~XAudio2AudioSystem() {
} }
const DWORD ChannelMasks[] =
{
0, // TODO: fixme
0, // TODO: fixme
SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY,
0, // TODO: fixme
0, // TODO: fixme
0, // TODO: fixme
SPEAKER_FRONT_LEFT | SPEAKER_FRONT_CENTER | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
0, // TODO: fixme
};
void XAudio2AudioSystem::Initialize() { void XAudio2AudioSystem::Initialize() {
AudioSystem::Initialize(); AudioSystem::Initialize();
HRESULT hr;
wait_handle_ = CreateEvent(NULL, TRUE, TRUE, NULL);
voice_callback_ = new VoiceCallback(wait_handle_);
hr = XAudio2Create(&audio_, 0, XAUDIO2_DEFAULT_PROCESSOR);
if (FAILED(hr)) {
XELOGE("XAudio2Create failed with %.8X", hr);
XEASSERTALWAYS();
return;
} }
active_channels_ = 6; X_STATUS XAudio2AudioSystem::CreateDriver(size_t index, HANDLE wait, AudioDriver** out_driver) {
XEASSERTNOTNULL(out_driver);
XAUDIO2_DEBUG_CONFIGURATION config; auto driver = new XAudio2AudioDriver(emulator_, wait);
config.TraceMask = XAUDIO2_LOG_ERRORS | XAUDIO2_LOG_WARNINGS; driver->Initialize();
config.BreakMask = 0; *out_driver = driver;
config.LogThreadID = FALSE; return X_STATUS_SUCCESS;
config.LogTiming = TRUE;
config.LogFunctionName = TRUE;
config.LogFileline = TRUE;
audio_->SetDebugConfiguration(&config);
hr = audio_->CreateMasteringVoice(&mastering_voice_, active_channels_, 48000);
if (FAILED(hr)) {
XELOGE("CreateMasteringVoice failed with %.8X", hr);
XEASSERTALWAYS();
return;
} }
WAVEFORMATIEEEFLOATEX waveformat; void XAudio2AudioSystem::DestroyDriver(AudioDriver* driver) {
XEASSERTNOTNULL(driver);
//waveformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; auto xdriver = static_cast<XAudio2AudioDriver*>(driver);
waveformat.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; xdriver->Shutdown();
waveformat.Format.nChannels = active_channels_; XEASSERTNOTNULL(xdriver);
waveformat.Format.nSamplesPerSec = 48000; delete xdriver;
waveformat.Format.wBitsPerSample = 32;
waveformat.Format.nBlockAlign = (waveformat.Format.nChannels * waveformat.Format.wBitsPerSample) / 8;
waveformat.Format.nAvgBytesPerSec = waveformat.Format.nSamplesPerSec * waveformat.Format.nBlockAlign;
waveformat.Format.cbSize = sizeof(waveformat)-sizeof(WAVEFORMATEX);
waveformat.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
waveformat.Samples.wValidBitsPerSample = waveformat.Format.wBitsPerSample;
waveformat.dwChannelMask = ChannelMasks[waveformat.Format.nChannels];
hr = audio_->CreateSourceVoice(&pcm_voice_, &waveformat.Format,
0,
XAUDIO2_DEFAULT_FREQ_RATIO, voice_callback_);
if (FAILED(hr)) {
XELOGE("CreateSourceVoice failed with %.8X", hr);
XEASSERTALWAYS();
return;
}
pcm_voice_->Start();
}
void XAudio2AudioSystem::Pump() {
XAUDIO2_VOICE_STATE state;
pcm_voice_->GetState(&state);
auto n = state.BuffersQueued;
if (n > 0) {
// Only allow one buffer to be queued at once. We only have one static
// store of data, and if we called back the game audio driver it would
// overwrite it.
//ResetEvent(wait_handle_);
}
WaitForSingleObject(wait_handle_, INFINITE);
}
void XAudio2AudioSystem::SubmitFrame(uint32_t frame_ptr) {
ResetEvent(wait_handle_);
// Process samples! They are big-endian floats.
HRESULT hr;
auto input_frame = reinterpret_cast<float*>(emulator_->memory()->membase() + frame_ptr);
auto output_frame = reinterpret_cast<float*>(frame_);
// interleave the data
for (int index = 0, o = 0; index < 256; ++index) {
for (int channel = 0, table = 0;
channel < 6 && channel < active_channels_;
++channel, table += 256) {
output_frame[o++] = XESWAPF32BE(input_frame[table + index]);
}
}
XAUDIO2_BUFFER buffer;
buffer.Flags = 0;
buffer.pAudioData = reinterpret_cast<BYTE*>(frame_);
buffer.AudioBytes = sizeof(frame_);
buffer.PlayBegin = 0;
buffer.PlayLength = 256;
buffer.LoopBegin = XAUDIO2_NO_LOOP_REGION;
buffer.LoopLength = 0;
buffer.LoopCount = 0;
buffer.pContext = 0;
hr = pcm_voice_->SubmitSourceBuffer(&buffer);
if (FAILED(hr)) {
XELOGE("SubmitSourceBuffer failed with %.8X", hr);
XEASSERTALWAYS();
return;
}
}
void XAudio2AudioSystem::Shutdown() {
pcm_voice_->Stop();
pcm_voice_->DestroyVoice();
pcm_voice_ = NULL;
mastering_voice_->DestroyVoice();
mastering_voice_ = NULL;
audio_->StopEngine();
XESAFERELEASE(audio_);
delete voice_callback_;
CloseHandle(wait_handle_);
AudioSystem::Shutdown();
} }

View File

@ -28,25 +28,11 @@ public:
XAudio2AudioSystem(Emulator* emulator); XAudio2AudioSystem(Emulator* emulator);
virtual ~XAudio2AudioSystem(); virtual ~XAudio2AudioSystem();
virtual void Shutdown(); virtual X_RESULT CreateDriver(size_t index, HANDLE wait, AudioDriver** out_driver);
virtual void DestroyDriver(AudioDriver* driver);
virtual void SubmitFrame(uint32_t frame_ptr);
protected: protected:
virtual void Initialize(); virtual void Initialize();
virtual void Pump();
private:
IXAudio2* audio_;
IXAudio2MasteringVoice* mastering_voice_;
IXAudio2SourceVoice* pcm_voice_;
int active_channels_;
static const int frame_channels_ = 6;
float frame_[frame_channels_ * 256];
HANDLE wait_handle_;
class VoiceCallback;
VoiceCallback* voice_callback_;
}; };

View File

@ -114,10 +114,16 @@ SHIM_CALL XAudioRegisterRenderDriverClient_shim(
callback_ptr, callback, callback_arg, driver_ptr); callback_ptr, callback, callback_arg, driver_ptr);
auto audio_system = state->emulator()->audio_system(); auto audio_system = state->emulator()->audio_system();
audio_system->RegisterClient(callback, callback_arg);
SHIM_SET_MEM_32(driver_ptr, 0xAADD1100); size_t index;
auto result = audio_system->RegisterClient(callback, callback_arg, &index);
if (XFAILED(result)) {
SHIM_SET_RETURN_32(result);
return;
}
XEASSERTTRUE(!(index & ~0x0000FFFF));
SHIM_SET_MEM_32(driver_ptr, 0x41550000 | (index & 0x0000FFFF));
SHIM_SET_RETURN_32(X_ERROR_SUCCESS); SHIM_SET_RETURN_32(X_ERROR_SUCCESS);
} }
@ -130,11 +136,10 @@ SHIM_CALL XAudioUnregisterRenderDriverClient_shim(
"XAudioUnregisterRenderDriverClient(%.8X)", "XAudioUnregisterRenderDriverClient(%.8X)",
driver_ptr); driver_ptr);
XEASSERT(driver_ptr == 0xAADD1100); XEASSERT((driver_ptr & 0xFFFF0000) == 0x41550000);
auto audio_system = state->emulator()->audio_system(); auto audio_system = state->emulator()->audio_system();
audio_system->UnregisterClient(); audio_system->UnregisterClient(driver_ptr & 0x0000FFFF);
SHIM_SET_RETURN_32(X_ERROR_SUCCESS); SHIM_SET_RETURN_32(X_ERROR_SUCCESS);
} }
@ -148,10 +153,10 @@ SHIM_CALL XAudioSubmitRenderDriverFrame_shim(
"XAudioSubmitRenderDriverFrame(%.8X, %.8X)", "XAudioSubmitRenderDriverFrame(%.8X, %.8X)",
driver_ptr, samples_ptr); driver_ptr, samples_ptr);
XEASSERT(driver_ptr == 0xAADD1100); XEASSERT((driver_ptr & 0xFFFF0000) == 0x41550000);
auto audio_system = state->emulator()->audio_system(); auto audio_system = state->emulator()->audio_system();
audio_system->SubmitFrame(samples_ptr); audio_system->SubmitFrame(driver_ptr & 0x0000FFFF, samples_ptr);
SHIM_SET_RETURN_32(X_ERROR_SUCCESS); SHIM_SET_RETURN_32(X_ERROR_SUCCESS);
} }