Update to spice2x-25-04-25 (pre-apply)

> broken commit
This commit is contained in:
[ ]
2025-05-07 00:25:31 +09:00
parent 04dee88276
commit c94de456b5
543 changed files with 78491 additions and 91698 deletions
+34 -3
View File
@@ -20,6 +20,16 @@ DEFINE_GUID(CLSID_MMDeviceEnumerator,
0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E);
#endif
// {ef985e71-d5c7-42d4-ba4d-2d073e2e96f4}
DEFINE_GUID(CLSID_DirectSoundI3DL2ReverbDMO,
0xef985e71, 0xd5c7, 0x42d4,
0xba, 0x4d, 0x2d, 0x07, 0x3e, 0x2e, 0x96, 0xf4);
// {87fc0268-9a55-4360-95aa-004a1d9de26c}
DEFINE_GUID(CLSID_DirectSoundWavesReverbDMO,
0x87fc0268, 0x9a55, 0x4360,
0x95, 0xaa, 0x00, 0x4a, 0x1d, 0x9d, 0xe2, 0x6c);
// function pointers
static decltype(CoCreateInstance) *CoCreateInstance_orig = nullptr;
@@ -27,14 +37,16 @@ namespace hooks::audio {
// public globals
bool ENABLED = true;
bool VOLUME_HOOK_ENABLED = true;
bool USE_DUMMY = false;
WAVEFORMATEXTENSIBLE FORMAT {};
std::optional<Backend> BACKEND = std::nullopt;
size_t ASIO_DRIVER_ID = 0;
bool ASIO_FORCE_UNLOAD_ON_STOP = false;
// private globals
IAudioClient *CLIENT = nullptr;
std::mutex INITIALIZE_LOCK;
std::mutex INITIALIZE_LOCK; // for asio
}
static HRESULT STDAPICALLTYPE CoCreateInstance_hook(
@@ -44,12 +56,28 @@ static HRESULT STDAPICALLTYPE CoCreateInstance_hook(
REFIID riid,
LPVOID *ppv)
{
HRESULT ret;
// call original
HRESULT ret = CoCreateInstance_orig(rclsid, pUnkOuter, dwClsContext, riid, ppv);
ret = CoCreateInstance_orig(rclsid, pUnkOuter, dwClsContext, riid, ppv);
if (FAILED(ret)) {
if (IsEqualCLSID(rclsid, CLSID_MMDeviceEnumerator)) {
log_warning("audio", "CoCreateInstance failed, hr={}", FMT_HRESULT(ret));
log_warning("audio", "CoCreateInstance failed for CLSID_MMDeviceEnumerator, hr={}", FMT_HRESULT(ret));
}
// Windows 11 KB5052093 issue - reverb DMO went missing from dsdmo.dll (fails with 0x80040154)
if (IsEqualCLSID(rclsid, CLSID_DirectSoundI3DL2ReverbDMO) && ret == (HRESULT)0x80040154) {
log_warning(
"audio",
"CoCreateInstance for CLSID_DirectSoundI3DL2ReverbDMO failed with 0x80040154, swap with CLSID_DirectSoundWavesReverbDMO "
"(workaround for Windows 11 KB5052093 issue); REVERB EX replaced with REVERB");
ret = CoCreateInstance_orig(CLSID_DirectSoundWavesReverbDMO, pUnkOuter, dwClsContext, riid, ppv);
if (FAILED(ret)) {
log_warning("audio", "CoCreateInstance(CLSID_DirectSoundWavesReverbDMO...) failed, hr={}", FMT_HRESULT(ret));
}
}
return ret;
}
@@ -72,16 +100,19 @@ namespace hooks::audio {
}
log_info("audio", "initializing");
init_low_latency();
// general hooks
CoCreateInstance_orig = detour::iat_try("CoCreateInstance", CoCreateInstance_hook);
}
void stop() {
log_info("audio", "stopping");
if (CLIENT) {
CLIENT->Stop();
CLIENT->Release();
CLIENT = nullptr;
}
stop_low_latency();
}
}
+5
View File
@@ -4,8 +4,10 @@
#include <windows.h>
#include <mmreg.h>
#ifndef _MSC_VER
#include <ks.h>
#include <ksmedia.h>
#endif
namespace hooks::audio {
enum class Backend {
@@ -14,10 +16,13 @@ namespace hooks::audio {
};
extern bool ENABLED;
extern bool VOLUME_HOOK_ENABLED;
extern bool USE_DUMMY;
extern WAVEFORMATEXTENSIBLE FORMAT;
extern std::optional<Backend> BACKEND;
extern size_t ASIO_DRIVER_ID;
extern bool ASIO_FORCE_UNLOAD_ON_STOP;
extern bool LOW_LATENCY_SHARED_WASAPI;
void init();
void stop();
+3
View File
@@ -4,6 +4,7 @@
#include <windows.h>
#include <audioclient.h>
#include "hooks/audio/backends/wasapi/low_latency_client.h"
constexpr bool AUDIO_LOG_HRESULT = true;
@@ -11,4 +12,6 @@ namespace hooks::audio {
extern IAudioClient *CLIENT;
extern std::mutex INITIALIZE_LOCK;
extern bool VOLUME_HOOK_ENABLED;
extern LowLatencyAudioClient *LOW_LATENCY_CLIENT;
}
@@ -0,0 +1,112 @@
#include "audio_endpoint_volume.h"
#include "util/logging.h"
HRESULT STDMETHODCALLTYPE WrappedIAudioEndpointVolume::QueryInterface(REFIID riid, void **ppvObj) {
if (ppvObj == nullptr) {
return E_POINTER;
}
if (riid == __uuidof(IAudioEndpointVolume)) {
this->AddRef();
*ppvObj = this;
return S_OK;
}
return pReal->QueryInterface(riid, ppvObj);
}
ULONG STDMETHODCALLTYPE WrappedIAudioEndpointVolume::AddRef() {
return pReal->AddRef();
}
ULONG STDMETHODCALLTYPE WrappedIAudioEndpointVolume::Release() {
// get reference count of underlying interface
ULONG refs = pReal != nullptr ? pReal->Release() : 0;
if (refs == 0) {
delete this;
}
return refs;
}
HRESULT STDMETHODCALLTYPE WrappedIAudioEndpointVolume::SetMasterVolumeLevelScalar(float fLevel, LPCGUID pguidEventContext) {
log_misc("audio", "WrappedIAudioEndpointVolume::SetMasterVolumeLevelScalar called; ignoring volume change");
return S_OK;
}
HRESULT STDMETHODCALLTYPE WrappedIAudioEndpointVolume::RegisterControlChangeNotify(IAudioEndpointVolumeCallback *pNotify) {
return pReal->RegisterControlChangeNotify(pNotify);
}
HRESULT STDMETHODCALLTYPE WrappedIAudioEndpointVolume::UnregisterControlChangeNotify(IAudioEndpointVolumeCallback *pNotify) {
return pReal->UnregisterControlChangeNotify(pNotify);
}
HRESULT STDMETHODCALLTYPE WrappedIAudioEndpointVolume::GetChannelCount(uint32_t *pnChannelCount) {
return pReal->GetChannelCount(pnChannelCount);
}
HRESULT STDMETHODCALLTYPE WrappedIAudioEndpointVolume::SetMasterVolumeLevel(float fLevelDB, LPCGUID pguidEventContext) {
log_misc("audio", "WrappedIAudioEndpointVolume::SetMasterVolumeLevel called; ignoring volume change");
return S_OK;
}
HRESULT STDMETHODCALLTYPE WrappedIAudioEndpointVolume::GetMasterVolumeLevel(float *fLevelDB) {
return pReal->GetMasterVolumeLevel(fLevelDB);
}
HRESULT STDMETHODCALLTYPE WrappedIAudioEndpointVolume::GetMasterVolumeLevelScalar(float *fLevel) {
return pReal->GetMasterVolumeLevelScalar(fLevel);
}
HRESULT STDMETHODCALLTYPE WrappedIAudioEndpointVolume::SetChannelVolumeLevel(uint32_t nChannel, float fLevelDB, LPCGUID pguidEventContext) {
log_misc("audio", "WrappedIAudioEndpointVolume::SetChannelVolumeLevel called; ignoring volume change");
return S_OK;
}
HRESULT STDMETHODCALLTYPE WrappedIAudioEndpointVolume::SetChannelVolumeLevelScalar(uint32_t nChannel, float fLevel, LPCGUID pguidEventContext) {
log_misc("audio", "WrappedIAudioEndpointVolume::SetChannelVolumeLevelScalar called; ignoring volume change");
return S_OK;
}
HRESULT STDMETHODCALLTYPE WrappedIAudioEndpointVolume::GetChannelVolumeLevel(uint32_t nChannel, float *fLevelDB) {
return pReal->GetChannelVolumeLevel(nChannel, fLevelDB);
}
HRESULT STDMETHODCALLTYPE WrappedIAudioEndpointVolume::GetChannelVolumeLevelScalar(uint32_t nChannel, float *fLevel) {
return pReal->GetChannelVolumeLevelScalar(nChannel, fLevel);
}
HRESULT STDMETHODCALLTYPE WrappedIAudioEndpointVolume::SetMute(WINBOOL bMute, LPCGUID pguidEventContext) {
log_misc("audio", "WrappedIAudioEndpointVolume::SetMute called; ignoring volume change");
return S_OK;
}
HRESULT STDMETHODCALLTYPE WrappedIAudioEndpointVolume::GetMute(WINBOOL *bMute) {
return pReal->GetMute(bMute);
}
HRESULT STDMETHODCALLTYPE WrappedIAudioEndpointVolume::GetVolumeStepInfo(uint32_t *pnStep, uint32_t *pnStepCount) {
return pReal->GetVolumeStepInfo(pnStep, pnStepCount);
}
HRESULT STDMETHODCALLTYPE WrappedIAudioEndpointVolume::VolumeStepUp(LPCGUID pguidEventContext) {
log_misc("audio", "WrappedIAudioEndpointVolume::VolumeStepUp called; ignoring volume change");
return S_OK;
}
HRESULT STDMETHODCALLTYPE WrappedIAudioEndpointVolume::VolumeStepDown(LPCGUID pguidEventContext) {
log_misc("audio", "WrappedIAudioEndpointVolume::VolumeStepDown called; ignoring volume change");
return S_OK;
}
HRESULT STDMETHODCALLTYPE WrappedIAudioEndpointVolume::QueryHardwareSupport(DWORD *pdwHardwareSupportMask) {
return pReal->QueryHardwareSupport(pdwHardwareSupportMask);
}
HRESULT STDMETHODCALLTYPE WrappedIAudioEndpointVolume::GetVolumeRange(float *pflVolumeMindB, float *pflVolumeMaxdB, float *pflVolumeIncrementdB) {
return pReal->GetVolumeRange(pflVolumeMindB, pflVolumeMaxdB, pflVolumeIncrementdB);
}
@@ -0,0 +1,43 @@
#pragma once
#include <stdint.h>
#include <endpointvolume.h>
struct WrappedIAudioEndpointVolume : IAudioEndpointVolume {
explicit WrappedIAudioEndpointVolume(IAudioEndpointVolume *orig) : pReal(orig) {}
WrappedIAudioEndpointVolume(const WrappedIAudioEndpointVolume &) = delete;
WrappedIAudioEndpointVolume &operator=(const WrappedIAudioEndpointVolume &) = delete;
virtual ~WrappedIAudioEndpointVolume() = default;
#pragma region IUnknown
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObj) override;
ULONG STDMETHODCALLTYPE AddRef() override;
ULONG STDMETHODCALLTYPE Release() override;
#pragma endregion
#pragma region IAudioEndpointVolume
HRESULT STDMETHODCALLTYPE RegisterControlChangeNotify(IAudioEndpointVolumeCallback *pNotify) override;
HRESULT STDMETHODCALLTYPE UnregisterControlChangeNotify(IAudioEndpointVolumeCallback *pNotify) override;
HRESULT STDMETHODCALLTYPE GetChannelCount(uint32_t *pnChannelCount) override;
HRESULT STDMETHODCALLTYPE SetMasterVolumeLevel(float fLevelDB, LPCGUID pguidEventContext) override;
HRESULT STDMETHODCALLTYPE SetMasterVolumeLevelScalar(float fLevel, LPCGUID pguidEventContext) override;
HRESULT STDMETHODCALLTYPE GetMasterVolumeLevel(float *fLevelDB) override;
HRESULT STDMETHODCALLTYPE GetMasterVolumeLevelScalar(float *fLevel) override;
HRESULT STDMETHODCALLTYPE SetChannelVolumeLevel(uint32_t nChannel, float fLevelDB, LPCGUID pguidEventContext) override;
HRESULT STDMETHODCALLTYPE SetChannelVolumeLevelScalar(uint32_t nChannel, float fLevel, LPCGUID pguidEventContext) override;
HRESULT STDMETHODCALLTYPE GetChannelVolumeLevel(uint32_t nChannel, float *fLevelDB) override;
HRESULT STDMETHODCALLTYPE GetChannelVolumeLevelScalar(uint32_t nChannel, float *fLevel) override;
HRESULT STDMETHODCALLTYPE SetMute(WINBOOL bMute, LPCGUID pguidEventContext) override;
HRESULT STDMETHODCALLTYPE GetMute(WINBOOL *bMute) override;
HRESULT STDMETHODCALLTYPE GetVolumeStepInfo(uint32_t *pnStep, uint32_t *pnStepCount) override;
HRESULT STDMETHODCALLTYPE VolumeStepUp(LPCGUID pguidEventContext) override;
HRESULT STDMETHODCALLTYPE VolumeStepDown(LPCGUID pguidEventContext) override;
HRESULT STDMETHODCALLTYPE QueryHardwareSupport(DWORD *pdwHardwareSupportMask) override;
HRESULT STDMETHODCALLTYPE GetVolumeRange(float *pflVolumeMindB, float *pflVolumeMaxdB, float *pflVolumeIncrementdB) override;
#pragma endregion
private:
IAudioEndpointVolume *const pReal;
};
+5
View File
@@ -3,8 +3,10 @@
#include <mutex>
#include <audioclient.h>
#include <endpointvolume.h>
#include "hooks/audio/audio_private.h"
#include "hooks/audio/backends/mmdevice/audio_endpoint_volume.h"
#include "hooks/audio/backends/wasapi/audio_client.h"
#define PRINT_FAILED_RESULT(name, ret) \
@@ -108,6 +110,9 @@ HRESULT STDMETHODCALLTYPE WrappedIMMDevice::Activate(
// persist the audio client
hooks::audio::CLIENT = client;
hooks::audio::CLIENT->AddRef();
} else if (iid == __uuidof(IAudioEndpointVolume) && hooks::audio::VOLUME_HOOK_ENABLED) {
*ppInterface = new WrappedIAudioEndpointVolume(reinterpret_cast<IAudioEndpointVolume *>(*ppInterface));
}
return ret;
@@ -1,8 +1,6 @@
#include "device_enumerator.h"
#include "util/logging.h"
#include "device.h"
#include "util/utils.h"
#ifdef _MSC_VER
DEFINE_GUID(IID_IMMDeviceEnumerator,
@@ -61,6 +59,38 @@ HRESULT STDMETHODCALLTYPE WrappedIMMDeviceEnumerator::GetDefaultAudioEndpoint(
return ret;
}
// dump friendly name of audio endpoint
// https://learn.microsoft.com/en-us/windows/win32/coreaudio/device-properties?redirectedfrom=MSDN
{
IPropertyStore *pProps = nullptr;
HRESULT hr;
hr = (*ppEndpoint)->OpenPropertyStore(STGM_READ, &pProps);
if (SUCCEEDED(hr)) {
PROPVARIANT varName;
// instead of including giant windows headers, hardcoding the GUID here
PROPERTYKEY key;
GUID IDevice_FriendlyName = { 0xa45c254e, 0xdf1c, 0x4efd, { 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0 } };
key.pid = 14;
key.fmtid = IDevice_FriendlyName;
// get friendly name
PropVariantInit(&varName);
hr = pProps->GetValue(key, &varName);
if (SUCCEEDED(hr) && varName.vt != VT_EMPTY) {
log_info("audio", "IMMDeviceEnumerator::GetDefaultAudioEndpoint: using {}", ws2s(varName.pwszVal));
}
// cleanup
PropVariantClear(&varName);
}
if (pProps) {
pProps->Release();
pProps = nullptr;
}
}
// wrap interface
*ppEndpoint = new WrappedIMMDevice(*ppEndpoint);
@@ -0,0 +1,153 @@
#include "low_latency_client.h"
#include "util/logging.h"
#ifdef _MSC_VER
DEFINE_GUID(CLSID_MMDeviceEnumerator,
0xBCDE0395, 0xE52F, 0x467C,
0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E);
#endif
#define PRINT_FAILED_RESULT(name, ret) \
log_warning("audio::lowlatency", "{} failed, hr={}", name, FMT_HRESULT(ret))
namespace hooks::audio {
bool LOW_LATENCY_SHARED_WASAPI = false;
static bool COM_INITIALIZED = false;
static LowLatencyAudioClient *LOW_LATENCY_CLIENT = nullptr;
void init_low_latency() {
if (!LOW_LATENCY_SHARED_WASAPI) {
return;
}
log_info("audio::lowlatency", "initializing");
HRESULT hr;
// initialize COM
COM_INITIALIZED = true;
hr = CoInitialize(NULL);
if (FAILED(hr)) {
PRINT_FAILED_RESULT("CoInitialize", hr);
return;
}
// initialize device enumerator
IMMDeviceEnumerator* enumerator;
hr = CoCreateInstance(
CLSID_MMDeviceEnumerator,
NULL,
CLSCTX_ALL,
IID_IMMDeviceEnumerator,
reinterpret_cast<void**>(&enumerator));
if (FAILED(hr)) {
PRINT_FAILED_RESULT("CoCreateInstance(CLSID_MMDeviceEnumerator)", hr);
return;
}
// get default audio endpoint from enumerator
IMMDevice* device;
hr = enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &device);
if (FAILED(hr)) {
PRINT_FAILED_RESULT("GetDefaultAudioEndpoint", hr);
return;
}
enumerator->Release();
// start the client using the default audio endpoint
LOW_LATENCY_CLIENT = hooks::audio::LowLatencyAudioClient::Create(device);
log_info("audio::lowlatency", "initialized");
}
void stop_low_latency() {
if (!LOW_LATENCY_SHARED_WASAPI) {
return;
}
log_info("audio::lowlatency", "stopping");
if (LOW_LATENCY_CLIENT) {
delete LOW_LATENCY_CLIENT;
LOW_LATENCY_CLIENT = nullptr;
}
if (COM_INITIALIZED) {
COM_INITIALIZED = false;
CoUninitialize();
}
log_info("audio::lowlatency", "stopped");
}
}
using namespace hooks::audio;
LowLatencyAudioClient::LowLatencyAudioClient(IAudioClient3* audioClient) : audioClient(audioClient) {}
LowLatencyAudioClient::~LowLatencyAudioClient() {
if (this->audioClient) {
HRESULT ret;
ret = this->audioClient->Stop();
if (FAILED(ret)) {
PRINT_FAILED_RESULT("IAudioClient3::Stop", ret);
}
this->audioClient->Release();
this->audioClient = nullptr;
}
}
LowLatencyAudioClient *LowLatencyAudioClient::Create(IMMDevice *device) {
HRESULT ret;
UINT32 minPeriod;
UINT32 defaultPeriod;
UINT32 fundamentalPeriod;
UINT32 maxPeriod;
PWAVEFORMATEX pFormat;
IAudioClient3* audioClient;
ret = device->Activate(__uuidof(IAudioClient3), CLSCTX_ALL, NULL, reinterpret_cast<void**>(&audioClient));
device->Release();
if (FAILED(ret)) {
PRINT_FAILED_RESULT("IMMDevice::Activate(IID_IAudioClient3...)", ret);
log_warning("audio::lowlatency", "note that only Windows 10 and above supports IAudioClient3");
return nullptr;
}
ret = audioClient->GetMixFormat(&pFormat);
if (FAILED(ret)) {
PRINT_FAILED_RESULT("IAudioClient3::GetMixFormat", ret);
audioClient->Release();
audioClient = nullptr;
return nullptr;
}
ret = audioClient->GetSharedModeEnginePeriod(pFormat, &defaultPeriod, &fundamentalPeriod, &minPeriod, &maxPeriod);
if (FAILED(ret)) {
PRINT_FAILED_RESULT("IAudioClient3::GetSharedModeEnginePeriod", ret);
audioClient->Release();
audioClient = nullptr;
return nullptr;
}
ret = audioClient->InitializeSharedAudioStream(0, minPeriod, pFormat, NULL);
if (FAILED(ret)) {
PRINT_FAILED_RESULT("IAudioClient3::InitializeSharedAudioStream", ret);
audioClient->Release();
audioClient = nullptr;
return nullptr;
}
ret = audioClient->Start();
if (FAILED(ret)) {
PRINT_FAILED_RESULT("IAudioClient3::Start", ret);
audioClient->Release();
audioClient = nullptr;
return nullptr;
}
log_info("audio::lowlatency", "low latency shared mode audio client initialized successfully.");
log_info("audio::lowlatency", "this is NOT used to output sound...");
log_info("audio::lowlatency", "but rather to reduce buffer sizes when the game requests an audio client at a later point");
log_info("audio::lowlatency", "has no effect if the game uses exclusive WASAPI or ASIO!");
log_info("audio::lowlatency", "... sample rate : {} Hz", pFormat->nSamplesPerSec);
log_info("audio::lowlatency", "... min buffer size : {} samples ({} ms)", minPeriod, 1000.0f * minPeriod / pFormat->nSamplesPerSec);
log_info("audio::lowlatency", "... max buffer size : {} samples ({} ms)", maxPeriod, 1000.0f * maxPeriod / pFormat->nSamplesPerSec);
log_info("audio::lowlatency", "... default buffer size : {} samples ({} ms)", defaultPeriod, 1000.0f * defaultPeriod / pFormat->nSamplesPerSec);
log_info("audio::lowlatency", "... Windows will use minimum buffer size (instead of default) for shared mode audio clients from now on");
return new LowLatencyAudioClient(audioClient);
}
@@ -0,0 +1,22 @@
#pragma once
#include <initguid.h>
#include <audioclient.h>
#include <mmdeviceapi.h>
#include "hooks/audio/audio.h"
namespace hooks::audio {
void init_low_latency();
void stop_low_latency();
class LowLatencyAudioClient {
public:
static LowLatencyAudioClient *Create(IMMDevice *device);
~LowLatencyAudioClient();
private:
LowLatencyAudioClient(IAudioClient3* audioClient);
IAudioClient3* audioClient;
};
}
+8
View File
@@ -968,7 +968,15 @@ HRESULT AsioBackend::on_stop() noexcept {
return AUDCLNT_E_NOT_INITIALIZED;
}
if (hooks::audio::ASIO_FORCE_UNLOAD_ON_STOP) {
log_misc("audio::asio", "AsioBackend::on_stop - using ASIO_FORCE_UNLOAD_ON_STOP workaround");
}
auto result = this->run_on_asio_thread([this]() {
if (hooks::audio::ASIO_FORCE_UNLOAD_ON_STOP) {
this->unload_driver();
return (AsioError)ASE_OK;
}
return this->asio_driver->stop();
});
if (result != ASE_OK) {