mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2024-12-23 03:05:37 +00:00
WASAPI: default device detection
This commit is contained in:
parent
31e17477bc
commit
20b0515582
18
README.md
18
README.md
|
@ -24,9 +24,8 @@ behavior on every platform.
|
||||||
- [PulseAudio](http://www.freedesktop.org/wiki/Software/PulseAudio/)
|
- [PulseAudio](http://www.freedesktop.org/wiki/Software/PulseAudio/)
|
||||||
- [ALSA](http://www.alsa-project.org/)
|
- [ALSA](http://www.alsa-project.org/)
|
||||||
- [CoreAudio](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/CoreAudioOverview/Introduction/Introduction.html)
|
- [CoreAudio](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/CoreAudioOverview/Introduction/Introduction.html)
|
||||||
|
- (in progress) [WASAPI](https://msdn.microsoft.com/en-us/library/windows/desktop/dd371455%28v=vs.85%29.aspx)
|
||||||
- Dummy (silence)
|
- Dummy (silence)
|
||||||
- (planned) [WASAPI](https://msdn.microsoft.com/en-us/library/windows/desktop/dd371455%28v=vs.85%29.aspx)
|
|
||||||
- (planned) [ASIO](http://www.asio4all.com/)
|
|
||||||
* Supports optimal usage of each supported backend. The same API does the
|
* Supports optimal usage of each supported backend. The same API does the
|
||||||
right thing whether the backend has a fixed buffer size, such as on JACK and
|
right thing whether the backend has a fixed buffer size, such as on JACK and
|
||||||
CoreAudio, or whether it allows directly managing the buffer, such as on
|
CoreAudio, or whether it allows directly managing the buffer, such as on
|
||||||
|
@ -45,6 +44,9 @@ behavior on every platform.
|
||||||
an ALSA device open and a JACK device open at the same time.
|
an ALSA device open and a JACK device open at the same time.
|
||||||
* Meticulously checks all return codes and memory allocations and uses
|
* Meticulously checks all return codes and memory allocations and uses
|
||||||
meaningful error codes.
|
meaningful error codes.
|
||||||
|
* Exposes extra API that is only available on some backends. For example you
|
||||||
|
can provide application name and stream names which is used by JACK and
|
||||||
|
PulseAudio.
|
||||||
|
|
||||||
## Synopsis
|
## Synopsis
|
||||||
|
|
||||||
|
@ -166,7 +168,6 @@ or the server not running, or the platform is wrong, the next backend is tried.
|
||||||
0. ALSA (Linux)
|
0. ALSA (Linux)
|
||||||
0. CoreAudio (OSX)
|
0. CoreAudio (OSX)
|
||||||
0. WASAPI (Windows)
|
0. WASAPI (Windows)
|
||||||
0. ASIO (Windows)
|
|
||||||
0. Dummy
|
0. Dummy
|
||||||
|
|
||||||
If you don't like this order, you can use `soundio_connect_backend` to
|
If you don't like this order, you can use `soundio_connect_backend` to
|
||||||
|
@ -249,7 +250,15 @@ view `coverage/index.html` in a browser.
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
0. implement WASAPI (Windows) backend, get examples working
|
0. implement WASAPI (Windows) backend, get examples working
|
||||||
0. implement ASIO (Windows) backend, get examples working
|
- list devices
|
||||||
|
- period duration
|
||||||
|
- buffer duration
|
||||||
|
- raw mode
|
||||||
|
- channel layout
|
||||||
|
- formats
|
||||||
|
- watching
|
||||||
|
- sine wave
|
||||||
|
- microphone
|
||||||
0. Make sure PulseAudio can handle refresh devices crashing before
|
0. Make sure PulseAudio can handle refresh devices crashing before
|
||||||
block_until_have_devices
|
block_until_have_devices
|
||||||
0. Do we really want `period_duration` in the API?
|
0. Do we really want `period_duration` in the API?
|
||||||
|
@ -288,6 +297,7 @@ view `coverage/index.html` in a browser.
|
||||||
0. Consider testing on FreeBSD
|
0. Consider testing on FreeBSD
|
||||||
0. In ALSA do we need to wake up the poll when destroying the in or out stream?
|
0. In ALSA do we need to wake up the poll when destroying the in or out stream?
|
||||||
0. Detect PulseAudio server going offline and emit `on_backend_disconnect`.
|
0. Detect PulseAudio server going offline and emit `on_backend_disconnect`.
|
||||||
|
0. Add [sndio](http://www.sndio.org/) backend to support OpenBSD.
|
||||||
|
|
||||||
## Planned Uses for libsoundio
|
## Planned Uses for libsoundio
|
||||||
|
|
||||||
|
|
|
@ -162,7 +162,7 @@ double soundio_os_get_time(void) {
|
||||||
#if defined(SOUNDIO_OS_WINDOWS)
|
#if defined(SOUNDIO_OS_WINDOWS)
|
||||||
static DWORD WINAPI run_win32_thread(LPVOID userdata) {
|
static DWORD WINAPI run_win32_thread(LPVOID userdata) {
|
||||||
struct SoundIoOsThread *thread = (struct SoundIoOsThread *)userdata;
|
struct SoundIoOsThread *thread = (struct SoundIoOsThread *)userdata;
|
||||||
HRESULT err = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
HRESULT err = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
|
||||||
assert(err == S_OK);
|
assert(err == S_OK);
|
||||||
thread->run(thread->arg);
|
thread->run(thread->arg);
|
||||||
CoUninitialize();
|
CoUninitialize();
|
||||||
|
|
|
@ -10,7 +10,15 @@
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <mmdeviceapi.h>
|
#include <mmdeviceapi.h>
|
||||||
#include <functiondiscoverykeys_devpkey.h>
|
#include <functiondiscoverykeys_devpkey.h>
|
||||||
|
#include <mmreg.h>
|
||||||
|
|
||||||
|
// Attempting to use the Windows-supplied versions of these constants resulted
|
||||||
|
// in `undefined reference` linker errors.
|
||||||
|
const static GUID SOUNDIO_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {
|
||||||
|
0x00000003,0x0000,0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
|
||||||
|
|
||||||
|
const static GUID SOUNDIO_KSDATAFORMAT_SUBTYPE_PCM = {
|
||||||
|
0x00000001,0x0000,0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
|
||||||
|
|
||||||
// converts a windows wide string to a UTF-8 encoded char *
|
// converts a windows wide string to a UTF-8 encoded char *
|
||||||
// Possible errors:
|
// Possible errors:
|
||||||
|
@ -45,6 +53,8 @@ static SoundIoDeviceAim data_flow_to_aim(EDataFlow data_flow) {
|
||||||
struct RefreshDevices {
|
struct RefreshDevices {
|
||||||
IMMDeviceCollection *collection;
|
IMMDeviceCollection *collection;
|
||||||
IMMDevice *mm_device;
|
IMMDevice *mm_device;
|
||||||
|
IMMDevice *default_render_device;
|
||||||
|
IMMDevice *default_capture_device;
|
||||||
IMMEndpoint *endpoint;
|
IMMEndpoint *endpoint;
|
||||||
IPropertyStore *prop_store;
|
IPropertyStore *prop_store;
|
||||||
LPWSTR lpwstr;
|
LPWSTR lpwstr;
|
||||||
|
@ -52,6 +62,10 @@ struct RefreshDevices {
|
||||||
bool prop_variant_value_inited;
|
bool prop_variant_value_inited;
|
||||||
SoundIoDevicesInfo *devices_info;
|
SoundIoDevicesInfo *devices_info;
|
||||||
SoundIoDevice *device;
|
SoundIoDevice *device;
|
||||||
|
char *default_render_id;
|
||||||
|
int default_render_id_len;
|
||||||
|
char *default_capture_id;
|
||||||
|
int default_capture_id_len;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void deinit_refresh_devices(RefreshDevices *rd) {
|
static void deinit_refresh_devices(RefreshDevices *rd) {
|
||||||
|
@ -59,6 +73,10 @@ static void deinit_refresh_devices(RefreshDevices *rd) {
|
||||||
soundio_device_unref(rd->device);
|
soundio_device_unref(rd->device);
|
||||||
if (rd->mm_device)
|
if (rd->mm_device)
|
||||||
IMMDevice_Release(rd->mm_device);
|
IMMDevice_Release(rd->mm_device);
|
||||||
|
if (rd->default_render_device)
|
||||||
|
IMMDevice_Release(rd->default_render_device);
|
||||||
|
if (rd->default_capture_device)
|
||||||
|
IMMDevice_Release(rd->default_capture_device);
|
||||||
if (rd->collection)
|
if (rd->collection)
|
||||||
IMMDeviceCollection_Release(rd->collection);
|
IMMDeviceCollection_Release(rd->collection);
|
||||||
if (rd->lpwstr)
|
if (rd->lpwstr)
|
||||||
|
@ -77,6 +95,43 @@ static int refresh_devices(SoundIoPrivate *si) {
|
||||||
RefreshDevices rd = {0};
|
RefreshDevices rd = {0};
|
||||||
int err;
|
int err;
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
|
||||||
|
if (FAILED(hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(siw->device_enumerator, eRender,
|
||||||
|
eMultimedia, &rd.default_render_device)))
|
||||||
|
{
|
||||||
|
deinit_refresh_devices(&rd);
|
||||||
|
return SoundIoErrorOpeningDevice;
|
||||||
|
}
|
||||||
|
if (rd.lpwstr)
|
||||||
|
CoTaskMemFree(rd.lpwstr);
|
||||||
|
if (FAILED(hr = IMMDevice_GetId(rd.default_render_device, &rd.lpwstr))) {
|
||||||
|
deinit_refresh_devices(&rd);
|
||||||
|
return SoundIoErrorOpeningDevice;
|
||||||
|
}
|
||||||
|
if ((err = from_lpwstr(rd.lpwstr, &rd.default_render_id, &rd.default_render_id_len))) {
|
||||||
|
deinit_refresh_devices(&rd);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (FAILED(hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(siw->device_enumerator, eCapture,
|
||||||
|
eMultimedia, &rd.default_capture_device)))
|
||||||
|
{
|
||||||
|
deinit_refresh_devices(&rd);
|
||||||
|
return SoundIoErrorOpeningDevice;
|
||||||
|
}
|
||||||
|
if (rd.lpwstr)
|
||||||
|
CoTaskMemFree(rd.lpwstr);
|
||||||
|
if (FAILED(hr = IMMDevice_GetId(rd.default_capture_device, &rd.lpwstr))) {
|
||||||
|
deinit_refresh_devices(&rd);
|
||||||
|
return SoundIoErrorOpeningDevice;
|
||||||
|
}
|
||||||
|
if ((err = from_lpwstr(rd.lpwstr, &rd.default_capture_id, &rd.default_capture_id_len))) {
|
||||||
|
deinit_refresh_devices(&rd);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (FAILED(hr = IMMDeviceEnumerator_EnumAudioEndpoints(siw->device_enumerator,
|
if (FAILED(hr = IMMDeviceEnumerator_EnumAudioEndpoints(siw->device_enumerator,
|
||||||
eAll, DEVICE_STATE_ACTIVE, &rd.collection)))
|
eAll, DEVICE_STATE_ACTIVE, &rd.collection)))
|
||||||
{
|
{
|
||||||
|
@ -177,16 +232,51 @@ static int refresh_devices(SoundIoPrivate *si) {
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
if (rd.prop_variant_value_inited)
|
||||||
|
PropVariantClear(&rd.prop_variant_value);
|
||||||
|
PropVariantInit(&rd.prop_variant_value);
|
||||||
|
rd.prop_variant_value_inited = true;
|
||||||
|
if ((FAILED(hr = IPropertyStore_GetValue(rd.prop_store,
|
||||||
|
PKEY_AudioEngine_DeviceFormat, &rd.prop_variant_value))))
|
||||||
|
{
|
||||||
|
deinit_refresh_devices(&rd);
|
||||||
|
return SoundIoErrorOpeningDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
WAVEFORMATEXTENSIBLE *wave_format = (WAVEFORMATEXTENSIBLE *)rd.prop_variant_value.blob.pBlobData;
|
||||||
|
if (wave_format->Format.wFormatTag != WAVE_FORMAT_EXTENSIBLE) {
|
||||||
|
deinit_refresh_devices(&rd);
|
||||||
|
return SoundIoErrorOpeningDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (IsEqualGUID(wave_format->SubFormat, SOUNDIO_KSDATAFORMAT_SUBTYPE_PCM)) {
|
||||||
|
} else if (IsEqualGUID(wave_format->SubFormat, SOUNDIO_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
|
||||||
|
} else {
|
||||||
|
deinit_refresh_devices(&rd);
|
||||||
|
return SoundIoErrorOpeningDevice;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
rd.device->sample_rate_count = 1;
|
||||||
|
rd.device->sample_rates = &dev->prealloc_sample_rate_range;
|
||||||
|
rd.device->sample_rate_current = wave_format->Format.nSamplesPerSec;
|
||||||
|
rd.device->sample_rates[0].min = rd.device->sample_rate_current;
|
||||||
|
rd.device->sample_rates[0].max = rd.device->sample_rate_current;
|
||||||
|
|
||||||
|
fprintf(stderr, "bits per sample: %d\n", (int)wave_format->Format.wBitsPerSample);
|
||||||
|
|
||||||
|
|
||||||
SoundIoList<SoundIoDevice *> *device_list;
|
SoundIoList<SoundIoDevice *> *device_list;
|
||||||
if (rd.device->aim == SoundIoDeviceAimOutput) {
|
if (rd.device->aim == SoundIoDeviceAimOutput) {
|
||||||
device_list = &rd.devices_info->output_devices;
|
device_list = &rd.devices_info->output_devices;
|
||||||
// TODO default detection
|
if (soundio_streql(rd.device->id, device_id_len, rd.default_render_id, rd.default_render_id_len))
|
||||||
|
rd.devices_info->default_output_index = device_list->length;
|
||||||
} else {
|
} else {
|
||||||
assert(rd.device->aim == SoundIoDeviceAimInput);
|
assert(rd.device->aim == SoundIoDeviceAimInput);
|
||||||
device_list = &rd.devices_info->input_devices;
|
device_list = &rd.devices_info->input_devices;
|
||||||
// TODO default detection
|
if (soundio_streql(rd.device->id, device_id_len, rd.default_capture_id, rd.default_capture_id_len))
|
||||||
|
rd.devices_info->default_input_index = device_list->length;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = device_list->append(rd.device))) {
|
if ((err = device_list->append(rd.device))) {
|
||||||
|
@ -244,6 +334,7 @@ static void device_thread_run(void *arg) {
|
||||||
if (err)
|
if (err)
|
||||||
shutdown_backend(si, err);
|
shutdown_backend(si, err);
|
||||||
if (!siw->have_devices_flag.exchange(true)) {
|
if (!siw->have_devices_flag.exchange(true)) {
|
||||||
|
// TODO separate cond for signaling devices like coreaudio
|
||||||
soundio_os_cond_signal(siw->cond, nullptr);
|
soundio_os_cond_signal(siw->cond, nullptr);
|
||||||
soundio->on_events_signal(soundio);
|
soundio->on_events_signal(soundio);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue