CoreAudio: get the full list of available sample rates

This commit is contained in:
Andrew Kelley 2015-08-03 17:06:38 -07:00
parent c347629c8d
commit a774a72958
5 changed files with 92 additions and 17 deletions

View file

@ -244,8 +244,11 @@ view `coverage/index.html` in a browser.
0. implement CoreAudio (OSX) backend, get examples working
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
0. Make sure sending bogus device id results in "SoundIoErrorNoSuchDevice" on each backend
0. Make sure PulseAudio can handle refresh devices crashing before
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 ASIO (Windows) backend, get examples working
0. Integrate into libgroove and test with Groove Basin

View file

@ -22,6 +22,7 @@ enum SoundIoError {
SoundIoErrorInitAudioBackend,
SoundIoErrorSystemResources,
SoundIoErrorOpeningDevice,
SoundIoErrorNoSuchDevice,
SoundIoErrorInvalid,
SoundIoErrorBackendUnavailable,
SoundIoErrorStreaming,

View file

@ -71,6 +71,10 @@ static void destroy_ca(struct SoundIoPrivate *si) {
soundio_destroy_devices_info(sica->ready_devices_info);
}
static CFStringRef to_cf_string(const char *str) {
return CFStringCreateWithCString(kCFAllocatorDefault, str, kCFStringEncodingUTF8);
}
// Possible errors:
// * SoundIoErrorNoMem
// * SoundIoErrorEncodingString
@ -325,6 +329,7 @@ struct RefreshDevices {
AudioChannelLayout *audio_channel_layout;
char *device_uid;
int device_uid_len;
AudioValueRange *avr_array;
};
static void deinit_refresh_devices(RefreshDevices *rd) {
@ -337,6 +342,7 @@ static void deinit_refresh_devices(RefreshDevices *rd) {
soundio_device_unref(rd->device);
free(rd->audio_channel_layout);
free(rd->device_uid);
free(rd->avr_array);
}
// 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) {
AudioObjectID deviceID = rd.devices[i];
for (int device_i = 0; device_i < device_count; device_i += 1) {
AudioObjectID deviceID = rd.devices[device_i];
prop_address.mSelector = kAudioObjectPropertyName;
prop_address.mScope = kAudioObjectPropertyScopeGlobal;
@ -559,16 +565,36 @@ static int refresh_devices(struct SoundIoPrivate *si) {
prop_address.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
prop_address.mScope = aim_to_scope(aim);
prop_address.mElement = kAudioObjectPropertyElementMaster;
io_size = sizeof(AudioValueRange);
AudioValueRange avr;
if ((os_err = AudioObjectGetPropertyData(deviceID, &prop_address, 0, nullptr,
&io_size, &avr)))
if ((os_err = AudioObjectGetPropertyDataSize(deviceID, &prop_address, 0, nullptr,
&io_size)))
{
deinit_refresh_devices(&rd);
return SoundIoErrorOpeningDevice;
}
rd.device->sample_rate_min = avr.mMinimum;
rd.device->sample_rate_max = avr.mMaximum;
int avr_array_len = io_size / sizeof(AudioValueRange);
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.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.mElement = kAudioObjectPropertyElementMaster;
io_size = sizeof(AudioValueRange);
AudioValueRange avr;
if ((os_err = AudioObjectGetPropertyData(deviceID, &prop_address, 0, nullptr,
&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) {
soundio_panic("TODO");
static void outstream_destroy_ca(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
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");
}

View file

@ -36,13 +36,9 @@ struct SoundIoCoreAudio {
bool emitted_shutdown_cb;
};
struct SoundIoOutStreamCoreAudioPort {
};
struct SoundIoOutStreamCoreAudio {
};
struct SoundIoInStreamCoreAudioPort {
CFStringRef device_uid_string_ref;
AudioDeviceID device_id;
};
struct SoundIoInStreamCoreAudio {

View file

@ -61,6 +61,7 @@ const char *soundio_strerror(int error) {
case SoundIoErrorInitAudioBackend: return "unable to initialize audio backend";
case SoundIoErrorSystemResources: return "system resource not available";
case SoundIoErrorOpeningDevice: return "unable to open device";
case SoundIoErrorNoSuchDevice: return "unable to open device";
case SoundIoErrorInvalid: return "invalid value";
case SoundIoErrorBackendUnavailable: return "backend unavailable";
case SoundIoErrorStreaming: return "unrecoverable streaming failure";