(WASAPI) Shared buffer refactor + cleanup (#15929)

This commit is contained in:
sonninnos 2023-11-19 18:44:25 +02:00 committed by GitHub
parent ee417c0f75
commit 598a0c0d37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 248 additions and 230 deletions

View File

@ -151,7 +151,35 @@ static const char* wasapi_data_flow_name(EDataFlow data_flow)
}
static void wasapi_set_format(WAVEFORMATEXTENSIBLE *wf,
bool float_fmt, unsigned rate, unsigned channels);
bool float_fmt, unsigned rate, unsigned channels)
{
WORD wBitsPerSample = float_fmt ? 32 : 16;
WORD nBlockAlign = (channels * wBitsPerSample) / 8;
DWORD nAvgBytesPerSec = rate * nBlockAlign;
wf->Format.nChannels = channels;
wf->Format.nSamplesPerSec = rate;
wf->Format.nAvgBytesPerSec = nAvgBytesPerSec;
wf->Format.nBlockAlign = nBlockAlign;
wf->Format.wBitsPerSample = wBitsPerSample;
if (float_fmt)
{
wf->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
wf->Format.cbSize = sizeof(WORD) + sizeof(DWORD) + sizeof(GUID);
wf->Samples.wValidBitsPerSample = wBitsPerSample;
wf->dwChannelMask = channels == 1 ? KSAUDIO_SPEAKER_MONO : KSAUDIO_SPEAKER_STEREO;
wf->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
}
else
{
wf->Format.wFormatTag = WAVE_FORMAT_PCM;
wf->Format.cbSize = 0;
wf->Samples.wValidBitsPerSample = 0;
wf->dwChannelMask = 0;
memset(&wf->SubFormat, 0, sizeof(wf->SubFormat));
}
}
/**
* @param[in] format The format to check.
@ -186,6 +214,7 @@ static bool wasapi_is_format_suitable(const WAVEFORMATEXTENSIBLE *format)
return true;
}
/**
* Selects a sample format suitable for the given device.
* @param[in,out] format The place where the chosen format will be written,
@ -201,7 +230,7 @@ static bool wasapi_select_device_format(WAVEFORMATEXTENSIBLE *format, IAudioClie
{
static const unsigned preferred_rates[] = { 48000, 44100, 96000, 192000, 32000 };
const bool preferred_formats[] = {format->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE, format->Format.wFormatTag != WAVE_FORMAT_EXTENSIBLE};
/* Try the requested sample format first, then try the other one */
/* Try the requested sample format first, then try the other one. */
WAVEFORMATEXTENSIBLE *suggested_format = NULL;
bool result = false;
HRESULT hr = _IAudioClient_IsFormatSupported(
@ -213,15 +242,13 @@ static bool wasapi_select_device_format(WAVEFORMATEXTENSIBLE *format, IAudioClie
switch (hr)
{
case S_OK:
/* The requested format is okay without any changes */
RARCH_DBG("[WASAPI]: Desired format (%s, %u-channel, %uHz) can be used as-is.\n",
wave_format_name(format), format->Format.nChannels, format->Format.nSamplesPerSec);
/* The requested format is okay without any changes. */
result = true;
break;
case S_FALSE:
/* The requested format is unsupported, but Windows has suggested a similar one. */
RARCH_DBG("[WASAPI]: Windows suggests a format of (%s, %u-channel, %uHz).\n",
wave_format_name(suggested_format), suggested_format->Format.nChannels, suggested_format->Format.nSamplesPerSec);
wave_format_name(suggested_format), suggested_format->Format.nChannels, suggested_format->Format.nSamplesPerSec);
if (wasapi_is_format_suitable(suggested_format))
{
*format = *suggested_format;
@ -234,10 +261,11 @@ static bool wasapi_select_device_format(WAVEFORMATEXTENSIBLE *format, IAudioClie
}
break;
case AUDCLNT_E_UNSUPPORTED_FORMAT:
{ /* The requested format is unsupported
* and Windows was unable to suggest another.
* Usually happens with exclusive mode.
* RetroArch will try selecting a format. */
{
/* The requested format is unsupported
* and Windows was unable to suggest another.
* Usually happens with exclusive mode.
* RetroArch will try selecting a format. */
size_t i, j;
WAVEFORMATEXTENSIBLE possible_format;
HRESULT format_check_hr;
@ -253,60 +281,29 @@ static bool wasapi_select_device_format(WAVEFORMATEXTENSIBLE *format, IAudioClie
*format = possible_format;
result = true;
RARCH_DBG("[WASAPI]: RetroArch suggests a format of (%s, %u-channel, %uHz).\n",
wave_format_name(format), format->Format.nChannels, format->Format.nSamplesPerSec);
wave_format_name(format), format->Format.nChannels, format->Format.nSamplesPerSec);
goto done;
}
}
}
RARCH_ERR("[WASAPI]: Failed to select client format: No suitable format available\n");
RARCH_ERR("[WASAPI]: Failed to select client format: No suitable format available.\n");
result = false;
break;
}
default:
/* Something else went wrong. */
RARCH_ERR("[WASAPI]: Failed to select client format: %s\n", hresult_name(hr));
RARCH_ERR("[WASAPI]: Failed to select client format: %s.\n", hresult_name(hr));
result = false;
break;
}
done:
/* IAudioClient::IsFormatSupported allocates a format object */
/* IAudioClient::IsFormatSupported allocates a format object. */
if (suggested_format)
CoTaskMemFree(suggested_format);
return result;
}
static void wasapi_set_format(WAVEFORMATEXTENSIBLE *wf,
bool float_fmt, unsigned rate, unsigned channels)
{
WORD wBitsPerSample = float_fmt ? 32 : 16;
WORD nBlockAlign = (channels * wBitsPerSample) / 8;
DWORD nAvgBytesPerSec = rate * nBlockAlign;
wf->Format.nChannels = channels;
wf->Format.nSamplesPerSec = rate;
wf->Format.nAvgBytesPerSec = nAvgBytesPerSec;
wf->Format.nBlockAlign = nBlockAlign;
wf->Format.wBitsPerSample = wBitsPerSample;
if (float_fmt)
{
wf->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
wf->Format.cbSize = sizeof(WORD) + sizeof(DWORD) + sizeof(GUID);
wf->Samples.wValidBitsPerSample = wBitsPerSample;
wf->dwChannelMask = channels == 1 ? KSAUDIO_SPEAKER_MONO : KSAUDIO_SPEAKER_STEREO;
wf->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
}
else
{
wf->Format.wFormatTag = WAVE_FORMAT_PCM;
wf->Format.cbSize = 0;
wf->Samples.wValidBitsPerSample = 0;
wf->dwChannelMask = 0;
memset(&wf->SubFormat, 0, sizeof(wf->SubFormat));
}
}
static IAudioClient *wasapi_init_client_ex(IMMDevice *device,
bool *float_fmt, unsigned *rate, unsigned latency, unsigned channels)
{
@ -320,59 +317,54 @@ static IAudioClient *wasapi_init_client_ex(IMMDevice *device,
if (FAILED(hr))
{
RARCH_ERR("[WASAPI]: IMMDevice::Activate failed: %s\n", hresult_name(hr));
RARCH_ERR("[WASAPI]: IMMDevice::Activate failed: %s.\n", hresult_name(hr));
return NULL;
}
hr = _IAudioClient_GetDevicePeriod(client, NULL, &minimum_period);
if (FAILED(hr))
{
RARCH_ERR("[WASAPI]: Failed to get device period of exclusive-mode client: %s\n", hresult_name(hr));
RARCH_ERR("[WASAPI]: Failed to get minimum device period of exclusive client: %s.\n", hresult_name(hr));
goto error;
}
/* buffer_duration is in 100ns units */
/* Buffer_duration is in 100ns units. */
buffer_duration = latency * 10000.0;
if (buffer_duration < minimum_period)
buffer_duration = minimum_period;
wasapi_set_format(&wf, *float_fmt, *rate, channels);
RARCH_DBG("[WASAPI]: Requesting format: %u-bit %u-channel client with %s samples at %uHz\n",
wf.Format.wBitsPerSample,
wf.Format.nChannels, wave_format_name(&wf), wf.Format.nSamplesPerSec);
if (wasapi_select_device_format(&wf, client, AUDCLNT_SHAREMODE_EXCLUSIVE, channels))
{
RARCH_DBG("[WASAPI]: Using format: %u-bit %u-channel client with %s samples at %uHz\n",
RARCH_DBG("[WASAPI]: Requesting exclusive %u-bit %u-channel client with %s samples at %uHz %ums.\n",
wf.Format.wBitsPerSample,
wf.Format.nChannels, wave_format_name(&wf), wf.Format.nSamplesPerSec);
}
else
wf.Format.nChannels, wave_format_name(&wf), wf.Format.nSamplesPerSec, latency);
if (!wasapi_select_device_format(&wf, client, AUDCLNT_SHAREMODE_EXCLUSIVE, channels))
{
RARCH_ERR("[WASAPI]: Failed to select a suitable device format\n");
RARCH_ERR("[WASAPI]: Failed to select a suitable device format.\n");
goto error;
}
hr = _IAudioClient_Initialize(client, AUDCLNT_SHAREMODE_EXCLUSIVE,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST,
buffer_duration, buffer_duration, (WAVEFORMATEX*)&wf, NULL);
if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED)
{
RARCH_WARN("[WASAPI]: Unaligned buffer size: %s\n", hresult_name(hr));
RARCH_WARN("[WASAPI]: Unaligned buffer size: %s.\n", hresult_name(hr));
hr = _IAudioClient_GetBufferSize(client, &buffer_length);
if (FAILED(hr))
{
RARCH_ERR("[WASAPI]: Failed to get buffer size of client: %s\n", hresult_name(hr));
RARCH_ERR("[WASAPI]: Failed to get buffer size of client: %s.\n", hresult_name(hr));
goto error;
}
IFACE_RELEASE(client);
hr = _IMMDevice_Activate(device,
hr = _IMMDevice_Activate(device,
IID_IAudioClient,
CLSCTX_ALL, NULL, (void**)&client);
if (FAILED(hr))
{
RARCH_ERR("[WASAPI]: IMMDevice::Activate failed: %s\n", hresult_name(hr));
RARCH_ERR("[WASAPI]: IMMDevice::Activate failed: %s.\n", hresult_name(hr));
return NULL;
}
@ -384,12 +376,12 @@ 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))
{
RARCH_ERR("[WASAPI] IMMDevice::Activate failed: %s\n", hresult_name(hr));
RARCH_ERR("[WASAPI] IMMDevice::Activate failed: %s.\n", hresult_name(hr));
return NULL;
}
@ -408,16 +400,13 @@ static IAudioClient *wasapi_init_client_ex(IMMDevice *device,
if (FAILED(hr))
{
RARCH_ERR("[WASAPI]: Failed to create exclusive-mode client: %s\n", hresult_name(hr));
RARCH_ERR("[WASAPI]: IAudioClient::Initialize failed: %s.\n", hresult_name(hr));
goto error;
}
*float_fmt = wf.Format.wFormatTag != WAVE_FORMAT_PCM;
*rate = wf.Format.nSamplesPerSec;
RARCH_DBG("[WASAPI]: Initialized exclusive %s client at %uHz, latency %ums\n",
*float_fmt ? "float" : "pcm", *rate, latency);
return client;
error:
@ -430,27 +419,43 @@ static IAudioClient *wasapi_init_client_sh(IMMDevice *device,
bool *float_fmt, unsigned *rate, unsigned latency, unsigned channels)
{
WAVEFORMATEXTENSIBLE wf;
IAudioClient *client = NULL;
bool float_fmt_res = *float_fmt;
unsigned rate_res = *rate;
HRESULT hr = _IMMDevice_Activate(device,
IID_IAudioClient,
CLSCTX_ALL, NULL, (void**)&client);
IAudioClient *client = NULL;
settings_t *settings = config_get_ptr();
unsigned sh_buffer_length = settings->uints.audio_wasapi_sh_buffer_length;
REFERENCE_TIME default_period = 0;
REFERENCE_TIME buffer_duration = 0;
HRESULT hr = _IMMDevice_Activate(device,
IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&client);
if (FAILED(hr))
{ /* If we couldn't create the IAudioClient... */
RARCH_ERR("[WASAPI]: Failed to create %s IAudioClient: %s\n", hresult_name(hr));
{
RARCH_ERR("[WASAPI]: IMMDevice::Activate failed: %s.\n", hresult_name(hr));
return NULL;
}
wasapi_set_format(&wf, float_fmt_res, rate_res, channels);
if (wasapi_select_device_format(&wf, client, AUDCLNT_SHAREMODE_SHARED, channels))
hr = _IAudioClient_GetDevicePeriod(client, &default_period, NULL);
if (FAILED(hr))
{
RARCH_DBG("[WASAPI]: Requesting %u-channel shared-mode client with %s samples at %uHz.\n",
wf.Format.nChannels, wave_format_name(&wf), wf.Format.nSamplesPerSec);
RARCH_ERR("[WASAPI]: Failed to get default device period of shared client: %s.\n", hresult_name(hr));
goto error;
}
else
/* Use audio latency setting for buffer size if allowed */
if ( sh_buffer_length < WASAPI_SH_BUFFER_DEVICE_PERIOD
|| sh_buffer_length > WASAPI_SH_BUFFER_CLIENT_BUFFER)
{
/* Buffer_duration is in 100ns units. */
buffer_duration = latency * 10000.0;
if (buffer_duration < default_period)
buffer_duration = default_period;
}
wasapi_set_format(&wf, *float_fmt, *rate, channels);
RARCH_DBG("[WASAPI]: Requesting shared %u-bit %u-channel client with %s samples at %uHz %ums.\n",
wf.Format.wBitsPerSample,
wf.Format.nChannels, wave_format_name(&wf), wf.Format.nSamplesPerSec, latency);
if (!wasapi_select_device_format(&wf, client, AUDCLNT_SHAREMODE_SHARED, channels))
{
RARCH_ERR("[WASAPI]: Failed to select a suitable device format.\n");
goto error;
@ -458,37 +463,34 @@ static IAudioClient *wasapi_init_client_sh(IMMDevice *device,
hr = _IAudioClient_Initialize(client, AUDCLNT_SHAREMODE_SHARED,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
0, 0, (WAVEFORMATEX*)&wf, NULL);
buffer_duration, 0, (WAVEFORMATEX*)&wf, NULL);
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))
{
RARCH_ERR("[WASAPI]: IMMDevice::Activate failed: %s\n", hresult_name(hr));
RARCH_ERR("[WASAPI]: IMMDevice::Activate failed: %s.\n", hresult_name(hr));
return NULL;
}
hr = _IAudioClient_Initialize(client, AUDCLNT_SHAREMODE_SHARED,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
0, 0, (WAVEFORMATEX*)&wf, NULL);
buffer_duration, 0, (WAVEFORMATEX*)&wf, NULL);
}
if (FAILED(hr))
{
RARCH_ERR("[WASAPI]: IAudioClient::Initialize failed: %s\n", hresult_name(hr));
RARCH_ERR("[WASAPI]: IAudioClient::Initialize failed: %s.\n", hresult_name(hr));
goto error;
}
*float_fmt = wf.Format.wFormatTag != WAVE_FORMAT_PCM;
*rate = wf.Format.nSamplesPerSec;
RARCH_DBG("[WASAPI]: Initialized shared %s client at %uHz.\n",
wave_format_name(&wf), *rate);
return client;
error:
@ -516,16 +518,17 @@ IMMDevice *wasapi_init_device(const char *id, EDataFlow data_flow)
IID_IMMDeviceEnumerator, (void **)&enumerator);
#else
hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
&IID_IMMDeviceEnumerator, (void **)&enumerator);
&IID_IMMDeviceEnumerator, (void **)&enumerator);
#endif
if (FAILED(hr))
{
RARCH_ERR("[WASAPI]: Failed to create device enumerator: %s\n", hresult_name(hr));
RARCH_ERR("[WASAPI]: Failed to create device enumerator: %s.\n", hresult_name(hr));
goto error;
}
if (id)
{ /* If a specific device was requested... */
{
/* If a specific device was requested... */
int32_t idx_found = -1;
struct string_list *list = (struct string_list*)mmdevice_list_new(NULL, data_flow);
@ -536,20 +539,21 @@ IMMDevice *wasapi_init_device(const char *id, EDataFlow data_flow)
}
if (list->elems)
{ /* If any devices were found... */
{
/* If any devices were found... */
unsigned d;
for (d = 0; d < list->size; d++)
{
if (string_is_equal(id, list->elems[d].data))
{
RARCH_DBG("[WASAPI]: Found device #%d: \"%s\"\n", d, list->elems[d].data);
RARCH_DBG("[WASAPI]: Found device #%d: \"%s\".\n", d, list->elems[d].data);
idx_found = d;
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);
@ -565,14 +569,14 @@ IMMDevice *wasapi_init_device(const char *id, EDataFlow data_flow)
data_flow, DEVICE_STATE_ACTIVE, &collection);
if (FAILED(hr))
{
RARCH_ERR("[WASAPI]: Failed to enumerate audio endpoints: %s\n", hresult_name(hr));
RARCH_ERR("[WASAPI]: Failed to enumerate audio endpoints: %s.\n", hresult_name(hr));
goto error;
}
hr = _IMMDeviceCollection_GetCount(collection, &dev_count);
if (FAILED(hr))
{
RARCH_ERR("[WASAPI]: Failed to count IMMDevices: %s\n", hresult_name(hr));
RARCH_ERR("[WASAPI]: Failed to count IMMDevices: %s.\n", hresult_name(hr));
goto error;
}
@ -581,7 +585,7 @@ IMMDevice *wasapi_init_device(const char *id, EDataFlow data_flow)
hr = _IMMDeviceCollection_Item(collection, i, &device);
if (FAILED(hr))
{
RARCH_ERR("[WASAPI]: Failed to get IMMDevice #%d: %s\n", i, hresult_name(hr));
RARCH_ERR("[WASAPI]: Failed to get IMMDevice #%d: %s.\n", i, hresult_name(hr));
goto error;
}
@ -597,7 +601,7 @@ IMMDevice *wasapi_init_device(const char *id, EDataFlow data_flow)
enumerator, data_flow, eConsole, &device);
if (FAILED(hr))
{
RARCH_ERR("[WASAPI]: Failed to get default audio endpoint: %s\n", hresult_name(hr));
RARCH_ERR("[WASAPI]: Failed to get default audio endpoint: %s.\n", hresult_name(hr));
goto error;
}
}
@ -615,13 +619,9 @@ error:
IFACE_RELEASE(enumerator);
if (id)
{
RARCH_WARN("[WASAPI]: Failed to initialize %s device \"%s\".\n", data_flow_name, id);
}
else
{
RARCH_ERR("[WASAPI]: Failed to initialize default %s device.\n", data_flow_name);
}
return NULL;
}
@ -631,14 +631,11 @@ IAudioClient *wasapi_init_client(IMMDevice *device, bool *exclusive,
{
HRESULT hr;
IAudioClient *client;
double latency_res;
REFERENCE_TIME device_period = 0;
REFERENCE_TIME stream_latency = 0;
UINT32 buffer_length = 0;
RARCH_DBG("[WASAPI]: Requesting %s %s client (rate=%uHz, latency=%ums).\n",
*exclusive ? "exclusive" : "shared",
*float_fmt ? "float" : "pcm", *rate, latency);
float latency_res;
REFERENCE_TIME device_period = 0;
REFERENCE_TIME device_period_min = 0;
REFERENCE_TIME stream_latency = 0;
UINT32 buffer_length = 0;
if (*exclusive)
{
@ -666,45 +663,24 @@ IAudioClient *wasapi_init_client(IMMDevice *device, bool *exclusive,
if (!client)
return NULL;
/* next calls are allowed to fail (we losing info only) */
/* Remaining calls are for logging purposes. */
if (*exclusive)
hr = _IAudioClient_GetDevicePeriod(client, &device_period, &device_period_min);
if (SUCCEEDED(hr))
{
hr = _IAudioClient_GetDevicePeriod(client, NULL, &device_period);
if (SUCCEEDED(hr))
{
RARCH_DBG("[WASAPI]: Minimum exclusive-mode device period is %uns (%.1fms).\n",
device_period * 100, (double)device_period * 100 / 1e6);
}
/* device_period is in 100ns units */
RARCH_DBG("[WASAPI]: Default device period is %.1fms.\n", (float)device_period * 100 / 1e6);
RARCH_DBG("[WASAPI]: Minimum device period is %.1fms.\n", (float)device_period_min * 100 / 1e6);
}
else
{
hr = _IAudioClient_GetDevicePeriod(client, &device_period, NULL);
if (SUCCEEDED(hr))
{
RARCH_DBG("[WASAPI]: Default shared-mode device period is %uns (%.1fms).\n",
device_period * 100, (double)device_period * 100 / 1e6);
}
}
if (FAILED(hr))
{
RARCH_WARN("[WASAPI]: IAudioClient::GetDevicePeriod failed: %s\n", hresult_name(hr));
}
RARCH_WARN("[WASAPI]: IAudioClient::GetDevicePeriod failed: %s.\n", hresult_name(hr));
if (!*exclusive)
{
hr = _IAudioClient_GetStreamLatency(client, &stream_latency);
if (SUCCEEDED(hr))
{
RARCH_DBG("[WASAPI]: Shared stream latency is %uns (%.1fms).\n",
stream_latency * 100, (double)stream_latency * 100 / 1e6);
}
RARCH_DBG("[WASAPI]: Shared stream latency is %.1fms.\n", (float)stream_latency * 100 / 1e6);
else
{
RARCH_WARN("[WASAPI]: IAudioClient::GetStreamLatency failed: %s\n", hresult_name(hr));
}
RARCH_WARN("[WASAPI]: IAudioClient::GetStreamLatency failed: %s.\n", hresult_name(hr));
}
hr = _IAudioClient_GetBufferSize(client, &buffer_length);
@ -712,45 +688,38 @@ IAudioClient *wasapi_init_client(IMMDevice *device, bool *exclusive,
{
size_t num_samples = buffer_length * channels;
size_t num_bytes = num_samples * (*float_fmt ? sizeof(float) : sizeof(int16_t));
RARCH_DBG("[WASAPI]: Endpoint buffer size is %u frames (%u samples, %u bytes).\n",
buffer_length, num_samples, num_bytes);
RARCH_DBG("[WASAPI]: Endpoint buffer size is %u frames (%u samples, %u bytes, %.1f ms).\n",
buffer_length, num_samples, num_bytes, (float)buffer_length * 1000.0 / *rate);
}
else
{
RARCH_WARN("[WASAPI]: IAudioClient::GetBufferSize failed: %s.\n", hresult_name(hr));
}
if (*exclusive)
latency_res = (double)buffer_length * 1000.0 / (*rate);
latency_res = (float)buffer_length * 1000.0 / (*rate);
else
{
settings_t *settings = config_get_ptr();
int sh_buffer_length = settings->ints.audio_wasapi_sh_buffer_length;
settings_t *settings = config_get_ptr();
unsigned sh_buffer_length = settings->uints.audio_wasapi_sh_buffer_length;
if (sh_buffer_length < 0)
switch (sh_buffer_length)
{
#ifdef USE_CLIENT_BUFFER
latency_res = (double)buffer_length * 1000.0 / (*rate);
#else
latency_res = (double)(stream_latency + device_period) / 10000.0;
#endif
case WASAPI_SH_BUFFER_AUDIO_LATENCY:
case WASAPI_SH_BUFFER_CLIENT_BUFFER:
latency_res = (float)buffer_length * 1000.0 / (*rate);
break;
case WASAPI_SH_BUFFER_DEVICE_PERIOD:
latency_res = (float)(stream_latency + device_period) / 10000.0;
break;
default:
latency_res = (float)sh_buffer_length * 1000.0 / (*rate);
break;
}
else if (sh_buffer_length > 0)
latency_res = sh_buffer_length * 1000.0 / (*rate);
else
latency_res = 0;
}
RARCH_LOG("[WASAPI]: Client initialized (%s, %s, %uHz, %.1fms).\n",
*exclusive ? "exclusive" : "shared",
*float_fmt ? "float" : "pcm",
*float_fmt ? "FLOAT" : "PCM",
*rate, latency_res);
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 %lld frames (%.1fms).\n",
device_period * (*rate) / 10000000, (double)device_period / 10000.0);
return client;
}

View File

@ -26,8 +26,10 @@
#include "../common/mmdevice_common_inline.h"
#include "boolean.h"
/* Get automatic buffer size from client buffer instead of device period */
#define USE_CLIENT_BUFFER
/* Shared buffer size replacement placeholders */
#define WASAPI_SH_BUFFER_AUDIO_LATENCY 0
#define WASAPI_SH_BUFFER_DEVICE_PERIOD 32
#define WASAPI_SH_BUFFER_CLIENT_BUFFER 64
const char *hresult_name(HRESULT hr);
const char* wasapi_error(DWORD error);

View File

@ -33,9 +33,9 @@ typedef struct
IMMDevice *device;
IAudioClient *client;
IAudioRenderClient *renderer;
fifo_buffer_t *buffer; /* NULL in unbuffered shared mode */
size_t frame_size; /* 4 or 8 only */
fifo_buffer_t *buffer;
size_t engine_buffer_size;
unsigned char frame_size; /* 4 or 8 only */
bool exclusive;
bool nonblock;
bool running;
@ -51,7 +51,7 @@ static void *wasapi_init(const char *dev_id, unsigned rate, unsigned latency,
settings_t *settings = config_get_ptr();
bool float_format = settings->bools.audio_wasapi_float_format;
bool exclusive_mode = settings->bools.audio_wasapi_exclusive_mode;
int sh_buffer_length = settings->ints.audio_wasapi_sh_buffer_length;
unsigned sh_buffer_length = settings->uints.audio_wasapi_sh_buffer_length;
wasapi_t *w = (wasapi_t*)calloc(1, sizeof(wasapi_t));
if (!w)
@ -73,43 +73,39 @@ static void *wasapi_init(const char *dev_id, unsigned rate, unsigned latency,
if (FAILED(hr))
goto error;
w->frame_size = float_format ? 8 : 4;
w->engine_buffer_size = frame_count * w->frame_size;
w->frame_size = float_format ? 8 : 4;
w->engine_buffer_size = frame_count * w->frame_size;
if (w->exclusive)
{
w->buffer = fifo_new(w->engine_buffer_size);
if (!w->buffer)
goto error;
RARCH_LOG("[WASAPI]: Intermediate buffer length is %u frames (%.1fms).\n",
frame_count, (double)frame_count * 1000.0 / rate);
}
else if (sh_buffer_length)
else
{
if (sh_buffer_length < 0)
switch (sh_buffer_length)
{
#ifdef USE_CLIENT_BUFFER
sh_buffer_length = frame_count;
#else
hr = _IAudioClient_GetDevicePeriod(w->client, &dev_period, NULL);
case WASAPI_SH_BUFFER_AUDIO_LATENCY:
case WASAPI_SH_BUFFER_CLIENT_BUFFER:
sh_buffer_length = frame_count;
break;
case WASAPI_SH_BUFFER_DEVICE_PERIOD:
hr = _IAudioClient_GetDevicePeriod(w->client, &dev_period, NULL);
if (FAILED(hr))
goto error;
if (FAILED(hr))
goto error;
sh_buffer_length = dev_period * rate / 10000000;
#endif
sh_buffer_length = dev_period * rate / 10000000;
break;
default:
break;
}
w->buffer = fifo_new(sh_buffer_length * w->frame_size);
if (!w->buffer)
goto error;
RARCH_LOG("[WASAPI]: Intermediate buffer length is %u frames (%.1fms).\n",
sh_buffer_length, (double)sh_buffer_length * 1000.0 / rate);
}
else
RARCH_LOG("[WASAPI]: Intermediate buffer is off. \n");
w->write_event = CreateEventA(NULL, FALSE, FALSE, NULL);
if (!w->write_event)
@ -203,7 +199,7 @@ static ssize_t wasapi_write_sh_buffer(wasapi_t *w, const void * data, size_t siz
if (!write_avail)
{
size_t read_avail = 0;
size_t read_avail = 0;
if (!(WaitForSingleObject(w->write_event, INFINITE) == WAIT_OBJECT_0))
return -1;
@ -252,9 +248,9 @@ 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)
{
ssize_t written = -1;
size_t write_avail = 0;
UINT32 padding = 0;
ssize_t written = -1;
size_t write_avail = 0;
UINT32 padding = 0;
if (w->buffer)
{
@ -320,6 +316,7 @@ static ssize_t wasapi_write_ex(wasapi_t *w, const void * data, size_t size, DWOR
static ssize_t wasapi_write(void *wh, const void *data, size_t size)
{
size_t written = 0;
ssize_t ir = 0;
wasapi_t *w = (wasapi_t*)wh;
if (w->nonblock)
@ -331,7 +328,6 @@ static ssize_t wasapi_write(void *wh, const void *data, size_t size)
if (w->exclusive)
{
ssize_t ir;
for (ir = -1; written < size; written += ir)
{
ir = wasapi_write_ex(w, (char*)data + written, size - written, INFINITE);
@ -341,7 +337,6 @@ static ssize_t wasapi_write(void *wh, const void *data, size_t size)
}
else
{
ssize_t ir;
if (w->buffer)
{
for (ir = -1; written < size; written += ir)
@ -426,7 +421,6 @@ static void wasapi_free(void *wh)
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))
return;

View File

@ -1147,7 +1147,7 @@
#define DEFAULT_WASAPI_EXCLUSIVE_MODE false
#define DEFAULT_WASAPI_FLOAT_FORMAT false
/* Automatic shared mode buffer */
#define DEFAULT_WASAPI_SH_BUFFER_LENGTH -16
#define DEFAULT_WASAPI_SH_BUFFER_LENGTH 0
#endif
/* Automatically mute audio when fast forward

View File

@ -1783,7 +1783,6 @@ static struct config_bool_setting *populate_settings_bool(
#ifdef HAVE_MICROPHONE
SETTING_BOOL("microphone_enable", &settings->bools.microphone_enable, true, DEFAULT_MICROPHONE_ENABLE, false);
#ifdef HAVE_WASAPI
SETTING_BOOL("microphone_wasapi_exclusive_mode", &settings->bools.microphone_wasapi_exclusive_mode, true, DEFAULT_WASAPI_EXCLUSIVE_MODE, false);
SETTING_BOOL("microphone_wasapi_float_format", &settings->bools.microphone_wasapi_float_format, true, DEFAULT_WASAPI_FLOAT_FORMAT, false);
@ -2335,6 +2334,11 @@ static struct config_uint_setting *populate_settings_uint(
SETTING_UINT("audio_resampler_quality", &settings->uints.audio_resampler_quality, true, DEFAULT_AUDIO_RESAMPLER_QUALITY_LEVEL, false);
SETTING_UINT("audio_block_frames", &settings->uints.audio_block_frames, true, 0, false);
SETTING_UINT("midi_volume", &settings->uints.midi_volume, true, DEFAULT_MIDI_VOLUME, false);
#ifdef HAVE_WASAPI
SETTING_UINT("audio_wasapi_sh_buffer_length", &settings->uints.audio_wasapi_sh_buffer_length, true, DEFAULT_WASAPI_SH_BUFFER_LENGTH, false);
#endif
#ifdef HAVE_MICROPHONE
SETTING_UINT("microphone_latency", &settings->uints.microphone_latency, false, 0 /* TODO */, false);
SETTING_UINT("microphone_resampler_quality", &settings->uints.microphone_resampler_quality, true, DEFAULT_AUDIO_RESAMPLER_QUALITY_LEVEL, false);
@ -2549,9 +2553,6 @@ static struct config_int_setting *populate_settings_int(
SETTING_INT("menu_xmb_title_margin_horizontal_offset", &settings->ints.menu_xmb_title_margin_horizontal_offset, true, DEFAULT_XMB_TITLE_MARGIN_HORIZONTAL_OFFSET, false);
#endif
#ifdef HAVE_WASAPI
SETTING_INT("audio_wasapi_sh_buffer_length", &settings->ints.audio_wasapi_sh_buffer_length, true, DEFAULT_WASAPI_SH_BUFFER_LENGTH, false);
#endif
SETTING_INT("crt_switch_center_adjust", &settings->ints.crt_switch_center_adjust, false, DEFAULT_CRT_SWITCH_CENTER_ADJUST, false);
SETTING_INT("crt_switch_porch_adjust", &settings->ints.crt_switch_porch_adjust, false, DEFAULT_CRT_SWITCH_PORCH_ADJUST, false);
#ifdef HAVE_WINDOW_OFFSET
@ -3770,6 +3771,16 @@ static bool config_load_file(global_t *global,
audio_set_float(AUDIO_ACTION_MIXER_VOLUME_GAIN, settings->floats.audio_mixer_volume);
#endif
#ifdef HAVE_WASAPI
{
/* Migrate from old deprecated negative value */
int wasapi_sh_buffer_length = settings->uints.audio_wasapi_sh_buffer_length;
if (wasapi_sh_buffer_length < 0)
settings->uints.audio_wasapi_sh_buffer_length = 0;
}
#endif
/* MIDI fallback for old OFF-string */
if (string_is_equal(settings->arrays.midi_input, "Off"))
configuration_set_string(settings,

View File

@ -106,7 +106,6 @@ typedef struct settings
int location_update_interval_distance;
int state_slot;
int replay_slot;
int audio_wasapi_sh_buffer_length;
int crt_switch_center_adjust;
int crt_switch_porch_adjust;
#ifdef HAVE_VULKAN
@ -161,12 +160,18 @@ typedef struct settings
unsigned audio_block_frames;
unsigned audio_latency;
#ifdef HAVE_WASAPI
unsigned audio_wasapi_sh_buffer_length;
#endif
#ifdef HAVE_MICROPHONE
unsigned microphone_sample_rate;
unsigned microphone_block_frames;
unsigned microphone_latency;
unsigned microphone_wasapi_sh_buffer_length;
unsigned microphone_resampler_quality;
#ifdef HAVE_WASAPI
unsigned microphone_wasapi_sh_buffer_length;
#endif
#endif
unsigned fps_update_interval;
@ -623,11 +628,14 @@ typedef struct settings
bool audio_enable_menu_scroll;
bool audio_sync;
bool audio_rate_control;
bool audio_wasapi_exclusive_mode;
bool audio_wasapi_float_format;
bool audio_fastforward_mute;
bool audio_fastforward_speedup;
#ifdef HAVE_WASAPI
bool audio_wasapi_exclusive_mode;
bool audio_wasapi_float_format;
#endif
#ifdef HAVE_MICROPHONE
/* Microphone */
bool microphone_enable;

View File

@ -7184,6 +7184,7 @@ unsigned menu_displaylist_build_list(
MENU_ENUM_LABEL_AUDIO_BLOCK_FRAMES,
PARSE_ONLY_UINT, false) == 0)
count++;
#ifdef HAVE_WASAPI
if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list,
MENU_ENUM_LABEL_AUDIO_WASAPI_EXCLUSIVE_MODE,
PARSE_ONLY_BOOL, false) == 0)
@ -7194,8 +7195,9 @@ unsigned menu_displaylist_build_list(
count++;
if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list,
MENU_ENUM_LABEL_AUDIO_WASAPI_SH_BUFFER_LENGTH,
PARSE_ONLY_INT, false) == 0)
PARSE_ONLY_UINT, false) == 0)
count++;
#endif
}
break;
#ifdef HAVE_MICROPHONE
@ -7235,6 +7237,7 @@ unsigned menu_displaylist_build_list(
MENU_ENUM_LABEL_MICROPHONE_BLOCK_FRAMES,
PARSE_ONLY_UINT, false) == 0)
count++;
#ifdef HAVE_WASAPI
if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list,
MENU_ENUM_LABEL_MICROPHONE_WASAPI_EXCLUSIVE_MODE,
PARSE_ONLY_BOOL, false) == 0)
@ -7247,6 +7250,7 @@ unsigned menu_displaylist_build_list(
MENU_ENUM_LABEL_MICROPHONE_WASAPI_SH_BUFFER_LENGTH,
PARSE_ONLY_UINT, false) == 0)
count++;
#endif
break;
#endif
case DISPLAYLIST_AUDIO_SYNCHRONIZATION_SETTINGS_LIST:

View File

@ -51,6 +51,10 @@
#include <vfs/vfs_implementation_cdrom.h>
#endif
#ifdef HAVE_WASAPI
#include "../audio/common/wasapi.h"
#endif
#include "../config.def.h"
#include "../config.def.keybinds.h"
@ -4941,29 +4945,52 @@ static void setting_get_string_representation_uint_custom_viewport_height(rarch_
}
#ifdef HAVE_WASAPI
static void setting_get_string_representation_int_audio_wasapi_sh_buffer_length(rarch_setting_t *setting,
static void setting_get_string_representation_uint_audio_wasapi_sh_buffer_length(rarch_setting_t *setting,
char *s, size_t len)
{
if (!setting)
settings_t *settings = config_get_ptr();
if (!setting || !settings)
return;
if (*setting->value.target.integer > 0)
snprintf(s, len, "%d", *setting->value.target.integer);
else if (*setting->value.target.integer == 0)
strlcpy(s, "0 (Off)", len);
else
strlcpy(s, "Auto", len);
switch (*setting->value.target.integer)
{
case WASAPI_SH_BUFFER_AUDIO_LATENCY:
snprintf(s, len, "%u (%s)",
*setting->value.target.integer,
"Audio Latency");
break;
case WASAPI_SH_BUFFER_DEVICE_PERIOD:
snprintf(s, len, "%u (%s)",
*setting->value.target.integer,
"Device Period");
break;
case WASAPI_SH_BUFFER_CLIENT_BUFFER:
snprintf(s, len, "%u (%s)",
*setting->value.target.integer,
"Client Buffer");
break;
default:
snprintf(s, len, "%u (%.1f ms)",
*setting->value.target.integer,
(float)*setting->value.target.integer * 1000 / settings->uints.audio_output_sample_rate);
break;
}
}
#ifdef HAVE_MICROPHONE
static void setting_get_string_representation_uint_microphone_wasapi_sh_buffer_length(rarch_setting_t *setting,
char *s, size_t len)
{
if (!setting)
settings_t *settings = config_get_ptr();
if (!setting || !settings)
return;
if (*setting->value.target.unsigned_integer > 0)
snprintf(s, len, "%u", *setting->value.target.unsigned_integer);
if (*setting->value.target.integer > 0)
snprintf(s, len, "%u (%.1f ms)",
*setting->value.target.integer,
(float)*setting->value.target.integer * 1000 / settings->uints.audio_output_sample_rate);
else
strlcpy(s, "Auto", len);
}
@ -8315,9 +8342,11 @@ static void general_write_handler(rarch_setting_t *setting)
break;
case MENU_ENUM_LABEL_AUDIO_LATENCY:
case MENU_ENUM_LABEL_AUDIO_OUTPUT_RATE:
#ifdef HAVE_WASAPI
case MENU_ENUM_LABEL_AUDIO_WASAPI_EXCLUSIVE_MODE:
case MENU_ENUM_LABEL_AUDIO_WASAPI_FLOAT_FORMAT:
case MENU_ENUM_LABEL_AUDIO_WASAPI_SH_BUFFER_LENGTH:
#endif
rarch_cmd = CMD_EVENT_AUDIO_REINIT;
break;
#ifdef HAVE_MICROPHONE
@ -14107,9 +14136,9 @@ static bool setting_append_list(
SD_FLAG_NONE
);
CONFIG_INT(
CONFIG_UINT(
list, list_info,
&settings->ints.audio_wasapi_sh_buffer_length,
&settings->uints.audio_wasapi_sh_buffer_length,
MENU_ENUM_LABEL_AUDIO_WASAPI_SH_BUFFER_LENGTH,
MENU_ENUM_LABEL_VALUE_AUDIO_WASAPI_SH_BUFFER_LENGTH,
DEFAULT_WASAPI_SH_BUFFER_LENGTH,
@ -14118,11 +14147,12 @@ static bool setting_append_list(
parent_group,
general_write_handler,
general_read_handler);
menu_settings_list_current_add_range(list, list_info, -16.0f, 0.0f, 16.0f, true, false);
SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ADVANCED);
(*list)[list_info->index - 1].action_ok = &setting_action_ok_uint_special;
(*list)[list_info->index - 1].get_string_representation =
&setting_get_string_representation_int_audio_wasapi_sh_buffer_length;
}
&setting_get_string_representation_uint_audio_wasapi_sh_buffer_length;
menu_settings_list_current_add_range(list, list_info, 0, 32.0f * 200, 32.0f, true, true);
SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ADVANCED);
}
#endif
END_SUB_GROUP(list, list_info, parent_group);
@ -14302,11 +14332,12 @@ static bool setting_append_list(
parent_group,
general_write_handler,
general_read_handler);
menu_settings_list_current_add_range(list, list_info, 0.0f, 0.0f, 16.0f, true, false);
SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ADVANCED);
(*list)[list_info->index - 1].action_ok = &setting_action_ok_uint_special;
(*list)[list_info->index - 1].get_string_representation =
&setting_get_string_representation_uint_microphone_wasapi_sh_buffer_length;
}
menu_settings_list_current_add_range(list, list_info, 0, 32.0f * 200, 32.0f, true, true);
SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ADVANCED);
}
#endif
END_SUB_GROUP(list, list_info, parent_group);

View File

@ -6005,8 +6005,7 @@ static enum runloop_state_enum runloop_check_state(
menu->state = 0;
}
if (settings->bools.audio_enable_menu
&& !libretro_running)
if (!libretro_running)
audio_driver_menu_sample();
}