Merge branch 'spice2x' into spice22x

This commit is contained in:
[ ]
2025-05-09 02:54:57 +09:00
550 changed files with 78570 additions and 91704 deletions
@@ -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;
};
}