mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2024-12-23 03:05:37 +00:00
WASAPI: get the number of available audio devices
This commit is contained in:
parent
21bf405859
commit
97abe08f0e
|
@ -34,8 +34,7 @@ behavior on every platform.
|
||||||
* C library. Depends only on the respective backend API libraries and libc.
|
* C library. Depends only on the respective backend API libraries and libc.
|
||||||
Does *not* depend on libstdc++, and does *not* have exceptions, run-time type
|
Does *not* depend on libstdc++, and does *not* have exceptions, run-time type
|
||||||
information, or [setjmp](http://latentcontent.net/2007/12/05/libpng-worst-api-ever/).
|
information, or [setjmp](http://latentcontent.net/2007/12/05/libpng-worst-api-ever/).
|
||||||
* Errors are communicated via return codes, not logging to stdio. This is one
|
* Errors are communicated via return codes, not logging to stdio.
|
||||||
of my many complaints against [PortAudio](http://www.portaudio.com/).
|
|
||||||
* Supports channel layouts (also known as channel maps), important for
|
* Supports channel layouts (also known as channel maps), important for
|
||||||
surround sound applications.
|
surround sound applications.
|
||||||
* Ability to monitor devices and get an event when available devices change.
|
* Ability to monitor devices and get an event when available devices change.
|
||||||
|
|
|
@ -355,7 +355,7 @@ struct SoundIoDevice {
|
||||||
// PulseAudio allows any value and so reasonable min/max of 0.10 and 4.0
|
// PulseAudio allows any value and so reasonable min/max of 0.10 and 4.0
|
||||||
// are used. You may check that the current backend is PulseAudio and
|
// are used. You may check that the current backend is PulseAudio and
|
||||||
// ignore these min/max values.
|
// ignore these min/max values.
|
||||||
// For JACK, buffer duration and period duration are the same.
|
// For JACK and CoreAudio, buffer duration and period duration are the same.
|
||||||
double buffer_duration_min;
|
double buffer_duration_min;
|
||||||
double buffer_duration_max;
|
double buffer_duration_max;
|
||||||
double buffer_duration_current;
|
double buffer_duration_current;
|
||||||
|
@ -420,7 +420,8 @@ struct SoundIoOutStream {
|
||||||
// actual period duration, as near to this value as possible.
|
// actual period duration, as near to this value as possible.
|
||||||
// Defaults to `buffer_duration / 2` (and then clamped into range).
|
// Defaults to `buffer_duration / 2` (and then clamped into range).
|
||||||
// If the device has unknown period duration min and max values, you may
|
// If the device has unknown period duration min and max values, you may
|
||||||
// still set this. This value is meaningless for PulseAudio.
|
// still set this. This value is meaningless for PulseAudio, JACK, and
|
||||||
|
// CoreAudio.
|
||||||
double period_duration;
|
double period_duration;
|
||||||
|
|
||||||
// Defaults to NULL. Put whatever you want here.
|
// Defaults to NULL. Put whatever you want here.
|
||||||
|
@ -494,8 +495,7 @@ struct SoundIoInStream {
|
||||||
// Defaults to 1 second (and then clamped into range). For PulseAudio,
|
// Defaults to 1 second (and then clamped into range). For PulseAudio,
|
||||||
// defaults to PulseAudio's default value, usually large. If you set this
|
// defaults to PulseAudio's default value, usually large. If you set this
|
||||||
// and the backend is PulseAudio, it sets `PA_STREAM_ADJUST_LATENCY` and
|
// and the backend is PulseAudio, it sets `PA_STREAM_ADJUST_LATENCY` and
|
||||||
// is the value used for `maxlength`. With PulseAudio, this value is not
|
// is the value used for `maxlength`.
|
||||||
// replaced with the actual duration until `soundio_instream_start`.
|
|
||||||
double buffer_duration;
|
double buffer_duration;
|
||||||
|
|
||||||
// The latency of the captured audio.
|
// The latency of the captured audio.
|
||||||
|
@ -505,8 +505,6 @@ struct SoundIoInStream {
|
||||||
// Defaults to `buffer_duration / 8`.
|
// Defaults to `buffer_duration / 8`.
|
||||||
// If you set this and the backend is PulseAudio, it sets
|
// If you set this and the backend is PulseAudio, it sets
|
||||||
// `PA_STREAM_ADJUST_LATENCY` and is the value used for `fragsize`.
|
// `PA_STREAM_ADJUST_LATENCY` and is the value used for `fragsize`.
|
||||||
// With PulseAudio, this value is not replaced with the actual duration
|
|
||||||
// until `soundio_instream_start`.
|
|
||||||
double period_duration;
|
double period_duration;
|
||||||
|
|
||||||
// Defaults to NULL. Put whatever you want here.
|
// Defaults to NULL. Put whatever you want here.
|
||||||
|
|
|
@ -724,6 +724,10 @@ static int refresh_devices(struct SoundIoPrivate *si) {
|
||||||
rd.device->buffer_duration_min = avr.mMinimum / use_sample_rate;
|
rd.device->buffer_duration_min = avr.mMinimum / use_sample_rate;
|
||||||
rd.device->buffer_duration_max = avr.mMaximum / use_sample_rate;
|
rd.device->buffer_duration_max = avr.mMaximum / use_sample_rate;
|
||||||
|
|
||||||
|
rd.device->period_duration_min = rd.device->buffer_duration_min;
|
||||||
|
rd.device->period_duration_max = rd.device->buffer_duration_max;
|
||||||
|
rd.device->period_duration_current = rd.device->buffer_duration_current;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
SoundIoList<SoundIoDevice *> *device_list;
|
SoundIoList<SoundIoDevice *> *device_list;
|
||||||
|
@ -907,6 +911,8 @@ static int outstream_open_ca(struct SoundIoPrivate *si, struct SoundIoOutStreamP
|
||||||
outstream->buffer_duration,
|
outstream->buffer_duration,
|
||||||
device->buffer_duration_max);
|
device->buffer_duration_max);
|
||||||
|
|
||||||
|
outstream->period_duration = outstream->outstream->buffer_duration;
|
||||||
|
|
||||||
AudioComponentDescription desc = {0};
|
AudioComponentDescription desc = {0};
|
||||||
desc.componentType = kAudioUnitType_Output;
|
desc.componentType = kAudioUnitType_Output;
|
||||||
desc.componentSubType = kAudioUnitSubType_HALOutput;
|
desc.componentSubType = kAudioUnitSubType_HALOutput;
|
||||||
|
|
|
@ -49,6 +49,7 @@
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <mmsystem.h>
|
#include <mmsystem.h>
|
||||||
|
#include <objbase.h>
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
@ -161,7 +162,10 @@ 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);
|
||||||
|
assert(err == S_OK);
|
||||||
thread->run(thread->arg);
|
thread->run(thread->arg);
|
||||||
|
CoUninitialize();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
|
172
src/wasapi.cpp
172
src/wasapi.cpp
|
@ -1,16 +1,142 @@
|
||||||
#include "wasapi.hpp"
|
#include "wasapi.hpp"
|
||||||
#include "soundio.hpp"
|
#include "soundio.hpp"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define INITGUID
|
||||||
|
#define CINTERFACE
|
||||||
|
#define COBJMACROS
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <mmdeviceapi.h>
|
||||||
|
|
||||||
|
struct RefreshDevices {
|
||||||
|
IMMDeviceCollection *collection;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void deinit_refresh_devices(RefreshDevices *rd) {
|
||||||
|
if (rd->collection)
|
||||||
|
IMMDeviceCollection_Release(rd->collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int refresh_devices(SoundIoPrivate *si) {
|
||||||
|
SoundIoWasapi *siw = &si->backend_data.wasapi;
|
||||||
|
RefreshDevices rd = {0};
|
||||||
|
HRESULT hr;
|
||||||
|
if (FAILED(hr = IMMDeviceEnumerator_EnumAudioEndpoints(siw->device_enumerator,
|
||||||
|
eAll, DEVICE_STATE_ACTIVE, &rd.collection)))
|
||||||
|
{
|
||||||
|
deinit_refresh_devices(&rd);
|
||||||
|
return SoundIoErrorOpeningDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT device_count;
|
||||||
|
if (FAILED(hr = IMMDeviceCollection_GetCount(rd.collection, &device_count))) {
|
||||||
|
deinit_refresh_devices(&rd);
|
||||||
|
return SoundIoErrorOpeningDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
fprintf(stderr, "device count: %d\n", (int) device_count);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void shutdown_backend(SoundIoPrivate *si, int err) {
|
||||||
|
SoundIo *soundio = &si->pub;
|
||||||
|
SoundIoWasapi *siw = &si->backend_data.wasapi;
|
||||||
|
soundio_os_mutex_lock(siw->mutex);
|
||||||
|
siw->shutdown_err = err;
|
||||||
|
soundio->on_events_signal(soundio);
|
||||||
|
soundio_os_mutex_unlock(siw->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void device_thread_run(void *arg) {
|
||||||
|
SoundIoPrivate *si = (SoundIoPrivate *)arg;
|
||||||
|
SoundIo *soundio = &si->pub;
|
||||||
|
SoundIoWasapi *siw = &si->backend_data.wasapi;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr,
|
||||||
|
CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&siw->device_enumerator);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
shutdown_backend(si, SoundIoErrorSystemResources);
|
||||||
|
if (!siw->have_devices_flag.exchange(true)) {
|
||||||
|
soundio_os_cond_signal(siw->cond, nullptr);
|
||||||
|
soundio->on_events_signal(soundio);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
if (!siw->abort_flag.test_and_set())
|
||||||
|
break;
|
||||||
|
if (siw->device_scan_queued.exchange(false)) {
|
||||||
|
err = refresh_devices(si);
|
||||||
|
if (err)
|
||||||
|
shutdown_backend(si, err);
|
||||||
|
if (!siw->have_devices_flag.exchange(true)) {
|
||||||
|
soundio_os_cond_signal(siw->cond, nullptr);
|
||||||
|
soundio->on_events_signal(soundio);
|
||||||
|
}
|
||||||
|
if (err)
|
||||||
|
break;
|
||||||
|
soundio_os_cond_signal(siw->cond, nullptr);
|
||||||
|
}
|
||||||
|
soundio_os_cond_wait(siw->cond, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
IMMDeviceEnumerator_Release(siw->device_enumerator);
|
||||||
|
siw->device_enumerator = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void block_until_have_devices(SoundIoWasapi *siw) {
|
||||||
|
if (siw->have_devices_flag.load())
|
||||||
|
return;
|
||||||
|
while (!siw->have_devices_flag.load())
|
||||||
|
soundio_os_cond_wait(siw->cond, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
static void flush_events_wasapi(struct SoundIoPrivate *si) {
|
static void flush_events_wasapi(struct SoundIoPrivate *si) {
|
||||||
soundio_panic("TODO");
|
SoundIo *soundio = &si->pub;
|
||||||
|
SoundIoWasapi *siw = &si->backend_data.wasapi;
|
||||||
|
block_until_have_devices(siw);
|
||||||
|
|
||||||
|
bool change = false;
|
||||||
|
bool cb_shutdown = false;
|
||||||
|
SoundIoDevicesInfo *old_devices_info = nullptr;
|
||||||
|
|
||||||
|
soundio_os_mutex_lock(siw->mutex);
|
||||||
|
|
||||||
|
if (siw->shutdown_err && !siw->emitted_shutdown_cb) {
|
||||||
|
siw->emitted_shutdown_cb = true;
|
||||||
|
cb_shutdown = true;
|
||||||
|
} else if (siw->ready_devices_info) {
|
||||||
|
old_devices_info = si->safe_devices_info;
|
||||||
|
si->safe_devices_info = siw->ready_devices_info;
|
||||||
|
siw->ready_devices_info = nullptr;
|
||||||
|
change = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
soundio_os_mutex_unlock(siw->mutex);
|
||||||
|
|
||||||
|
if (cb_shutdown)
|
||||||
|
soundio->on_backend_disconnect(soundio, siw->shutdown_err);
|
||||||
|
else if (change)
|
||||||
|
soundio->on_devices_change(soundio);
|
||||||
|
|
||||||
|
soundio_destroy_devices_info(old_devices_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wait_events_wasapi(struct SoundIoPrivate *si) {
|
static void wait_events_wasapi(struct SoundIoPrivate *si) {
|
||||||
soundio_panic("TODO");
|
SoundIoWasapi *siw = &si->backend_data.wasapi;
|
||||||
|
flush_events_wasapi(si);
|
||||||
|
soundio_os_cond_wait(siw->cond, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wakeup_wasapi(struct SoundIoPrivate *si) {
|
static void wakeup_wasapi(struct SoundIoPrivate *si) {
|
||||||
soundio_panic("TODO");
|
SoundIoWasapi *siw = &si->backend_data.wasapi;
|
||||||
|
soundio_os_cond_signal(siw->cond, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void outstream_destroy_wasapi(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
|
static void outstream_destroy_wasapi(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
|
||||||
|
@ -73,10 +199,48 @@ static int instream_end_read_wasapi(struct SoundIoPrivate *si, struct SoundIoInS
|
||||||
|
|
||||||
|
|
||||||
static void destroy_wasapi(struct SoundIoPrivate *si) {
|
static void destroy_wasapi(struct SoundIoPrivate *si) {
|
||||||
soundio_panic("TODO");
|
SoundIoWasapi *siw = &si->backend_data.wasapi;
|
||||||
|
|
||||||
|
if (siw->thread) {
|
||||||
|
siw->abort_flag.clear();
|
||||||
|
soundio_os_cond_signal(siw->cond, nullptr);
|
||||||
|
soundio_os_thread_destroy(siw->thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (siw->cond)
|
||||||
|
soundio_os_cond_destroy(siw->cond);
|
||||||
|
|
||||||
|
if (siw->mutex)
|
||||||
|
soundio_os_mutex_destroy(siw->mutex);
|
||||||
|
|
||||||
|
soundio_destroy_devices_info(siw->ready_devices_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
int soundio_wasapi_init(SoundIoPrivate *si) {
|
int soundio_wasapi_init(SoundIoPrivate *si) {
|
||||||
|
SoundIoWasapi *siw = &si->backend_data.wasapi;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
siw->have_devices_flag.store(false);
|
||||||
|
siw->device_scan_queued.store(true);
|
||||||
|
siw->abort_flag.test_and_set();
|
||||||
|
|
||||||
|
siw->mutex = soundio_os_mutex_create();
|
||||||
|
if (!siw->mutex) {
|
||||||
|
destroy_wasapi(si);
|
||||||
|
return SoundIoErrorNoMem;
|
||||||
|
}
|
||||||
|
|
||||||
|
siw->cond = soundio_os_cond_create();
|
||||||
|
if (!siw->cond) {
|
||||||
|
destroy_wasapi(si);
|
||||||
|
return SoundIoErrorNoMem;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = soundio_os_thread_create(device_thread_run, si, false, &siw->thread))) {
|
||||||
|
destroy_wasapi(si);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
si->destroy = destroy_wasapi;
|
si->destroy = destroy_wasapi;
|
||||||
si->flush_events = flush_events_wasapi;
|
si->flush_events = flush_events_wasapi;
|
||||||
si->wait_events = wait_events_wasapi;
|
si->wait_events = wait_events_wasapi;
|
||||||
|
|
|
@ -9,6 +9,9 @@
|
||||||
#define SOUNDIO_WASAPI_HPP
|
#define SOUNDIO_WASAPI_HPP
|
||||||
|
|
||||||
#include "soundio/soundio.h"
|
#include "soundio/soundio.h"
|
||||||
|
#include "soundio/os.h"
|
||||||
|
#include "atomics.hpp"
|
||||||
|
struct IMMDeviceEnumerator;
|
||||||
|
|
||||||
int soundio_wasapi_init(struct SoundIoPrivate *si);
|
int soundio_wasapi_init(struct SoundIoPrivate *si);
|
||||||
|
|
||||||
|
@ -16,6 +19,18 @@ struct SoundIoDeviceWasapi {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SoundIoWasapi {
|
struct SoundIoWasapi {
|
||||||
|
SoundIoOsMutex *mutex;
|
||||||
|
SoundIoOsCond *cond;
|
||||||
|
struct SoundIoOsThread *thread;
|
||||||
|
atomic_flag abort_flag;
|
||||||
|
// this one is ready to be read with flush_events. protected by mutex
|
||||||
|
struct SoundIoDevicesInfo *ready_devices_info;
|
||||||
|
atomic_bool have_devices_flag;
|
||||||
|
atomic_bool device_scan_queued;
|
||||||
|
int shutdown_err;
|
||||||
|
bool emitted_shutdown_cb;
|
||||||
|
|
||||||
|
IMMDeviceEnumerator* device_enumerator;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SoundIoOutStreamWasapi {
|
struct SoundIoOutStreamWasapi {
|
||||||
|
|
Loading…
Reference in a new issue