diff --git a/audio/drivers/wasapi.c b/audio/drivers/wasapi.c index f67544c0b3..4b1e070d3e 100644 --- a/audio/drivers/wasapi.c +++ b/audio/drivers/wasapi.c @@ -84,10 +84,11 @@ typedef struct IAudioClient *client; IAudioRenderClient *renderer; HANDLE write_event; - void *buffer; /* NULL in shared mode */ - size_t buffer_size; /* in shared mode holds WASAPI engine buffer size */ - size_t buffer_usage; /* valid in exclusive mode only */ + void *buffer; /* NULL in unbuffered shared mode */ + size_t buffer_size; /* in unbuffered shared mode holds WASAPI engine buffer size */ + size_t buffer_usage; /* 0 in unbuffered shared mode */ size_t frame_size; /* 4 or 8 only */ + bool exclusive; bool blocking; bool running; } wasapi_t; @@ -260,7 +261,7 @@ static void wasapi_set_format(WAVEFORMATEXTENSIBLE *wf, } static IAudioClient *wasapi_init_client_sh(IMMDevice *device, - bool *float_fmt, unsigned *rate, double *latency) + bool *float_fmt, unsigned *rate, double *latency, bool buffered) { HRESULT hr; WAVEFORMATEXTENSIBLE wf; @@ -270,6 +271,8 @@ static IAudioClient *wasapi_init_client_sh(IMMDevice *device, unsigned rate_res = *rate; REFERENCE_TIME default_period = 0; REFERENCE_TIME stream_latency = 0; + UINT32 buffer_length = 0; + double buffer_latency = 0.0; hr = device->lpVtbl->Activate(device, &IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&client); @@ -311,7 +314,7 @@ static IAudioClient *wasapi_init_client_sh(IMMDevice *device, wasapi_warn("Unsupported format"); rate_res = wasapi_pref_rate(j); - if (rate_res == *rate) /* requested allready tested */ + if (rate_res == *rate) /* requested rate is allready tested */ rate_res = wasapi_pref_rate(++j); /* skip it */ } } @@ -322,14 +325,23 @@ static IAudioClient *wasapi_init_client_sh(IMMDevice *device, *rate = rate_res; *latency = 0.0; - /* next two calls are allowed to fail (we losing latency info only) */ + /* next three calls are allowed to fail (we losing latency info only) */ hr = client->lpVtbl->GetStreamLatency(client, &stream_latency); WASAPI_HR_WARN(hr, "IAudioClient::GetStreamLatency", return client); hr = client->lpVtbl->GetDevicePeriod(client, &default_period, NULL); WASAPI_HR_WARN(hr, "IAudioClient::GetDevicePeriod", return client); - *latency = (double)(stream_latency + default_period) / 10000.0; + if (buffered) + { + hr = client->lpVtbl->GetBufferSize(client, &buffer_length); + WASAPI_HR_CHECK(hr, "IAudioClient::GetBufferSize", return client); + + /* buffer size is half of WASAPI internal buffer size */ + buffer_latency = 1000.0 / rate_res * buffer_length / 2.0; + } + + *latency = (double)(stream_latency + default_period) / 10000.0 + buffer_latency; return client; @@ -424,7 +436,7 @@ static IAudioClient *wasapi_init_client_ex(IMMDevice *device, wasapi_warn("Unsupported format"); rate_res = wasapi_pref_rate(j); - if (rate_res == *rate) /* requested allready tested */ + if (rate_res == *rate) /* requested rate is allready tested */ rate_res = wasapi_pref_rate(++j); /* skip it */ } } @@ -439,6 +451,7 @@ static IAudioClient *wasapi_init_client_ex(IMMDevice *device, hr = client->lpVtbl->GetStreamLatency(client, &stream_latency); WASAPI_HR_WARN(hr, "IAudioClient::GetStreamLatency", return client); + /* our buffer latency is half of WASAPI internal two-buffer latency */ *latency = (double)stream_latency / 10000.0 * 1.5; return client; @@ -450,7 +463,7 @@ error: } static IAudioClient *wasapi_init_client(IMMDevice *device, bool *exclusive, - bool *float_fmt, unsigned *rate, unsigned latency) + bool *float_fmt, unsigned *rate, unsigned latency, bool buffered) { HRESULT hr; double latency_res = latency; @@ -461,14 +474,16 @@ static IAudioClient *wasapi_init_client(IMMDevice *device, bool *exclusive, client = wasapi_init_client_ex(device, float_fmt, rate, &latency_res); if (!client) { - client = wasapi_init_client_sh(device, float_fmt, rate, &latency_res); + client = wasapi_init_client_sh(device, + float_fmt, rate, &latency_res, buffered); if (client) *exclusive = false; } } else { - client = wasapi_init_client_sh(device, float_fmt, rate, &latency_res); + client = wasapi_init_client_sh(device, + float_fmt, rate, &latency_res, buffered); if (!client) { client = wasapi_init_client_ex(device, float_fmt, rate, &latency_res); @@ -482,7 +497,7 @@ 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); + *rate, latency_res + 0.05); return client; } @@ -491,13 +506,14 @@ static void *wasapi_init(const char *dev_id, unsigned rate, unsigned latency, unsigned u, unsigned *new_rate) { HRESULT hr; - bool com_initialized = false; - UINT32 frame_count = 0; - BYTE *dest = NULL; - settings_t *settings = config_get_ptr(); - bool exclusive = settings->audio.wasapi.exclusive_mode; - bool float_format = settings->audio.wasapi.float_format; - wasapi_t *w = (wasapi_t*)calloc(1, sizeof(wasapi_t)); + bool com_initialized = false; + UINT32 frame_count = 0; + BYTE *dest = NULL; + settings_t *settings = config_get_ptr(); + bool float_format = settings->audio.wasapi.float_format; + bool buffered = settings->audio.wasapi.shared_mode_buffering; + wasapi_t *w = (wasapi_t*)calloc(1, sizeof(wasapi_t)); + w->exclusive = settings->audio.wasapi.exclusive_mode; WASAPI_CHECK(w, "Out of memory", return NULL); @@ -514,7 +530,7 @@ static void *wasapi_init(const char *dev_id, unsigned rate, unsigned latency, *new_rate = rate; w->client = wasapi_init_client(w->device, - &exclusive, &float_format, new_rate, latency); + &w->exclusive, &float_format, new_rate, latency, buffered); if (!w->client) goto error; @@ -523,8 +539,15 @@ static void *wasapi_init(const char *dev_id, unsigned rate, unsigned latency, w->frame_size = float_format ? 8 : 4; w->buffer_size = frame_count * w->frame_size; - if (exclusive) + if (w->exclusive || buffered) { + /* Buffer size in shared buffered mode + * is half of WASAPI internal buffer size. + * Size changes here must be followed by changes in functions + * wasapi_init_client_sh (latency calculation), and + * wasapi_write_sh (WASAPI engine buffer free size comparison). */ + if (!w->exclusive) + w->buffer_size /= 2; w->buffer = malloc(w->buffer_size); WASAPI_CHECK(w->buffer, "Out of memory", goto error); } @@ -584,26 +607,61 @@ static ssize_t wasapi_write_sh(wasapi_t *w, const void * data, size_t size) size_t buffer_avail; HRESULT hr; bool br; - ssize_t result; + ssize_t result = -1; UINT32 padding = 0; - if (w->blocking) + if (w->buffer) { - ir = WaitForSingleObject(w->write_event, INFINITE); - WASAPI_SR_CHECK(ir == WAIT_OBJECT_0, "WaitForSingleObject", return -1); + if (w->buffer_usage == w->buffer_size) + { + if (w->blocking) + { + ir = WaitForSingleObject(w->write_event, INFINITE); + WASAPI_SR_CHECK(ir == WAIT_OBJECT_0, "WaitForSingleObject", return -1); + } + + hr = w->client->lpVtbl->GetCurrentPadding(w->client, &padding); + WASAPI_HR_CHECK(hr, "IAudioClient::GetCurrentPadding", return -1); + + /* this is ok for current buffer size + * (half of WASAPI internal buffer size)*/ + if (padding * w->frame_size > w->buffer_size) + return 0; + + br = wasapi_flush(w, w->buffer, w->buffer_size); + if (!br) + return -1; + + w->buffer_usage = 0; + } + + buffer_avail = w->buffer_size - w->buffer_usage; + result = size < buffer_avail ? size : buffer_avail; + memcpy(w->buffer + w->buffer_usage, data, result); + w->buffer_usage += result; + } + else + { + if (w->blocking) + { + ir = WaitForSingleObject(w->write_event, INFINITE); + WASAPI_SR_CHECK(ir == WAIT_OBJECT_0, "WaitForSingleObject", return -1); + } + + hr = w->client->lpVtbl->GetCurrentPadding(w->client, &padding); + WASAPI_HR_CHECK(hr, "IAudioClient::GetCurrentPadding", return -1); + + buffer_avail = w->buffer_size - padding * w->frame_size; + if (!buffer_avail) + return 0; + + result = size > buffer_avail ? buffer_avail : size; + br = wasapi_flush(w, data, result); + if (!br) + result = -1; } - hr = w->client->lpVtbl->GetCurrentPadding(w->client, &padding); - WASAPI_HR_CHECK(hr, "IAudioClient::GetCurrentPadding", return -1); - - buffer_avail = w->buffer_size - padding * w->frame_size; - if (!buffer_avail) - return 0; - - result = size > buffer_avail ? buffer_avail : size; - br = wasapi_flush(w, data, result); - - return br ? result : -1; + return result; } static ssize_t wasapi_write_ex(wasapi_t *w, const void * data, size_t size) @@ -649,7 +707,7 @@ static ssize_t wasapi_write(void *wh, const void *data, size_t size, bool u) { for (writen = 0, ir = -1; writen < size && ir; writen += ir) { - if (w->buffer) + if (w->exclusive) ir = wasapi_write_ex(w, data + writen, size - writen); else ir = wasapi_write_sh(w, data + writen, size - writen); @@ -660,7 +718,7 @@ static ssize_t wasapi_write(void *wh, const void *data, size_t size, bool u) } } } - else if (w->buffer) + else if (w->exclusive) writen = wasapi_write_ex(w, data, size); else writen = wasapi_write_sh(w, data, size); @@ -672,7 +730,6 @@ static bool wasapi_stop(void *wh) { wasapi_t *w = (wasapi_t*)wh; HRESULT hr = w->client->lpVtbl->Stop(w->client); - WASAPI_HR_CHECK(hr, "IAudioClient::Stop", return !w->running); w->running = false; @@ -684,6 +741,8 @@ static bool wasapi_start(void *wh, bool u) { wasapi_t *w = (wasapi_t*)wh; HRESULT hr = w->client->lpVtbl->Start(w->client); + WASAPI_WARN(hr != AUDCLNT_E_NOT_STOPPED, "Already started", + return w->running); WASAPI_HR_CHECK(hr, "IAudioClient::Start", return w->running); diff --git a/configuration.c b/configuration.c index c6a9190d74..34e4499ca0 100644 --- a/configuration.c +++ b/configuration.c @@ -868,8 +868,9 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings, SETTING_BOOL("input_autodetect_enable", &settings->input.autodetect_enable, true, input_autodetect_enable, false); SETTING_BOOL("audio_rate_control", &settings->audio.rate_control, true, rate_control, false); #ifdef HAVE_WASAPI - SETTING_BOOL("audio_wasapi_exclusive_mode", &settings->audio.wasapi.exclusive_mode, true, true, false); - SETTING_BOOL("audio_wasapi_float_format", &settings->audio.wasapi.float_format, true, true, false); + SETTING_BOOL("audio_wasapi_exclusive_mode", &settings->audio.wasapi.exclusive_mode, true, true, false); + SETTING_BOOL("audio_wasapi_float_format", &settings->audio.wasapi.float_format, true, false, false); + SETTING_BOOL("audio_wasapi_shared_mode_buffering", &settings->audio.wasapi.shared_mode_buffering, true, true, false); #endif if (global) diff --git a/configuration.h b/configuration.h index 1e49018ac2..5c8188790e 100644 --- a/configuration.h +++ b/configuration.h @@ -262,6 +262,7 @@ typedef struct settings { bool exclusive_mode; bool float_format; + bool shared_mode_buffering; /* ignored in exclusive mode */ } wasapi; #endif