diff --git a/src/xenia/apu/xaudio2/xaudio2_api.h b/src/xenia/apu/xaudio2/xaudio2_api.h new file mode 100644 index 000000000..00c42349d --- /dev/null +++ b/src/xenia/apu/xaudio2/xaudio2_api.h @@ -0,0 +1,315 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2019 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_APU_XAUDIO2_XAUDIO2_API_H_ +#define XENIA_APU_XAUDIO2_XAUDIO2_API_H_ + +#include + +#include "xenia/base/platform_win.h" + +namespace xe { +namespace apu { +namespace xaudio2 { +namespace api { + +// Parts of XAudio2.h needed for Xenia. The header in the Windows SDK (for 2.8+) +// or the DirectX SDK (for 2.7 and below) is for a single version that is chosen +// for the target OS version at compile time, and cannot be useful for different +// DLL versions. +// +// XAudio 2.8 is also not available on Windows 7, but including the 2.7 header +// is complicated because it has the same name and include guard as the 2.8 one, +// and also 2.7 is outdated - support already dropped in Microsoft Store apps, +// for instance. +// +// Copyright (c) Microsoft Corporation. All rights reserved. + +class __declspec(uuid("5a508685-a254-4fba-9b82-9a24b00306af")) XAudio2_7; +interface __declspec(uuid("8bcf1f58-9fe7-4583-8ac6-e2adc465c8bb")) IXAudio2_7; +interface __declspec(uuid("60d8dac8-5aa1-4e8e-b597-2f5e2883d484")) IXAudio2_8; + +#pragma pack(push, 1) + +static constexpr UINT32 XE_XAUDIO2_MAX_QUEUED_BUFFERS = 64; +static constexpr float XE_XAUDIO2_MAX_FREQ_RATIO = 1024.0f; +static constexpr float XE_XAUDIO2_DEFAULT_FREQ_RATIO = 2.0f; + +static constexpr UINT32 XE_XAUDIO2_COMMIT_NOW = 0; +static constexpr UINT32 XE_XAUDIO2_NO_LOOP_REGION = 0; +static constexpr UINT32 XE_XAUDIO2_DEFAULT_CHANNELS = 0; +static constexpr UINT32 XE_XAUDIO2_DEFAULT_SAMPLERATE = 0; + +// 2.8+ only. +static constexpr UINT32 XE_XAUDIO2_VOICE_NOSAMPLESPLAYED = 0x0100; + +interface IXAudio2_7; +interface IXAudio2_8; +interface IXAudio2Voice; +interface IXAudio2_7SourceVoice; +interface IXAudio2_8SourceVoice; +interface IXAudio2SubmixVoice; +interface IXAudio2_7MasteringVoice; +interface IXAudio2_8MasteringVoice; +interface IXAudio2EngineCallback; +interface IXAudio2VoiceCallback; + +typedef UINT32 XAUDIO2_PROCESSOR; +static constexpr XAUDIO2_PROCESSOR XE_XAUDIO2_ANY_PROCESSOR = 0xFFFFFFFF; +static constexpr XAUDIO2_PROCESSOR XE_XAUDIO2_7_DEFAULT_PROCESSOR = + XE_XAUDIO2_ANY_PROCESSOR; +static constexpr XAUDIO2_PROCESSOR XE_XAUDIO2_8_DEFAULT_PROCESSOR = 0x00000001; + +// 2.7 only. +struct XAUDIO2_DEVICE_DETAILS; + +struct XAUDIO2_VOICE_DETAILS; +struct XAUDIO2_VOICE_SENDS; +struct XAUDIO2_EFFECT_CHAIN; +struct XAUDIO2_FILTER_PARAMETERS; + +struct XAUDIO2_BUFFER { + UINT32 Flags; + UINT32 AudioBytes; + const BYTE* pAudioData; + UINT32 PlayBegin; + UINT32 PlayLength; + UINT32 LoopBegin; + UINT32 LoopLength; + UINT32 LoopCount; + void* pContext; +}; + +struct XAUDIO2_BUFFER_WMA; + +struct XAUDIO2_VOICE_STATE { + void* pCurrentBufferContext; + UINT32 BuffersQueued; + UINT64 SamplesPlayed; +}; + +struct XAUDIO2_PERFORMANCE_DATA; + +struct XAUDIO2_DEBUG_CONFIGURATION { + UINT32 TraceMask; + UINT32 BreakMask; + BOOL LogThreadID; + BOOL LogFileline; + BOOL LogFunctionName; + BOOL LogTiming; +}; + +static constexpr UINT32 XE_XAUDIO2_LOG_ERRORS = 0x0001; +static constexpr UINT32 XE_XAUDIO2_LOG_WARNINGS = 0x0002; + +// clang-format off + +// IXAudio2 2.7. +DECLARE_INTERFACE_(IXAudio2_7, IUnknown) { + STDMETHOD(QueryInterface)(REFIID riid, void** ppvInterface) = 0; + STDMETHOD_(ULONG, AddRef)() = 0; + STDMETHOD_(ULONG, Release)() = 0; + // 2.7 only. + STDMETHOD(GetDeviceCount)(UINT32* pCount) = 0; + // 2.7 only. + STDMETHOD(GetDeviceDetails)(UINT32 Index, + XAUDIO2_DEVICE_DETAILS* pDeviceDetails) = 0; + // 2.7 only. + STDMETHOD(Initialize)( + UINT32 Flags = 0, + XAUDIO2_PROCESSOR XAudio2Processor = XE_XAUDIO2_7_DEFAULT_PROCESSOR) = 0; + STDMETHOD(RegisterForCallbacks)(IXAudio2EngineCallback* pCallback) = 0; + STDMETHOD_(void, UnregisterForCallbacks)( + IXAudio2EngineCallback* pCallback) = 0; + STDMETHOD(CreateSourceVoice)( + IXAudio2_7SourceVoice** ppSourceVoice, const WAVEFORMATEX* pSourceFormat, + UINT32 Flags = 0, float MaxFrequencyRatio = XE_XAUDIO2_DEFAULT_FREQ_RATIO, + IXAudio2VoiceCallback* pCallback = nullptr, + const XAUDIO2_VOICE_SENDS* pSendList = nullptr, + const XAUDIO2_EFFECT_CHAIN* pEffectChain = nullptr) = 0; + STDMETHOD(CreateSubmixVoice)( + IXAudio2SubmixVoice** ppSubmixVoice, UINT32 InputChannels, + UINT32 InputSampleRate, UINT32 Flags = 0, UINT32 ProcessingStage = 0, + const XAUDIO2_VOICE_SENDS* pSendList = nullptr, + const XAUDIO2_EFFECT_CHAIN* pEffectChain = nullptr) = 0; + // 2.7: Device index instead of device ID, no stream category. + STDMETHOD(CreateMasteringVoice)( + IXAudio2_7MasteringVoice** ppMasteringVoice, + UINT32 InputChannels = XE_XAUDIO2_DEFAULT_CHANNELS, + UINT32 InputSampleRate = XE_XAUDIO2_DEFAULT_SAMPLERATE, UINT32 Flags = 0, + UINT32 DeviceIndex = 0, + const XAUDIO2_EFFECT_CHAIN* pEffectChain = nullptr) = 0; + STDMETHOD(StartEngine)() = 0; + STDMETHOD_(void, StopEngine)() = 0; + STDMETHOD(CommitChanges)(UINT32 OperationSet) = 0; + STDMETHOD_(void, GetPerformanceData)(XAUDIO2_PERFORMANCE_DATA* pPerfData) = 0; + STDMETHOD_(void, SetDebugConfiguration)( + const XAUDIO2_DEBUG_CONFIGURATION* pDebugConfiguration, + void* pReserved = nullptr) = 0; +}; + +// IXAudio2 2.8. +DECLARE_INTERFACE_(IXAudio2_8, IUnknown) { + STDMETHOD(QueryInterface)(REFIID riid, void** ppvInterface) = 0; + STDMETHOD_(ULONG, AddRef)() = 0; + STDMETHOD_(ULONG, Release)() = 0; + // 2.8: No GetDeviceCount, GetDeviceDetails and Initialize. + STDMETHOD(RegisterForCallbacks)(IXAudio2EngineCallback* pCallback) = 0; + STDMETHOD_(void, UnregisterForCallbacks)( + IXAudio2EngineCallback* pCallback) = 0; + STDMETHOD(CreateSourceVoice)( + IXAudio2_8SourceVoice** ppSourceVoice, const WAVEFORMATEX* pSourceFormat, + UINT32 Flags = 0, float MaxFrequencyRatio = XE_XAUDIO2_DEFAULT_FREQ_RATIO, + IXAudio2VoiceCallback* pCallback = nullptr, + const XAUDIO2_VOICE_SENDS* pSendList = nullptr, + const XAUDIO2_EFFECT_CHAIN* pEffectChain = nullptr) = 0; + STDMETHOD(CreateSubmixVoice)( + IXAudio2SubmixVoice** ppSubmixVoice, UINT32 InputChannels, + UINT32 InputSampleRate, UINT32 Flags = 0, UINT32 ProcessingStage = 0, + const XAUDIO2_VOICE_SENDS* pSendList = nullptr, + const XAUDIO2_EFFECT_CHAIN* pEffectChain = nullptr) = 0; + // 2.8: Device ID instead of device index, added stream category. + STDMETHOD(CreateMasteringVoice)( + IXAudio2_8MasteringVoice** ppMasteringVoice, + UINT32 InputChannels = XE_XAUDIO2_DEFAULT_CHANNELS, + UINT32 InputSampleRate = XE_XAUDIO2_DEFAULT_SAMPLERATE, UINT32 Flags = 0, + LPCWSTR szDeviceId = nullptr, + const XAUDIO2_EFFECT_CHAIN* pEffectChain = nullptr, + AUDIO_STREAM_CATEGORY StreamCategory = AudioCategory_GameEffects) = 0; + STDMETHOD(StartEngine)() = 0; + STDMETHOD_(void, StopEngine)() = 0; + STDMETHOD(CommitChanges)(UINT32 OperationSet) = 0; + STDMETHOD_(void, GetPerformanceData)(XAUDIO2_PERFORMANCE_DATA* pPerfData) = 0; + STDMETHOD_(void, SetDebugConfiguration)( + const XAUDIO2_DEBUG_CONFIGURATION* pDebugConfiguration, + void* pReserved = nullptr) = 0; +}; + +DECLARE_INTERFACE(IXAudio2Voice) { +#define XE_APU_XAUDIO2_API_DECLARE_IXAUDIO2VOICE_METHODS \ + STDMETHOD_(void, GetVoiceDetails)(XAUDIO2_VOICE_DETAILS* pVoiceDetails) = 0; \ + STDMETHOD(SetOutputVoices)(const XAUDIO2_VOICE_SENDS* pSendList) = 0; \ + STDMETHOD(SetEffectChain)(const XAUDIO2_EFFECT_CHAIN* pEffectChain) = 0; \ + STDMETHOD(EnableEffect)(UINT32 EffectIndex, \ + UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0; \ + STDMETHOD(DisableEffect)(UINT32 EffectIndex, \ + UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0; \ + STDMETHOD_(void, GetEffectState)(UINT32 EffectIndex, BOOL* pEnabled) = 0; \ + STDMETHOD(SetEffectParameters)( \ + UINT32 EffectIndex, const void* pParameters, UINT32 ParametersByteSize, \ + UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0; \ + STDMETHOD(GetEffectParameters)(UINT32 EffectIndex, void* pParameters, \ + UINT32 ParametersByteSize) = 0; \ + STDMETHOD(SetFilterParameters)( \ + const XAUDIO2_FILTER_PARAMETERS* pParameters, \ + UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0; \ + STDMETHOD_(void, GetFilterParameters)( \ + XAUDIO2_FILTER_PARAMETERS* pParameters) = 0; \ + STDMETHOD(SetOutputFilterParameters)( \ + IXAudio2Voice* pDestinationVoice, \ + const XAUDIO2_FILTER_PARAMETERS* pParameters, \ + UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0; \ + STDMETHOD_(void, GetOutputFilterParameters)( \ + IXAudio2Voice* pDestinationVoice, \ + XAUDIO2_FILTER_PARAMETERS* pParameters) = 0; \ + STDMETHOD(SetVolume)(float Volume, \ + UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0; \ + STDMETHOD_(void, GetVolume)(float* pVolume) = 0; \ + STDMETHOD(SetChannelVolumes)( \ + UINT32 Channels, const float* pVolumes, \ + UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0; \ + STDMETHOD_(void, GetChannelVolumes)(UINT32 Channels, float* pVolumes) = 0; \ + STDMETHOD(SetOutputMatrix)(IXAudio2Voice* pDestinationVoice, \ + UINT32 SourceChannels, \ + UINT32 DestinationChannels, \ + const float* pLevelMatrix, \ + UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0; \ + STDMETHOD_(void, GetOutputMatrix)(IXAudio2Voice* pDestinationVoice, \ + UINT32 SourceChannels, \ + UINT32 DestinationChannels, \ + float* pLevelMatrix) = 0; \ + STDMETHOD_(void, DestroyVoice)() = 0; + XE_APU_XAUDIO2_API_DECLARE_IXAUDIO2VOICE_METHODS +}; + +// IXAudio2SourceVoice 2.7. +DECLARE_INTERFACE_(IXAudio2_7SourceVoice, IXAudio2Voice) { + XE_APU_XAUDIO2_API_DECLARE_IXAUDIO2VOICE_METHODS + STDMETHOD(Start)(UINT32 Flags = 0, + UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0; + STDMETHOD(Stop)(UINT32 Flags = 0, + UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0; + STDMETHOD(SubmitSourceBuffer)( + const XAUDIO2_BUFFER* pBuffer, + const XAUDIO2_BUFFER_WMA* pBufferWMA = nullptr) = 0; + STDMETHOD(FlushSourceBuffers)() = 0; + STDMETHOD(Discontinuity)() = 0; + STDMETHOD(ExitLoop)(UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0; + // 2.7: No Flags. + STDMETHOD_(void, GetState)(XAUDIO2_VOICE_STATE* pVoiceState) = 0; + STDMETHOD(SetFrequencyRatio)(float Ratio, + UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0; + STDMETHOD_(void, GetFrequencyRatio)(float* pRatio) = 0; + STDMETHOD(SetSourceSampleRate)(UINT32 NewSourceSampleRate) = 0; +}; + +// IXAudio2SourceVoice 2.8. +DECLARE_INTERFACE_(IXAudio2_8SourceVoice, IXAudio2Voice) { + XE_APU_XAUDIO2_API_DECLARE_IXAUDIO2VOICE_METHODS + STDMETHOD(Start)(UINT32 Flags = 0, + UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0; + STDMETHOD(Stop)(UINT32 Flags = 0, + UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0; + STDMETHOD(SubmitSourceBuffer)( + const XAUDIO2_BUFFER* pBuffer, + const XAUDIO2_BUFFER_WMA* pBufferWMA = nullptr) = 0; + STDMETHOD(FlushSourceBuffers)() = 0; + STDMETHOD(Discontinuity)() = 0; + STDMETHOD(ExitLoop)(UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0; + // 2.8: Flags added. + STDMETHOD_(void, GetState)(XAUDIO2_VOICE_STATE* pVoiceState, + UINT32 Flags = 0) = 0; + STDMETHOD(SetFrequencyRatio)(float Ratio, + UINT32 OperationSet = XE_XAUDIO2_COMMIT_NOW) = 0; + STDMETHOD_(void, GetFrequencyRatio)(float* pRatio) = 0; + STDMETHOD(SetSourceSampleRate)(UINT32 NewSourceSampleRate) = 0; +}; + +// IXAudio2MasteringVoice 2.7. +DECLARE_INTERFACE_(IXAudio2_7MasteringVoice, IXAudio2Voice) { + XE_APU_XAUDIO2_API_DECLARE_IXAUDIO2VOICE_METHODS + // 2.7: No GetChannelMask. +}; + +// IXAudio2MasteringVoice 2.8. +DECLARE_INTERFACE_(IXAudio2_8MasteringVoice, IXAudio2Voice) { + XE_APU_XAUDIO2_API_DECLARE_IXAUDIO2VOICE_METHODS + // 2.8: GetChannelMask added. + STDMETHOD(GetChannelMask)(DWORD* pChannelmask) = 0; +}; + +DECLARE_INTERFACE(IXAudio2VoiceCallback) { + // Called just before this voice's processing pass begins. + STDMETHOD_(void, OnVoiceProcessingPassStart)(UINT32 BytesRequired) = 0; + STDMETHOD_(void, OnVoiceProcessingPassEnd)() = 0; + STDMETHOD_(void, OnStreamEnd)() = 0; + STDMETHOD_(void, OnBufferStart)(void* pBufferContext) = 0; + STDMETHOD_(void, OnBufferEnd)(void* pBufferContext) = 0; + STDMETHOD_(void, OnLoopEnd)(void* pBufferContext) = 0; + STDMETHOD_(void, OnVoiceError)(void* pBufferContext, HRESULT Error) = 0; +}; + +#pragma pack(pop) + +} // namespace api +} // namespace xaudio2 +} // namespace apu +} // namespace xe + +#endif // XENIA_APU_XAUDIO2_XAUDIO2_API_H_ diff --git a/src/xenia/apu/xaudio2/xaudio2_audio_driver.cc b/src/xenia/apu/xaudio2/xaudio2_audio_driver.cc index fe3f8bd10..1f5d2ffc8 100644 --- a/src/xenia/apu/xaudio2/xaudio2_audio_driver.cc +++ b/src/xenia/apu/xaudio2/xaudio2_audio_driver.cc @@ -12,8 +12,6 @@ // Must be included before xaudio2.h so we get the right windows.h include. #include "xenia/base/platform_win.h" -#include // NOLINT(build/include_order) - #include "xenia/apu/apu_flags.h" #include "xenia/base/clock.h" #include "xenia/base/logging.h" @@ -22,7 +20,7 @@ namespace xe { namespace apu { namespace xaudio2 { -class XAudio2AudioDriver::VoiceCallback : public IXAudio2VoiceCallback { +class XAudio2AudioDriver::VoiceCallback : public api::IXAudio2VoiceCallback { public: explicit VoiceCallback(xe::threading::Semaphore* semaphore) : semaphore_(semaphore) {} @@ -45,74 +43,97 @@ class XAudio2AudioDriver::VoiceCallback : public IXAudio2VoiceCallback { XAudio2AudioDriver::XAudio2AudioDriver(Memory* memory, xe::threading::Semaphore* semaphore) - : AudioDriver(memory), semaphore_(semaphore) { - static_assert(frame_count_ == XAUDIO2_MAX_QUEUED_BUFFERS, - "xaudio header differs"); -} + : AudioDriver(memory), semaphore_(semaphore) {} XAudio2AudioDriver::~XAudio2AudioDriver() = default; -const DWORD ChannelMasks[] = { - 0, - 0, - SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY, - 0, - 0, - 0, - SPEAKER_FRONT_LEFT | SPEAKER_FRONT_CENTER | SPEAKER_FRONT_RIGHT | - SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT, - 0, -}; - bool XAudio2AudioDriver::Initialize() { HRESULT hr; voice_callback_ = new VoiceCallback(semaphore_); - // Load XAudio2_8.dll dynamically - Windows 8.1 SDK references XAudio2_8.dll - // in xaudio2.lib, which is available on Windows 8.1, however, Windows 10 SDK - // references XAudio2_9.dll in it, which is only available in Windows 10, and - // XAudio2_8.dll is linked through a different .lib - xaudio2_8.lib, so easier - // not to link the .lib at all. + // Load the XAudio2 DLL dynamically. Needed both for 2.7 and for + // differentiating between 2.8 and later versions. Windows 8.1 SDK references + // XAudio2_8.dll in xaudio2.lib, which is available on Windows 8.1, however, + // Windows 10 SDK references XAudio2_9.dll in it, which is only available in + // Windows 10, and XAudio2_8.dll is linked through a different .lib - + // xaudio2_8.lib, so easier not to link the .lib at all. xaudio2_module_ = reinterpret_cast(LoadLibrary(L"XAudio2_8.dll")); - if (!xaudio2_module_) { - XELOGE("LoadLibrary(XAudio2_8.dll) failed"); - assert_always(); - return false; + if (xaudio2_module_) { + api_minor_version_ = 8; + } else { + xaudio2_module_ = reinterpret_cast(LoadLibrary(L"XAudio2_7.dll")); + if (xaudio2_module_) { + api_minor_version_ = 7; + } else { + XELOGE("Failed to load XAudio 2.8 or 2.7 library DLL"); + assert_always(); + return false; + } } - union { - HRESULT(__stdcall* xaudio2_create) - (IXAudio2** xaudio2_out, UINT32 flags, XAUDIO2_PROCESSOR xaudio2_processor); - FARPROC xaudio2_create_ptr; - }; - xaudio2_create_ptr = GetProcAddress( - reinterpret_cast(xaudio2_module_), "XAudio2Create"); - if (!xaudio2_create_ptr) { - XELOGE("GetProcAddress(XAudio2_8.dll, XAudio2Create) failed"); - assert_always(); - return false; + // First CPU (2.8 default). XAUDIO2_ANY_PROCESSOR (2.7 default) steals too + // much time from other things. Ideally should process audio on what roughly + // represents thread 4 (5th) on the Xbox 360 (2.7 default on the console), or + // even beyond the 6 guest cores. + api::XAUDIO2_PROCESSOR processor = 0x00000001; + if (api_minor_version_ >= 8) { + union { + // clang-format off + HRESULT (__stdcall* xaudio2_create)( + api::IXAudio2_8** xaudio2_out, UINT32 flags, + api::XAUDIO2_PROCESSOR xaudio2_processor); + // clang-format on + FARPROC xaudio2_create_ptr; + }; + xaudio2_create_ptr = GetProcAddress( + reinterpret_cast(xaudio2_module_), "XAudio2Create"); + if (!xaudio2_create_ptr) { + XELOGE("XAudio2Create not found in XAudio2_8.dll"); + assert_always(); + return false; + } + hr = xaudio2_create(&objects_.api_2_8.audio, 0, processor); + if (FAILED(hr)) { + XELOGE("XAudio2Create failed with %.8X", hr); + assert_always(); + return false; + } + return InitializeObjects(objects_.api_2_8); + } else { + hr = CoCreateInstance(__uuidof(api::XAudio2_7), NULL, CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&objects_.api_2_7.audio)); + if (FAILED(hr)) { + XELOGE("CoCreateInstance for XAudio2 failed with %.8X", hr); + assert_always(); + return false; + } + hr = objects_.api_2_7.audio->Initialize(0, processor); + if (FAILED(hr)) { + XELOGE("IXAudio2::Initialize failed with %.8X", hr); + assert_always(); + return false; + } + return InitializeObjects(objects_.api_2_7); } +} - hr = xaudio2_create(&audio_, 0, XAUDIO2_DEFAULT_PROCESSOR); - if (FAILED(hr)) { - XELOGE("XAudio2Create failed with %.8X", hr); - assert_always(); - return false; - } +template +bool XAudio2AudioDriver::InitializeObjects(Objects& objects) { + HRESULT hr; - XAUDIO2_DEBUG_CONFIGURATION config; - config.TraceMask = XAUDIO2_LOG_ERRORS | XAUDIO2_LOG_WARNINGS; + api::XAUDIO2_DEBUG_CONFIGURATION config; + config.TraceMask = api::XE_XAUDIO2_LOG_ERRORS | api::XE_XAUDIO2_LOG_WARNINGS; config.BreakMask = 0; config.LogThreadID = FALSE; config.LogTiming = TRUE; config.LogFunctionName = TRUE; config.LogFileline = TRUE; - audio_->SetDebugConfiguration(&config); + objects.audio->SetDebugConfiguration(&config); - hr = audio_->CreateMasteringVoice(&mastering_voice_); + hr = objects.audio->CreateMasteringVoice(&objects.mastering_voice); if (FAILED(hr)) { - XELOGE("CreateMasteringVoice failed with %.8X", hr); + XELOGE("IXAudio2::CreateMasteringVoice failed with %.8X", hr); assert_always(); return false; } @@ -132,27 +153,38 @@ bool XAudio2AudioDriver::Initialize() { waveformat.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; waveformat.Samples.wValidBitsPerSample = waveformat.Format.wBitsPerSample; - waveformat.dwChannelMask = ChannelMasks[waveformat.Format.nChannels]; + static const DWORD kChannelMasks[] = { + 0, + 0, + SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT, + 0, + 0, + 0, + SPEAKER_FRONT_LEFT | SPEAKER_FRONT_CENTER | SPEAKER_FRONT_RIGHT | + SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT, + 0, + }; + waveformat.dwChannelMask = kChannelMasks[waveformat.Format.nChannels]; - hr = audio_->CreateSourceVoice( - &pcm_voice_, &waveformat.Format, - 0, // XAUDIO2_VOICE_NOSRC | XAUDIO2_VOICE_NOPITCH, - XAUDIO2_MAX_FREQ_RATIO, voice_callback_); + hr = objects.audio->CreateSourceVoice( + &objects.pcm_voice, &waveformat.Format, + 0, // api::XE_XAUDIO2_VOICE_NOSRC | api::XE_XAUDIO2_VOICE_NOPITCH, + api::XE_XAUDIO2_MAX_FREQ_RATIO, voice_callback_); if (FAILED(hr)) { - XELOGE("CreateSourceVoice failed with %.8X", hr); + XELOGE("IXAudio2::CreateSourceVoice failed with %.8X", hr); assert_always(); return false; } - hr = pcm_voice_->Start(); + hr = objects.pcm_voice->Start(); if (FAILED(hr)) { - XELOGE("Start failed with %.8X", hr); + XELOGE("IXAudio2SourceVoice::Start failed with %.8X", hr); assert_always(); return false; } if (cvars::mute) { - pcm_voice_->SetVolume(0.0f); + objects.pcm_voice->SetVolume(0.0f); } return true; @@ -162,8 +194,13 @@ void XAudio2AudioDriver::SubmitFrame(uint32_t frame_ptr) { // Process samples! They are big-endian floats. HRESULT hr; - XAUDIO2_VOICE_STATE state; - pcm_voice_->GetState(&state, XAUDIO2_VOICE_NOSAMPLESPLAYED); + api::XAUDIO2_VOICE_STATE state; + if (api_minor_version_ >= 8) { + objects_.api_2_8.pcm_voice->GetState(&state, + api::XE_XAUDIO2_VOICE_NOSAMPLESPLAYED); + } else { + objects_.api_2_7.pcm_voice->GetState(&state); + } assert_true(state.BuffersQueued < frame_count_); auto input_frame = memory_->TranslateVirtual(frame_ptr); @@ -178,17 +215,21 @@ void XAudio2AudioDriver::SubmitFrame(uint32_t frame_ptr) { } } - XAUDIO2_BUFFER buffer; + api::XAUDIO2_BUFFER buffer; buffer.Flags = 0; buffer.pAudioData = reinterpret_cast(output_frame); buffer.AudioBytes = frame_size_; buffer.PlayBegin = 0; buffer.PlayLength = channel_samples_; - buffer.LoopBegin = XAUDIO2_NO_LOOP_REGION; + buffer.LoopBegin = api::XE_XAUDIO2_NO_LOOP_REGION; buffer.LoopLength = 0; buffer.LoopCount = 0; buffer.pContext = 0; - hr = pcm_voice_->SubmitSourceBuffer(&buffer); + if (api_minor_version_ >= 8) { + hr = objects_.api_2_8.pcm_voice->SubmitSourceBuffer(&buffer); + } else { + hr = objects_.api_2_7.pcm_voice->SubmitSourceBuffer(&buffer); + } if (FAILED(hr)) { XELOGE("SubmitSourceBuffer failed with %.8X", hr); assert_always(); @@ -199,26 +240,19 @@ void XAudio2AudioDriver::SubmitFrame(uint32_t frame_ptr) { // Update playback ratio to our time scalar. // This will keep audio in sync with the game clock. - pcm_voice_->SetFrequencyRatio( - static_cast(xe::Clock::guest_time_scalar())); + float frequency_ratio = static_cast(xe::Clock::guest_time_scalar()); + if (api_minor_version_ >= 8) { + objects_.api_2_8.pcm_voice->SetFrequencyRatio(frequency_ratio); + } else { + objects_.api_2_7.pcm_voice->SetFrequencyRatio(frequency_ratio); + } } void XAudio2AudioDriver::Shutdown() { - if (pcm_voice_) { - pcm_voice_->Stop(); - pcm_voice_->DestroyVoice(); - pcm_voice_ = NULL; - } - - if (mastering_voice_) { - mastering_voice_->DestroyVoice(); - mastering_voice_ = NULL; - } - - if (audio_) { - audio_->StopEngine(); - audio_->Release(); - audio_ = nullptr; + if (api_minor_version_ >= 8) { + ShutdownObjects(objects_.api_2_8); + } else { + ShutdownObjects(objects_.api_2_7); } if (xaudio2_module_) { @@ -232,6 +266,26 @@ void XAudio2AudioDriver::Shutdown() { } } +template +void XAudio2AudioDriver::ShutdownObjects(Objects& objects) { + if (objects.pcm_voice) { + objects.pcm_voice->Stop(); + objects.pcm_voice->DestroyVoice(); + objects.pcm_voice = nullptr; + } + + if (objects.mastering_voice) { + objects.mastering_voice->DestroyVoice(); + objects.mastering_voice = nullptr; + } + + if (objects.audio) { + objects.audio->StopEngine(); + objects.audio->Release(); + objects.audio = nullptr; + } +} + } // namespace xaudio2 } // namespace apu } // namespace xe diff --git a/src/xenia/apu/xaudio2/xaudio2_audio_driver.h b/src/xenia/apu/xaudio2/xaudio2_audio_driver.h index 32429d084..42d4b59a9 100644 --- a/src/xenia/apu/xaudio2/xaudio2_audio_driver.h +++ b/src/xenia/apu/xaudio2/xaudio2_audio_driver.h @@ -11,6 +11,7 @@ #define XENIA_APU_XAUDIO2_XAUDIO2_AUDIO_DRIVER_H_ #include "xenia/apu/audio_driver.h" +#include "xenia/apu/xaudio2/xaudio2_api.h" #include "xenia/base/threading.h" struct IXAudio2; @@ -31,16 +32,31 @@ class XAudio2AudioDriver : public AudioDriver { void Shutdown(); private: + template + bool InitializeObjects(Objects& objects); + template + void ShutdownObjects(Objects& objects); + void* xaudio2_module_ = nullptr; - IXAudio2* audio_ = nullptr; - IXAudio2MasteringVoice* mastering_voice_ = nullptr; - IXAudio2SourceVoice* pcm_voice_ = nullptr; + uint32_t api_minor_version_ = 7; + union { + struct { + api::IXAudio2_7* audio; + api::IXAudio2_7MasteringVoice* mastering_voice; + api::IXAudio2_7SourceVoice* pcm_voice; + } api_2_7; + struct { + api::IXAudio2_8* audio; + api::IXAudio2_8MasteringVoice* mastering_voice; + api::IXAudio2_8SourceVoice* pcm_voice; + } api_2_8; + } objects_ = {}; xe::threading::Semaphore* semaphore_ = nullptr; class VoiceCallback; VoiceCallback* voice_callback_ = nullptr; - static const uint32_t frame_count_ = 64; + static const uint32_t frame_count_ = api::XE_XAUDIO2_MAX_QUEUED_BUFFERS; static const uint32_t frame_channels_ = 6; static const uint32_t channel_samples_ = 256; static const uint32_t frame_samples_ = frame_channels_ * channel_samples_; diff --git a/src/xenia/apu/xaudio2/xaudio2_audio_system.cc b/src/xenia/apu/xaudio2/xaudio2_audio_system.cc index 37869a196..e9ddeb6ec 100644 --- a/src/xenia/apu/xaudio2/xaudio2_audio_system.cc +++ b/src/xenia/apu/xaudio2/xaudio2_audio_system.cc @@ -9,8 +9,6 @@ #include "xenia/apu/xaudio2/xaudio2_audio_system.h" -#include - #include "xenia/apu/apu_flags.h" #include "xenia/apu/xaudio2/xaudio2_audio_driver.h"