mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2024-12-22 22:25:28 +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/)
|
||||
- [ALSA](http://www.alsa-project.org/)
|
||||
- [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)
|
||||
- (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
|
||||
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
|
||||
|
@ -45,6 +44,9 @@ behavior on every platform.
|
|||
an ALSA device open and a JACK device open at the same time.
|
||||
* Meticulously checks all return codes and memory allocations and uses
|
||||
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
|
||||
|
||||
|
@ -166,7 +168,6 @@ or the server not running, or the platform is wrong, the next backend is tried.
|
|||
0. ALSA (Linux)
|
||||
0. CoreAudio (OSX)
|
||||
0. WASAPI (Windows)
|
||||
0. ASIO (Windows)
|
||||
0. Dummy
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
block_until_have_devices
|
||||
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. 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. Add [sndio](http://www.sndio.org/) backend to support OpenBSD.
|
||||
|
||||
## Planned Uses for libsoundio
|
||||
|
||||
|
|
|
@ -162,7 +162,7 @@ double soundio_os_get_time(void) {
|
|||
#if defined(SOUNDIO_OS_WINDOWS)
|
||||
static DWORD WINAPI run_win32_thread(LPVOID userdata) {
|
||||
struct SoundIoOsThread *thread = (struct SoundIoOsThread *)userdata;
|
||||
HRESULT err = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||
HRESULT err = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
|
||||
assert(err == S_OK);
|
||||
thread->run(thread->arg);
|
||||
CoUninitialize();
|
||||
|
|
|
@ -10,7 +10,15 @@
|
|||
#include <windows.h>
|
||||
#include <mmdeviceapi.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 *
|
||||
// Possible errors:
|
||||
|
@ -45,6 +53,8 @@ static SoundIoDeviceAim data_flow_to_aim(EDataFlow data_flow) {
|
|||
struct RefreshDevices {
|
||||
IMMDeviceCollection *collection;
|
||||
IMMDevice *mm_device;
|
||||
IMMDevice *default_render_device;
|
||||
IMMDevice *default_capture_device;
|
||||
IMMEndpoint *endpoint;
|
||||
IPropertyStore *prop_store;
|
||||
LPWSTR lpwstr;
|
||||
|
@ -52,6 +62,10 @@ struct RefreshDevices {
|
|||
bool prop_variant_value_inited;
|
||||
SoundIoDevicesInfo *devices_info;
|
||||
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) {
|
||||
|
@ -59,6 +73,10 @@ static void deinit_refresh_devices(RefreshDevices *rd) {
|
|||
soundio_device_unref(rd->device);
|
||||
if (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)
|
||||
IMMDeviceCollection_Release(rd->collection);
|
||||
if (rd->lpwstr)
|
||||
|
@ -77,6 +95,43 @@ static int refresh_devices(SoundIoPrivate *si) {
|
|||
RefreshDevices rd = {0};
|
||||
int err;
|
||||
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,
|
||||
eAll, DEVICE_STATE_ACTIVE, &rd.collection)))
|
||||
{
|
||||
|
@ -177,16 +232,51 @@ static int refresh_devices(SoundIoPrivate *si) {
|
|||
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;
|
||||
if (rd.device->aim == SoundIoDeviceAimOutput) {
|
||||
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 {
|
||||
assert(rd.device->aim == SoundIoDeviceAimInput);
|
||||
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))) {
|
||||
|
@ -244,6 +334,7 @@ static void device_thread_run(void *arg) {
|
|||
if (err)
|
||||
shutdown_backend(si, err);
|
||||
if (!siw->have_devices_flag.exchange(true)) {
|
||||
// TODO separate cond for signaling devices like coreaudio
|
||||
soundio_os_cond_signal(siw->cond, nullptr);
|
||||
soundio->on_events_signal(soundio);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue