mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2024-12-23 04:45:40 +00:00
WASAPI: detecting device changes
This commit is contained in:
parent
e82c3e89da
commit
61b95a458c
|
@ -26,6 +26,10 @@ behavior on every platform.
|
||||||
- [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)
|
- (in progress) [WASAPI](https://msdn.microsoft.com/en-us/library/windows/desktop/dd371455%28v=vs.85%29.aspx)
|
||||||
- Dummy (silence)
|
- Dummy (silence)
|
||||||
|
* Exposes both raw devices and shared devices. Raw devices give you the best
|
||||||
|
performance but prevent other applications from using them. Shared devices
|
||||||
|
are default and usually provide sample rate conversion and format
|
||||||
|
conversion.
|
||||||
* 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
|
||||||
|
@ -250,8 +254,6 @@ 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
|
||||||
- list devices
|
|
||||||
- watching
|
|
||||||
- sine wave
|
- sine wave
|
||||||
- microphone
|
- microphone
|
||||||
0. Make sure PulseAudio can handle refresh devices crashing before
|
0. Make sure PulseAudio can handle refresh devices crashing before
|
||||||
|
|
117
src/wasapi.cpp
117
src/wasapi.cpp
|
@ -10,16 +10,6 @@
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#define INITGUID
|
|
||||||
#define CINTERFACE
|
|
||||||
#define COBJMACROS
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#include <windows.h>
|
|
||||||
#include <mmdeviceapi.h>
|
|
||||||
#include <functiondiscoverykeys_devpkey.h>
|
|
||||||
#include <mmreg.h>
|
|
||||||
#include <audioclient.h>
|
|
||||||
|
|
||||||
// Attempting to use the Windows-supplied versions of these constants resulted
|
// Attempting to use the Windows-supplied versions of these constants resulted
|
||||||
// in `undefined reference` linker errors.
|
// in `undefined reference` linker errors.
|
||||||
const static GUID SOUNDIO_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {
|
const static GUID SOUNDIO_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {
|
||||||
|
@ -849,6 +839,16 @@ static void device_thread_run(void *arg) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FAILED(hr = IMMDeviceEnumerator_RegisterEndpointNotificationCallback(
|
||||||
|
siw->device_enumerator, &siw->device_events)))
|
||||||
|
{
|
||||||
|
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 (;;) {
|
for (;;) {
|
||||||
if (!siw->abort_flag.test_and_set())
|
if (!siw->abort_flag.test_and_set())
|
||||||
|
@ -858,7 +858,6 @@ 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);
|
||||||
}
|
}
|
||||||
|
@ -866,9 +865,10 @@ static void device_thread_run(void *arg) {
|
||||||
break;
|
break;
|
||||||
soundio_os_cond_signal(siw->cond, nullptr);
|
soundio_os_cond_signal(siw->cond, nullptr);
|
||||||
}
|
}
|
||||||
soundio_os_cond_wait(siw->cond, nullptr);
|
soundio_os_cond_wait(siw->scan_devices_cond, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(siw->device_enumerator, &siw->device_events);
|
||||||
IMMDeviceEnumerator_Release(siw->device_enumerator);
|
IMMDeviceEnumerator_Release(siw->device_enumerator);
|
||||||
siw->device_enumerator = nullptr;
|
siw->device_enumerator = nullptr;
|
||||||
}
|
}
|
||||||
|
@ -986,19 +986,101 @@ static void destroy_wasapi(struct SoundIoPrivate *si) {
|
||||||
|
|
||||||
if (siw->thread) {
|
if (siw->thread) {
|
||||||
siw->abort_flag.clear();
|
siw->abort_flag.clear();
|
||||||
soundio_os_cond_signal(siw->cond, nullptr);
|
soundio_os_cond_signal(siw->scan_devices_cond, nullptr);
|
||||||
soundio_os_thread_destroy(siw->thread);
|
soundio_os_thread_destroy(siw->thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (siw->cond)
|
if (siw->cond)
|
||||||
soundio_os_cond_destroy(siw->cond);
|
soundio_os_cond_destroy(siw->cond);
|
||||||
|
|
||||||
|
if (siw->scan_devices_cond)
|
||||||
|
soundio_os_cond_destroy(siw->scan_devices_cond);
|
||||||
|
|
||||||
if (siw->mutex)
|
if (siw->mutex)
|
||||||
soundio_os_mutex_destroy(siw->mutex);
|
soundio_os_mutex_destroy(siw->mutex);
|
||||||
|
|
||||||
soundio_destroy_devices_info(siw->ready_devices_info);
|
soundio_destroy_devices_info(siw->ready_devices_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline SoundIoPrivate *soundio_MMNotificationClient_si(IMMNotificationClient *client) {
|
||||||
|
SoundIoWasapi *siw = (SoundIoWasapi *)(((char *)client) - offsetof(SoundIoWasapi, device_events));
|
||||||
|
SoundIoPrivate *si = (SoundIoPrivate *)(((char *)siw) - offsetof(SoundIoPrivate, backend_data));
|
||||||
|
return si;
|
||||||
|
}
|
||||||
|
|
||||||
|
static STDMETHODIMP soundio_MMNotificationClient_QueryInterface(IMMNotificationClient *client,
|
||||||
|
REFIID riid, void **ppv)
|
||||||
|
{
|
||||||
|
if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IMMNotificationClient)) {
|
||||||
|
*ppv = client;
|
||||||
|
IUnknown_AddRef(client);
|
||||||
|
return S_OK;
|
||||||
|
} else {
|
||||||
|
*ppv = nullptr;
|
||||||
|
return E_NOINTERFACE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static STDMETHODIMP_(ULONG) soundio_MMNotificationClient_AddRef(IMMNotificationClient *client) {
|
||||||
|
SoundIoPrivate *si = soundio_MMNotificationClient_si(client);
|
||||||
|
SoundIoWasapi *siw = &si->backend_data.wasapi;
|
||||||
|
return InterlockedIncrement(&siw->device_events_refs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static STDMETHODIMP_(ULONG) soundio_MMNotificationClient_Release(IMMNotificationClient *client) {
|
||||||
|
SoundIoPrivate *si = soundio_MMNotificationClient_si(client);
|
||||||
|
SoundIoWasapi *siw = &si->backend_data.wasapi;
|
||||||
|
return InterlockedDecrement(&siw->device_events_refs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT queue_device_scan(IMMNotificationClient *client) {
|
||||||
|
SoundIoPrivate *si = soundio_MMNotificationClient_si(client);
|
||||||
|
SoundIoWasapi *siw = &si->backend_data.wasapi;
|
||||||
|
|
||||||
|
siw->device_scan_queued.store(true);
|
||||||
|
soundio_os_cond_signal(siw->scan_devices_cond, nullptr);
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static STDMETHODIMP soundio_MMNotificationClient_OnDeviceStateChanged(IMMNotificationClient *client,
|
||||||
|
LPCWSTR wid, DWORD state)
|
||||||
|
{
|
||||||
|
return queue_device_scan(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
static STDMETHODIMP soundio_MMNotificationClient_OnDeviceAdded(IMMNotificationClient *client, LPCWSTR wid) {
|
||||||
|
return queue_device_scan(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
static STDMETHODIMP soundio_MMNotificationClient_OnDeviceRemoved(IMMNotificationClient *client, LPCWSTR wid) {
|
||||||
|
return queue_device_scan(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
static STDMETHODIMP soundio_MMNotificationClient_OnDefaultDeviceChange(IMMNotificationClient *client,
|
||||||
|
EDataFlow flow, ERole role, LPCWSTR wid)
|
||||||
|
{
|
||||||
|
return queue_device_scan(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
static STDMETHODIMP soundio_MMNotificationClient_OnPropertyValueChanged(IMMNotificationClient *client,
|
||||||
|
LPCWSTR wid, const PROPERTYKEY key)
|
||||||
|
{
|
||||||
|
return queue_device_scan(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct IMMNotificationClientVtbl soundio_MMNotificationClient = {
|
||||||
|
soundio_MMNotificationClient_QueryInterface,
|
||||||
|
soundio_MMNotificationClient_AddRef,
|
||||||
|
soundio_MMNotificationClient_Release,
|
||||||
|
soundio_MMNotificationClient_OnDeviceStateChanged,
|
||||||
|
soundio_MMNotificationClient_OnDeviceAdded,
|
||||||
|
soundio_MMNotificationClient_OnDeviceRemoved,
|
||||||
|
soundio_MMNotificationClient_OnDefaultDeviceChange,
|
||||||
|
soundio_MMNotificationClient_OnPropertyValueChanged,
|
||||||
|
};
|
||||||
|
|
||||||
int soundio_wasapi_init(SoundIoPrivate *si) {
|
int soundio_wasapi_init(SoundIoPrivate *si) {
|
||||||
SoundIoWasapi *siw = &si->backend_data.wasapi;
|
SoundIoWasapi *siw = &si->backend_data.wasapi;
|
||||||
int err;
|
int err;
|
||||||
|
@ -1019,6 +1101,15 @@ int soundio_wasapi_init(SoundIoPrivate *si) {
|
||||||
return SoundIoErrorNoMem;
|
return SoundIoErrorNoMem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
siw->scan_devices_cond = soundio_os_cond_create();
|
||||||
|
if (!siw->scan_devices_cond) {
|
||||||
|
destroy_wasapi(si);
|
||||||
|
return SoundIoErrorNoMem;
|
||||||
|
}
|
||||||
|
|
||||||
|
siw->device_events.lpVtbl = &soundio_MMNotificationClient;
|
||||||
|
siw->device_events_refs = 1;
|
||||||
|
|
||||||
if ((err = soundio_os_thread_create(device_thread_run, si, false, &siw->thread))) {
|
if ((err = soundio_os_thread_create(device_thread_run, si, false, &siw->thread))) {
|
||||||
destroy_wasapi(si);
|
destroy_wasapi(si);
|
||||||
return err;
|
return err;
|
||||||
|
|
|
@ -12,8 +12,16 @@
|
||||||
#include "soundio/os.h"
|
#include "soundio/os.h"
|
||||||
#include "atomics.hpp"
|
#include "atomics.hpp"
|
||||||
#include "list.hpp"
|
#include "list.hpp"
|
||||||
struct IMMDeviceEnumerator;
|
|
||||||
struct IAudioClient;
|
#define INITGUID
|
||||||
|
#define CINTERFACE
|
||||||
|
#define COBJMACROS
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
#include <mmdeviceapi.h>
|
||||||
|
#include <functiondiscoverykeys_devpkey.h>
|
||||||
|
#include <mmreg.h>
|
||||||
|
#include <audioclient.h>
|
||||||
|
|
||||||
int soundio_wasapi_init(struct SoundIoPrivate *si);
|
int soundio_wasapi_init(struct SoundIoPrivate *si);
|
||||||
|
|
||||||
|
@ -25,6 +33,7 @@ struct SoundIoDeviceWasapi {
|
||||||
struct SoundIoWasapi {
|
struct SoundIoWasapi {
|
||||||
SoundIoOsMutex *mutex;
|
SoundIoOsMutex *mutex;
|
||||||
SoundIoOsCond *cond;
|
SoundIoOsCond *cond;
|
||||||
|
SoundIoOsCond *scan_devices_cond;
|
||||||
struct SoundIoOsThread *thread;
|
struct SoundIoOsThread *thread;
|
||||||
atomic_flag abort_flag;
|
atomic_flag abort_flag;
|
||||||
// this one is ready to be read with flush_events. protected by mutex
|
// this one is ready to be read with flush_events. protected by mutex
|
||||||
|
@ -35,6 +44,8 @@ struct SoundIoWasapi {
|
||||||
bool emitted_shutdown_cb;
|
bool emitted_shutdown_cb;
|
||||||
|
|
||||||
IMMDeviceEnumerator* device_enumerator;
|
IMMDeviceEnumerator* device_enumerator;
|
||||||
|
IMMNotificationClient device_events;
|
||||||
|
LONG device_events_refs;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SoundIoOutStreamWasapi {
|
struct SoundIoOutStreamWasapi {
|
||||||
|
|
Loading…
Reference in a new issue