mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-02 06:40:42 -07:00
b9f3be4df2
## Link to GitHub Issue or related Pull Request, if one exists n/a ## Description of change IIDX likes to call `CoCreateInstance` and `init` multiple times on ASIO drivers, but some ASIO drivers like `Neva Uno` really don't like that and ends up crashing. In our wrapper, cache the instances and try to reuse them for better compatibility. ## Testing Worked fine for FlexASIO / Xonar AE / Realtek ASIO though none of these drivers repro the crashing behavior.
148 lines
4.7 KiB
C++
148 lines
4.7 KiB
C++
#include "audio.h"
|
|
|
|
#include <vector>
|
|
|
|
#include <windows.h>
|
|
#include <initguid.h>
|
|
#include <audioclient.h>
|
|
#include <mmdeviceapi.h>
|
|
|
|
#include "hooks/audio/backends/mmdevice/device_enumerator.h"
|
|
#include "util/detour.h"
|
|
#include "util/logging.h"
|
|
#include "util/memutils.h"
|
|
|
|
#include "audio_private.h"
|
|
#include "acm.h"
|
|
#include "asio_proxy.h"
|
|
|
|
#ifdef _MSC_VER
|
|
DEFINE_GUID(CLSID_MMDeviceEnumerator,
|
|
0xBCDE0395, 0xE52F, 0x467C,
|
|
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;
|
|
|
|
namespace hooks::audio {
|
|
|
|
// public globals
|
|
bool ENABLED = true;
|
|
bool VOLUME_HOOK_ENABLED = true;
|
|
std::optional<DownmixAlgorithm> DOWNMIX_ALGORITHM = std::nullopt;
|
|
float VOLUME_BOOST = 1.0f;
|
|
std::optional<uint32_t> RESAMPLE_RATE = std::nullopt;
|
|
std::optional<uint32_t> EXCLUSIVE_BUFFER_MS = std::nullopt;
|
|
bool WASAPI_COMPATIBILITY_MODE = false;
|
|
bool USE_DUMMY = false;
|
|
WAVEFORMATEXTENSIBLE FORMAT {};
|
|
std::optional<Backend> BACKEND = std::nullopt;
|
|
bool INJECT_FAKE_REALTEK_AUDIO = false;
|
|
std::optional<size_t> ASIO_DRIVER_ID = std::nullopt;
|
|
std::string ASIO_DRIVER_NAME = "";
|
|
bool ASIO_FORCE_UNLOAD_ON_STOP = false;
|
|
|
|
// private globals
|
|
IAudioClient *CLIENT = nullptr;
|
|
std::mutex INITIALIZE_LOCK; // for asio
|
|
}
|
|
|
|
static HRESULT STDAPICALLTYPE CoCreateInstance_hook(
|
|
REFCLSID rclsid,
|
|
LPUNKNOWN pUnkOuter,
|
|
DWORD dwClsContext,
|
|
REFIID riid,
|
|
LPVOID *ppv)
|
|
{
|
|
|
|
HRESULT ret;
|
|
|
|
// ASIO: if we already hold a cached instance for this CLSID, hand back a fresh
|
|
// wrapper over it instead of re-creating the real driver (see asio_proxy.cpp)
|
|
if (ppv != nullptr && hooks::audio::asio::is_asio_creation(rclsid, riid)) {
|
|
if (IUnknown *reused = hooks::audio::asio::wrap_existing(rclsid)) {
|
|
*ppv = reused;
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
// call original
|
|
ret = CoCreateInstance_orig(rclsid, pUnkOuter, dwClsContext, riid, ppv);
|
|
if (FAILED(ret)) {
|
|
if (IsEqualCLSID(rclsid, CLSID_MMDeviceEnumerator)) {
|
|
log_warning("audio", "CoCreateInstance failed for CLSID_MMDeviceEnumerator, hr={}", FMT_HRESULT(ret));
|
|
} else if (IsEqualCLSID(rclsid, CLSID_DirectSoundI3DL2ReverbDMO)) {
|
|
// deal with reverb DMO missing from dsdmo.dll
|
|
// it usually fails with 0x80040154 (REGDB_E_CLASSNOTREG), but we have also seen 0x8007007e (ERROR_MOD_NOT_FOUND)
|
|
static std::once_flag printed;
|
|
std::call_once(printed, [ret]() {
|
|
log_warning(
|
|
"audio",
|
|
"CoCreateInstance for CLSID_DirectSoundI3DL2ReverbDMO failed with {}, swap with CLSID_DirectSoundWavesReverbDMO...",
|
|
FMT_HRESULT(ret));
|
|
});
|
|
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;
|
|
}
|
|
|
|
// check if this is the audio device enumerator
|
|
if (IsEqualCLSID(rclsid, CLSID_MMDeviceEnumerator)) {
|
|
|
|
// wrap object
|
|
auto mmde = reinterpret_cast<IMMDeviceEnumerator **>(ppv);
|
|
*mmde = new WrappedIMMDeviceEnumerator(*mmde);
|
|
} else if (ppv != nullptr && *ppv != nullptr && hooks::audio::asio::is_asio_creation(rclsid, riid)) {
|
|
|
|
// wrap every ASIO driver so calls pass through to the real driver
|
|
*ppv = hooks::audio::asio::wrap(rclsid, *ppv);
|
|
}
|
|
|
|
// return original result
|
|
return ret;
|
|
}
|
|
|
|
namespace hooks::audio {
|
|
void init() {
|
|
if (!ENABLED) {
|
|
return;
|
|
}
|
|
|
|
log_info("audio", "initializing");
|
|
init_low_latency();
|
|
hooks::audio::acm::init();
|
|
|
|
// 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();
|
|
|
|
// release the ASIO drivers we kept pinned for the process lifetime now that we are
|
|
// at a controlled shutdown point (see asio_proxy.cpp)
|
|
hooks::audio::asio::release_all_wrappers();
|
|
}
|
|
}
|