From 47c71d702ade6eb987ea1a08a2ebd19e3b16821e Mon Sep 17 00:00:00 2001 From: gibbed Date: Sat, 25 Jan 2014 03:18:22 -0800 Subject: [PATCH] Audio system overhaul for support of multiple audio drivers. --- src/xenia/apu/audio_driver.cc | 27 +++ src/xenia/apu/audio_driver.h | 44 +++++ src/xenia/apu/audio_system.cc | 93 +++++++--- src/xenia/apu/audio_system.h | 21 ++- src/xenia/apu/nop/nop_audio_system.cc | 16 +- src/xenia/apu/nop/nop_audio_system.h | 11 +- src/xenia/apu/sources.gypi | 2 + src/xenia/apu/xaudio2/sources.gypi | 2 + src/xenia/apu/xaudio2/xaudio2_audio_driver.cc | 171 ++++++++++++++++++ src/xenia/apu/xaudio2/xaudio2_audio_driver.h | 54 ++++++ src/xenia/apu/xaudio2/xaudio2_audio_system.cc | 171 ++---------------- src/xenia/apu/xaudio2/xaudio2_audio_system.h | 18 +- src/xenia/kernel/xboxkrnl_audio.cc | 19 +- 13 files changed, 418 insertions(+), 231 deletions(-) create mode 100644 src/xenia/apu/audio_driver.cc create mode 100644 src/xenia/apu/audio_driver.h create mode 100644 src/xenia/apu/xaudio2/xaudio2_audio_driver.cc create mode 100644 src/xenia/apu/xaudio2/xaudio2_audio_driver.h diff --git a/src/xenia/apu/audio_driver.cc b/src/xenia/apu/audio_driver.cc new file mode 100644 index 000000000..e3bb88756 --- /dev/null +++ b/src/xenia/apu/audio_driver.cc @@ -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 + +#include +#include +#include + + +using namespace xe; +using namespace xe::apu; +using namespace xe::cpu; + + +AudioDriver::AudioDriver(Emulator* emulator) : + emulator_(emulator), memory_(emulator->memory()) { +} + +AudioDriver::~AudioDriver() { +} diff --git a/src/xenia/apu/audio_driver.h b/src/xenia/apu/audio_driver.h new file mode 100644 index 000000000..3ec11bdca --- /dev/null +++ b/src/xenia/apu/audio_driver.h @@ -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 +#include + + +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_ diff --git a/src/xenia/apu/audio_system.cc b/src/xenia/apu/audio_system.cc index 60edbf7e3..1793fc92d 100644 --- a/src/xenia/apu/audio_system.cc +++ b/src/xenia/apu/audio_system.cc @@ -8,6 +8,7 @@ */ #include +#include #include #include @@ -21,12 +22,19 @@ using namespace xe::cpu; AudioSystem::AudioSystem(Emulator* emulator) : emulator_(emulator), memory_(emulator->memory()), - thread_(0), running_(false), - client_({ 0 }) { + thread_(0), running_(false) { 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() { + for (size_t i = 0; i < maximum_client_count_; ++i) { + CloseHandle(client_wait_handles_[i]); + } xe_mutex_free(lock_); } @@ -70,27 +78,34 @@ void AudioSystem::ThreadStart() { // Main run loop. while (running_) { - // Pump worker. - // This may block. - Pump(); - - xe_mutex_lock(lock_); - uint32_t client_callback = client_.callback; - uint32_t client_callback_arg = client_.wrapped_callback_arg; - xe_mutex_unlock(lock_); - if (client_callback) { - processor->Execute( - thread_state_, client_callback, client_callback_arg, 0); - } else { - Sleep(500); + auto result = WaitForMultipleObjectsEx(maximum_client_count_, client_wait_handles_, FALSE, INFINITE, FALSE); + if (result == WAIT_FAILED) { + 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_); + uint32_t client_callback = clients_[index].callback; + uint32_t client_callback_arg = clients_[index].wrapped_callback_arg; + xe_mutex_unlock(lock_); + if (client_callback) { + processor->Execute(thread_state_, client_callback, client_callback_arg, 0); + } + pumped++; + index++; + } while (index < maximum_client_count_ && WaitForSingleObject(client_wait_handles_[index], 0) == WAIT_OBJECT_0); } if (!running_) { break; } - // Pump audio system. - Pump(); + if (!pumped) { + Sleep(500); + } } running_ = false; @@ -109,23 +124,53 @@ void AudioSystem::Shutdown() { memory()->HeapFree(thread_block_, 0); } -void AudioSystem::RegisterClient( - uint32_t callback, uint32_t callback_arg) { - // Only support one client for now. - XEASSERTZERO(client_.callback); +X_STATUS AudioSystem::RegisterClient( + uint32_t callback, uint32_t callback_arg, size_t* out_index) { + XEASSERTTRUE(unused_clients_.size()); + 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); auto mem = memory()->membase(); 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_); - 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_); } -void AudioSystem::UnregisterClient() { +void AudioSystem::UnregisterClient(size_t index) { 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_); } diff --git a/src/xenia/apu/audio_system.h b/src/xenia/apu/audio_system.h index b2c82ebd2..25d0b5829 100644 --- a/src/xenia/apu/audio_system.h +++ b/src/xenia/apu/audio_system.h @@ -13,11 +13,12 @@ #include #include +#include XEDECLARECLASS1(xe, Emulator); XEDECLARECLASS2(xe, cpu, Processor); XEDECLARECLASS2(xe, cpu, XenonThreadState); - +XEDECLARECLASS2(xe, apu, AudioDriver); namespace xe { namespace apu { @@ -34,9 +35,12 @@ public: virtual X_STATUS Setup(); virtual void Shutdown(); - void RegisterClient(uint32_t callback, uint32_t callback_arg); - void UnregisterClient(); - virtual void SubmitFrame(uint32_t samples_ptr) = 0; + X_STATUS RegisterClient(uint32_t callback, uint32_t callback_arg, size_t* out_index); + void UnregisterClient(size_t index); + 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); virtual uint64_t ReadRegister(uint64_t addr); @@ -44,7 +48,6 @@ public: protected: virtual void Initialize(); - virtual void Pump() = 0; private: static void ThreadStartThunk(AudioSystem* this_ptr) { @@ -76,11 +79,17 @@ protected: bool running_; xe_mutex_t* lock_; + + static const size_t maximum_client_count_ = 8; + struct { + AudioDriver* driver; uint32_t callback; uint32_t callback_arg; uint32_t wrapped_callback_arg; - } client_; + } clients_[maximum_client_count_]; + HANDLE client_wait_handles_[maximum_client_count_]; + std::queue unused_clients_; }; diff --git a/src/xenia/apu/nop/nop_audio_system.cc b/src/xenia/apu/nop/nop_audio_system.cc index 446d2078f..fae7b153f 100644 --- a/src/xenia/apu/nop/nop_audio_system.cc +++ b/src/xenia/apu/nop/nop_audio_system.cc @@ -24,18 +24,10 @@ NopAudioSystem::NopAudioSystem(Emulator* emulator) : NopAudioSystem::~NopAudioSystem() { } -void NopAudioSystem::Initialize() { - AudioSystem::Initialize(); +X_STATUS NopAudioSystem::CreateDriver(size_t index, HANDLE wait_handle, AudioDriver** out_driver) { + return X_STATUS_NOT_IMPLEMENTED; } -void NopAudioSystem::Pump() { - // -} - -void NopAudioSystem::SubmitFrame(uint32_t frame_ptr) { - // Process samples! They are big-endian floats. -} - -void NopAudioSystem::Shutdown() { - AudioSystem::Shutdown(); +void NopAudioSystem::DestroyDriver(AudioDriver* driver) { + XEASSERTALWAYS(); } diff --git a/src/xenia/apu/nop/nop_audio_system.h b/src/xenia/apu/nop/nop_audio_system.h index f64b0d1d0..55027789c 100644 --- a/src/xenia/apu/nop/nop_audio_system.h +++ b/src/xenia/apu/nop/nop_audio_system.h @@ -26,15 +26,8 @@ public: NopAudioSystem(Emulator* emulator); virtual ~NopAudioSystem(); - virtual void Shutdown(); - - virtual void SubmitFrame(uint32_t frame_ptr); - -protected: - virtual void Initialize(); - virtual void Pump(); - -private: + virtual X_STATUS CreateDriver(size_t index, HANDLE wait_handle, AudioDriver** out_driver); + virtual void DestroyDriver(AudioDriver* driver); }; diff --git a/src/xenia/apu/sources.gypi b/src/xenia/apu/sources.gypi index d82003e84..92ea3258f 100644 --- a/src/xenia/apu/sources.gypi +++ b/src/xenia/apu/sources.gypi @@ -4,6 +4,8 @@ 'apu-private.h', 'apu.cc', 'apu.h', + 'audio_driver.cc', + 'audio_driver.h', 'audio_system.cc', 'audio_system.h', ], diff --git a/src/xenia/apu/xaudio2/sources.gypi b/src/xenia/apu/xaudio2/sources.gypi index 800f699bd..181c4f1c0 100644 --- a/src/xenia/apu/xaudio2/sources.gypi +++ b/src/xenia/apu/xaudio2/sources.gypi @@ -4,6 +4,8 @@ 'xaudio2_apu-private.h', 'xaudio2_apu.cc', 'xaudio2_apu.h', + 'xaudio2_audio_driver.cc', + 'xaudio2_audio_driver.h', 'xaudio2_audio_system.cc', 'xaudio2_audio_system.h', ], diff --git a/src/xenia/apu/xaudio2/xaudio2_audio_driver.cc b/src/xenia/apu/xaudio2/xaudio2_audio_driver.cc new file mode 100644 index 000000000..1447d2c59 --- /dev/null +++ b/src/xenia/apu/xaudio2/xaudio2_audio_driver.cc @@ -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 + +#include + +#include + +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(emulator_->memory()->membase() + frame_ptr); + auto output_frame = reinterpret_cast(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(&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_); +} diff --git a/src/xenia/apu/xaudio2/xaudio2_audio_driver.h b/src/xenia/apu/xaudio2/xaudio2_audio_driver.h new file mode 100644 index 000000000..b1eac228c --- /dev/null +++ b/src/xenia/apu/xaudio2/xaudio2_audio_driver.h @@ -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 + +#include +#include + +#include + + +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_ diff --git a/src/xenia/apu/xaudio2/xaudio2_audio_system.cc b/src/xenia/apu/xaudio2/xaudio2_audio_system.cc index a564d7868..465c51b5e 100644 --- a/src/xenia/apu/xaudio2/xaudio2_audio_system.cc +++ b/src/xenia/apu/xaudio2/xaudio2_audio_system.cc @@ -8,183 +8,40 @@ */ #include +#include #include #include - using namespace xe; using namespace xe::apu; 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) : - audio_(0), mastering_voice_(0), pcm_voice_(0), - active_channels_(0), - wait_handle_(NULL), voice_callback_(0), - AudioSystem(emulator) { + AudioSystem(emulator) { } 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() { 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; - - 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_, active_channels_, 48000); - if (FAILED(hr)) { - XELOGE("CreateMasteringVoice failed with %.8X", hr); - XEASSERTALWAYS(); - return; - } - - WAVEFORMATIEEEFLOATEX waveformat; - - //waveformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - waveformat.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; - waveformat.Format.nChannels = active_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, - 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); +X_STATUS XAudio2AudioSystem::CreateDriver(size_t index, HANDLE wait, AudioDriver** out_driver) { + XEASSERTNOTNULL(out_driver); + auto driver = new XAudio2AudioDriver(emulator_, wait); + driver->Initialize(); + *out_driver = driver; + return X_STATUS_SUCCESS; } -void XAudio2AudioSystem::SubmitFrame(uint32_t frame_ptr) { - ResetEvent(wait_handle_); - - // Process samples! They are big-endian floats. - HRESULT hr; - - auto input_frame = reinterpret_cast(emulator_->memory()->membase() + frame_ptr); - auto output_frame = reinterpret_cast(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(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(); +void XAudio2AudioSystem::DestroyDriver(AudioDriver* driver) { + XEASSERTNOTNULL(driver); + auto xdriver = static_cast(driver); + xdriver->Shutdown(); + XEASSERTNOTNULL(xdriver); + delete xdriver; } diff --git a/src/xenia/apu/xaudio2/xaudio2_audio_system.h b/src/xenia/apu/xaudio2/xaudio2_audio_system.h index 0befa2a5d..8c6adc6ad 100644 --- a/src/xenia/apu/xaudio2/xaudio2_audio_system.h +++ b/src/xenia/apu/xaudio2/xaudio2_audio_system.h @@ -28,25 +28,11 @@ public: XAudio2AudioSystem(Emulator* emulator); virtual ~XAudio2AudioSystem(); - virtual void Shutdown(); - - virtual void SubmitFrame(uint32_t frame_ptr); + virtual X_RESULT CreateDriver(size_t index, HANDLE wait, AudioDriver** out_driver); + virtual void DestroyDriver(AudioDriver* driver); protected: 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_; }; diff --git a/src/xenia/kernel/xboxkrnl_audio.cc b/src/xenia/kernel/xboxkrnl_audio.cc index 17497150c..c1ba83ed5 100644 --- a/src/xenia/kernel/xboxkrnl_audio.cc +++ b/src/xenia/kernel/xboxkrnl_audio.cc @@ -114,10 +114,16 @@ SHIM_CALL XAudioRegisterRenderDriverClient_shim( callback_ptr, callback, callback_arg, driver_ptr); 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); } @@ -130,11 +136,10 @@ SHIM_CALL XAudioUnregisterRenderDriverClient_shim( "XAudioUnregisterRenderDriverClient(%.8X)", driver_ptr); - XEASSERT(driver_ptr == 0xAADD1100); + XEASSERT((driver_ptr & 0xFFFF0000) == 0x41550000); auto audio_system = state->emulator()->audio_system(); - audio_system->UnregisterClient(); - + audio_system->UnregisterClient(driver_ptr & 0x0000FFFF); SHIM_SET_RETURN_32(X_ERROR_SUCCESS); } @@ -148,10 +153,10 @@ SHIM_CALL XAudioSubmitRenderDriverFrame_shim( "XAudioSubmitRenderDriverFrame(%.8X, %.8X)", driver_ptr, samples_ptr); - XEASSERT(driver_ptr == 0xAADD1100); + XEASSERT((driver_ptr & 0xFFFF0000) == 0x41550000); 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); }