WASAPI Frame Delay fix + cleanups (#15187)

This commit is contained in:
sonninnos 2023-04-13 21:20:47 +03:00 committed by GitHub
parent 4d8db0b5b8
commit 07c371533f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 75 additions and 56 deletions

View File

@ -26,6 +26,9 @@
#include "../../verbosity.h" #include "../../verbosity.h"
#include "../../configuration.h" #include "../../configuration.h"
/* Get automatic buffer size from client buffer instead of device period */
#define USE_CLIENT_BUFFER
typedef struct typedef struct
{ {
HANDLE write_event; HANDLE write_event;
@ -49,13 +52,9 @@ static IMMDevice *wasapi_init_device(const char *id)
IMMDeviceCollection *collection = NULL; IMMDeviceCollection *collection = NULL;
if (id) if (id)
{ RARCH_LOG("[WASAPI]: Initializing device: \"%s\"..\n", id);
RARCH_LOG("[WASAPI]: Initializing device %s ...\n", id);
}
else else
{ RARCH_LOG("[WASAPI]: Initializing default device..\n");
RARCH_LOG("[WASAPI]: Initializing default device.. \n");
}
#ifdef __cplusplus #ifdef __cplusplus
hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
@ -80,16 +79,16 @@ static IMMDevice *wasapi_init_device(const char *id)
unsigned i; unsigned i;
for (i = 0; i < list->size; i++) for (i = 0; i < list->size; i++)
{ {
RARCH_LOG("[WASAPI]: %d : %s\n", i, list->elems[i].data);
if (string_is_equal(id, list->elems[i].data)) if (string_is_equal(id, list->elems[i].data))
{ {
RARCH_DBG("[WASAPI]: Found device #%d: \"%s\"\n", i, list->elems[i].data);
idx_found = i; idx_found = i;
break; break;
} }
} }
/* Index was not found yet based on name string, /* Index was not found yet based on name string,
* just assume id is a one-character number index. */ * just assume id is a one-character number index. */
if (idx_found == -1 && isdigit(id[0])) if (idx_found == -1 && isdigit(id[0]))
{ {
idx_found = strtoul(id, NULL, 0); idx_found = strtoul(id, NULL, 0);
@ -145,13 +144,9 @@ error:
IFACE_RELEASE(enumerator); IFACE_RELEASE(enumerator);
if (id) if (id)
{ RARCH_ERR("[WASAPI]: Failed to initialize device: \"%s\".\n", id);
RARCH_WARN("[WASAPI]: Failed to initialize device.\n");
}
else else
{ RARCH_ERR("[WASAPI]: Failed to initialize default device.\n");
RARCH_ERR("[WASAPI]: Failed to initialize device.\n");
}
return NULL; return NULL;
} }
@ -217,20 +212,19 @@ static IAudioClient *wasapi_init_client_sh(IMMDevice *device,
/* for requested rate (first) and all preferred rates */ /* for requested rate (first) and all preferred rates */
for (j = 0; rate_res; ++j) for (j = 0; rate_res; ++j)
{ {
RARCH_LOG("[WASAPI]: Initializing client (shared, %s, %uHz, %ums) ...\n", RARCH_LOG("[WASAPI]: Initializing client (shared, %s, %uHz, %.1fms)..\n",
float_fmt_res ? "float" : "pcm", rate_res, latency); float_fmt_res ? "float" : "pcm", rate_res, (float)latency);
wasapi_set_format(&wf, float_fmt_res, rate_res); wasapi_set_format(&wf, float_fmt_res, rate_res);
#ifdef __cplusplus #ifdef __cplusplus
hr = client->Initialize(AUDCLNT_SHAREMODE_SHARED, hr = client->Initialize(AUDCLNT_SHAREMODE_SHARED,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
0, 0, (WAVEFORMATEX*)&wf, NULL); 0, 0, (WAVEFORMATEX*)&wf, NULL);
#else #else
hr = client->lpVtbl->Initialize(client, AUDCLNT_SHAREMODE_SHARED, hr = client->lpVtbl->Initialize(client, AUDCLNT_SHAREMODE_SHARED,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
0, 0, (WAVEFORMATEX*)&wf, NULL); 0, 0, (WAVEFORMATEX*)&wf, NULL);
#endif #endif
if (hr == AUDCLNT_E_ALREADY_INITIALIZED) if (hr == AUDCLNT_E_ALREADY_INITIALIZED)
{ {
HRESULT hr; HRESULT hr;
@ -243,11 +237,11 @@ static IAudioClient *wasapi_init_client_sh(IMMDevice *device,
#ifdef __cplusplus #ifdef __cplusplus
hr = client->Initialize(AUDCLNT_SHAREMODE_SHARED, hr = client->Initialize(AUDCLNT_SHAREMODE_SHARED,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
0, 0, (WAVEFORMATEX*)&wf, NULL); 0, 0, (WAVEFORMATEX*)&wf, NULL);
#else #else
hr = client->lpVtbl->Initialize(client, AUDCLNT_SHAREMODE_SHARED, hr = client->lpVtbl->Initialize(client, AUDCLNT_SHAREMODE_SHARED,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
0, 0, (WAVEFORMATEX*)&wf, NULL); 0, 0, (WAVEFORMATEX*)&wf, NULL);
#endif #endif
} }
@ -259,7 +253,7 @@ static IAudioClient *wasapi_init_client_sh(IMMDevice *device,
RARCH_WARN("[WASAPI]: Unsupported format.\n"); RARCH_WARN("[WASAPI]: Unsupported format.\n");
rate_res = wasapi_pref_rate(j); rate_res = wasapi_pref_rate(j);
if (rate_res == *rate) /* requested rate is allready tested */ if (rate_res == *rate) /* requested rate is already tested */
rate_res = wasapi_pref_rate(++j); /* skip it */ rate_res = wasapi_pref_rate(++j); /* skip it */
} }
} }
@ -314,8 +308,8 @@ static IAudioClient *wasapi_init_client_ex(IMMDevice *device,
/* for requested rate (first) and all preferred rates */ /* for requested rate (first) and all preferred rates */
for (j = 0; rate_res; ++j) for (j = 0; rate_res; ++j)
{ {
RARCH_LOG("[WASAPI]: Initializing client (exclusive, %s, %uHz, %ums) ...\n", RARCH_LOG("[WASAPI]: Initializing client (exclusive, %s, %uHz, %.1fms)..\n",
float_fmt_res ? "float" : "pcm", rate_res, latency); float_fmt_res ? "float" : "pcm", rate_res, (float)latency);
wasapi_set_format(&wf, float_fmt_res, rate_res); wasapi_set_format(&wf, float_fmt_res, rate_res);
#ifdef __cplusplus #ifdef __cplusplus
@ -334,13 +328,14 @@ static IAudioClient *wasapi_init_client_ex(IMMDevice *device,
goto error; goto error;
IFACE_RELEASE(client); IFACE_RELEASE(client);
hr = _IMMDevice_Activate(device, hr = _IMMDevice_Activate(device,
IID_IAudioClient, IID_IAudioClient,
CLSCTX_ALL, NULL, (void**)&client); CLSCTX_ALL, NULL, (void**)&client);
if (FAILED(hr)) if (FAILED(hr))
return NULL; return NULL;
buffer_duration = 10000.0 * 1000.0 / rate_res * buffer_length + 0.5; buffer_duration = 10000.0 * 1000.0 / rate_res * buffer_length + 0.5;
#ifdef __cplusplus #ifdef __cplusplus
hr = client->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, hr = client->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST,
@ -354,7 +349,7 @@ static IAudioClient *wasapi_init_client_ex(IMMDevice *device,
if (hr == AUDCLNT_E_ALREADY_INITIALIZED) if (hr == AUDCLNT_E_ALREADY_INITIALIZED)
{ {
IFACE_RELEASE(client); IFACE_RELEASE(client);
hr = _IMMDevice_Activate(device, hr = _IMMDevice_Activate(device,
IID_IAudioClient, IID_IAudioClient,
CLSCTX_ALL, NULL, (void**)&client); CLSCTX_ALL, NULL, (void**)&client);
if (FAILED(hr)) if (FAILED(hr))
@ -384,7 +379,7 @@ static IAudioClient *wasapi_init_client_ex(IMMDevice *device,
RARCH_WARN("[WASAPI]: Unsupported format.\n"); RARCH_WARN("[WASAPI]: Unsupported format.\n");
rate_res = wasapi_pref_rate(j); rate_res = wasapi_pref_rate(j);
if (rate_res == *rate) /* requested rate is allready tested */ if (rate_res == *rate) /* requested rate is already tested */
rate_res = wasapi_pref_rate(++j); /* skip it */ rate_res = wasapi_pref_rate(++j); /* skip it */
} }
} }
@ -445,24 +440,18 @@ static IAudioClient *wasapi_init_client(IMMDevice *device, bool *exclusive,
hr = _IAudioClient_GetDevicePeriod(client, &device_period, NULL); hr = _IAudioClient_GetDevicePeriod(client, &device_period, NULL);
if (FAILED(hr)) if (FAILED(hr))
{
RARCH_WARN("[WASAPI]: IAudioClient::GetDevicePeriod failed with error 0x%.8X.\n", hr); RARCH_WARN("[WASAPI]: IAudioClient::GetDevicePeriod failed with error 0x%.8X.\n", hr);
}
if (!*exclusive) if (!*exclusive)
{ {
hr = _IAudioClient_GetStreamLatency(client, &stream_latency); hr = _IAudioClient_GetStreamLatency(client, &stream_latency);
if (FAILED(hr)) if (FAILED(hr))
{
RARCH_WARN("[WASAPI]: IAudioClient::GetStreamLatency failed with error 0x%.8X.\n", hr); RARCH_WARN("[WASAPI]: IAudioClient::GetStreamLatency failed with error 0x%.8X.\n", hr);
}
} }
hr = _IAudioClient_GetBufferSize(client, &buffer_length); hr = _IAudioClient_GetBufferSize(client, &buffer_length);
if (FAILED(hr)) if (FAILED(hr))
{
RARCH_WARN("[WASAPI]: IAudioClient::GetBufferSize failed with error 0x%.8X.\n", hr); RARCH_WARN("[WASAPI]: IAudioClient::GetBufferSize failed with error 0x%.8X.\n", hr);
}
if (*exclusive) if (*exclusive)
latency_res = (double)buffer_length * 1000.0 / (*rate); latency_res = (double)buffer_length * 1000.0 / (*rate);
@ -471,13 +460,16 @@ static IAudioClient *wasapi_init_client(IMMDevice *device, bool *exclusive,
RARCH_LOG("[WASAPI]: Client initialized (%s, %s, %uHz, %.1fms).\n", RARCH_LOG("[WASAPI]: Client initialized (%s, %s, %uHz, %.1fms).\n",
*exclusive ? "exclusive" : "shared", *exclusive ? "exclusive" : "shared",
*float_fmt ? "float" : "pcm", *rate, latency_res); *float_fmt ? "float" : "pcm",
*rate, latency_res);
RARCH_LOG("[WASAPI]: Client's buffer length is %u frames (%.1fms).\n", RARCH_LOG("[WASAPI]: Client buffer length is %u frames (%.1fms).\n",
buffer_length, (double)buffer_length * 1000.0 / (*rate)); buffer_length,
(double)buffer_length * 1000.0 / (*rate));
RARCH_LOG("[WASAPI]: Device period is %.1fms (%lld frames).\n", RARCH_LOG("[WASAPI]: Device period is %.1fms (%lld frames).\n",
(double)device_period / 10000.0, device_period * (*rate) / 10000000); (double)device_period / 10000.0,
device_period * (*rate) / 10000000);
return client; return client;
} }
@ -530,11 +522,16 @@ static void *wasapi_init(const char *dev_id, unsigned rate, unsigned latency,
{ {
if (sh_buffer_length < 0) if (sh_buffer_length < 0)
{ {
#ifdef USE_CLIENT_BUFFER
sh_buffer_length = frame_count;
#else
hr = _IAudioClient_GetDevicePeriod(w->client, &dev_period, NULL); hr = _IAudioClient_GetDevicePeriod(w->client, &dev_period, NULL);
if (FAILED(hr)) if (FAILED(hr))
goto error; goto error;
sh_buffer_length = dev_period * rate / 10000000; sh_buffer_length = dev_period * rate / 10000000;
#endif
} }
w->buffer = fifo_new(sh_buffer_length * w->frame_size); w->buffer = fifo_new(sh_buffer_length * w->frame_size);
@ -575,6 +572,7 @@ static void *wasapi_init(const char *dev_id, unsigned rate, unsigned latency,
hr = _IAudioClient_Start(w->client); hr = _IAudioClient_Start(w->client);
if (FAILED(hr)) if (FAILED(hr))
goto error; goto error;
w->running = true; w->running = true;
w->nonblock = !settings->bools.audio_sync; w->nonblock = !settings->bools.audio_sync;
@ -599,13 +597,13 @@ static bool wasapi_flush(wasapi_t * w, const void * data, size_t size)
UINT32 frame_count = size / w->frame_size; UINT32 frame_count = size / w->frame_size;
if (FAILED(_IAudioRenderClient_GetBuffer( if (FAILED(_IAudioRenderClient_GetBuffer(
w->renderer, frame_count, &dest))) w->renderer, frame_count, &dest)))
return false; return false;
memcpy(dest, data, size); memcpy(dest, data, size);
if (FAILED(_IAudioRenderClient_ReleaseBuffer( if (FAILED(_IAudioRenderClient_ReleaseBuffer(
w->renderer, frame_count, w->renderer, frame_count, 0)))
0)))
return false; return false;
return true; return true;
@ -615,14 +613,15 @@ static bool wasapi_flush_buffer(wasapi_t * w, size_t size)
{ {
BYTE *dest = NULL; BYTE *dest = NULL;
UINT32 frame_count = size / w->frame_size; UINT32 frame_count = size / w->frame_size;
if (FAILED(_IAudioRenderClient_GetBuffer( if (FAILED(_IAudioRenderClient_GetBuffer(
w->renderer, frame_count, &dest))) w->renderer, frame_count, &dest)))
return false; return false;
fifo_read(w->buffer, dest, size); fifo_read(w->buffer, dest, size);
if (FAILED(_IAudioRenderClient_ReleaseBuffer( if (FAILED(_IAudioRenderClient_ReleaseBuffer(
w->renderer, frame_count, w->renderer, frame_count, 0)))
0)))
return false; return false;
return true; return true;
@ -631,8 +630,8 @@ static bool wasapi_flush_buffer(wasapi_t * w, size_t size)
static ssize_t wasapi_write_sh_buffer(wasapi_t *w, const void * data, size_t size) static ssize_t wasapi_write_sh_buffer(wasapi_t *w, const void * data, size_t size)
{ {
ssize_t written = -1; ssize_t written = -1;
UINT32 padding = 0;
size_t write_avail = FIFO_WRITE_AVAIL(w->buffer); size_t write_avail = FIFO_WRITE_AVAIL(w->buffer);
UINT32 padding = 0;
if (!write_avail) if (!write_avail)
{ {
@ -661,8 +660,8 @@ static ssize_t wasapi_write_sh_buffer(wasapi_t *w, const void * data, size_t siz
static ssize_t wasapi_write_sh(wasapi_t *w, const void * data, size_t size) static ssize_t wasapi_write_sh(wasapi_t *w, const void * data, size_t size)
{ {
size_t write_avail = 0;
ssize_t written = -1; ssize_t written = -1;
size_t write_avail = 0;
UINT32 padding = 0; UINT32 padding = 0;
if (!(WaitForSingleObject(w->write_event, INFINITE) == WAIT_OBJECT_0)) if (!(WaitForSingleObject(w->write_event, INFINITE) == WAIT_OBJECT_0))
@ -685,16 +684,16 @@ static ssize_t wasapi_write_sh(wasapi_t *w, const void * data, size_t size)
static ssize_t wasapi_write_sh_nonblock(wasapi_t *w, const void * data, size_t size) static ssize_t wasapi_write_sh_nonblock(wasapi_t *w, const void * data, size_t size)
{ {
size_t write_avail = 0;
ssize_t written = -1; ssize_t written = -1;
size_t write_avail = 0;
UINT32 padding = 0; UINT32 padding = 0;
if (w->buffer) if (w->buffer)
{ {
write_avail = FIFO_WRITE_AVAIL(w->buffer); write_avail = FIFO_WRITE_AVAIL(w->buffer);
if (!write_avail) if (!write_avail)
{ {
size_t read_avail = 0; size_t read_avail = 0;
if (FAILED(_IAudioClient_GetCurrentPadding(w->client, &padding))) if (FAILED(_IAudioClient_GetCurrentPadding(w->client, &padding)))
return -1; return -1;
@ -801,6 +800,7 @@ static ssize_t wasapi_write(void *wh, const void *data, size_t size)
static bool wasapi_stop(void *wh) static bool wasapi_stop(void *wh)
{ {
wasapi_t *w = (wasapi_t*)wh; wasapi_t *w = (wasapi_t*)wh;
if (FAILED(_IAudioClient_Stop(w->client))) if (FAILED(_IAudioClient_Stop(w->client)))
return !w->running; return !w->running;
@ -836,7 +836,7 @@ static void wasapi_set_nonblock_state(void *wh, bool nonblock)
{ {
wasapi_t *w = (wasapi_t*)wh; wasapi_t *w = (wasapi_t*)wh;
RARCH_LOG("[WASAPI]: Sync %s.\n", nonblock ? "off" : "on"); RARCH_DBG("[WASAPI]: Sync %s.\n", nonblock ? "off" : "on");
w->nonblock = nonblock; w->nonblock = nonblock;
} }
@ -858,9 +858,7 @@ static void wasapi_free(void *wh)
ir = WaitForSingleObject(write_event, 20); ir = WaitForSingleObject(write_event, 20);
if (ir == WAIT_FAILED) if (ir == WAIT_FAILED)
{
RARCH_ERR("[WASAPI]: WaitForSingleObject failed with error %d.\n", GetLastError()); RARCH_ERR("[WASAPI]: WaitForSingleObject failed with error %d.\n", GetLastError());
}
/* If event isn't signaled log and leak */ /* If event isn't signaled log and leak */
if (!(ir == WAIT_OBJECT_0)) if (!(ir == WAIT_OBJECT_0))

View File

@ -1115,9 +1115,9 @@
#ifdef HAVE_WASAPI #ifdef HAVE_WASAPI
/* WASAPI defaults */ /* WASAPI defaults */
#define DEFAULT_WASAPI_EXCLUSIVE_MODE true #define DEFAULT_WASAPI_EXCLUSIVE_MODE false
#define DEFAULT_WASAPI_FLOAT_FORMAT false #define DEFAULT_WASAPI_FLOAT_FORMAT false
/* auto */ /* Automatic shared mode buffer */
#define DEFAULT_WASAPI_SH_BUFFER_LENGTH -16 #define DEFAULT_WASAPI_SH_BUFFER_LENGTH -16
#endif #endif
@ -1554,8 +1554,12 @@
#endif #endif
/* MIDI */ /* MIDI */
#define DEFAULT_MIDI_INPUT "OFF" #if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)
#define DEFAULT_MIDI_OUTPUT "Microsoft GS Wavetable Synth"
#else
#define DEFAULT_MIDI_OUTPUT "OFF" #define DEFAULT_MIDI_OUTPUT "OFF"
#endif
#define DEFAULT_MIDI_INPUT "OFF"
#define DEFAULT_MIDI_VOLUME 100 #define DEFAULT_MIDI_VOLUME 100
#ifdef HAVE_MIST #ifdef HAVE_MIST

View File

@ -5119,8 +5119,7 @@ static void setting_get_string_representation_int_audio_wasapi_sh_buffer_length(
return; return;
if (*setting->value.target.integer > 0) if (*setting->value.target.integer > 0)
snprintf(s, len, "%d", snprintf(s, len, "%d", *setting->value.target.integer);
*setting->value.target.integer);
else if (*setting->value.target.integer == 0) else if (*setting->value.target.integer == 0)
strlcpy(s, "0 (Off)", len); strlcpy(s, "0 (Off)", len);
else else

View File

@ -22,6 +22,11 @@
#include "midi_driver.h" #include "midi_driver.h"
#include "verbosity.h" #include "verbosity.h"
#ifdef HAVE_WASAPI
#include "audio/audio_driver.h"
#include "gfx/video_driver.h"
#endif
#define MIDI_DRIVER_BUF_SIZE 4096 #define MIDI_DRIVER_BUF_SIZE 4096
#define MIDI_DRIVER_OFF "OFF" #define MIDI_DRIVER_OFF "OFF"
@ -116,6 +121,19 @@ bool midi_driver_set_all_sounds_off(void)
if (!rarch_midi_drv_data || !rarch_midi_drv_output_enabled) if (!rarch_midi_drv_data || !rarch_midi_drv_output_enabled)
return false; return false;
#ifdef HAVE_WASAPI
/* FIXME: Due to some mysterious reason Frame Delay does not
* work with WASAPI unless MIDI output is active, even when
* MIDI is not used. Frame Delay also breaks if MIDI sounds
* are "set off", which happens on menu toggle, therefore
* skip this if WASAPI is used and Frame Delay is effective.. */
if (string_is_equal(audio_state_get_ptr()->current_audio->ident, "wasapi"))
{
if (video_state_get_ptr()->frame_delay_effective > 0)
return false;
}
#endif
event.data = data; event.data = data;
event.data_size = sizeof(data); event.data_size = sizeof(data);
event.delta_time = 0; event.delta_time = 0;

View File

@ -956,7 +956,7 @@ void drivers_init(
if (flags & DRIVER_LED_MASK) if (flags & DRIVER_LED_MASK)
led_driver_init(settings->arrays.led_driver); led_driver_init(settings->arrays.led_driver);
/* Initialize MIDI driver */ /* Initialize MIDI driver */
if (flags & DRIVER_MIDI_MASK) if (flags & DRIVER_MIDI_MASK)
midi_driver_init(settings); midi_driver_init(settings);