mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-01 22:30:42 -07:00
6bcadccf09
## Link to GitHub Issue or related Pull Request, if one exists n/a ## Description of change This new option, `-wasapishared`, detects when the game opens WASAPI Exclusive stream and forcibly opens a shared mode stream instead on the real device. #### Benefits of this: 1. No need for `force wasapi shared` patches 2. No need to change sample rate of audio devices before starting up games 3. Adds ability to boot in shared mode even for games that do not have shared wasapi changes (old GITADORA, popn HC) As a result, this option is strictly better than `shared wasapi` patches available for many games. #### Downsides: 1. OS resampling will add a small latency, but not big enough to be noticeable. 2. Using shared mode instead of exclusive mode adds latency, of course. (If you cared about latency, you would use the low latency option, or use exclusive or asio) Basically it's the "make things work" button for audio that should work for almost all games that we support. The option has no effect if the game opens in shared mode. ## Testing SDVX7 - ok GITADORA GW - ok popn HC - ok IIDX - ok
135 lines
4.1 KiB
C++
135 lines
4.1 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;
|
|
|
|
// 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();
|
|
}
|
|
}
|