Merge branch 'spice2x' into spice22x
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
#include "hooks/audio/audio_private.h"
|
||||
#include "hooks/audio/backends/mmdevice/audio_endpoint_volume.h"
|
||||
#include "hooks/audio/backends/wasapi/audio_client.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
#define PRINT_FAILED_RESULT(name, ret) \
|
||||
do { \
|
||||
@@ -56,6 +57,7 @@ ULONG STDMETHODCALLTYPE WrappedIMMDevice::Release() {
|
||||
ULONG refs = pReal != nullptr ? pReal->Release() : 0;
|
||||
|
||||
if (refs == 0) {
|
||||
log_misc("audio::mmdevice", "WrappedIMMDevice::Release");
|
||||
delete this;
|
||||
}
|
||||
|
||||
@@ -69,7 +71,7 @@ HRESULT STDMETHODCALLTYPE WrappedIMMDevice::Activate(
|
||||
PROPVARIANT *pActivationParams,
|
||||
void **ppInterface)
|
||||
{
|
||||
log_misc("audio::mmdevice", "WrappedIMMDevice::Activate");
|
||||
log_misc("audio::mmdevice", "WrappedIMMDevice::Activate {}", guid2s(iid));
|
||||
|
||||
// call original
|
||||
HRESULT ret = pReal->Activate(iid, dwClsCtx, pActivationParams, ppInterface);
|
||||
@@ -80,7 +82,10 @@ HRESULT STDMETHODCALLTYPE WrappedIMMDevice::Activate(
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (iid == IID_IAudioClient) {
|
||||
// almost all games request IAudioClient
|
||||
// so far we have not seen any games request IAudioClient2
|
||||
// SDVX EG Final uses IID_IAudioClient3, but only when shared mode patch is on
|
||||
if (iid == IID_IAudioClient || iid == IID_IAudioClient3) {
|
||||
|
||||
// prevent initialization recursion when using some ASIO backends that proxy to DirectSound, WASAPI, or WDM
|
||||
// like ASIO4All or FlexASIO
|
||||
@@ -90,23 +95,18 @@ HRESULT STDMETHODCALLTYPE WrappedIMMDevice::Activate(
|
||||
}
|
||||
std::lock_guard initialize_guard(hooks::audio::INITIALIZE_LOCK, std::adopt_lock);
|
||||
|
||||
auto client = reinterpret_cast<IAudioClient *>(*ppInterface);
|
||||
|
||||
// release old audio client if initialized
|
||||
if (hooks::audio::CLIENT) {
|
||||
hooks::audio::CLIENT->Release();
|
||||
}
|
||||
|
||||
/*
|
||||
ret = wrap_audio_client(pReal, dwClsCtx, pActivationParams, &client);
|
||||
if (FAILED(ret)) {
|
||||
return ret;
|
||||
IAudioClient *client = nullptr;
|
||||
if (iid == IID_IAudioClient) {
|
||||
client = wrap_audio_client(reinterpret_cast<IAudioClient *>(*ppInterface));
|
||||
} else { // IID_IAudioClient3
|
||||
client = wrap_audio_client3(reinterpret_cast<IAudioClient3 *>(*ppInterface));
|
||||
}
|
||||
*/
|
||||
client = wrap_audio_client(client);
|
||||
|
||||
*ppInterface = client;
|
||||
|
||||
// persist the audio client
|
||||
hooks::audio::CLIENT = client;
|
||||
hooks::audio::CLIENT->AddRef();
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
#include "device_collection.h"
|
||||
#include "device.h"
|
||||
#include "util/utils.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
HRESULT STDMETHODCALLTYPE WrappedIMMDeviceCollection::QueryInterface(REFIID riid, void **ppvObj) {
|
||||
if (ppvObj == nullptr) {
|
||||
return E_POINTER;
|
||||
}
|
||||
if (riid == __uuidof(IMMDeviceCollection)) {
|
||||
this->AddRef();
|
||||
*ppvObj = this;
|
||||
return S_OK;
|
||||
}
|
||||
return pReal->QueryInterface(riid, ppvObj);
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE WrappedIMMDeviceCollection::AddRef() {
|
||||
return pReal->AddRef();
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE WrappedIMMDeviceCollection::Release() {
|
||||
// get reference count of underlying interface
|
||||
ULONG refs = pReal != nullptr ? pReal->Release() : 0;
|
||||
if (refs == 0) {
|
||||
delete this;
|
||||
}
|
||||
return refs;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE WrappedIMMDeviceCollection::GetCount(UINT *pcDevices) {
|
||||
return pReal->GetCount(pcDevices);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE WrappedIMMDeviceCollection::Item(UINT nDevice, IMMDevice **ppDevice) {
|
||||
log_info("audio", "WrappedIMMDeviceCollection::Item[{}]", nDevice);
|
||||
|
||||
// call original
|
||||
const auto hr = pReal->Item(nDevice, ppDevice);
|
||||
|
||||
// wrap interface
|
||||
*ppDevice = new WrappedIMMDevice(*ppDevice);
|
||||
return hr;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <initguid.h>
|
||||
#include <mmdeviceapi.h>
|
||||
|
||||
struct WrappedIMMDeviceCollection : IMMDeviceCollection {
|
||||
explicit WrappedIMMDeviceCollection(IMMDeviceCollection *orig) : pReal(orig) {
|
||||
}
|
||||
|
||||
WrappedIMMDeviceCollection(const WrappedIMMDeviceCollection &) = delete;
|
||||
WrappedIMMDeviceCollection &operator=(const WrappedIMMDeviceCollection &) = delete;
|
||||
|
||||
virtual ~WrappedIMMDeviceCollection() = default;
|
||||
|
||||
#pragma region IUnknown
|
||||
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObj) override;
|
||||
virtual ULONG STDMETHODCALLTYPE AddRef() override;
|
||||
virtual ULONG STDMETHODCALLTYPE Release() override;
|
||||
#pragma endregion
|
||||
|
||||
#pragma region IMMDeviceCollection
|
||||
virtual HRESULT STDMETHODCALLTYPE GetCount(UINT *pcDevices) override;
|
||||
virtual HRESULT STDMETHODCALLTYPE Item(UINT nDevice, IMMDevice **ppDevice) override;
|
||||
#pragma endregion
|
||||
|
||||
private:
|
||||
IMMDeviceCollection *const pReal;
|
||||
};
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "device_enumerator.h"
|
||||
#include "device_collection.h"
|
||||
#include "device.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
@@ -42,7 +43,11 @@ HRESULT STDMETHODCALLTYPE WrappedIMMDeviceEnumerator::EnumAudioEndpoints(
|
||||
DWORD dwStateMask,
|
||||
IMMDeviceCollection **ppDevices)
|
||||
{
|
||||
return pReal->EnumAudioEndpoints(dataFlow, dwStateMask, ppDevices);
|
||||
const auto hr = pReal->EnumAudioEndpoints(dataFlow, dwStateMask, ppDevices);
|
||||
if (SUCCEEDED(hr) && (ppDevices != nullptr) && (*ppDevices != nullptr)) {
|
||||
*ppDevices = new WrappedIMMDeviceCollection(*ppDevices);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE WrappedIMMDeviceEnumerator::GetDefaultAudioEndpoint(
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "hooks/audio/implementations/pipewire.h"
|
||||
#include "hooks/audio/implementations/none.h"
|
||||
//#include "util/co_task_mem_ptr.h"
|
||||
#include "util/utils.h"
|
||||
|
||||
#include "defs.h"
|
||||
#include "dummy_audio_client.h"
|
||||
@@ -42,114 +43,9 @@ static void fix_rec_format(WAVEFORMATEX *pFormat) {
|
||||
pFormat->nAvgBytesPerSec = pFormat->nSamplesPerSec * pFormat->nBlockAlign;
|
||||
}
|
||||
|
||||
// TODO(felix): is it appropriate to automatically switch to shared mode? should we do a
|
||||
// `MessageBox` to notify the user?
|
||||
/*
|
||||
static bool check_for_exclusive_access(IAudioClient *client) {
|
||||
static bool checked_once = false;
|
||||
static bool previous_check_result = false;
|
||||
|
||||
CoTaskMemPtr<WAVEFORMATEX> mix_format;
|
||||
REFERENCE_TIME requested_duration = 0;
|
||||
|
||||
if (checked_once) {
|
||||
return previous_check_result;
|
||||
}
|
||||
if (audio::BACKEND.has_value()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// scope function so it has access to the local static variables
|
||||
auto set_result = [](bool result) {
|
||||
checked_once = true;
|
||||
previous_check_result = result;
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
HRESULT ret = client->GetMixFormat(mix_format.ppv());
|
||||
if (FAILED(ret)) {
|
||||
PRINT_FAILED_RESULT("IAudioClient::GetMixFormat", ret);
|
||||
return set_result(false);
|
||||
}
|
||||
|
||||
log_info("audio::wasapi", "Mix Format:");
|
||||
print_format(mix_format.data());
|
||||
|
||||
ret = client->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, mix_format.data(), nullptr);
|
||||
if (ret == AUDCLNT_E_UNSUPPORTED_FORMAT) {
|
||||
auto mix_format_ex = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(mix_format.data());
|
||||
|
||||
log_warning("audio::wasapi", "device does not natively support the mix format, converting to PCM");
|
||||
|
||||
if (mix_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
|
||||
IsEqualGUID(GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, mix_format_ex->SubFormat))
|
||||
{
|
||||
mix_format_ex->Format.wBitsPerSample = 16;
|
||||
mix_format_ex->Format.nBlockAlign = mix_format_ex->Format.nChannels * (mix_format_ex->Format.wBitsPerSample / 8);
|
||||
mix_format_ex->Format.nAvgBytesPerSec = mix_format_ex->Format.nSamplesPerSec * mix_format_ex->Format.nBlockAlign;
|
||||
mix_format_ex->Samples.wValidBitsPerSample = 16;
|
||||
mix_format_ex->SubFormat = GUID_KSDATAFORMAT_SUBTYPE_PCM;
|
||||
} else if (mix_format->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
|
||||
mix_format->wBitsPerSample = 16;
|
||||
mix_format->nBlockAlign = mix_format->nChannels * (mix_format->wBitsPerSample / 8);
|
||||
mix_format->nAvgBytesPerSec = mix_format->nSamplesPerSec * mix_format->nBlockAlign;
|
||||
mix_format->wFormatTag = WAVE_FORMAT_PCM;
|
||||
} else {
|
||||
log_warning("audio::wasapi", "mix format is not a floating point format");
|
||||
return set_result(false);
|
||||
}
|
||||
|
||||
ret = client->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, mix_format.data(), nullptr);
|
||||
if (FAILED(ret)) {
|
||||
log_warning("audio::wasapi", "mix format is not supported");
|
||||
return set_result(false);
|
||||
}
|
||||
}
|
||||
|
||||
ret = client->GetDevicePeriod(nullptr, &requested_duration);
|
||||
if (FAILED(ret)) {
|
||||
PRINT_FAILED_RESULT("IAudioClient::GetDevicePeriod", ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = client->Initialize(
|
||||
AUDCLNT_SHAREMODE_EXCLUSIVE,
|
||||
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
|
||||
requested_duration,
|
||||
requested_duration,
|
||||
mix_format.data(),
|
||||
nullptr);
|
||||
|
||||
if (ret == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED || SUCCEEDED(ret)) {
|
||||
log_info("audio::wasapi", "exclusive mode is available, disabling backend");
|
||||
return set_result(true);
|
||||
} else {
|
||||
log_warning("audio::wasapi", "exclusive mode is not available, enabling backend, hr={}", FMT_HRESULT(ret));
|
||||
}
|
||||
|
||||
return set_result(false);
|
||||
}
|
||||
|
||||
HRESULT wrap_audio_client(
|
||||
IMMDevice *device,
|
||||
DWORD cls_ctx,
|
||||
PROPVARIANT *activation_params,
|
||||
IAudioClient **audio_client)
|
||||
{
|
||||
auto exclusive_available = check_for_exclusive_access(*audio_client);
|
||||
(*audio_client)->Stop();
|
||||
(*audio_client)->Reset();
|
||||
(*audio_client)->Release();
|
||||
*audio_client = nullptr;
|
||||
|
||||
SAFE_CALL("IMMDevice", "Activate", device->Activate(
|
||||
IID_IAudioClient,
|
||||
cls_ctx,
|
||||
activation_params,
|
||||
reinterpret_cast<void **>(audio_client)));
|
||||
*/
|
||||
IAudioClient *wrap_audio_client(IAudioClient *audio_client) {
|
||||
log_misc("audio::wasapi", "wrapping IAudioClient");
|
||||
|
||||
AudioBackend *backend = nullptr;
|
||||
bool requires_dummy = false;
|
||||
|
||||
@@ -172,9 +68,6 @@ IAudioClient *wrap_audio_client(IAudioClient *audio_client) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
//} else if (!exclusive_available) {
|
||||
// backend = new WaveOutBackend();
|
||||
//}
|
||||
|
||||
IAudioClient *new_client;
|
||||
|
||||
@@ -190,6 +83,23 @@ IAudioClient *wrap_audio_client(IAudioClient *audio_client) {
|
||||
|
||||
return new_client;
|
||||
}
|
||||
IAudioClient3 *wrap_audio_client3(IAudioClient3 *audio_client) {
|
||||
// TODO: ASIO backend for IAudioClient3, if there is a game that needs it
|
||||
log_misc("audio::wasapi", "wrapping IAudioClient3");
|
||||
|
||||
if (hooks::audio::BACKEND.has_value()) {
|
||||
log_fatal(
|
||||
"audio::wasapi",
|
||||
"IAudioClient3 does not currently support backends! clear -audiobackend and try again");
|
||||
}
|
||||
if (hooks::audio::USE_DUMMY) {
|
||||
log_fatal(
|
||||
"audio::wasapi",
|
||||
"IAudioClient3 does not currently support dummy context, clear -audiodummy and try again");
|
||||
}
|
||||
|
||||
return new WrappedIAudioClient(audio_client, nullptr);
|
||||
}
|
||||
|
||||
// IUnknown
|
||||
HRESULT STDMETHODCALLTYPE WrappedIAudioClient::QueryInterface(REFIID riid, void **ppvObj) {
|
||||
@@ -198,8 +108,8 @@ HRESULT STDMETHODCALLTYPE WrappedIAudioClient::QueryInterface(REFIID riid, void
|
||||
}
|
||||
|
||||
if (riid == IID_WrappedIAudioClient ||
|
||||
riid == IID_IAudioClient)
|
||||
{
|
||||
riid == IID_IAudioClient ||
|
||||
riid == IID_IAudioClient3) {
|
||||
this->AddRef();
|
||||
*ppvObj = this;
|
||||
|
||||
@@ -290,20 +200,6 @@ HRESULT STDMETHODCALLTYPE WrappedIAudioClient::Initialize(
|
||||
}
|
||||
|
||||
log_info("audio::wasapi", "IAudioClient::Initialize success, hr={}", FMT_HRESULT(ret));
|
||||
|
||||
/*
|
||||
if (ShareMode == AUDCLNT_SHAREMODE_SHARED) {
|
||||
IAudioClockAdjustment *clock = nullptr;
|
||||
|
||||
SAFE_CALL("IAudioClient", "GetService", pReal->GetService(
|
||||
IID_IAudioClockAdjustment,
|
||||
reinterpret_cast<void **>(&clock)));
|
||||
|
||||
SAFE_CALL("IAudioClockAdjustment", "SetSampleRate", clock->SetSampleRate(
|
||||
static_cast<float>(pFormat->nSamplesPerSec)));
|
||||
}
|
||||
*/
|
||||
|
||||
copy_wave_format(&hooks::audio::FORMAT, pFormat);
|
||||
|
||||
return ret;
|
||||
@@ -490,3 +386,93 @@ HRESULT STDMETHODCALLTYPE WrappedIAudioClient::GetService(REFIID riid, void **pp
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE WrappedIAudioClient::IsOffloadCapable(
|
||||
AUDIO_STREAM_CATEGORY Category,
|
||||
BOOL *pbOffloadCapable) {
|
||||
WRAP_VERBOSE;
|
||||
|
||||
CHECK_RESULT(pReal3->IsOffloadCapable(Category, pbOffloadCapable));
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE WrappedIAudioClient::SetClientProperties(
|
||||
const AudioClientProperties *pProperties) {
|
||||
|
||||
WRAP_VERBOSE;
|
||||
CHECK_RESULT(pReal3->SetClientProperties(pProperties));
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE WrappedIAudioClient::GetBufferSizeLimits(
|
||||
const WAVEFORMATEX *pFormat,
|
||||
BOOL bEventDriven,
|
||||
REFERENCE_TIME *phnsMinBufferDuration,
|
||||
REFERENCE_TIME *phnsMaxBufferDuration) {
|
||||
|
||||
WRAP_VERBOSE;
|
||||
CHECK_RESULT(pReal3->GetBufferSizeLimits(
|
||||
pFormat,
|
||||
bEventDriven,
|
||||
phnsMinBufferDuration,
|
||||
phnsMaxBufferDuration));
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE WrappedIAudioClient::GetSharedModeEnginePeriod(
|
||||
const WAVEFORMATEX *pFormat,
|
||||
UINT32 *pDefaultPeriodInFrames,
|
||||
UINT32 *pFundamentalPeriodInFrames,
|
||||
UINT32 *pMinPeriodInFrames,
|
||||
UINT32 *pMaxPeriodInFrames) {
|
||||
|
||||
WRAP_VERBOSE;
|
||||
CHECK_RESULT(pReal3->GetSharedModeEnginePeriod(
|
||||
pFormat,
|
||||
pDefaultPeriodInFrames,
|
||||
pFundamentalPeriodInFrames,
|
||||
pMinPeriodInFrames,
|
||||
pMaxPeriodInFrames));
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE WrappedIAudioClient::GetCurrentSharedModeEnginePeriod(
|
||||
WAVEFORMATEX **ppFormat,
|
||||
UINT32 *pCurrentPeriodInFrames) {
|
||||
|
||||
WRAP_VERBOSE;
|
||||
CHECK_RESULT(pReal3->GetCurrentSharedModeEnginePeriod(
|
||||
ppFormat,
|
||||
pCurrentPeriodInFrames));
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE WrappedIAudioClient::InitializeSharedAudioStream(
|
||||
DWORD StreamFlags,
|
||||
UINT32 PeriodInFrames,
|
||||
const WAVEFORMATEX *pFormat,
|
||||
LPCGUID AudioSessionGuid) {
|
||||
|
||||
if (!pFormat) {
|
||||
return E_POINTER;
|
||||
}
|
||||
|
||||
// verbose output
|
||||
log_info("audio::wasapi", "IAudioClient3::InitializeSharedAudioStream hook hit");
|
||||
log_info("audio::wasapi", "... ShareMode : {}", share_mode_str(AUDCLNT_SHAREMODE_SHARED));
|
||||
log_info("audio::wasapi", "... StreamFlags : {}", stream_flags_str(StreamFlags));
|
||||
log_info("audio::wasapi", "... PeriodInFrames : {}", PeriodInFrames);
|
||||
print_format(pFormat);
|
||||
|
||||
// call next
|
||||
HRESULT ret = pReal3->InitializeSharedAudioStream(
|
||||
StreamFlags,
|
||||
PeriodInFrames,
|
||||
pFormat,
|
||||
AudioSessionGuid);
|
||||
|
||||
// check for failure
|
||||
if (FAILED(ret)) {
|
||||
PRINT_FAILED_RESULT("IAudioClient3", "InitializeSharedAudioStream", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
log_info("audio::wasapi", "IAudioClient3::InitializeSharedAudioStream success, hr={}", FMT_HRESULT(ret));
|
||||
copy_wave_format(&hooks::audio::FORMAT, pFormat);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -16,9 +16,16 @@ static const GUID IID_WrappedIAudioClient = {
|
||||
};
|
||||
|
||||
IAudioClient *wrap_audio_client(IAudioClient *client);
|
||||
IAudioClient3 *wrap_audio_client3(IAudioClient3 *client);
|
||||
|
||||
struct WrappedIAudioClient : IAudioClient {
|
||||
explicit WrappedIAudioClient(IAudioClient *orig, AudioBackend *backend) : pReal(orig), backend(backend) {
|
||||
struct WrappedIAudioClient : IAudioClient3 {
|
||||
|
||||
explicit WrappedIAudioClient(IAudioClient3 *orig3, AudioBackend *backend) :
|
||||
pReal(orig3), pReal3(orig3), backend(backend) {
|
||||
}
|
||||
|
||||
explicit WrappedIAudioClient(IAudioClient *orig, AudioBackend *backend) :
|
||||
pReal(orig), pReal3(nullptr), backend(backend) {
|
||||
}
|
||||
|
||||
WrappedIAudioClient(const WrappedIAudioClient &) = delete;
|
||||
@@ -47,9 +54,43 @@ struct WrappedIAudioClient : IAudioClient {
|
||||
HRESULT STDMETHODCALLTYPE GetService(REFIID riid, void **ppv) override;
|
||||
#pragma endregion
|
||||
|
||||
IAudioClient *const pReal;
|
||||
AudioBackend *const backend;
|
||||
#pragma region IAudioClient2
|
||||
HRESULT STDMETHODCALLTYPE IsOffloadCapable(
|
||||
AUDIO_STREAM_CATEGORY Category,
|
||||
BOOL *pbOffloadCapable) override;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE SetClientProperties(
|
||||
const AudioClientProperties *pProperties) override;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetBufferSizeLimits(
|
||||
const WAVEFORMATEX *pFormat,
|
||||
BOOL bEventDriven,
|
||||
REFERENCE_TIME *phnsMinBufferDuration,
|
||||
REFERENCE_TIME *phnsMaxBufferDuration) override;
|
||||
#pragma endregion
|
||||
|
||||
#pragma region IAudioClient3
|
||||
HRESULT STDMETHODCALLTYPE GetSharedModeEnginePeriod(
|
||||
const WAVEFORMATEX *pFormat,
|
||||
UINT32 *pDefaultPeriodInFrames,
|
||||
UINT32 *pFundamentalPeriodInFrames,
|
||||
UINT32 *pMinPeriodInFrames,
|
||||
UINT32 *pMaxPeriodInFrames) override;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE GetCurrentSharedModeEnginePeriod(
|
||||
WAVEFORMATEX **ppFormat,
|
||||
UINT32 *pCurrentPeriodInFrames) override;
|
||||
|
||||
HRESULT STDMETHODCALLTYPE InitializeSharedAudioStream(
|
||||
DWORD StreamFlags,
|
||||
UINT32 PeriodInFrames,
|
||||
const WAVEFORMATEX *pFormat,
|
||||
LPCGUID AudioSessionGuid) override;
|
||||
#pragma endregion
|
||||
|
||||
IAudioClient *const pReal;
|
||||
IAudioClient3 *const pReal3;
|
||||
AudioBackend *const backend;
|
||||
bool exclusive_mode = false;
|
||||
int frame_size = 0;
|
||||
};
|
||||
|
||||
@@ -149,5 +149,12 @@ LowLatencyAudioClient *LowLatencyAudioClient::Create(IMMDevice *device) {
|
||||
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");
|
||||
if (minPeriod < defaultPeriod) {
|
||||
log_info("audio::lowlatency", "minimum period is less than default period, you will see latency improvement");
|
||||
} else {
|
||||
log_warning("audio::lowlatency", "minimum period is not less than default period, you will see NO latency improvement");
|
||||
log_warning("audio::lowlatency", "this an audio driver / hardware limitation");
|
||||
}
|
||||
|
||||
return new LowLatencyAudioClient(audioClient);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user