From 129151d5fc97449261dfb5939e43634d884494a3 Mon Sep 17 00:00:00 2001 From: Zoran Vuckovic Date: Sun, 9 Apr 2017 23:53:55 +0200 Subject: [PATCH] add wasapi audio driver --- Makefile.common | 6 + Makefile.win | 1 + audio/audio_driver.c | 3 + audio/audio_driver.h | 1 + audio/drivers/wasapi.c | 702 ++++++++++++++++++++++++++++++++++++++++ config.def.h | 3 + config.features.h | 6 + configuration.c | 2 + intl/msg_hash_chs.h | 2 + intl/msg_hash_de.h | 2 + intl/msg_hash_eo.h | 2 + intl/msg_hash_es.c | 2 + intl/msg_hash_fr.h | 2 + intl/msg_hash_it.h | 2 + intl/msg_hash_ja.h | 2 + intl/msg_hash_ko.h | 2 + intl/msg_hash_nl.h | 2 + intl/msg_hash_pl.c | 2 + intl/msg_hash_pt_br.c | 2 + intl/msg_hash_pt_br.h | 2 + intl/msg_hash_ru.h | 2 + intl/msg_hash_us.h | 2 + intl/msg_hash_vn.c | 2 + menu/menu_displaylist.c | 9 + msg_hash.h | 1 + qb/config.libs.sh | 1 + retroarch.c | 1 + 27 files changed, 766 insertions(+) create mode 100644 audio/drivers/wasapi.c diff --git a/Makefile.common b/Makefile.common index 8ffa44c9c1..3658ffe9c9 100644 --- a/Makefile.common +++ b/Makefile.common @@ -433,6 +433,12 @@ ifeq ($(HAVE_DSOUND), 1) LIBS += -ldxguid -ldsound endif +ifeq ($(HAVE_WASAPI), 1) + OBJ += audio/drivers/wasapi.o + DEFINES += -DHAVE_WASAPI + LIBS += -lole32 -lksuser +endif + ifeq ($(HAVE_XAUDIO), 1) OBJ += audio/drivers/xaudio.o DEFINES += -DHAVE_XAUDIO diff --git a/Makefile.win b/Makefile.win index a198553aa7..f3a1aaf466 100644 --- a/Makefile.win +++ b/Makefile.win @@ -3,6 +3,7 @@ TARGET = retroarch.exe HAVE_DINPUT = 1 HAVE_XAUDIO = 1 HAVE_DSOUND = 1 +HAVE_WASAPI = 1 HAVE_OPENGL = 1 HAVE_FBO = 1 HAVE_DYLIB = 1 diff --git a/audio/audio_driver.c b/audio/audio_driver.c index be714d9062..5c27676b6a 100644 --- a/audio/audio_driver.c +++ b/audio/audio_driver.c @@ -84,6 +84,9 @@ static const audio_driver_t *audio_drivers[] = { #ifdef HAVE_DSOUND &audio_dsound, #endif +#ifdef HAVE_WASAPI + &audio_wasapi, +#endif #ifdef HAVE_PULSE &audio_pulse, #endif diff --git a/audio/audio_driver.h b/audio/audio_driver.h index d78c5d2869..911d4edacd 100644 --- a/audio/audio_driver.h +++ b/audio/audio_driver.h @@ -230,6 +230,7 @@ extern audio_driver_t audio_sdl; extern audio_driver_t audio_xa; extern audio_driver_t audio_pulse; extern audio_driver_t audio_dsound; +extern audio_driver_t audio_wasapi; extern audio_driver_t audio_coreaudio; extern audio_driver_t audio_xenon360; extern audio_driver_t audio_ps3; diff --git a/audio/drivers/wasapi.c b/audio/drivers/wasapi.c new file mode 100644 index 0000000000..f99b179a65 --- /dev/null +++ b/audio/drivers/wasapi.c @@ -0,0 +1,702 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2014 - Hans-Kristian Arntzen + * Copyright (C) 2011-2017 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include + +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif +#define _WIN32_WINNT 0x0600 +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../audio_driver.h" +#include "../../verbosity.h" +#include "../libretro-common/include/lists/string_list.h" + +#define WASAPI_CHECK(bool_exp, err_str, err_exp)\ + if (!(bool_exp)) {\ + wasapi_err(err_str);\ + err_exp; } + +#define WASAPI_HR_CHECK(hr, fun_str, err_exp)\ + if (FAILED(hr)) {\ + wasapi_com_err(fun_str, hr);\ + err_exp; } + +#define WASAPI_SR_CHECK(bool_exp, fun_str, err_exp)\ + if (!(bool_exp)) {\ + wasapi_sys_err(fun_str);\ + err_exp; } + +#define WASAPI_RELEASE(iface)\ + if(iface) {\ + iface->lpVtbl->Release(iface);\ + iface = NULL;\ + } + +#define WASAPI_FREE(ptr)\ + if(ptr) {\ + free(ptr);\ + ptr = NULL;\ + } + +typedef struct +{ + IMMDevice *device; // always valid + IAudioClient *client; // may be NULL + IAudioRenderClient *renderer; // may be NULL + 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 + size_t frame_size; // 4 or 8 only + unsigned latency; // in ms (requested, not real) + unsigned frame_rate; + bool running; +} wasapi_t; + +static void wasapi_err(const char *err) +{ + RARCH_ERR("[WASAPI]: %s.\n", err); +} + +static void wasapi_com_err(const char *fun, HRESULT hr) +{ + RARCH_ERR("[WASAPI]: %s failed with error 0x%.8X.\n", fun, hr); +} + +static void wasapi_sys_err(const char *fun) +{ + RARCH_ERR("[WASAPI]: %s failed with error %d.\n", fun, GetLastError()); +} + +static bool wasapi_check_device_id(IMMDevice *device, const char *id) +{ + int id_length; + LPWSTR dev_id = NULL, dev_cmp_id = NULL; + HRESULT hr; + bool result; + + id_length = MultiByteToWideChar(CP_ACP, 0, id, -1, NULL, 0); + WASAPI_SR_CHECK(id_length > 0, "MultiByteToWideChar", goto error); + + dev_cmp_id = (LPWSTR)malloc(id_length * sizeof(WCHAR)); + WASAPI_CHECK(dev_cmp_id, "Out of memory", goto error); + + id_length = MultiByteToWideChar(CP_ACP, 0, id, -1, dev_cmp_id, id_length); + WASAPI_SR_CHECK(id_length > 0, "MultiByteToWideChar", goto error); + + hr = device->lpVtbl->GetId(device, &dev_id); + WASAPI_HR_CHECK(hr, "IMMDevice::GetId", goto error); + + result = lstrcmpW(dev_cmp_id, dev_id) == 0 ? true : false; + + CoTaskMemFree(dev_id); + free(dev_cmp_id); + + return result; + +error: + if (dev_id) + CoTaskMemFree(dev_id); + if (dev_cmp_id) + free(dev_cmp_id); + + return false; +} + +static IMMDevice *wasapi_init_device(const char *id) +{ + HRESULT hr; + IMMDeviceEnumerator * enumerator = NULL; + IMMDevice *device = NULL; + IMMDeviceCollection * collection = NULL; + UINT32 dev_count, i; + + if (id) + RARCH_LOG("[WASAPI]: Initializing device %s ...\n", id); + else + RARCH_LOG("[WASAPI]: Initializing default device ...\n"); + + hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, + &IID_IMMDeviceEnumerator, (void **)&enumerator); + WASAPI_HR_CHECK(hr, "CoCreateInstance", goto error); + + if (id) + { + hr = enumerator->lpVtbl->EnumAudioEndpoints(enumerator, + eRender, DEVICE_STATE_ACTIVE, &collection); + WASAPI_HR_CHECK(hr, "IMMDeviceEnumerator::EnumAudioEndpoints", + goto error); + + hr = collection->lpVtbl->GetCount(collection, &dev_count); + WASAPI_HR_CHECK(hr, "IMMDeviceCollection::GetCount", goto error); + + for (i = 0; i < dev_count; ++i) + { + hr = collection->lpVtbl->Item(collection, i, &device); + WASAPI_HR_CHECK(hr, "IMMDeviceCollection::Item", continue); + + if (wasapi_check_device_id(device, id)) + break; + + device->lpVtbl->Release(device); + device = NULL; + } + } + else + { + hr = enumerator->lpVtbl->GetDefaultAudioEndpoint(enumerator, + eRender, eConsole, &device); + WASAPI_HR_CHECK(hr, "IMMDeviceEnumerator::GetDefaultAudioEndpoint", + goto error); + } + + if (!device) + goto error; + + if (collection) + collection->lpVtbl->Release(collection); + enumerator->lpVtbl->Release(enumerator); + + RARCH_LOG("[WASAPI]: Device initialized.\n"); + + return device; + +error: + if (collection) + collection->lpVtbl->Release(collection); + if (enumerator) + enumerator->lpVtbl->Release(enumerator); + + RARCH_ERR("[WASAPI]: Failed to initialize device.\n"); + + return NULL; +} + +static IAudioClient *wasapi_init_client(IMMDevice *device, bool exclusive, + bool format_float, unsigned rate, unsigned latency) +{ + RARCH_LOG("[WASAPI]: Initializing client " + "(%s, %s, %uHz, %ums) ...\n", + exclusive ? "exclusive" : "shared", + format_float ? "float" : "pcm", + rate, latency); + + IAudioClient *client = NULL; + HRESULT hr; + REFERENCE_TIME default_period, minimum_period; + REFERENCE_TIME buffer_duration, stream_latency = 0; + AUDCLNT_SHAREMODE share_mode = exclusive ? + AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED; + DWORD stream_flags = AUDCLNT_STREAMFLAGS_NOPERSIST | + AUDCLNT_STREAMFLAGS_EVENTCALLBACK; + UINT32 buffer_length = 0; + + hr = device->lpVtbl->Activate(device, &IID_IAudioClient, + CLSCTX_ALL, NULL, (void**)&client); + WASAPI_HR_CHECK(hr, "IMMDevice::Activate", goto error); + + hr = client->lpVtbl->GetDevicePeriod(client, &default_period, &minimum_period); + WASAPI_HR_CHECK(hr, "IAudioClient::GetDevicePeriod", goto error); + + if (exclusive) + { + if (latency < minimum_period / 10000) + buffer_duration = minimum_period; + else + buffer_duration = latency * 10000; + } + else + buffer_duration = 0; + + WAVEFORMATEXTENSIBLE wf; + wf.Format.nChannels = 2; + wf.Format.nSamplesPerSec = rate; + if (format_float) + { + wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + wf.Format.nAvgBytesPerSec = rate * 8; + wf.Format.nBlockAlign = 8; + wf.Format.wBitsPerSample = 32; + wf.Format.cbSize = sizeof(WORD) + sizeof(DWORD) + sizeof(GUID); + wf.Samples.wValidBitsPerSample = 32; + wf.dwChannelMask = KSAUDIO_SPEAKER_STEREO; + wf.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + } + else + { + wf.Format.wFormatTag = WAVE_FORMAT_PCM; + wf.Format.nAvgBytesPerSec = rate * 4; + wf.Format.nBlockAlign = 4; + wf.Format.wBitsPerSample = 16; + wf.Format.cbSize = 0; + } + + hr = client->lpVtbl->Initialize(client, share_mode, stream_flags, + buffer_duration, buffer_duration, (WAVEFORMATEX*)&wf, NULL); + if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) + { + hr = client->lpVtbl->GetBufferSize(client, &buffer_length); + WASAPI_HR_CHECK(hr, "IAudioClient::GetBufferSize", goto error); + + client->lpVtbl->Release(client); + hr = device->lpVtbl->Activate(device, &IID_IAudioClient, + CLSCTX_ALL, NULL, (void**)&client); + WASAPI_HR_CHECK(hr, "IMMDevice::Activate", goto error); + + buffer_duration = 10000.0 * 1000.0 / rate * buffer_length + 0.5; + hr = client->lpVtbl->Initialize(client, share_mode, stream_flags, + buffer_duration, buffer_duration, (WAVEFORMATEX*)&wf, NULL); + } + WASAPI_HR_CHECK(hr, "IAudioClient::Initialize", goto error); + + hr = client->lpVtbl->GetStreamLatency(client, &stream_latency); + if(hr != S_OK) + wasapi_com_err("IAudioClient::GetStreamLatency", hr); + else if (!exclusive) + stream_latency += default_period; + + RARCH_LOG("[WASAPI]: Client initialized (latency %ums).\n", + (unsigned)((double)stream_latency / 10000.0 + 0.5)); + + return client; + +error: + if (client) + client->lpVtbl->Release(client); + + RARCH_ERR("[WASAPI]: Failed to initialize client.\n"); + + return NULL; +} + +static void *wasapi_init(const char *dev_id, unsigned rate, unsigned latency, + unsigned u1, unsigned *u2) +{ + wasapi_t *w; + HRESULT hr; + bool com_initialized = false; + UINT32 frame_count = 0; + BYTE *dest; + bool exclusive; + + w = (wasapi_t*)calloc(1, sizeof(wasapi_t)); + WASAPI_CHECK(w, "Out of memory", return NULL); + + hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + WASAPI_HR_CHECK(hr, "CoInitializeEx", goto error); + + com_initialized = true; + + w->device = wasapi_init_device(dev_id); + if (!w->device) + goto error; + + if ((w->client = wasapi_init_client(w->device, + true, true, rate, latency))) + { + exclusive = true; + w->frame_size = 8; + } + else if ((w->client = wasapi_init_client(w->device, + true, false, rate, latency))) + { + exclusive = true; + w->frame_size = 4; + } + else if ((w->client = wasapi_init_client(w->device, + false, true, rate, latency))) + { + exclusive = false; + w->frame_size = 8; + } + else if ((w->client = wasapi_init_client(w->device, + false, false, rate, latency))) + { + exclusive = false; + w->frame_size = 4; + } + else + goto error; + + w->frame_rate = rate; + w->latency = latency; + + hr = w->client->lpVtbl->GetBufferSize(w->client, &frame_count); + WASAPI_HR_CHECK(hr, "IAudioClient::GetBufferSize", goto error); + + w->buffer_size = frame_count * w->frame_size; + if (exclusive) + { + w->buffer = malloc(w->buffer_size); + WASAPI_CHECK(w, "Out of memory", goto error); + } + + hr = w->client->lpVtbl->GetService(w->client, + &IID_IAudioRenderClient, (void**)&w->renderer); + WASAPI_HR_CHECK(hr, "IAudioClient::GetService", goto error); + + hr = w->renderer->lpVtbl->GetBuffer(w->renderer, frame_count, &dest); + if (hr != S_OK) + wasapi_com_err("IAudioRenderClient::GetBuffer", hr); + else + { + hr = w->renderer->lpVtbl->ReleaseBuffer(w->renderer, frame_count, + AUDCLNT_BUFFERFLAGS_SILENT); + WASAPI_HR_CHECK(hr, "IAudioRenderClient::ReleaseBuffer", goto error); + } + + w->write_event = CreateEventA(NULL, FALSE, FALSE, NULL); + WASAPI_SR_CHECK(w->write_event, "CreateEventA", goto error); + + hr = w->client->lpVtbl->SetEventHandle(w->client, w->write_event); + WASAPI_HR_CHECK(hr, "IAudioClient::SetEventHandle", goto error); + + return w; + +error: + WASAPI_RELEASE(w->renderer); + WASAPI_RELEASE(w->client); + WASAPI_RELEASE(w->device); + if (com_initialized) + CoUninitialize(); + if (w->write_event) + CloseHandle(w->write_event); + WASAPI_FREE(w->buffer); + free(w); + + return NULL; +} + +static bool wasapi_flush(wasapi_t * w, const void * data, size_t size) +{ + HRESULT hr; + BYTE * dest = NULL; + UINT32 frame_count = size / w->frame_size; + + hr = w->renderer->lpVtbl->GetBuffer(w->renderer, frame_count, &dest); + WASAPI_HR_CHECK(hr, "IAudioRenderClient::GetBuffer", return false); + + memcpy(dest, data, size); + + hr = w->renderer->lpVtbl->ReleaseBuffer(w->renderer, frame_count, 0); + WASAPI_HR_CHECK(hr, "IAudioRenderClient::ReleaseBuffer", return false); + + return true; +} + +static ssize_t wasapi_process(wasapi_t *w, const void * data, size_t size) +{ + DWORD ir; + ssize_t result = 0; + bool br; + size_t buffer_avail; + HRESULT hr; + UINT32 padding = 0; + + if (w->buffer) // exclusive mode + { + if (w->buffer_usage == w->buffer_size) + { + ir = WaitForSingleObject(w->write_event, INFINITE); + WASAPI_SR_CHECK(ir == WAIT_OBJECT_0, + "WaitForSingleObject", return -1); + + 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 // shared mode + { + 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) + { + result = size < buffer_avail ? size : buffer_avail; + br = wasapi_flush(w, data, result); + if (!br) + return -1; + } + } + + return result; +} + +static ssize_t wasapi_write(void *wh, const void *data, size_t size, bool u) +{ + wasapi_t *w = (wasapi_t*)wh; + size_t writen; + ssize_t r; + + for (writen = 0, r = -1; writen < size && r; writen += r) + { + r = wasapi_process(w, data + writen, size - writen); + if (r == -1) + return -1; + } + + return writen; +} + +static bool wasapi_stop(void *wh) +{ + wasapi_t *w = (wasapi_t*)wh; + HRESULT hr; + + hr = w->client->lpVtbl->Stop(w->client); + WASAPI_HR_CHECK(hr, "IAudioClient::Stop", return !w->running); + + w->running = false; + + return true; +} + +static bool wasapi_start(void *wh, bool u) +{ + wasapi_t *w = (wasapi_t*)wh; + HRESULT hr; + + hr = w->client->lpVtbl->Start(w->client); + WASAPI_HR_CHECK(hr, "IAudioClient::Start", return w->running); + + w->running = true; + + return true; +} + +static bool wasapi_alive(void *wh) +{ + wasapi_t *w = (wasapi_t*)wh; + + return w->running; +} + +static void wasapi_set_nonblock_state(void *u, bool nonblock) +{ + if (nonblock) + RARCH_ERR("[WASAPI]: Nonblocking mode not supported!!!\n"); +} + +static void wasapi_free(void *wh) +{ + wasapi_t *w = (wasapi_t*)wh; + + w->renderer->lpVtbl->Release(w->renderer); + w->client->lpVtbl->Stop(w->client); + w->client->lpVtbl->Release(w->client); + w->device->lpVtbl->Release(w->device); + CoUninitialize(); + CloseHandle(w->write_event); + free(w->buffer); + free(w); +} + +static bool wasapi_use_float(void *wh) +{ + wasapi_t *w = (wasapi_t*)wh; + + return w->frame_size == 8;; +} + +static void *wasapi_device_list_new(void *u) +{ + struct string_list *sl = NULL; + union string_list_elem_attr attr; + HRESULT hr; + IMMDeviceEnumerator *enumerator = NULL; + IMMDeviceCollection *collection = NULL; + UINT dev_count = 0; + IMMDevice *device = NULL; + UINT i; + LPWSTR dev_id_wstr = NULL; + LPWSTR dev_name_wstr = NULL; + IPropertyStore *prop_store = NULL; + PROPVARIANT prop_var; + bool prop_var_init = false; + char *dev_id_str = NULL; + char *dev_name_str = NULL; + int ir; + bool br; + + RARCH_LOG("[WASAPI]: Enumerating active devices ...\n"); + + sl = string_list_new(); + WASAPI_CHECK(sl, "string_list_new failed", return NULL); + + attr.i = 0; + hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, + &IID_IMMDeviceEnumerator, (void **)&enumerator); + WASAPI_HR_CHECK(hr, "CoCreateInstance", goto error); + + hr = enumerator->lpVtbl->EnumAudioEndpoints(enumerator, + eRender, DEVICE_STATE_ACTIVE, &collection); + WASAPI_HR_CHECK(hr, "IMMDeviceEnumerator::EnumAudioEndpoints", goto error); + + hr = collection->lpVtbl->GetCount(collection, &dev_count); + WASAPI_HR_CHECK(hr, "IMMDeviceCollection::GetCount", goto error); + + for (i = 0; i < dev_count; ++i) + { + hr = collection->lpVtbl->Item(collection, i, &device); + WASAPI_HR_CHECK(hr, "IMMDeviceCollection::Item", goto error); + + hr = device->lpVtbl->GetId(device, &dev_id_wstr); + WASAPI_HR_CHECK(hr, "IMMDevice::GetId", goto error); + + ir = WideCharToMultiByte(CP_ACP, 0, dev_id_wstr, -1, + NULL, 0, NULL, NULL); + WASAPI_SR_CHECK(ir, "WideCharToMultiByte", goto error); + + dev_id_str = (char *)malloc(ir); + WASAPI_CHECK(dev_id_str, "Out of memory", goto error); + + ir = WideCharToMultiByte(CP_ACP, 0, dev_id_wstr, -1, + dev_id_str, ir, NULL, NULL); + WASAPI_SR_CHECK(ir, "WideCharToMultiByte", goto error); + + hr = device->lpVtbl->OpenPropertyStore(device, STGM_READ, &prop_store); + WASAPI_HR_CHECK(hr, "IMMDevice::OpenPropertyStore", goto error); + + PropVariantInit(&prop_var); + prop_var_init = true; + hr = prop_store->lpVtbl->GetValue(prop_store, + &PKEY_Device_FriendlyName, &prop_var); + WASAPI_HR_CHECK(hr, "IPropertyStore::GetValue", goto error); + + ir = WideCharToMultiByte(CP_ACP, 0, prop_var.pwszVal, -1, + NULL, 0, NULL, NULL); + WASAPI_SR_CHECK(ir, "WideCharToMultiByte", goto error); + + dev_name_str = (char *)malloc(ir); + WASAPI_CHECK(dev_name_str, "Out of memory", goto error); + + ir = WideCharToMultiByte(CP_ACP, 0, prop_var.pwszVal, -1, + dev_name_str, ir, NULL, NULL); + WASAPI_SR_CHECK(ir, "WideCharToMultiByte", goto error); + + RARCH_LOG("[WASAPI]: %s %s\n", dev_name_str, dev_id_str); + + br = string_list_append(sl, dev_id_str, attr); + WASAPI_CHECK(br, "string_list_append failed", goto error); + + PropVariantClear(&prop_var); + prop_var_init = false; + CoTaskMemFree(dev_id_wstr); + dev_id_wstr = NULL; + CoTaskMemFree(dev_name_wstr); + dev_name_wstr = NULL; + WASAPI_FREE(dev_id_str); + WASAPI_FREE(dev_name_str); + WASAPI_RELEASE(prop_store); + WASAPI_RELEASE(device); + } + + collection->lpVtbl->Release(collection); + enumerator->lpVtbl->Release(enumerator); + + RARCH_LOG("[WASAPI]: Devices enumerated.\n"); + + return sl; + +error: + WASAPI_FREE(dev_id_str); + WASAPI_FREE(dev_name_str); + if (prop_var_init) + PropVariantClear(&prop_var); + WASAPI_RELEASE(prop_store); + if (dev_id_wstr) + CoTaskMemFree(dev_id_wstr); + if (dev_name_wstr) + CoTaskMemFree(dev_name_wstr); + WASAPI_RELEASE(device); + WASAPI_RELEASE(collection); + WASAPI_RELEASE(enumerator); + if (sl) + string_list_free(sl); + + RARCH_ERR("[WASAPI]: Device enumeration failed.\n"); + + return NULL; +} + +static void wasapi_device_list_free(void *u, void *slp) +{ + struct string_list *sl = (struct string_list*)slp; + + string_list_free(sl); +} + +static size_t wasapi_write_avail(void *wh) +{ + wasapi_t *w = (wasapi_t*)wh; + UINT32 padding = 0; + HRESULT hr; + + if (w->buffer) + return w->buffer_size - w->buffer_usage; + + hr = w->client->lpVtbl->GetCurrentPadding(w->client, &padding); + WASAPI_HR_CHECK(hr, "IAudioClient::GetCurrentPadding", return 0); + + return w->buffer_size - padding * w->frame_size; +} + +static size_t wasapi_buffer_size(void *wh) +{ + wasapi_t *w = (wasapi_t*)wh; + + return w->buffer_size; +} + +audio_driver_t audio_wasapi = { + wasapi_init, + wasapi_write, + wasapi_stop, + wasapi_start, + wasapi_alive, + wasapi_set_nonblock_state, + wasapi_free, + wasapi_use_float, + "wasapi", + wasapi_device_list_new, + wasapi_device_list_free, + wasapi_write_avail, + wasapi_buffer_size +}; diff --git a/config.def.h b/config.def.h index f82305141b..4a34cff56f 100644 --- a/config.def.h +++ b/config.def.h @@ -73,6 +73,7 @@ enum audio_driver_enum AUDIO_PULSE, AUDIO_EXT, AUDIO_DSOUND, + AUDIO_WASAPI, AUDIO_COREAUDIO, AUDIO_PS3, AUDIO_XENON360, @@ -252,6 +253,8 @@ enum record_driver_enum #define AUDIO_DEFAULT_DRIVER AUDIO_XAUDIO #elif defined(HAVE_DSOUND) #define AUDIO_DEFAULT_DRIVER AUDIO_DSOUND +#elif defined(HAVE_WASAPI) +#define AUDIO_DEFAULT_DRIVER AUDIO_WASAPI #elif defined(HAVE_AL) #define AUDIO_DEFAULT_DRIVER AUDIO_AL #elif defined(HAVE_SL) diff --git a/config.features.h b/config.features.h index ed14ae8c94..a4327e01fe 100644 --- a/config.features.h +++ b/config.features.h @@ -194,6 +194,12 @@ static const bool _dsound_supp = true; static const bool _dsound_supp = false; #endif +#ifdef HAVE_WASAPI +static const bool _wasapi_supp = true; +#else +static const bool _wasapi_supp = false; +#endif + #ifdef HAVE_XAUDIO static const bool _xaudio_supp = true; #else diff --git a/configuration.c b/configuration.c index eb2f42d3f4..331d7b3275 100644 --- a/configuration.c +++ b/configuration.c @@ -184,6 +184,8 @@ const char *config_get_default_audio(void) return "sdl2"; case AUDIO_DSOUND: return "dsound"; + case AUDIO_WASAPI: + return "wasapi"; case AUDIO_XAUDIO: return "xaudio"; case AUDIO_PULSE: diff --git a/intl/msg_hash_chs.h b/intl/msg_hash_chs.h index e01fe7bd62..342eeadecc 100644 --- a/intl/msg_hash_chs.h +++ b/intl/msg_hash_chs.h @@ -1356,6 +1356,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DISPLAY_METRIC_MM_WIDTH, "显示器度量宽度(mm)") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DSOUND_SUPPORT, "DirectSound 支持") +MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_WASAPI_SUPPORT, + "WASAPI 支持") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYLIB_SUPPORT, "动态链接库支持") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYNAMIC_SUPPORT, diff --git a/intl/msg_hash_de.h b/intl/msg_hash_de.h index d4107e40c3..239c63e218 100644 --- a/intl/msg_hash_de.h +++ b/intl/msg_hash_de.h @@ -1353,6 +1353,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DISPLAY_METRIC_MM_WIDTH, "Bildschirmbreite (mm)") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DSOUND_SUPPORT, "DirectSound-Unterstützung") +MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_WASAPI_SUPPORT, + "WASAPI-Unterstützung") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYLIB_SUPPORT, "Unterstützung für dynamische Bibliotheken") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYNAMIC_SUPPORT, diff --git a/intl/msg_hash_eo.h b/intl/msg_hash_eo.h index 025fbc0070..671eb27dc7 100644 --- a/intl/msg_hash_eo.h +++ b/intl/msg_hash_eo.h @@ -1250,6 +1250,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DISPLAY_METRIC_MM_WIDTH, "Display metric width (mm)") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DSOUND_SUPPORT, "DirectSound support") +MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_WASAPI_SUPPORT, + "WASAPI support") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYLIB_SUPPORT, "Dynamic library support") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYNAMIC_SUPPORT, diff --git a/intl/msg_hash_es.c b/intl/msg_hash_es.c index 464ad14003..a1b341c5df 100644 --- a/intl/msg_hash_es.c +++ b/intl/msg_hash_es.c @@ -2066,6 +2066,8 @@ const char *msg_hash_to_str_es(enum msg_hash_enums msg) return "Mostrar ancho métrico (mm)"; case MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DSOUND_SUPPORT: return "Soporte de DirectSound"; + case MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_WASAPI_SUPPORT: + return "Soporte de WASAPI"; case MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYLIB_SUPPORT: return "Soporte de librerías dinámicas"; case MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYNAMIC_SUPPORT: diff --git a/intl/msg_hash_fr.h b/intl/msg_hash_fr.h index 9364ffa4c0..acec9e022d 100644 --- a/intl/msg_hash_fr.h +++ b/intl/msg_hash_fr.h @@ -1238,6 +1238,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DISPLAY_METRIC_MM_WIDTH, "Largeur d'écran (mm)") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DSOUND_SUPPORT, "Support de DirectSoundt") +MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_WASAPI_SUPPORT, + "Support de WASAPI") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYLIB_SUPPORT, "Support des bibliothèques dynamiques") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_EGL_SUPPORT, diff --git a/intl/msg_hash_it.h b/intl/msg_hash_it.h index 7dcafd8560..925b43e3a2 100644 --- a/intl/msg_hash_it.h +++ b/intl/msg_hash_it.h @@ -1230,6 +1230,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DISPLAY_METRIC_MM_WIDTH, "Mostra larghezza (mm)") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DSOUND_SUPPORT, "Supporto DirectSound") +MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_WASAPI_SUPPORT, + "Supporto WASAPI") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYLIB_SUPPORT, "Supporto libreria dinamica") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYNAMIC_SUPPORT, diff --git a/intl/msg_hash_ja.h b/intl/msg_hash_ja.h index 70dc30acde..657b19d66b 100644 --- a/intl/msg_hash_ja.h +++ b/intl/msg_hash_ja.h @@ -1369,6 +1369,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DISPLAY_METRIC_MM_WIDTH, "ディスプレイの軽量横幅 (mm)") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DSOUND_SUPPORT, "DirectSound対応") +MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_WASAPI_SUPPORT, + "WASAPI対応") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYLIB_SUPPORT, "ダイナミックライブラリー対応") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYNAMIC_SUPPORT, diff --git a/intl/msg_hash_ko.h b/intl/msg_hash_ko.h index 46893dc2fa..a240fa9993 100644 --- a/intl/msg_hash_ko.h +++ b/intl/msg_hash_ko.h @@ -1353,6 +1353,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DISPLAY_METRIC_MM_WIDTH, "화면 너비(mm)") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DSOUND_SUPPORT, "DirectSound 지원") +MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_WASAPI_SUPPORT, + "WASAPI 지원") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYLIB_SUPPORT, "다이나믹 라이브러리 지원") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYNAMIC_SUPPORT, diff --git a/intl/msg_hash_nl.h b/intl/msg_hash_nl.h index 02e8b8596f..b2790f0b05 100644 --- a/intl/msg_hash_nl.h +++ b/intl/msg_hash_nl.h @@ -1250,6 +1250,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DISPLAY_METRIC_MM_WIDTH, "Display metric breedte (mm)") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DSOUND_SUPPORT, "DirectSound ondersteuning") +MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_WASAPI_SUPPORT, + "WASAPI ondersteuning") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYLIB_SUPPORT, "Dynamic library ondersteuning") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYNAMIC_SUPPORT, diff --git a/intl/msg_hash_pl.c b/intl/msg_hash_pl.c index d6d32186f8..efa5cd5417 100644 --- a/intl/msg_hash_pl.c +++ b/intl/msg_hash_pl.c @@ -608,6 +608,8 @@ const char *msg_hash_to_str_pl(enum msg_hash_enums msg) return "Metryczna szerokość wyświetlacza (mm)"; case MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DSOUND_SUPPORT: return "Wsparcie DirectSound"; + case MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_WASAPI_SUPPORT: + return "Wsparcie WASAPI"; case MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYLIB_SUPPORT: return "Wsparcie bibliotek dynamicznych"; case MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_EGL_SUPPORT: diff --git a/intl/msg_hash_pt_br.c b/intl/msg_hash_pt_br.c index 05f74f33da..cc6d4fda70 100644 --- a/intl/msg_hash_pt_br.c +++ b/intl/msg_hash_pt_br.c @@ -1515,6 +1515,8 @@ const char *msg_hash_to_str_pt_br(enum msg_hash_enums msg) return "Mostrar largura (mm)"; case MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DSOUND_SUPPORT: return "Suporte a DirectSound"; + case MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_WASAPI_SUPPORT: + return "Suporte a WASAPI"; case MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYLIB_SUPPORT: return "Suporte a bibliotecas dinâmicas"; case MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_EGL_SUPPORT: diff --git a/intl/msg_hash_pt_br.h b/intl/msg_hash_pt_br.h index 92fd8b43ad..67ba5fa38e 100644 --- a/intl/msg_hash_pt_br.h +++ b/intl/msg_hash_pt_br.h @@ -1347,6 +1347,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DISPLAY_METRIC_MM_WIDTH, "Exibir largura métrica (mm)") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DSOUND_SUPPORT, "Suporte DirectSound") +MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_WASAPI_SUPPORT, + "Suporte WASAPI") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYLIB_SUPPORT, "Suporte à biblioteca dinâmica") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYNAMIC_SUPPORT, diff --git a/intl/msg_hash_ru.h b/intl/msg_hash_ru.h index f19f9143b8..8a2b4d53b1 100644 --- a/intl/msg_hash_ru.h +++ b/intl/msg_hash_ru.h @@ -1255,6 +1255,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DISPLAY_METRIC_MM_WIDTH, "Display metric width (mm)") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DSOUND_SUPPORT, "DirectSound support") +MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_WASAPI_SUPPORT, + "WASAPI support") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYLIB_SUPPORT, "Dynamic library support") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYNAMIC_SUPPORT, diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 56a8137d26..48db9116d7 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -1353,6 +1353,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DISPLAY_METRIC_MM_WIDTH, "Display metric width (mm)") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DSOUND_SUPPORT, "DirectSound support") +MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_WASAPI_SUPPORT, + "WASAPI support") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYLIB_SUPPORT, "Dynamic library support") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYNAMIC_SUPPORT, diff --git a/intl/msg_hash_vn.c b/intl/msg_hash_vn.c index 1811a73a7a..6c83a00cce 100644 --- a/intl/msg_hash_vn.c +++ b/intl/msg_hash_vn.c @@ -2917,6 +2917,8 @@ const char *msg_hash_to_str_vn(enum msg_hash_enums msg) return "Display metric width (mm)"; case MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DSOUND_SUPPORT: return "DirectSound support"; + case MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_WASAPI_SUPPORT: + return "WASAPI support"; case MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYLIB_SUPPORT: return "Dynamic library support"; case MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DYNAMIC_SUPPORT: diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index d60dee63cb..e245d76a8f 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -1245,6 +1245,15 @@ static int menu_displaylist_parse_system_info(menu_displaylist_info_t *info) menu_entries_append_enum(info->list, feat_str, "", MENU_ENUM_LABEL_SYSTEM_INFO_ENTRY, MENU_SETTINGS_CORE_INFO_NONE, 0, 0); + snprintf(feat_str, sizeof(feat_str), + "%s: %s", + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_WASAPI_SUPPORT), + _wasapi_supp ? + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_YES) : + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO)); + menu_entries_append_enum(info->list, feat_str, "", + MENU_ENUM_LABEL_SYSTEM_INFO_ENTRY, MENU_SETTINGS_CORE_INFO_NONE, 0, 0); + snprintf(feat_str, sizeof(feat_str), "%s: %s", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_XAUDIO2_SUPPORT), diff --git a/msg_hash.h b/msg_hash.h index c462235274..69c5c80dfe 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -1425,6 +1425,7 @@ enum msg_hash_enums MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_JACK_SUPPORT, MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_PULSEAUDIO_SUPPORT, MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_DSOUND_SUPPORT, + MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_WASAPI_SUPPORT, MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_XAUDIO2_SUPPORT, MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_ZLIB_SUPPORT, MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_7ZIP_SUPPORT, diff --git a/qb/config.libs.sh b/qb/config.libs.sh index 94959cad98..3073b2ce14 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -274,6 +274,7 @@ if [ "$OS" = 'Win32' ]; then HAVE_XINPUT=yes fi + HAVE_WASAPI=yes HAVE_XAUDIO=yes else HAVE_D3D9=no diff --git a/retroarch.c b/retroarch.c index 0f2d8fdbd7..aa71826ab0 100644 --- a/retroarch.c +++ b/retroarch.c @@ -184,6 +184,7 @@ static void retroarch_print_features(void) _PSUPP(roar, "RoarAudio", "Audio driver"); _PSUPP(pulse, "PulseAudio", "Audio driver"); _PSUPP(dsound, "DirectSound", "Audio driver"); + _PSUPP(wasapi, "WASAPI", "Audio driver"); _PSUPP(xaudio, "XAudio2", "Audio driver"); _PSUPP(al, "OpenAL", "Audio driver"); _PSUPP(sl, "OpenSL", "Audio driver");