2015-08-08 22:22:50 +00:00
|
|
|
#include "wasapi.hpp"
|
|
|
|
#include "soundio.hpp"
|
|
|
|
|
2015-08-10 22:22:08 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#define INITGUID
|
|
|
|
#define CINTERFACE
|
|
|
|
#define COBJMACROS
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
2015-08-11 06:18:08 +00:00
|
|
|
#include <windows.h>
|
2015-08-10 22:22:08 +00:00
|
|
|
#include <mmdeviceapi.h>
|
2015-08-11 06:18:08 +00:00
|
|
|
#include <functiondiscoverykeys_devpkey.h>
|
2015-08-12 18:01:06 +00:00
|
|
|
#include <mmreg.h>
|
2015-08-12 22:32:44 +00:00
|
|
|
#include <audioclient.h>
|
2015-08-11 06:18:08 +00:00
|
|
|
|
2015-08-12 18:01:06 +00:00
|
|
|
// 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}};
|
2015-08-11 06:18:08 +00:00
|
|
|
|
|
|
|
// converts a windows wide string to a UTF-8 encoded char *
|
|
|
|
// Possible errors:
|
|
|
|
// * SoundIoErrorNoMem
|
|
|
|
// * SoundIoErrorEncodingString
|
|
|
|
static int from_lpwstr(LPWSTR lpwstr, char **out_str, int *out_str_len) {
|
|
|
|
DWORD flags = 0;
|
|
|
|
int buf_size = WideCharToMultiByte(CP_UTF8, flags, lpwstr, -1, nullptr, 0, nullptr, nullptr);
|
|
|
|
|
|
|
|
if (buf_size == 0)
|
|
|
|
return SoundIoErrorEncodingString;
|
|
|
|
|
|
|
|
char *buf = allocate<char>(buf_size + 1);
|
|
|
|
if (!buf)
|
|
|
|
return SoundIoErrorNoMem;
|
|
|
|
|
|
|
|
if (WideCharToMultiByte(CP_UTF8, flags, lpwstr, -1, buf, buf_size, nullptr, nullptr) != buf_size) {
|
|
|
|
free(buf);
|
|
|
|
return SoundIoErrorEncodingString;
|
|
|
|
}
|
|
|
|
|
|
|
|
*out_str = buf;
|
|
|
|
*out_str_len = buf_size;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SoundIoDeviceAim data_flow_to_aim(EDataFlow data_flow) {
|
|
|
|
return (data_flow == eRender) ? SoundIoDeviceAimOutput : SoundIoDeviceAimInput;
|
|
|
|
}
|
2015-08-10 22:22:08 +00:00
|
|
|
|
2015-08-12 22:32:44 +00:00
|
|
|
|
|
|
|
static double from_reference_time(REFERENCE_TIME rt) {
|
|
|
|
return ((double)rt) / 10000000.0;
|
|
|
|
}
|
|
|
|
|
2015-08-10 22:22:08 +00:00
|
|
|
struct RefreshDevices {
|
|
|
|
IMMDeviceCollection *collection;
|
2015-08-11 06:18:08 +00:00
|
|
|
IMMDevice *mm_device;
|
2015-08-12 18:01:06 +00:00
|
|
|
IMMDevice *default_render_device;
|
|
|
|
IMMDevice *default_capture_device;
|
2015-08-11 06:18:08 +00:00
|
|
|
IMMEndpoint *endpoint;
|
|
|
|
IPropertyStore *prop_store;
|
|
|
|
LPWSTR lpwstr;
|
|
|
|
PROPVARIANT prop_variant_value;
|
|
|
|
bool prop_variant_value_inited;
|
|
|
|
SoundIoDevicesInfo *devices_info;
|
|
|
|
SoundIoDevice *device;
|
2015-08-12 18:01:06 +00:00
|
|
|
char *default_render_id;
|
|
|
|
int default_render_id_len;
|
|
|
|
char *default_capture_id;
|
|
|
|
int default_capture_id_len;
|
2015-08-10 22:22:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static void deinit_refresh_devices(RefreshDevices *rd) {
|
2015-08-11 06:18:08 +00:00
|
|
|
soundio_destroy_devices_info(rd->devices_info);
|
|
|
|
soundio_device_unref(rd->device);
|
|
|
|
if (rd->mm_device)
|
|
|
|
IMMDevice_Release(rd->mm_device);
|
2015-08-12 18:01:06 +00:00
|
|
|
if (rd->default_render_device)
|
|
|
|
IMMDevice_Release(rd->default_render_device);
|
|
|
|
if (rd->default_capture_device)
|
|
|
|
IMMDevice_Release(rd->default_capture_device);
|
2015-08-10 22:22:08 +00:00
|
|
|
if (rd->collection)
|
|
|
|
IMMDeviceCollection_Release(rd->collection);
|
2015-08-11 06:18:08 +00:00
|
|
|
if (rd->lpwstr)
|
|
|
|
CoTaskMemFree(rd->lpwstr);
|
|
|
|
if (rd->endpoint)
|
|
|
|
IMMEndpoint_Release(rd->endpoint);
|
|
|
|
if (rd->prop_store)
|
|
|
|
IPropertyStore_Release(rd->prop_store);
|
|
|
|
if (rd->prop_variant_value_inited)
|
|
|
|
PropVariantClear(&rd->prop_variant_value);
|
2015-08-10 22:22:08 +00:00
|
|
|
}
|
|
|
|
|
2015-08-12 22:32:44 +00:00
|
|
|
static void destruct_device(SoundIoDevicePrivate *dev) {
|
|
|
|
SoundIoDeviceWasapi *dw = &dev->backend_data.wasapi;
|
|
|
|
if (dw->audio_client)
|
|
|
|
IUnknown_Release(dw->audio_client);
|
|
|
|
}
|
|
|
|
|
2015-08-10 22:22:08 +00:00
|
|
|
static int refresh_devices(SoundIoPrivate *si) {
|
2015-08-11 06:18:08 +00:00
|
|
|
SoundIo *soundio = &si->pub;
|
2015-08-10 22:22:08 +00:00
|
|
|
SoundIoWasapi *siw = &si->backend_data.wasapi;
|
|
|
|
RefreshDevices rd = {0};
|
2015-08-11 06:18:08 +00:00
|
|
|
int err;
|
2015-08-10 22:22:08 +00:00
|
|
|
HRESULT hr;
|
2015-08-12 18:01:06 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-08-10 22:22:08 +00:00
|
|
|
if (FAILED(hr = IMMDeviceEnumerator_EnumAudioEndpoints(siw->device_enumerator,
|
|
|
|
eAll, DEVICE_STATE_ACTIVE, &rd.collection)))
|
|
|
|
{
|
|
|
|
deinit_refresh_devices(&rd);
|
|
|
|
return SoundIoErrorOpeningDevice;
|
|
|
|
}
|
|
|
|
|
2015-08-11 06:18:08 +00:00
|
|
|
UINT unsigned_count;
|
|
|
|
if (FAILED(hr = IMMDeviceCollection_GetCount(rd.collection, &unsigned_count))) {
|
2015-08-10 22:22:08 +00:00
|
|
|
deinit_refresh_devices(&rd);
|
|
|
|
return SoundIoErrorOpeningDevice;
|
|
|
|
}
|
|
|
|
|
2015-08-11 06:18:08 +00:00
|
|
|
if (unsigned_count > (UINT)INT_MAX) {
|
|
|
|
deinit_refresh_devices(&rd);
|
|
|
|
return SoundIoErrorIncompatibleDevice;
|
|
|
|
}
|
|
|
|
|
|
|
|
int device_count = unsigned_count;
|
|
|
|
|
|
|
|
if (!(rd.devices_info = allocate<SoundIoDevicesInfo>(1))) {
|
|
|
|
deinit_refresh_devices(&rd);
|
|
|
|
return SoundIoErrorNoMem;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int device_i = 0; device_i < device_count; device_i += 1) {
|
|
|
|
if (rd.mm_device)
|
|
|
|
IMMDevice_Release(rd.mm_device);
|
|
|
|
if (FAILED(hr = IMMDeviceCollection_Item(rd.collection, device_i, &rd.mm_device))) {
|
|
|
|
deinit_refresh_devices(&rd);
|
|
|
|
return SoundIoErrorOpeningDevice;
|
|
|
|
}
|
|
|
|
if (rd.lpwstr)
|
|
|
|
CoTaskMemFree(rd.lpwstr);
|
|
|
|
if (FAILED(hr = IMMDevice_GetId(rd.mm_device, &rd.lpwstr))) {
|
|
|
|
deinit_refresh_devices(&rd);
|
|
|
|
return SoundIoErrorOpeningDevice;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-08-12 22:32:44 +00:00
|
|
|
|
2015-08-11 06:18:08 +00:00
|
|
|
SoundIoDevicePrivate *dev = allocate<SoundIoDevicePrivate>(1);
|
|
|
|
if (!dev) {
|
|
|
|
deinit_refresh_devices(&rd);
|
|
|
|
return SoundIoErrorNoMem;
|
|
|
|
}
|
2015-08-12 22:32:44 +00:00
|
|
|
SoundIoDeviceWasapi *dw = &dev->backend_data.wasapi;
|
|
|
|
dev->destruct = destruct_device;
|
2015-08-11 06:18:08 +00:00
|
|
|
assert(!rd.device);
|
|
|
|
rd.device = &dev->pub;
|
|
|
|
rd.device->ref_count = 1;
|
|
|
|
rd.device->soundio = soundio;
|
|
|
|
rd.device->is_raw = false; // TODO
|
|
|
|
|
|
|
|
int device_id_len;
|
|
|
|
if ((err = from_lpwstr(rd.lpwstr, &rd.device->id, &device_id_len))) {
|
|
|
|
deinit_refresh_devices(&rd);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2015-08-12 22:32:44 +00:00
|
|
|
if (FAILED(hr = IMMDevice_Activate(rd.mm_device, IID_IAudioClient,
|
|
|
|
CLSCTX_ALL, nullptr, (void**)&dw->audio_client)))
|
|
|
|
{
|
|
|
|
deinit_refresh_devices(&rd);
|
|
|
|
return SoundIoErrorOpeningDevice;
|
|
|
|
}
|
|
|
|
|
|
|
|
REFERENCE_TIME default_device_period;
|
|
|
|
REFERENCE_TIME min_device_period;
|
|
|
|
if (FAILED(hr = IAudioClient_GetDevicePeriod(dw->audio_client,
|
|
|
|
&default_device_period, &min_device_period)))
|
|
|
|
{
|
|
|
|
deinit_refresh_devices(&rd);
|
|
|
|
return SoundIoErrorOpeningDevice;
|
|
|
|
}
|
|
|
|
rd.device->period_duration_current = from_reference_time(default_device_period);
|
|
|
|
rd.device->period_duration_min = from_reference_time(min_device_period);
|
|
|
|
|
|
|
|
|
2015-08-11 06:18:08 +00:00
|
|
|
if (rd.endpoint)
|
|
|
|
IMMEndpoint_Release(rd.endpoint);
|
|
|
|
if (FAILED(hr = IMMDevice_QueryInterface(rd.mm_device, IID_IMMEndpoint, (void**)&rd.endpoint))) {
|
|
|
|
deinit_refresh_devices(&rd);
|
|
|
|
return SoundIoErrorOpeningDevice;
|
|
|
|
}
|
|
|
|
|
|
|
|
EDataFlow data_flow;
|
|
|
|
if (FAILED(hr = IMMEndpoint_GetDataFlow(rd.endpoint, &data_flow))) {
|
|
|
|
deinit_refresh_devices(&rd);
|
|
|
|
return SoundIoErrorOpeningDevice;
|
|
|
|
}
|
|
|
|
|
|
|
|
rd.device->aim = data_flow_to_aim(data_flow);
|
|
|
|
|
|
|
|
if (rd.prop_store)
|
|
|
|
IPropertyStore_Release(rd.prop_store);
|
|
|
|
if (FAILED(hr = IMMDevice_OpenPropertyStore(rd.mm_device, STGM_READ, &rd.prop_store))) {
|
|
|
|
deinit_refresh_devices(&rd);
|
|
|
|
return SoundIoErrorOpeningDevice;
|
|
|
|
}
|
|
|
|
|
|
|
|
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_Device_FriendlyName, &rd.prop_variant_value)))
|
|
|
|
{
|
|
|
|
deinit_refresh_devices(&rd);
|
|
|
|
return SoundIoErrorOpeningDevice;
|
|
|
|
}
|
|
|
|
if (!rd.prop_variant_value.pwszVal) {
|
|
|
|
deinit_refresh_devices(&rd);
|
|
|
|
return SoundIoErrorOpeningDevice;
|
|
|
|
}
|
|
|
|
int device_name_len;
|
|
|
|
if ((err = from_lpwstr(rd.prop_variant_value.pwszVal, &rd.device->name, &device_name_len))) {
|
|
|
|
deinit_refresh_devices(&rd);
|
|
|
|
return SoundIoErrorOpeningDevice;
|
|
|
|
}
|
|
|
|
|
2015-08-12 18:01:06 +00:00
|
|
|
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);
|
|
|
|
|
2015-08-11 06:18:08 +00:00
|
|
|
SoundIoList<SoundIoDevice *> *device_list;
|
|
|
|
if (rd.device->aim == SoundIoDeviceAimOutput) {
|
|
|
|
device_list = &rd.devices_info->output_devices;
|
2015-08-12 18:01:06 +00:00
|
|
|
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;
|
2015-08-11 06:18:08 +00:00
|
|
|
} else {
|
|
|
|
assert(rd.device->aim == SoundIoDeviceAimInput);
|
|
|
|
device_list = &rd.devices_info->input_devices;
|
2015-08-12 18:01:06 +00:00
|
|
|
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;
|
2015-08-11 06:18:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((err = device_list->append(rd.device))) {
|
|
|
|
deinit_refresh_devices(&rd);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
rd.device = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
soundio_os_mutex_lock(siw->mutex);
|
|
|
|
soundio_destroy_devices_info(siw->ready_devices_info);
|
|
|
|
siw->ready_devices_info = rd.devices_info;
|
|
|
|
soundio->on_events_signal(soundio);
|
|
|
|
soundio_os_mutex_unlock(siw->mutex);
|
|
|
|
|
|
|
|
rd.devices_info = nullptr;
|
|
|
|
deinit_refresh_devices(&rd);
|
|
|
|
|
2015-08-10 22:22:08 +00:00
|
|
|
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)) {
|
2015-08-12 18:01:06 +00:00
|
|
|
// TODO separate cond for signaling devices like coreaudio
|
2015-08-10 22:22:08 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2015-08-08 22:22:50 +00:00
|
|
|
static void flush_events_wasapi(struct SoundIoPrivate *si) {
|
2015-08-10 22:22:08 +00:00
|
|
|
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);
|
2015-08-08 22:22:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void wait_events_wasapi(struct SoundIoPrivate *si) {
|
2015-08-10 22:22:08 +00:00
|
|
|
SoundIoWasapi *siw = &si->backend_data.wasapi;
|
|
|
|
flush_events_wasapi(si);
|
|
|
|
soundio_os_cond_wait(siw->cond, nullptr);
|
2015-08-08 22:22:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void wakeup_wasapi(struct SoundIoPrivate *si) {
|
2015-08-10 22:22:08 +00:00
|
|
|
SoundIoWasapi *siw = &si->backend_data.wasapi;
|
|
|
|
soundio_os_cond_signal(siw->cond, nullptr);
|
2015-08-08 22:22:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void outstream_destroy_wasapi(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
|
|
|
|
soundio_panic("TODO");
|
|
|
|
}
|
|
|
|
|
|
|
|
static int outstream_open_wasapi(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
|
|
|
|
soundio_panic("TODO");
|
|
|
|
}
|
|
|
|
|
|
|
|
static int outstream_pause_wasapi(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os, bool pause) {
|
|
|
|
soundio_panic("TODO");
|
|
|
|
}
|
|
|
|
|
|
|
|
static int outstream_start_wasapi(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
|
|
|
|
soundio_panic("TODO");
|
|
|
|
}
|
|
|
|
|
|
|
|
static int outstream_begin_write_wasapi(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os,
|
|
|
|
SoundIoChannelArea **out_areas, int *frame_count)
|
|
|
|
{
|
|
|
|
soundio_panic("TODO");
|
|
|
|
}
|
|
|
|
|
|
|
|
static int outstream_end_write_wasapi(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
|
|
|
|
soundio_panic("TODO");
|
|
|
|
}
|
|
|
|
|
|
|
|
static int outstream_clear_buffer_wasapi(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
|
|
|
|
soundio_panic("TODO");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void instream_destroy_wasapi(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) {
|
|
|
|
soundio_panic("TODO");
|
|
|
|
}
|
|
|
|
|
|
|
|
static int instream_open_wasapi(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) {
|
|
|
|
soundio_panic("TODO");
|
|
|
|
}
|
|
|
|
|
|
|
|
static int instream_pause_wasapi(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is, bool pause) {
|
|
|
|
soundio_panic("TODO");
|
|
|
|
}
|
|
|
|
|
|
|
|
static int instream_start_wasapi(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) {
|
|
|
|
soundio_panic("TODO");
|
|
|
|
}
|
|
|
|
|
|
|
|
static int instream_begin_read_wasapi(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is,
|
|
|
|
SoundIoChannelArea **out_areas, int *frame_count)
|
|
|
|
{
|
|
|
|
soundio_panic("TODO");
|
|
|
|
}
|
|
|
|
|
|
|
|
static int instream_end_read_wasapi(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) {
|
|
|
|
soundio_panic("TODO");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void destroy_wasapi(struct SoundIoPrivate *si) {
|
2015-08-10 22:22:08 +00:00
|
|
|
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);
|
2015-08-08 22:22:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int soundio_wasapi_init(SoundIoPrivate *si) {
|
2015-08-10 22:22:08 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-08-08 22:22:50 +00:00
|
|
|
si->destroy = destroy_wasapi;
|
|
|
|
si->flush_events = flush_events_wasapi;
|
|
|
|
si->wait_events = wait_events_wasapi;
|
|
|
|
si->wakeup = wakeup_wasapi;
|
|
|
|
|
|
|
|
si->outstream_open = outstream_open_wasapi;
|
|
|
|
si->outstream_destroy = outstream_destroy_wasapi;
|
|
|
|
si->outstream_start = outstream_start_wasapi;
|
|
|
|
si->outstream_begin_write = outstream_begin_write_wasapi;
|
|
|
|
si->outstream_end_write = outstream_end_write_wasapi;
|
|
|
|
si->outstream_clear_buffer = outstream_clear_buffer_wasapi;
|
|
|
|
si->outstream_pause = outstream_pause_wasapi;
|
|
|
|
|
|
|
|
si->instream_open = instream_open_wasapi;
|
|
|
|
si->instream_destroy = instream_destroy_wasapi;
|
|
|
|
si->instream_start = instream_start_wasapi;
|
|
|
|
si->instream_begin_read = instream_begin_read_wasapi;
|
|
|
|
si->instream_end_read = instream_end_read_wasapi;
|
|
|
|
si->instream_pause = instream_pause_wasapi;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|