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

View file

@ -1115,9 +1115,9 @@
#ifdef HAVE_WASAPI
/* WASAPI defaults */
#define DEFAULT_WASAPI_EXCLUSIVE_MODE true
#define DEFAULT_WASAPI_EXCLUSIVE_MODE false
#define DEFAULT_WASAPI_FLOAT_FORMAT false
/* auto */
/* Automatic shared mode buffer */
#define DEFAULT_WASAPI_SH_BUFFER_LENGTH -16
#endif
@ -1554,8 +1554,12 @@
#endif
/* 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"
#endif
#define DEFAULT_MIDI_INPUT "OFF"
#define DEFAULT_MIDI_VOLUME 100
#ifdef HAVE_MIST

View file

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

View file

@ -22,6 +22,11 @@
#include "midi_driver.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_OFF "OFF"
@ -116,6 +121,19 @@ bool midi_driver_set_all_sounds_off(void)
if (!rarch_midi_drv_data || !rarch_midi_drv_output_enabled)
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_size = sizeof(data);
event.delta_time = 0;

View file

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