[APU] Support XAudio 2.7
This commit is contained in:
parent
443e6ed729
commit
1bb3cd45ca
|
@ -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_
|
|
@ -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
|
||||||
|
|
|
@ -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_;
|
||||||
|
|
|
@ -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"
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue