mirror of
https://github.com/XWine1/XboxAudio2.git
synced 2026-07-24 12:32:42 -07:00
Initial commit
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.{cpp,hpp,c,h,inl}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
cpp_indent_case_contents = true
|
||||
cpp_indent_case_labels = false
|
||||
cpp_indent_case_contents_when_block = true
|
||||
cpp_indent_goto_labels = one_left
|
||||
cpp_indent_preprocessor = one_left
|
||||
cpp_space_pointer_reference_alignment = right
|
||||
cpp_indent_lambda_braces_when_parameter = false
|
||||
@@ -0,0 +1 @@
|
||||
* text eol=lf
|
||||
@@ -0,0 +1,3 @@
|
||||
.cmake/
|
||||
.vs/
|
||||
artifacts/
|
||||
@@ -0,0 +1,14 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
project(XboxAudio2 LANGUAGES CXX ASM_MASM)
|
||||
|
||||
file(GLOB_RECURSE XAUDIO_SOURCES CONFIGURE_DEPENDS "include/*.h" "source/*.cpp" "source/*.h" "source/*.asm")
|
||||
add_library(XAudio2_9 SHARED ${XAUDIO_SOURCES})
|
||||
target_include_directories(XAudio2_9 PRIVATE include)
|
||||
|
||||
find_package(FFMPEG REQUIRED)
|
||||
target_include_directories(XAudio2_9 PRIVATE ${FFMPEG_INCLUDE_DIRS})
|
||||
target_link_directories(XAudio2_9 PRIVATE ${FFMPEG_LIBRARY_DIRS})
|
||||
target_link_libraries(XAudio2_9 PRIVATE ${FFMPEG_LIBRARIES})
|
||||
|
||||
find_package(wil CONFIG REQUIRED)
|
||||
target_link_libraries(XAudio2_9 PRIVATE WIL::WIL)
|
||||
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"version": 2,
|
||||
"cmakeMinimumRequired": {
|
||||
"major": 3,
|
||||
"minor": 23,
|
||||
"patch": 0
|
||||
},
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "default",
|
||||
"hidden": true,
|
||||
"displayName": "Default Config",
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "${sourceDir}/.cmake/${presetName}",
|
||||
"architecture": {
|
||||
"value": "x64",
|
||||
"strategy": "external"
|
||||
},
|
||||
"cacheVariables": {
|
||||
"CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
|
||||
"CMAKE_RUNTIME_OUTPUT_DIRECTORY": "${sourceDir}/artifacts/${presetName}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Debug",
|
||||
"displayName": "Debug",
|
||||
"inherits": "default",
|
||||
"cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" }
|
||||
},
|
||||
{
|
||||
"name": "Release",
|
||||
"displayName": "Release",
|
||||
"inherits": "default",
|
||||
"cacheVariables": { "CMAKE_BUILD_TYPE": "Release" }
|
||||
},
|
||||
{
|
||||
"name": "MinSizeRel",
|
||||
"displayName": "MinSizeRel",
|
||||
"inherits": "default",
|
||||
"cacheVariables": { "CMAKE_BUILD_TYPE": "MinSizeRel" }
|
||||
},
|
||||
{
|
||||
"name": "RelWithDebInfo",
|
||||
"displayName": "RelWithDebInfo",
|
||||
"inherits": "default",
|
||||
"cacheVariables": { "CMAKE_BUILD_TYPE": "RelWithDebInfo" }
|
||||
}
|
||||
],
|
||||
"buildPresets": [
|
||||
{
|
||||
"name": "Debug",
|
||||
"configurePreset": "Debug"
|
||||
},
|
||||
{
|
||||
"name": "Release",
|
||||
"configurePreset": "Release"
|
||||
},
|
||||
{
|
||||
"name": "MinSizeRel",
|
||||
"configurePreset": "MinSizeRel"
|
||||
},
|
||||
{
|
||||
"name": "RelWithDebInfo",
|
||||
"configurePreset": "RelWithDebInfo"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 XWine1
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <libavcodec/avcodec.h>
|
||||
}
|
||||
|
||||
struct AVPacketDeleter
|
||||
{
|
||||
void operator()(AVPacket *packet) const
|
||||
{
|
||||
av_packet_free(&packet);
|
||||
}
|
||||
};
|
||||
|
||||
using unique_avpacket = std::unique_ptr<AVPacket, AVPacketDeleter>;
|
||||
|
||||
struct AVFrameDeleter
|
||||
{
|
||||
void operator()(AVFrame *frame) const
|
||||
{
|
||||
av_frame_free(&frame);
|
||||
}
|
||||
};
|
||||
|
||||
using unique_avframe = std::unique_ptr<AVFrame, AVFrameDeleter>;
|
||||
|
||||
struct AVCodecContextDeleter
|
||||
{
|
||||
void operator()(AVCodecContext *context) const
|
||||
{
|
||||
avcodec_free_context(&context);
|
||||
}
|
||||
};
|
||||
|
||||
using unique_avcodec_context = std::unique_ptr<AVCodecContext, AVCodecContextDeleter>;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,33 @@
|
||||
.code
|
||||
extern OrdinalProc1 : qword
|
||||
extern OrdinalProc2 : qword
|
||||
extern OrdinalProc3 : qword
|
||||
extern OrdinalProc4 : qword
|
||||
extern OrdinalProc5 : qword
|
||||
extern OrdinalProc6 : qword
|
||||
|
||||
OrdinalThunk1 proc
|
||||
jmp qword ptr [OrdinalProc1]
|
||||
OrdinalThunk1 endp
|
||||
|
||||
OrdinalThunk2 proc
|
||||
jmp qword ptr [OrdinalProc2]
|
||||
OrdinalThunk2 endp
|
||||
|
||||
OrdinalThunk3 proc
|
||||
jmp qword ptr [OrdinalProc3]
|
||||
OrdinalThunk3 endp
|
||||
|
||||
OrdinalThunk4 proc
|
||||
jmp qword ptr [OrdinalProc4]
|
||||
OrdinalThunk4 endp
|
||||
|
||||
OrdinalThunk5 proc
|
||||
jmp qword ptr [OrdinalProc5]
|
||||
OrdinalThunk5 endp
|
||||
|
||||
OrdinalThunk6 proc
|
||||
jmp qword ptr [OrdinalProc6]
|
||||
OrdinalThunk6 endp
|
||||
|
||||
end
|
||||
@@ -0,0 +1,83 @@
|
||||
#include <Windows.h>
|
||||
#include "xaudio2_impl.h"
|
||||
#define ORDINAL_PROC(n) extern "C" void *OrdinalProc##n; void *OrdinalProc##n; __pragma(comment(linker, "/export:OrdinalThunk" #n ",@" #n ",NONAME"))
|
||||
|
||||
//
|
||||
// Variant 1 (older ERA titles)
|
||||
// 1: XAudio2Create
|
||||
// 2: CreateAudioReverb
|
||||
// 3: CreateAudioVolumeMeter
|
||||
// 4: CreateFX
|
||||
// 5: X3DAudioCalculate
|
||||
// 6: X3DAudioInitialize
|
||||
//
|
||||
// Variant 2 (newer ERA titles)
|
||||
// 1: CreateAudioReverb
|
||||
// 2: CreateAudioVolumeMeter
|
||||
// 3: CreateFX
|
||||
// 4: CreateXAudio2Object
|
||||
// 5: X3DAudioCalculate
|
||||
// 6: X3DAudioInitialize
|
||||
//
|
||||
ORDINAL_PROC(1);
|
||||
ORDINAL_PROC(2);
|
||||
ORDINAL_PROC(3);
|
||||
ORDINAL_PROC(4);
|
||||
ORDINAL_PROC(5);
|
||||
ORDINAL_PROC(6);
|
||||
|
||||
#pragma comment(linker, "/export:CreateXAudio2Object")
|
||||
#pragma comment(linker, "/export:XAudio2Create=CreateXAudio2Object")
|
||||
|
||||
// We need to export everything because GetModuleHandle("XAudio2_9") may resolve to this DLL.
|
||||
#define XAUDIO2_FORWARD(Name) __pragma(comment(linker, "/export:" #Name "=C:\\WINDOWS\\System32\\XAudio2_9." #Name))
|
||||
#define XAUDIO2_FORWARD_ORDINAL(Name, Ordinal) __pragma(comment(linker, "/export:" #Name "=C:\\WINDOWS\\System32\\XAudio2_9." #Name ",@" #Ordinal))
|
||||
XAUDIO2_FORWARD(CreateAudioReverb)
|
||||
XAUDIO2_FORWARD(CreateAudioVolumeMeter)
|
||||
XAUDIO2_FORWARD(CreateFX)
|
||||
XAUDIO2_FORWARD(X3DAudioCalculate)
|
||||
XAUDIO2_FORWARD(X3DAudioInitialize)
|
||||
XAUDIO2_FORWARD_ORDINAL(CreateAudioReverbV2_8, 7)
|
||||
XAUDIO2_FORWARD_ORDINAL(XAudio2CreateV2_9, 8)
|
||||
XAUDIO2_FORWARD_ORDINAL(XAudio2CreateWithVersionInfo, 9)
|
||||
XAUDIO2_FORWARD_ORDINAL(XAudio2CreateWithSharedContexts, 10)
|
||||
|
||||
BOOL WINAPI DllMain(
|
||||
_In_ HINSTANCE hinstDLL,
|
||||
_In_ DWORD fdwReason,
|
||||
_In_ LPVOID lpvReserved)
|
||||
{
|
||||
if (fdwReason != DLL_PROCESS_ATTACH)
|
||||
return TRUE;
|
||||
|
||||
// TODO: Dynamic check
|
||||
#if 1
|
||||
OrdinalProc1 = GetProcAddress(hinstDLL, "CreateAudioReverb");
|
||||
OrdinalProc2 = GetProcAddress(hinstDLL, "CreateAudioVolumeMeter");
|
||||
OrdinalProc3 = GetProcAddress(hinstDLL, "CreateFX");
|
||||
OrdinalProc4 = GetProcAddress(hinstDLL, "CreateXAudio2Object");
|
||||
OrdinalProc5 = GetProcAddress(hinstDLL, "X3DAudioCalculate");
|
||||
OrdinalProc6 = GetProcAddress(hinstDLL, "X3DAudioInitialize");
|
||||
#else
|
||||
OrdinalProc1 = GetProcAddress(hinstDLL, "XAudio2Create");
|
||||
OrdinalProc2 = GetProcAddress(hinstDLL, "CreateAudioReverb");
|
||||
OrdinalProc3 = GetProcAddress(hinstDLL, "CreateAudioVolumeMeter");
|
||||
OrdinalProc4 = GetProcAddress(hinstDLL, "CreateFX");
|
||||
OrdinalProc5 = GetProcAddress(hinstDLL, "X3DAudioCalculate");
|
||||
OrdinalProc6 = GetProcAddress(hinstDLL, "X3DAudioInitialize");
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
HRESULT CreateXAudio2Object(
|
||||
IXAudio2 **ppXAudio2,
|
||||
UINT32 Flags,
|
||||
XAUDIO2_PROCESSOR XAudio2Processor,
|
||||
void *pSharedShapeContexts)
|
||||
{
|
||||
HRESULT hr;
|
||||
Microsoft::WRL::ComPtr<IXAudio2> pXAudio2;
|
||||
RETURN_IF_FAILED(hr = XAudio2Create(&pXAudio2, Flags, XAudio2Processor));
|
||||
*ppXAudio2 = new CXAudio2(pXAudio2.Get());
|
||||
return hr;
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
#pragma once
|
||||
#include <xaudio2.h>
|
||||
#include <xma2defs.h>
|
||||
#include <wrl/client.h>
|
||||
#include <wil/result_macros.h>
|
||||
#include "xma_voice.h"
|
||||
|
||||
class CXAudio2 final : public IXAudio2
|
||||
{
|
||||
LONG m_RefCount;
|
||||
Microsoft::WRL::ComPtr<IXAudio2> m_pXAudio2;
|
||||
public:
|
||||
CXAudio2(IXAudio2 *pXAudio2) :
|
||||
m_RefCount(1),
|
||||
m_pXAudio2(pXAudio2)
|
||||
{
|
||||
}
|
||||
|
||||
//
|
||||
// IUnknown
|
||||
//
|
||||
HRESULT QueryInterface(REFIID riid, void **ppvObject) override
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_POINTER, ppvObject);
|
||||
|
||||
if (riid == __uuidof(IUnknown) ||
|
||||
riid == __uuidof(IXAudio2))
|
||||
{
|
||||
AddRef();
|
||||
*ppvObject = this;
|
||||
RETURN_HR(S_OK);
|
||||
}
|
||||
|
||||
*ppvObject = nullptr;
|
||||
RETURN_HR(E_NOINTERFACE);
|
||||
}
|
||||
|
||||
ULONG AddRef() override
|
||||
{
|
||||
return InterlockedIncrement(&m_RefCount);
|
||||
}
|
||||
|
||||
ULONG Release() override
|
||||
{
|
||||
ULONG uCount = InterlockedDecrement(&m_RefCount);
|
||||
|
||||
if (uCount == 0)
|
||||
delete this;
|
||||
|
||||
return uCount;
|
||||
}
|
||||
|
||||
//
|
||||
// IXAudio2
|
||||
//
|
||||
HRESULT RegisterForCallbacks(IXAudio2EngineCallback *pCallback) override
|
||||
{
|
||||
return m_pXAudio2->RegisterForCallbacks(pCallback);
|
||||
}
|
||||
|
||||
void UnregisterForCallbacks(IXAudio2EngineCallback *pCallback) override
|
||||
{
|
||||
m_pXAudio2->UnregisterForCallbacks(pCallback);
|
||||
}
|
||||
|
||||
HRESULT CreateSourceVoice(IXAudio2SourceVoice **ppSourceVoice, WAVEFORMATEX const *pSourceFormat, UINT32 Flags, float MaxFrequencyRatio, IXAudio2VoiceCallback *pCallback, XAUDIO2_VOICE_SENDS const *pSendList, XAUDIO2_EFFECT_CHAIN const *pEffectChain) override
|
||||
{
|
||||
if (pSourceFormat->wFormatTag == WAVE_FORMAT_XMA2)
|
||||
return CXAudio2SourceVoiceXMA2::Create(ppSourceVoice, this, (XMA2WAVEFORMATEX const *)pSourceFormat, Flags, MaxFrequencyRatio, pCallback, pSendList, pEffectChain);
|
||||
|
||||
return m_pXAudio2->CreateSourceVoice(ppSourceVoice, pSourceFormat, Flags, MaxFrequencyRatio, pCallback, pSendList, pEffectChain);
|
||||
}
|
||||
|
||||
HRESULT CreateSubmixVoice(IXAudio2SubmixVoice **ppSubmixVoice, UINT32 InputChannels, UINT32 InputSampleRate, UINT32 Flags, UINT32 ProcessingStage, XAUDIO2_VOICE_SENDS const *pSendList, XAUDIO2_EFFECT_CHAIN const *pEffectChain) override
|
||||
{
|
||||
return m_pXAudio2->CreateSubmixVoice(ppSubmixVoice, InputChannels, InputSampleRate, Flags, ProcessingStage, pSendList, pEffectChain);
|
||||
}
|
||||
|
||||
HRESULT CreateMasteringVoice(IXAudio2MasteringVoice **ppMasteringVoice, UINT32 InputChannels, UINT32 InputSampleRate, UINT32 Flags, LPCWSTR szDeviceId, XAUDIO2_EFFECT_CHAIN const *pEffectChain, AUDIO_STREAM_CATEGORY StreamCategory) override
|
||||
{
|
||||
return m_pXAudio2->CreateMasteringVoice(ppMasteringVoice, InputChannels, InputSampleRate, Flags, szDeviceId, pEffectChain, StreamCategory);
|
||||
}
|
||||
|
||||
HRESULT StartEngine() override
|
||||
{
|
||||
return m_pXAudio2->StartEngine();
|
||||
}
|
||||
|
||||
void StopEngine() override
|
||||
{
|
||||
m_pXAudio2->StopEngine();
|
||||
}
|
||||
|
||||
HRESULT CommitChanges(UINT32 OperationSet) override
|
||||
{
|
||||
return m_pXAudio2->CommitChanges(OperationSet);
|
||||
}
|
||||
|
||||
void GetPerformanceData(XAUDIO2_PERFORMANCE_DATA *pPerfData) override
|
||||
{
|
||||
m_pXAudio2->GetPerformanceData(pPerfData);
|
||||
}
|
||||
|
||||
void SetDebugConfiguration(XAUDIO2_DEBUG_CONFIGURATION const *pDebugConfiguration, void *pReserved)
|
||||
{
|
||||
m_pXAudio2->SetDebugConfiguration(pDebugConfiguration, pReserved);
|
||||
}
|
||||
};
|
||||
|
||||
extern "C" HRESULT WINAPI CreateXAudio2Object(
|
||||
IXAudio2 **ppXAudio2,
|
||||
UINT32 Flags,
|
||||
XAUDIO2_PROCESSOR XAudio2Processor,
|
||||
void *pSharedShapeContexts);
|
||||
@@ -0,0 +1,154 @@
|
||||
#include "xma_voice.h"
|
||||
#include <vector>
|
||||
|
||||
static void av_log_callback(void *ptr, int level, char const *fmt, va_list args)
|
||||
{
|
||||
static char buffer[4096];
|
||||
vsnprintf(buffer, sizeof(buffer), fmt, args);
|
||||
OutputDebugStringA(buffer);
|
||||
}
|
||||
|
||||
CXAudio2SourceVoiceXMA2::~CXAudio2SourceVoiceXMA2()
|
||||
{
|
||||
m_bDecodeStopRequest = TRUE;
|
||||
WaitForSingleObject(m_hDecodeThread.get(), INFINITE);
|
||||
m_pSource->DestroyVoice();
|
||||
}
|
||||
|
||||
CXAudio2SourceVoiceXMA2::CXAudio2SourceVoiceXMA2(IXAudio2SourceVoice *pSource, XMA2WAVEFORMATEX const *pSourceFormat) :
|
||||
m_pSource(pSource),
|
||||
m_hDecodeThread(nullptr),
|
||||
m_pAudioBuffers{}
|
||||
{
|
||||
#if 0
|
||||
av_log_set_level(AV_LOG_DEBUG);
|
||||
av_log_set_callback(av_log_callback);
|
||||
#endif
|
||||
m_pCodec = avcodec_find_decoder(AV_CODEC_ID_XMA2);
|
||||
m_pContext = unique_avcodec_context(avcodec_alloc_context3(m_pCodec));
|
||||
av_channel_layout_default(&m_pContext->ch_layout, pSourceFormat->wfx.nChannels);
|
||||
m_pContext->sample_rate = pSourceFormat->wfx.nSamplesPerSec;
|
||||
m_pContext->extradata_size = sizeof(*pSourceFormat) - sizeof(pSourceFormat->wfx);
|
||||
m_pContext->extradata = (uint8_t *)av_malloc(m_pContext->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
|
||||
memcpy(m_pContext->extradata, &pSourceFormat->wfx + 1, m_pContext->extradata_size);
|
||||
|
||||
if (avcodec_open2(m_pContext.get(), nullptr, nullptr) < 0)
|
||||
{
|
||||
av_free(m_pContext->extradata);
|
||||
MessageBoxW(nullptr, L"Failed to open XMA2 codec", __FUNCTIONW__, MB_OK);
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT CXAudio2SourceVoiceXMA2::Create(IXAudio2SourceVoice **ppSourceVoice, IXAudio2 *pXAudio2, XMA2WAVEFORMATEX const *pSourceFormat, UINT32 Flags, float MaxFrequencyRatio, IXAudio2VoiceCallback *pCallback, XAUDIO2_VOICE_SENDS const *pSendList, XAUDIO2_EFFECT_CHAIN const *pEffectChain)
|
||||
{
|
||||
RETURN_HR_IF_NULL(E_POINTER, ppSourceVoice);
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, pXAudio2);
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, pSourceFormat);
|
||||
|
||||
HRESULT hr;
|
||||
WORD nChannels = pSourceFormat->wfx.nChannels;
|
||||
|
||||
// TODO
|
||||
WAVEFORMATEX DestFormat
|
||||
{
|
||||
WAVE_FORMAT_IEEE_FLOAT,
|
||||
nChannels,
|
||||
pSourceFormat->wfx.nSamplesPerSec,
|
||||
pSourceFormat->wfx.nSamplesPerSec * (nChannels * 32 / 8),
|
||||
nChannels * 32 / 8,
|
||||
32,
|
||||
0
|
||||
};
|
||||
|
||||
IXAudio2SourceVoice *pSource;
|
||||
RETURN_IF_FAILED(hr = pXAudio2->CreateSourceVoice(&pSource, &DestFormat, Flags, MaxFrequencyRatio, pCallback, pSendList, pEffectChain));
|
||||
RETURN_IF_NULL_ALLOC(*ppSourceVoice = new(std::nothrow) CXAudio2SourceVoiceXMA2(pSource, pSourceFormat));
|
||||
return hr;
|
||||
}
|
||||
|
||||
struct DecodeThreadParams
|
||||
{
|
||||
CXAudio2SourceVoiceXMA2 *pSource;
|
||||
XAUDIO2_BUFFER Buffer;
|
||||
|
||||
DecodeThreadParams(CXAudio2SourceVoiceXMA2 *pSource, XAUDIO2_BUFFER const *pBuffer) :
|
||||
pSource(pSource),
|
||||
Buffer(*pBuffer)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
DWORD CXAudio2SourceVoiceXMA2::DecodeThreadProc(LPVOID lpParam)
|
||||
{
|
||||
auto params = *(DecodeThreadParams *)lpParam;
|
||||
delete lpParam;
|
||||
|
||||
auto frame = unique_avframe(av_frame_alloc());
|
||||
RETURN_IF_NULL_ALLOC(frame);
|
||||
|
||||
DWORD currentBufferIndex = 0;
|
||||
std::vector<float> samples{};
|
||||
|
||||
while (!params.pSource->m_bDecodeStopRequest && avcodec_receive_frame(params.pSource->m_pContext.get(), frame.get()) >= 0)
|
||||
{
|
||||
samples.clear();
|
||||
|
||||
for (int i = 0; i < frame->nb_samples; i++)
|
||||
{
|
||||
for (int j = 0; j < frame->ch_layout.nb_channels; j++)
|
||||
{
|
||||
samples.push_back(((float *)frame->data[j])[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (XAUDIO2_VOICE_STATE state; params.pSource->m_pSource->GetState(&state), (!params.pSource->m_bDecodeStopRequest && state.BuffersQueued >= MAX_AUDIO_BUFFER_COUNT);)
|
||||
{
|
||||
// TODO: Surely there is a better way of doing this that I am not thinking of right now
|
||||
Sleep(1);
|
||||
}
|
||||
|
||||
// Copy the sample buffer
|
||||
INT64 AudioBytes = samples.size() * sizeof(float);
|
||||
memcpy(params.pSource->m_pAudioBuffers[currentBufferIndex], samples.data(), AudioBytes);
|
||||
|
||||
XAUDIO2_BUFFER Buffer
|
||||
{
|
||||
// Only pass Flags for the last chunk
|
||||
AudioBytes <= XAUDIO2_MAX_BUFFER_BYTES ? params.Buffer.Flags : 0,
|
||||
|
||||
// Cannot pass more than XAUDIO2_MAX_BUFFER_BYTES
|
||||
min(AudioBytes, XAUDIO2_MAX_BUFFER_BYTES),
|
||||
params.pSource->m_pAudioBuffers[currentBufferIndex],
|
||||
|
||||
// TODO: Handle these
|
||||
0, // pBuffer->PlayBegin,
|
||||
min(AudioBytes, XAUDIO2_MAX_BUFFER_BYTES) / sizeof(float) / params.pSource->m_pContext->ch_layout.nb_channels, // pBuffer->PlayLength,
|
||||
0, // pBuffer->LoopBegin,
|
||||
0, // pBuffer->LoopLength,
|
||||
0, // pBuffer->LoopCount,
|
||||
params.Buffer.pContext,
|
||||
};
|
||||
|
||||
currentBufferIndex++;
|
||||
currentBufferIndex %= MAX_AUDIO_BUFFER_COUNT;
|
||||
RETURN_IF_FAILED(params.pSource->m_pSource->SubmitSourceBuffer(&Buffer, nullptr));
|
||||
}
|
||||
|
||||
RETURN_HR(S_OK);
|
||||
}
|
||||
|
||||
HRESULT CXAudio2SourceVoiceXMA2::SubmitSourceBuffer(XAUDIO2_BUFFER const *pBuffer, XAUDIO2_BUFFER_WMA const *pBufferWMA)
|
||||
{
|
||||
auto packet = unique_avpacket(av_packet_alloc());
|
||||
RETURN_IF_NULL_ALLOC(packet);
|
||||
|
||||
av_init_packet(packet.get());
|
||||
packet->data = (uint8_t *)pBuffer->pAudioData;
|
||||
packet->size = pBuffer->AudioBytes;
|
||||
avcodec_send_packet(m_pContext.get(), packet.get());
|
||||
|
||||
DecodeThreadParams *params;
|
||||
RETURN_IF_NULL_ALLOC(params = new(std::nothrow) DecodeThreadParams(this, pBuffer));
|
||||
m_hDecodeThread = wil::unique_handle(CreateThread(nullptr, 0, &DecodeThreadProc, params, 0, nullptr));
|
||||
RETURN_HR(S_OK);
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
#pragma once
|
||||
#include <xaudio2.h>
|
||||
#include <xma2defs.h>
|
||||
#include <wrl/client.h>
|
||||
#include <wil/result_macros.h>
|
||||
#include <wil/win32_helpers.h>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <libavcodec/avcodec.h>
|
||||
}
|
||||
|
||||
#include <avcodec_raii.h>
|
||||
|
||||
class CXAudio2SourceVoiceXMA2 final : public IXAudio2SourceVoice
|
||||
{
|
||||
static constexpr size_t AUDIO_BUFFER_SIZE = 256 * 8192;
|
||||
static constexpr size_t MAX_AUDIO_BUFFER_COUNT = 3;
|
||||
IXAudio2SourceVoice *m_pSource;
|
||||
AVCodec const *m_pCodec;
|
||||
unique_avcodec_context m_pContext;
|
||||
wil::unique_handle m_hDecodeThread;
|
||||
BOOL m_bDecodeStopRequest = FALSE;
|
||||
BYTE m_pAudioBuffers[MAX_AUDIO_BUFFER_COUNT][AUDIO_BUFFER_SIZE];
|
||||
~CXAudio2SourceVoiceXMA2();
|
||||
CXAudio2SourceVoiceXMA2(IXAudio2SourceVoice *pSource, XMA2WAVEFORMATEX const *pSourceFormat);
|
||||
static DWORD WINAPI DecodeThreadProc(LPVOID lpParam);
|
||||
public:
|
||||
static HRESULT Create(IXAudio2SourceVoice **ppSourceVoice, IXAudio2 *pXAudio2, XMA2WAVEFORMATEX const *pSourceFormat, UINT32 Flags, float MaxFrequencyRatio, IXAudio2VoiceCallback *pCallback, XAUDIO2_VOICE_SENDS const *pSendList, XAUDIO2_EFFECT_CHAIN const *pEffectChain);
|
||||
|
||||
//
|
||||
// IXAudio2Voice
|
||||
//
|
||||
void GetVoiceDetails(XAUDIO2_VOICE_DETAILS *pVoiceDetails) override
|
||||
{
|
||||
m_pSource->GetVoiceDetails(pVoiceDetails);
|
||||
}
|
||||
|
||||
HRESULT SetOutputVoices(XAUDIO2_VOICE_SENDS const *pSendList) override
|
||||
{
|
||||
return m_pSource->SetOutputVoices(pSendList);
|
||||
}
|
||||
|
||||
HRESULT SetEffectChain(XAUDIO2_EFFECT_CHAIN const *pEffectChain) override
|
||||
{
|
||||
return m_pSource->SetEffectChain(pEffectChain);
|
||||
}
|
||||
|
||||
HRESULT EnableEffect(UINT32 EffectIndex, UINT32 OperationSet) override
|
||||
{
|
||||
return m_pSource->EnableEffect(EffectIndex, OperationSet);
|
||||
}
|
||||
|
||||
HRESULT DisableEffect(UINT32 EffectIndex, UINT32 OperationSet) override
|
||||
{
|
||||
return m_pSource->DisableEffect(EffectIndex, OperationSet);
|
||||
}
|
||||
|
||||
void GetEffectState(UINT32 EffectIndex, BOOL *pEnabled) override
|
||||
{
|
||||
m_pSource->GetEffectState(EffectIndex, pEnabled);
|
||||
}
|
||||
|
||||
HRESULT SetEffectParameters(UINT32 EffectIndex, void const *pParameters, UINT32 ParametersByteSize, UINT32 OperationSet) override
|
||||
{
|
||||
return m_pSource->SetEffectParameters(EffectIndex, pParameters, ParametersByteSize, OperationSet);
|
||||
}
|
||||
|
||||
HRESULT GetEffectParameters(UINT32 EffectIndex, void *pParameters, UINT32 ParametersByteSize) override
|
||||
{
|
||||
return m_pSource->GetEffectParameters(EffectIndex, pParameters, ParametersByteSize);
|
||||
}
|
||||
|
||||
HRESULT SetFilterParameters(XAUDIO2_FILTER_PARAMETERS const *pParameters, UINT32 OperationSet) override
|
||||
{
|
||||
return m_pSource->SetFilterParameters(pParameters, OperationSet);
|
||||
}
|
||||
|
||||
void GetFilterParameters(XAUDIO2_FILTER_PARAMETERS *pParameters) override
|
||||
{
|
||||
return m_pSource->GetFilterParameters(pParameters);
|
||||
}
|
||||
|
||||
HRESULT SetOutputFilterParameters(IXAudio2Voice *pDestinationVoice, XAUDIO2_FILTER_PARAMETERS const *pParameters, UINT32 OperationSet) override
|
||||
{
|
||||
return m_pSource->SetOutputFilterParameters(pDestinationVoice, pParameters, OperationSet);
|
||||
}
|
||||
|
||||
void GetOutputFilterParameters(IXAudio2Voice *pDestinationVoice, XAUDIO2_FILTER_PARAMETERS *pParameters) override
|
||||
{
|
||||
m_pSource->GetOutputFilterParameters(pDestinationVoice, pParameters);
|
||||
}
|
||||
|
||||
HRESULT SetVolume(float Volume, UINT32 OperationSet) override
|
||||
{
|
||||
return m_pSource->SetVolume(Volume, OperationSet);
|
||||
}
|
||||
|
||||
void GetVolume(float *pVolume) override
|
||||
{
|
||||
m_pSource->GetVolume(pVolume);
|
||||
}
|
||||
|
||||
HRESULT SetChannelVolumes(UINT32 Channels, float const *pVolumes, UINT32 OperationSet) override
|
||||
{
|
||||
return m_pSource->SetChannelVolumes(Channels, pVolumes, OperationSet);
|
||||
}
|
||||
|
||||
void GetChannelVolumes(UINT32 Channels, float *pVolumes) override
|
||||
{
|
||||
m_pSource->GetChannelVolumes(Channels, pVolumes);
|
||||
}
|
||||
|
||||
HRESULT SetOutputMatrix(IXAudio2Voice *pDestinationVoice, UINT32 SourceChannels, UINT32 DestinationChannels, float const *pLevelMatrix, UINT32 OperationSet) override
|
||||
{
|
||||
return m_pSource->SetOutputMatrix(pDestinationVoice, SourceChannels, DestinationChannels, pLevelMatrix, OperationSet);
|
||||
}
|
||||
|
||||
void GetOutputMatrix(IXAudio2Voice *pDestinationVoice, UINT32 SourceChannels, UINT32 DestinationChannels, float *pLevelMatrix) override
|
||||
{
|
||||
m_pSource->GetOutputMatrix(pDestinationVoice, SourceChannels, DestinationChannels, pLevelMatrix);
|
||||
}
|
||||
|
||||
void DestroyVoice() override
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
//
|
||||
// IXAudio2SourceVoice
|
||||
//
|
||||
HRESULT Start(UINT32 Flags, UINT32 OperationSet) override
|
||||
{
|
||||
return m_pSource->Start(Flags, OperationSet);
|
||||
}
|
||||
|
||||
HRESULT Stop(UINT32 Flags, UINT32 OperationSet) override
|
||||
{
|
||||
return m_pSource->Stop(Flags, OperationSet);
|
||||
}
|
||||
|
||||
HRESULT SubmitSourceBuffer(XAUDIO2_BUFFER const *pBuffer, XAUDIO2_BUFFER_WMA const *pBufferWMA) override;
|
||||
|
||||
HRESULT FlushSourceBuffers() override
|
||||
{
|
||||
return m_pSource->FlushSourceBuffers();
|
||||
}
|
||||
|
||||
HRESULT Discontinuity() override
|
||||
{
|
||||
return m_pSource->Discontinuity();
|
||||
}
|
||||
|
||||
HRESULT ExitLoop(UINT32 OperationSet) override
|
||||
{
|
||||
return m_pSource->ExitLoop(OperationSet);
|
||||
}
|
||||
|
||||
void GetState(XAUDIO2_VOICE_STATE *pVoiceState, UINT32 Flags) override
|
||||
{
|
||||
m_pSource->GetState(pVoiceState, Flags);
|
||||
}
|
||||
|
||||
HRESULT SetFrequencyRatio(float Ratio, UINT32 OperationSet) override
|
||||
{
|
||||
return m_pSource->SetFrequencyRatio(Ratio, OperationSet);
|
||||
}
|
||||
|
||||
void GetFrequencyRatio(float *pRatio) override
|
||||
{
|
||||
m_pSource->GetFrequencyRatio(pRatio);
|
||||
}
|
||||
|
||||
HRESULT SetSourceSampleRate(UINT32 NewSourceSampleRate) override
|
||||
{
|
||||
return m_pSource->SetSourceSampleRate(NewSourceSampleRate);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg-configuration.schema.json",
|
||||
"default-registry": {
|
||||
"kind": "git",
|
||||
"baseline": "bb002e69deb36c00e9ce21810f69eff349a833f7",
|
||||
"repository": "https://github.com/microsoft/vcpkg"
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json",
|
||||
"dependencies": [
|
||||
"wil",
|
||||
{
|
||||
"name": "ffmpeg",
|
||||
"default-features": false,
|
||||
"features": ["avcodec"]
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user