mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2024-12-31 22:45:48 +00:00
CoreAudio: get the full list of available sample rates
This commit is contained in:
parent
c347629c8d
commit
a774a72958
|
@ -244,8 +244,11 @@ view `coverage/index.html` in a browser.
|
||||||
0. implement CoreAudio (OSX) backend, get examples working
|
0. implement CoreAudio (OSX) backend, get examples working
|
||||||
0. Add some builtin channel layouts from
|
0. Add some builtin channel layouts from
|
||||||
https://developer.apple.com/library/mac/documentation/MusicAudio/Reference/CoreAudioDataTypesRef/#//apple_ref/doc/constant_group/Audio_Channel_Layout_Tags
|
https://developer.apple.com/library/mac/documentation/MusicAudio/Reference/CoreAudioDataTypesRef/#//apple_ref/doc/constant_group/Audio_Channel_Layout_Tags
|
||||||
|
0. Make sure sending bogus device id results in "SoundIoErrorNoSuchDevice" on each backend
|
||||||
0. Make sure PulseAudio can handle refresh devices crashing before
|
0. Make sure PulseAudio can handle refresh devices crashing before
|
||||||
block_until_have_devices
|
block_until_have_devices
|
||||||
|
0. CoreAudio exposes a list of min/max pairs of supported sample rates. libsoundio
|
||||||
|
should do the same.
|
||||||
0. implement WASAPI (Windows) backend, get examples working
|
0. implement WASAPI (Windows) backend, get examples working
|
||||||
0. implement ASIO (Windows) backend, get examples working
|
0. implement ASIO (Windows) backend, get examples working
|
||||||
0. Integrate into libgroove and test with Groove Basin
|
0. Integrate into libgroove and test with Groove Basin
|
||||||
|
|
|
@ -22,6 +22,7 @@ enum SoundIoError {
|
||||||
SoundIoErrorInitAudioBackend,
|
SoundIoErrorInitAudioBackend,
|
||||||
SoundIoErrorSystemResources,
|
SoundIoErrorSystemResources,
|
||||||
SoundIoErrorOpeningDevice,
|
SoundIoErrorOpeningDevice,
|
||||||
|
SoundIoErrorNoSuchDevice,
|
||||||
SoundIoErrorInvalid,
|
SoundIoErrorInvalid,
|
||||||
SoundIoErrorBackendUnavailable,
|
SoundIoErrorBackendUnavailable,
|
||||||
SoundIoErrorStreaming,
|
SoundIoErrorStreaming,
|
||||||
|
|
|
@ -71,6 +71,10 @@ static void destroy_ca(struct SoundIoPrivate *si) {
|
||||||
soundio_destroy_devices_info(sica->ready_devices_info);
|
soundio_destroy_devices_info(sica->ready_devices_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static CFStringRef to_cf_string(const char *str) {
|
||||||
|
return CFStringCreateWithCString(kCFAllocatorDefault, str, kCFStringEncodingUTF8);
|
||||||
|
}
|
||||||
|
|
||||||
// Possible errors:
|
// Possible errors:
|
||||||
// * SoundIoErrorNoMem
|
// * SoundIoErrorNoMem
|
||||||
// * SoundIoErrorEncodingString
|
// * SoundIoErrorEncodingString
|
||||||
|
@ -325,6 +329,7 @@ struct RefreshDevices {
|
||||||
AudioChannelLayout *audio_channel_layout;
|
AudioChannelLayout *audio_channel_layout;
|
||||||
char *device_uid;
|
char *device_uid;
|
||||||
int device_uid_len;
|
int device_uid_len;
|
||||||
|
AudioValueRange *avr_array;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void deinit_refresh_devices(RefreshDevices *rd) {
|
static void deinit_refresh_devices(RefreshDevices *rd) {
|
||||||
|
@ -337,6 +342,7 @@ static void deinit_refresh_devices(RefreshDevices *rd) {
|
||||||
soundio_device_unref(rd->device);
|
soundio_device_unref(rd->device);
|
||||||
free(rd->audio_channel_layout);
|
free(rd->audio_channel_layout);
|
||||||
free(rd->device_uid);
|
free(rd->device_uid);
|
||||||
|
free(rd->avr_array);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO get the device UID which persists between unplug/plug
|
// TODO get the device UID which persists between unplug/plug
|
||||||
|
@ -408,8 +414,8 @@ static int refresh_devices(struct SoundIoPrivate *si) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < device_count; i += 1) {
|
for (int device_i = 0; device_i < device_count; device_i += 1) {
|
||||||
AudioObjectID deviceID = rd.devices[i];
|
AudioObjectID deviceID = rd.devices[device_i];
|
||||||
|
|
||||||
prop_address.mSelector = kAudioObjectPropertyName;
|
prop_address.mSelector = kAudioObjectPropertyName;
|
||||||
prop_address.mScope = kAudioObjectPropertyScopeGlobal;
|
prop_address.mScope = kAudioObjectPropertyScopeGlobal;
|
||||||
|
@ -559,16 +565,36 @@ static int refresh_devices(struct SoundIoPrivate *si) {
|
||||||
prop_address.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
|
prop_address.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
|
||||||
prop_address.mScope = aim_to_scope(aim);
|
prop_address.mScope = aim_to_scope(aim);
|
||||||
prop_address.mElement = kAudioObjectPropertyElementMaster;
|
prop_address.mElement = kAudioObjectPropertyElementMaster;
|
||||||
io_size = sizeof(AudioValueRange);
|
if ((os_err = AudioObjectGetPropertyDataSize(deviceID, &prop_address, 0, nullptr,
|
||||||
AudioValueRange avr;
|
&io_size)))
|
||||||
if ((os_err = AudioObjectGetPropertyData(deviceID, &prop_address, 0, nullptr,
|
|
||||||
&io_size, &avr)))
|
|
||||||
{
|
{
|
||||||
deinit_refresh_devices(&rd);
|
deinit_refresh_devices(&rd);
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
}
|
}
|
||||||
rd.device->sample_rate_min = avr.mMinimum;
|
int avr_array_len = io_size / sizeof(AudioValueRange);
|
||||||
rd.device->sample_rate_max = avr.mMaximum;
|
rd.avr_array = (AudioValueRange*)allocate<char>(io_size);
|
||||||
|
|
||||||
|
if (!rd.avr_array) {
|
||||||
|
deinit_refresh_devices(&rd);
|
||||||
|
return SoundIoErrorNoMem;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((os_err = AudioObjectGetPropertyData(deviceID, &prop_address, 0, nullptr,
|
||||||
|
&io_size, rd.avr_array)))
|
||||||
|
{
|
||||||
|
deinit_refresh_devices(&rd);
|
||||||
|
return SoundIoErrorOpeningDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < avr_array_len; i += 1) {
|
||||||
|
AudioValueRange *avr = &rd.avr_array[i];
|
||||||
|
int min_val = ceil(avr->mMinimum);
|
||||||
|
int max_val = floor(avr->mMaximum);
|
||||||
|
if (rd.device->sample_rate_min == 0 || min_val < rd.device->sample_rate_min)
|
||||||
|
rd.device->sample_rate_min = min_val;
|
||||||
|
if (rd.device->sample_rate_max == 0 || max_val > rd.device->sample_rate_max)
|
||||||
|
rd.device->sample_rate_max = max_val;
|
||||||
|
}
|
||||||
|
|
||||||
prop_address.mSelector = kAudioDevicePropertyBufferFrameSize;
|
prop_address.mSelector = kAudioDevicePropertyBufferFrameSize;
|
||||||
prop_address.mScope = aim_to_scope(aim);
|
prop_address.mScope = aim_to_scope(aim);
|
||||||
|
@ -589,6 +615,7 @@ static int refresh_devices(struct SoundIoPrivate *si) {
|
||||||
prop_address.mScope = aim_to_scope(aim);
|
prop_address.mScope = aim_to_scope(aim);
|
||||||
prop_address.mElement = kAudioObjectPropertyElementMaster;
|
prop_address.mElement = kAudioObjectPropertyElementMaster;
|
||||||
io_size = sizeof(AudioValueRange);
|
io_size = sizeof(AudioValueRange);
|
||||||
|
AudioValueRange avr;
|
||||||
if ((os_err = AudioObjectGetPropertyData(deviceID, &prop_address, 0, nullptr,
|
if ((os_err = AudioObjectGetPropertyData(deviceID, &prop_address, 0, nullptr,
|
||||||
&io_size, &avr)))
|
&io_size, &avr)))
|
||||||
{
|
{
|
||||||
|
@ -720,11 +747,58 @@ static void device_thread_run(void *arg) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int outstream_open_ca(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
|
static void outstream_destroy_ca(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
|
||||||
soundio_panic("TODO");
|
SoundIoOutStreamCoreAudio *osca = &os->backend_data.coreaudio;
|
||||||
|
if (osca->device_uid_string_ref)
|
||||||
|
CFRelease(osca->device_uid_string_ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void outstream_destroy_ca(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
|
/*
|
||||||
|
@constant kAudioHardwarePropertyTranslateUIDToDevice
|
||||||
|
This property fetches the AudioObjectID that corresponds to the AudioDevice
|
||||||
|
that has the given UID. The UID is passed in via the qualifier as a CFString
|
||||||
|
while the AudioObjectID for the AudioDevice is returned to the caller as the
|
||||||
|
property's data. Note that an error is not returned if the UID doesn't refer
|
||||||
|
to any AudioDevices. Rather, this property will return kAudioObjectUnknown
|
||||||
|
as the value of the property.
|
||||||
|
*/
|
||||||
|
static int outstream_open_ca(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
|
||||||
|
SoundIoOutStreamCoreAudio *osca = &os->backend_data.coreaudio;
|
||||||
|
SoundIoOutStream *outstream = &os->pub;
|
||||||
|
SoundIoDevice *device = outstream->device;
|
||||||
|
|
||||||
|
UInt32 io_size;
|
||||||
|
OSStatus os_err;
|
||||||
|
|
||||||
|
|
||||||
|
osca->device_uid_string_ref = to_cf_string(device->id);
|
||||||
|
if (!osca->device_uid_string_ref) {
|
||||||
|
outstream_destroy_ca(si, os);
|
||||||
|
return SoundIoErrorNoMem;
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioObjectPropertyAddress prop_address = {
|
||||||
|
kAudioHardwarePropertyTranslateUIDToDevice,
|
||||||
|
kAudioObjectPropertyScopeGlobal,
|
||||||
|
kAudioObjectPropertyElementMaster
|
||||||
|
};
|
||||||
|
io_size = sizeof(AudioDeviceID);
|
||||||
|
if ((os_err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &prop_address,
|
||||||
|
sizeof(CFStringRef), osca->device_uid_string_ref,
|
||||||
|
&io_size, &osca->device_id)))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "derpde derpdee derr\n");
|
||||||
|
outstream_destroy_ca(si, os);
|
||||||
|
return SoundIoErrorOpeningDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (osca->device_id == kAudioObjectUnknown) {
|
||||||
|
outstream_destroy_ca(si, os);
|
||||||
|
return SoundIoErrorNoSuchDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "id: %ld\n", (long)osca->device_id);
|
||||||
|
|
||||||
soundio_panic("TODO");
|
soundio_panic("TODO");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,13 +36,9 @@ struct SoundIoCoreAudio {
|
||||||
bool emitted_shutdown_cb;
|
bool emitted_shutdown_cb;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SoundIoOutStreamCoreAudioPort {
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SoundIoOutStreamCoreAudio {
|
struct SoundIoOutStreamCoreAudio {
|
||||||
};
|
CFStringRef device_uid_string_ref;
|
||||||
|
AudioDeviceID device_id;
|
||||||
struct SoundIoInStreamCoreAudioPort {
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SoundIoInStreamCoreAudio {
|
struct SoundIoInStreamCoreAudio {
|
||||||
|
|
|
@ -61,6 +61,7 @@ const char *soundio_strerror(int error) {
|
||||||
case SoundIoErrorInitAudioBackend: return "unable to initialize audio backend";
|
case SoundIoErrorInitAudioBackend: return "unable to initialize audio backend";
|
||||||
case SoundIoErrorSystemResources: return "system resource not available";
|
case SoundIoErrorSystemResources: return "system resource not available";
|
||||||
case SoundIoErrorOpeningDevice: return "unable to open device";
|
case SoundIoErrorOpeningDevice: return "unable to open device";
|
||||||
|
case SoundIoErrorNoSuchDevice: return "unable to open device";
|
||||||
case SoundIoErrorInvalid: return "invalid value";
|
case SoundIoErrorInvalid: return "invalid value";
|
||||||
case SoundIoErrorBackendUnavailable: return "backend unavailable";
|
case SoundIoErrorBackendUnavailable: return "backend unavailable";
|
||||||
case SoundIoErrorStreaming: return "unrecoverable streaming failure";
|
case SoundIoErrorStreaming: return "unrecoverable streaming failure";
|
||||||
|
|
Loading…
Reference in a new issue