mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2024-12-22 22:35:28 +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.
|
||||
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/).
|
||||
* Errors are communicated via return codes, not logging to stdio. This is one
|
||||
of my many complaints against [PortAudio](http://www.portaudio.com/).
|
||||
* Errors are communicated via return codes, not logging to stdio.
|
||||
* Supports channel layouts (also known as channel maps), important for
|
||||
surround sound applications.
|
||||
* 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
|
||||
// are used. You may check that the current backend is PulseAudio and
|
||||
// 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_max;
|
||||
double buffer_duration_current;
|
||||
|
@ -420,7 +420,8 @@ struct SoundIoOutStream {
|
|||
// actual period duration, as near to this value as possible.
|
||||
// Defaults to `buffer_duration / 2` (and then clamped into range).
|
||||
// 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;
|
||||
|
||||
// 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 PulseAudio's default value, usually large. If you set this
|
||||
// and the backend is PulseAudio, it sets `PA_STREAM_ADJUST_LATENCY` and
|
||||
// is the value used for `maxlength`. With PulseAudio, this value is not
|
||||
// replaced with the actual duration until `soundio_instream_start`.
|
||||
// is the value used for `maxlength`.
|
||||
double buffer_duration;
|
||||
|
||||
// The latency of the captured audio.
|
||||
|
@ -505,8 +505,6 @@ struct SoundIoInStream {
|
|||
// Defaults to `buffer_duration / 8`.
|
||||
// If you set this and the backend is PulseAudio, it sets
|
||||
// `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;
|
||||
|
||||
// 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_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;
|
||||
|
@ -907,6 +911,8 @@ static int outstream_open_ca(struct SoundIoPrivate *si, struct SoundIoOutStreamP
|
|||
outstream->buffer_duration,
|
||||
device->buffer_duration_max);
|
||||
|
||||
outstream->period_duration = outstream->outstream->buffer_duration;
|
||||
|
||||
AudioComponentDescription desc = {0};
|
||||
desc.componentType = kAudioUnitType_Output;
|
||||
desc.componentSubType = kAudioUnitSubType_HALOutput;
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
#include <objbase.h>
|
||||
|
||||
#else
|
||||
|
||||
|
@ -161,7 +162,10 @@ 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);
|
||||
assert(err == S_OK);
|
||||
thread->run(thread->arg);
|
||||
CoUninitialize();
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
|
|
172
src/wasapi.cpp
172
src/wasapi.cpp
|
@ -1,16 +1,142 @@
|
|||
#include "wasapi.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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
|
@ -73,10 +199,48 @@ static int instream_end_read_wasapi(struct SoundIoPrivate *si, struct SoundIoInS
|
|||
|
||||
|
||||
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) {
|
||||
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->flush_events = flush_events_wasapi;
|
||||
si->wait_events = wait_events_wasapi;
|
||||
|
|
|
@ -9,6 +9,9 @@
|
|||
#define SOUNDIO_WASAPI_HPP
|
||||
|
||||
#include "soundio/soundio.h"
|
||||
#include "soundio/os.h"
|
||||
#include "atomics.hpp"
|
||||
struct IMMDeviceEnumerator;
|
||||
|
||||
int soundio_wasapi_init(struct SoundIoPrivate *si);
|
||||
|
||||
|
@ -16,6 +19,18 @@ struct SoundIoDeviceWasapi {
|
|||
};
|
||||
|
||||
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 {
|
||||
|
|
Loading…
Reference in a new issue