[APU] Support XAudio 2.7

This commit is contained in:
Triang3l 2019-11-02 22:27:38 +03:00
parent 443e6ed729
commit 1bb3cd45ca
4 changed files with 470 additions and 87 deletions

View File

@ -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 <Audioclient.h>
#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_

View File

@ -12,8 +12,6 @@
// Must be included before xaudio2.h so we get the right windows.h include. // Must be included before xaudio2.h so we get the right windows.h include.
#include "xenia/base/platform_win.h" #include "xenia/base/platform_win.h"
#include <xaudio2.h> // NOLINT(build/include_order)
#include "xenia/apu/apu_flags.h" #include "xenia/apu/apu_flags.h"
#include "xenia/base/clock.h" #include "xenia/base/clock.h"
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
@ -22,7 +20,7 @@ namespace xe {
namespace apu { namespace apu {
namespace xaudio2 { namespace xaudio2 {
class XAudio2AudioDriver::VoiceCallback : public IXAudio2VoiceCallback { class XAudio2AudioDriver::VoiceCallback : public api::IXAudio2VoiceCallback {
public: public:
explicit VoiceCallback(xe::threading::Semaphore* semaphore) explicit VoiceCallback(xe::threading::Semaphore* semaphore)
: semaphore_(semaphore) {} : semaphore_(semaphore) {}
@ -45,74 +43,97 @@ class XAudio2AudioDriver::VoiceCallback : public IXAudio2VoiceCallback {
XAudio2AudioDriver::XAudio2AudioDriver(Memory* memory, XAudio2AudioDriver::XAudio2AudioDriver(Memory* memory,
xe::threading::Semaphore* semaphore) xe::threading::Semaphore* semaphore)
: AudioDriver(memory), semaphore_(semaphore) { : AudioDriver(memory), semaphore_(semaphore) {}
static_assert(frame_count_ == XAUDIO2_MAX_QUEUED_BUFFERS,
"xaudio header differs");
}
XAudio2AudioDriver::~XAudio2AudioDriver() = default; 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() { bool XAudio2AudioDriver::Initialize() {
HRESULT hr; HRESULT hr;
voice_callback_ = new VoiceCallback(semaphore_); voice_callback_ = new VoiceCallback(semaphore_);
// Load XAudio2_8.dll dynamically - Windows 8.1 SDK references XAudio2_8.dll // Load the XAudio2 DLL dynamically. Needed both for 2.7 and for
// in xaudio2.lib, which is available on Windows 8.1, however, Windows 10 SDK // differentiating between 2.8 and later versions. Windows 8.1 SDK references
// references XAudio2_9.dll in it, which is only available in Windows 10, and // XAudio2_8.dll in xaudio2.lib, which is available on Windows 8.1, however,
// XAudio2_8.dll is linked through a different .lib - xaudio2_8.lib, so easier // Windows 10 SDK references XAudio2_9.dll in it, which is only available in
// not to link the .lib at all. // 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<void*>(LoadLibrary(L"XAudio2_8.dll")); xaudio2_module_ = reinterpret_cast<void*>(LoadLibrary(L"XAudio2_8.dll"));
if (!xaudio2_module_) { if (xaudio2_module_) {
XELOGE("LoadLibrary(XAudio2_8.dll) failed"); api_minor_version_ = 8;
assert_always(); } else {
return false; xaudio2_module_ = reinterpret_cast<void*>(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 { // First CPU (2.8 default). XAUDIO2_ANY_PROCESSOR (2.7 default) steals too
HRESULT(__stdcall* xaudio2_create) // much time from other things. Ideally should process audio on what roughly
(IXAudio2** xaudio2_out, UINT32 flags, XAUDIO2_PROCESSOR xaudio2_processor); // represents thread 4 (5th) on the Xbox 360 (2.7 default on the console), or
FARPROC xaudio2_create_ptr; // even beyond the 6 guest cores.
}; api::XAUDIO2_PROCESSOR processor = 0x00000001;
xaudio2_create_ptr = GetProcAddress( if (api_minor_version_ >= 8) {
reinterpret_cast<HMODULE>(xaudio2_module_), "XAudio2Create"); union {
if (!xaudio2_create_ptr) { // clang-format off
XELOGE("GetProcAddress(XAudio2_8.dll, XAudio2Create) failed"); HRESULT (__stdcall* xaudio2_create)(
assert_always(); api::IXAudio2_8** xaudio2_out, UINT32 flags,
return false; api::XAUDIO2_PROCESSOR xaudio2_processor);
// clang-format on
FARPROC xaudio2_create_ptr;
};
xaudio2_create_ptr = GetProcAddress(
reinterpret_cast<HMODULE>(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); template <typename Objects>
if (FAILED(hr)) { bool XAudio2AudioDriver::InitializeObjects(Objects& objects) {
XELOGE("XAudio2Create failed with %.8X", hr); HRESULT hr;
assert_always();
return false;
}
XAUDIO2_DEBUG_CONFIGURATION config; api::XAUDIO2_DEBUG_CONFIGURATION config;
config.TraceMask = XAUDIO2_LOG_ERRORS | XAUDIO2_LOG_WARNINGS; config.TraceMask = api::XE_XAUDIO2_LOG_ERRORS | api::XE_XAUDIO2_LOG_WARNINGS;
config.BreakMask = 0; config.BreakMask = 0;
config.LogThreadID = FALSE; config.LogThreadID = FALSE;
config.LogTiming = TRUE; config.LogTiming = TRUE;
config.LogFunctionName = TRUE; config.LogFunctionName = TRUE;
config.LogFileline = 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)) { if (FAILED(hr)) {
XELOGE("CreateMasteringVoice failed with %.8X", hr); XELOGE("IXAudio2::CreateMasteringVoice failed with %.8X", hr);
assert_always(); assert_always();
return false; return false;
} }
@ -132,27 +153,38 @@ bool XAudio2AudioDriver::Initialize() {
waveformat.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; waveformat.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
waveformat.Samples.wValidBitsPerSample = waveformat.Format.wBitsPerSample; 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( hr = objects.audio->CreateSourceVoice(
&pcm_voice_, &waveformat.Format, &objects.pcm_voice, &waveformat.Format,
0, // XAUDIO2_VOICE_NOSRC | XAUDIO2_VOICE_NOPITCH, 0, // api::XE_XAUDIO2_VOICE_NOSRC | api::XE_XAUDIO2_VOICE_NOPITCH,
XAUDIO2_MAX_FREQ_RATIO, voice_callback_); api::XE_XAUDIO2_MAX_FREQ_RATIO, voice_callback_);
if (FAILED(hr)) { if (FAILED(hr)) {
XELOGE("CreateSourceVoice failed with %.8X", hr); XELOGE("IXAudio2::CreateSourceVoice failed with %.8X", hr);
assert_always(); assert_always();
return false; return false;
} }
hr = pcm_voice_->Start(); hr = objects.pcm_voice->Start();
if (FAILED(hr)) { if (FAILED(hr)) {
XELOGE("Start failed with %.8X", hr); XELOGE("IXAudio2SourceVoice::Start failed with %.8X", hr);
assert_always(); assert_always();
return false; return false;
} }
if (cvars::mute) { if (cvars::mute) {
pcm_voice_->SetVolume(0.0f); objects.pcm_voice->SetVolume(0.0f);
} }
return true; return true;
@ -162,8 +194,13 @@ void XAudio2AudioDriver::SubmitFrame(uint32_t frame_ptr) {
// Process samples! They are big-endian floats. // Process samples! They are big-endian floats.
HRESULT hr; HRESULT hr;
XAUDIO2_VOICE_STATE state; api::XAUDIO2_VOICE_STATE state;
pcm_voice_->GetState(&state, XAUDIO2_VOICE_NOSAMPLESPLAYED); 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_); assert_true(state.BuffersQueued < frame_count_);
auto input_frame = memory_->TranslateVirtual<float*>(frame_ptr); auto input_frame = memory_->TranslateVirtual<float*>(frame_ptr);
@ -178,17 +215,21 @@ void XAudio2AudioDriver::SubmitFrame(uint32_t frame_ptr) {
} }
} }
XAUDIO2_BUFFER buffer; api::XAUDIO2_BUFFER buffer;
buffer.Flags = 0; buffer.Flags = 0;
buffer.pAudioData = reinterpret_cast<BYTE*>(output_frame); buffer.pAudioData = reinterpret_cast<BYTE*>(output_frame);
buffer.AudioBytes = frame_size_; buffer.AudioBytes = frame_size_;
buffer.PlayBegin = 0; buffer.PlayBegin = 0;
buffer.PlayLength = channel_samples_; buffer.PlayLength = channel_samples_;
buffer.LoopBegin = XAUDIO2_NO_LOOP_REGION; buffer.LoopBegin = api::XE_XAUDIO2_NO_LOOP_REGION;
buffer.LoopLength = 0; buffer.LoopLength = 0;
buffer.LoopCount = 0; buffer.LoopCount = 0;
buffer.pContext = 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)) { if (FAILED(hr)) {
XELOGE("SubmitSourceBuffer failed with %.8X", hr); XELOGE("SubmitSourceBuffer failed with %.8X", hr);
assert_always(); assert_always();
@ -199,26 +240,19 @@ void XAudio2AudioDriver::SubmitFrame(uint32_t frame_ptr) {
// Update playback ratio to our time scalar. // Update playback ratio to our time scalar.
// This will keep audio in sync with the game clock. // This will keep audio in sync with the game clock.
pcm_voice_->SetFrequencyRatio( float frequency_ratio = static_cast<float>(xe::Clock::guest_time_scalar());
static_cast<float>(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() { void XAudio2AudioDriver::Shutdown() {
if (pcm_voice_) { if (api_minor_version_ >= 8) {
pcm_voice_->Stop(); ShutdownObjects(objects_.api_2_8);
pcm_voice_->DestroyVoice(); } else {
pcm_voice_ = NULL; ShutdownObjects(objects_.api_2_7);
}
if (mastering_voice_) {
mastering_voice_->DestroyVoice();
mastering_voice_ = NULL;
}
if (audio_) {
audio_->StopEngine();
audio_->Release();
audio_ = nullptr;
} }
if (xaudio2_module_) { if (xaudio2_module_) {
@ -232,6 +266,26 @@ void XAudio2AudioDriver::Shutdown() {
} }
} }
template <typename Objects>
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 xaudio2
} // namespace apu } // namespace apu
} // namespace xe } // namespace xe

View File

@ -11,6 +11,7 @@
#define XENIA_APU_XAUDIO2_XAUDIO2_AUDIO_DRIVER_H_ #define XENIA_APU_XAUDIO2_XAUDIO2_AUDIO_DRIVER_H_
#include "xenia/apu/audio_driver.h" #include "xenia/apu/audio_driver.h"
#include "xenia/apu/xaudio2/xaudio2_api.h"
#include "xenia/base/threading.h" #include "xenia/base/threading.h"
struct IXAudio2; struct IXAudio2;
@ -31,16 +32,31 @@ class XAudio2AudioDriver : public AudioDriver {
void Shutdown(); void Shutdown();
private: private:
template <typename Objects>
bool InitializeObjects(Objects& objects);
template <typename Objects>
void ShutdownObjects(Objects& objects);
void* xaudio2_module_ = nullptr; void* xaudio2_module_ = nullptr;
IXAudio2* audio_ = nullptr; uint32_t api_minor_version_ = 7;
IXAudio2MasteringVoice* mastering_voice_ = nullptr; union {
IXAudio2SourceVoice* pcm_voice_ = nullptr; 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; xe::threading::Semaphore* semaphore_ = nullptr;
class VoiceCallback; class VoiceCallback;
VoiceCallback* voice_callback_ = nullptr; 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 frame_channels_ = 6;
static const uint32_t channel_samples_ = 256; static const uint32_t channel_samples_ = 256;
static const uint32_t frame_samples_ = frame_channels_ * channel_samples_; static const uint32_t frame_samples_ = frame_channels_ * channel_samples_;

View File

@ -9,8 +9,6 @@
#include "xenia/apu/xaudio2/xaudio2_audio_system.h" #include "xenia/apu/xaudio2/xaudio2_audio_system.h"
#include <xaudio2.h>
#include "xenia/apu/apu_flags.h" #include "xenia/apu/apu_flags.h"
#include "xenia/apu/xaudio2/xaudio2_audio_driver.h" #include "xenia/apu/xaudio2/xaudio2_audio_driver.h"