From df0ca8a772e60581a4d3efa5f0fa327c8b5ca72f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 3 Aug 2015 22:27:09 -0700 Subject: [PATCH] CoreAudio: sine wave example works --- CMakeLists.txt | 7 ++ cmake/FindCoreAudio.cmake | 2 +- src/coreaudio.cpp | 184 ++++++++++++++++++++++++-------------- src/coreaudio.hpp | 13 ++- 4 files changed, 136 insertions(+), 70 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ce68d1..d611d22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,11 +65,16 @@ if(COREAUDIO_FOUND) find_path(COREFOUNDATION_INCLUDE_DIR NAMES CoreFoundation.h) find_library(COREFOUNDATION_LIBRARY NAMES CoreFoundation) include_directories(${COREFOUNDATION_INCLUDE_DIR}) + + find_path(AUDIOUNIT_INCLUDE_DIR NAMES AudioUnit.h) + find_library(AUDIOUNIT_LIBRARY NAMES AudioUnit) + include_directories(${AUDIOUNIT_INCLUDE_DIR}) else() set(STATUS_COREAUDIO "not found") set(SOUNDIO_HAVE_COREAUDIO false) set(COREAUDIO_LIBRARY "") set(COREFOUNDATION_LIBRARY "") + set(AUDIOUNIT_LIBRARY "") endif() @@ -180,6 +185,7 @@ target_link_libraries(libsoundio_shared LINK_PUBLIC ${ALSA_LIBRARIES} ${COREAUDIO_LIBRARY} ${COREFOUNDATION_LIBRARY} + ${AUDIOUNIT_LIBRARY} m ${CMAKE_THREAD_LIBS_INIT} ) @@ -235,6 +241,7 @@ target_link_libraries(unit_tests LINK_PUBLIC ${ALSA_LIBRARIES} ${COREAUDIO_LIBRARY} ${COREFOUNDATION_LIBRARY} + ${AUDIOUNIT_LIBRARY} m ) set_target_properties(unit_tests PROPERTIES diff --git a/cmake/FindCoreAudio.cmake b/cmake/FindCoreAudio.cmake index 9bbd752..85b1d34 100644 --- a/cmake/FindCoreAudio.cmake +++ b/cmake/FindCoreAudio.cmake @@ -6,7 +6,7 @@ # COREAUDIO_INCLUDE_DIR # COREAUDIO_LIBRARY -find_path(COREAUDIO_INCLUDE_DIR NAMES CoreAudio.h) +find_path(COREAUDIO_INCLUDE_DIR NAMES CoreAudio/CoreAudio.h) find_library(COREAUDIO_LIBRARY NAMES CoreAudio) diff --git a/src/coreaudio.cpp b/src/coreaudio.cpp index f051fc9..36c120f 100644 --- a/src/coreaudio.cpp +++ b/src/coreaudio.cpp @@ -71,9 +71,11 @@ static void destroy_ca(struct SoundIoPrivate *si) { soundio_destroy_devices_info(sica->ready_devices_info); } +/* TODO static CFStringRef to_cf_string(const char *str) { return CFStringCreateWithCString(kCFAllocatorDefault, str, kCFStringEncodingUTF8); } +*/ // Possible errors: // * SoundIoErrorNoMem @@ -354,8 +356,7 @@ static int refresh_devices(struct SoundIoPrivate *si) { OSStatus os_err; int err; - RefreshDevices rd; - memset(&rd, 0, sizeof(RefreshDevices)); + RefreshDevices rd = {0}; if (!(rd.devices_info = create())) { deinit_refresh_devices(&rd); @@ -415,7 +416,7 @@ static int refresh_devices(struct SoundIoPrivate *si) { } for (int device_i = 0; device_i < device_count; device_i += 1) { - AudioObjectID deviceID = rd.devices[device_i]; + AudioObjectID device_id = rd.devices[device_i]; prop_address.mSelector = kAudioObjectPropertyName; prop_address.mScope = kAudioObjectPropertyScopeGlobal; @@ -425,7 +426,7 @@ static int refresh_devices(struct SoundIoPrivate *si) { CFRelease(rd.string_ref); rd.string_ref = nullptr; } - if ((os_err = AudioObjectGetPropertyData(deviceID, &prop_address, + if ((os_err = AudioObjectGetPropertyData(device_id, &prop_address, 0, nullptr, &io_size, &rd.string_ref))) { deinit_refresh_devices(&rd); @@ -447,7 +448,7 @@ static int refresh_devices(struct SoundIoPrivate *si) { CFRelease(rd.string_ref); rd.string_ref = nullptr; } - if ((os_err = AudioObjectGetPropertyData(deviceID, &prop_address, + if ((os_err = AudioObjectGetPropertyData(device_id, &prop_address, 0, nullptr, &io_size, &rd.string_ref))) { deinit_refresh_devices(&rd); @@ -469,7 +470,7 @@ static int refresh_devices(struct SoundIoPrivate *si) { prop_address.mSelector = kAudioDevicePropertyStreamConfiguration; prop_address.mScope = aim_to_scope(aim); prop_address.mElement = kAudioObjectPropertyElementMaster; - if ((os_err = AudioObjectGetPropertyDataSize(deviceID, &prop_address, 0, nullptr, &io_size))) { + if ((os_err = AudioObjectGetPropertyDataSize(device_id, &prop_address, 0, nullptr, &io_size))) { deinit_refresh_devices(&rd); return SoundIoErrorOpeningDevice; } @@ -481,7 +482,7 @@ static int refresh_devices(struct SoundIoPrivate *si) { return SoundIoErrorNoMem; } - if ((os_err = AudioObjectGetPropertyData(deviceID, &prop_address, 0, nullptr, + if ((os_err = AudioObjectGetPropertyData(device_id, &prop_address, 0, nullptr, &io_size, rd.buffer_list))) { deinit_refresh_devices(&rd); @@ -501,6 +502,8 @@ static int refresh_devices(struct SoundIoPrivate *si) { deinit_refresh_devices(&rd); return SoundIoErrorNoMem; } + SoundIoDeviceCoreAudio *dca = &dev->backend_data.coreaudio; + dca->device_id = device_id; assert(!rd.device); rd.device = &dev->pub; rd.device->ref_count = 1; @@ -522,7 +525,7 @@ static int refresh_devices(struct SoundIoPrivate *si) { prop_address.mSelector = kAudioDevicePropertyPreferredChannelLayout; prop_address.mScope = aim_to_scope(aim); prop_address.mElement = kAudioObjectPropertyElementMaster; - if (!(os_err = AudioObjectGetPropertyDataSize(deviceID, &prop_address, + if (!(os_err = AudioObjectGetPropertyDataSize(device_id, &prop_address, 0, nullptr, &io_size))) { rd.audio_channel_layout = (AudioChannelLayout *)allocate(io_size); @@ -530,7 +533,7 @@ static int refresh_devices(struct SoundIoPrivate *si) { deinit_refresh_devices(&rd); return SoundIoErrorNoMem; } - if ((os_err = AudioObjectGetPropertyData(deviceID, &prop_address, 0, nullptr, + if ((os_err = AudioObjectGetPropertyData(device_id, &prop_address, 0, nullptr, &io_size, rd.audio_channel_layout))) { deinit_refresh_devices(&rd); @@ -555,7 +558,7 @@ static int refresh_devices(struct SoundIoPrivate *si) { prop_address.mScope = aim_to_scope(aim); prop_address.mElement = kAudioObjectPropertyElementMaster; io_size = sizeof(double); - if ((os_err = AudioObjectGetPropertyData(deviceID, &prop_address, 0, nullptr, + if ((os_err = AudioObjectGetPropertyData(device_id, &prop_address, 0, nullptr, &io_size, &rd.device->sample_rate_current))) { deinit_refresh_devices(&rd); @@ -565,7 +568,7 @@ static int refresh_devices(struct SoundIoPrivate *si) { prop_address.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; prop_address.mScope = aim_to_scope(aim); prop_address.mElement = kAudioObjectPropertyElementMaster; - if ((os_err = AudioObjectGetPropertyDataSize(deviceID, &prop_address, 0, nullptr, + if ((os_err = AudioObjectGetPropertyDataSize(device_id, &prop_address, 0, nullptr, &io_size))) { deinit_refresh_devices(&rd); @@ -579,7 +582,7 @@ static int refresh_devices(struct SoundIoPrivate *si) { return SoundIoErrorNoMem; } - if ((os_err = AudioObjectGetPropertyData(deviceID, &prop_address, 0, nullptr, + if ((os_err = AudioObjectGetPropertyData(device_id, &prop_address, 0, nullptr, &io_size, rd.avr_array))) { deinit_refresh_devices(&rd); @@ -601,7 +604,7 @@ static int refresh_devices(struct SoundIoPrivate *si) { prop_address.mElement = kAudioObjectPropertyElementMaster; io_size = sizeof(UInt32); UInt32 buffer_frame_size; - if ((os_err = AudioObjectGetPropertyData(deviceID, &prop_address, 0, nullptr, + if ((os_err = AudioObjectGetPropertyData(device_id, &prop_address, 0, nullptr, &io_size, &buffer_frame_size))) { deinit_refresh_devices(&rd); @@ -616,7 +619,7 @@ static int refresh_devices(struct SoundIoPrivate *si) { prop_address.mElement = kAudioObjectPropertyElementMaster; io_size = sizeof(AudioValueRange); AudioValueRange avr; - if ((os_err = AudioObjectGetPropertyData(deviceID, &prop_address, 0, nullptr, + if ((os_err = AudioObjectGetPropertyData(device_id, &prop_address, 0, nullptr, &io_size, &avr))) { deinit_refresh_devices(&rd); @@ -630,12 +633,12 @@ static int refresh_devices(struct SoundIoPrivate *si) { SoundIoList *device_list; if (rd.device->aim == SoundIoDeviceAimOutput) { device_list = &rd.devices_info->output_devices; - if (deviceID == default_output_id) + if (device_id == default_output_id) rd.devices_info->default_output_index = device_list->length; } else { assert(rd.device->aim == SoundIoDeviceAimInput); device_list = &rd.devices_info->input_devices; - if (deviceID == default_input_id) + if (device_id == default_input_id) rd.devices_info->default_input_index = device_list->length; } @@ -749,109 +752,160 @@ static void device_thread_run(void *arg) { 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); + if (osca->output_instance) + AudioComponentInstanceDispose(osca->output_instance); +} + +static OSStatus write_callback_ca(void *userdata, AudioUnitRenderActionFlags *io_action_flags, + const AudioTimeStamp *in_time_stamp, UInt32 in_bus_number, UInt32 in_number_frames, + AudioBufferList *io_data) +{ + SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *) userdata; + SoundIoOutStream *outstream = &os->pub; + SoundIoOutStreamCoreAudio *osca = &os->backend_data.coreaudio; + + osca->io_data = io_data; + osca->buffer_index = 0; + outstream->write_callback(outstream, in_number_frames); + osca->io_data = nullptr; + + return noErr; } -/* - @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; + SoundIoDevicePrivate *dev = (SoundIoDevicePrivate *)device; + SoundIoDeviceCoreAudio *dca = &dev->backend_data.coreaudio; + + AudioComponentDescription desc; + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_HALOutput; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + + AudioComponent output_comp = AudioComponentFindNext(nullptr, &desc); + if (!output_comp) { + soundio_panic("find next comp"); + } - 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; + if ((os_err = AudioComponentInstanceNew(output_comp, &osca->output_instance))) { + soundio_panic("AudioComponentInstanceNew"); } - 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))) + if ((os_err = AudioUnitInitialize(osca->output_instance))) { + soundio_panic("AudioUnitInitialize"); + } + + AudioStreamBasicDescription format = {0}; + format.mSampleRate = outstream->sample_rate; + format.mFormatID = kAudioFormatLinearPCM; + format.mFormatFlags = kAudioFormatFlagIsFloat; + format.mBytesPerPacket = outstream->bytes_per_frame; + format.mFramesPerPacket = 1; + format.mBytesPerFrame = outstream->bytes_per_frame; + format.mChannelsPerFrame = outstream->layout.channel_count; + format.mBitsPerChannel = 32; + + if ((os_err = AudioUnitSetProperty(osca->output_instance, kAudioOutputUnitProperty_CurrentDevice, + kAudioUnitScope_Input, 0, &dca->device_id, sizeof(AudioDeviceID)))) { - fprintf(stderr, "derpde derpdee derr\n"); - outstream_destroy_ca(si, os); - return SoundIoErrorOpeningDevice; + soundio_panic("set device"); } - if (osca->device_id == kAudioObjectUnknown) { - outstream_destroy_ca(si, os); - return SoundIoErrorNoSuchDevice; + if ((os_err = AudioUnitSetProperty(osca->output_instance, kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, 0, &format, sizeof(AudioStreamBasicDescription)))) + { + soundio_panic("AudioUnitSetProperty format %d", os_err); } - fprintf(stderr, "id: %ld\n", (long)osca->device_id); + AURenderCallbackStruct render_callback = {write_callback_ca, os}; + if ((os_err = AudioUnitSetProperty(osca->output_instance, kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Input, 0, &render_callback, sizeof(AURenderCallbackStruct)))) + { + soundio_panic("AudioUnitSetProperty callback"); + } - soundio_panic("TODO"); + return 0; } static int outstream_start_ca(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) { - soundio_panic("TODO"); + SoundIoOutStreamCoreAudio *osca = &os->backend_data.coreaudio; + OSStatus os_err; + if ((os_err = AudioOutputUnitStart(osca->output_instance))) { + soundio_panic("audio outut unit start"); + } + + return 0; } static int outstream_begin_write_ca(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os, - SoundIoChannelArea **out_areas, int *frame_count) + SoundIoChannelArea **out_areas, int *out_frame_count) { - soundio_panic("TODO"); + SoundIoOutStream *outstream = &os->pub; + SoundIoOutStreamCoreAudio *osca = &os->backend_data.coreaudio; + + if (osca->buffer_index < osca->io_data->mNumberBuffers) { + AudioBuffer *audio_buffer = &osca->io_data->mBuffers[osca->buffer_index]; + assert(audio_buffer->mNumberChannels == outstream->layout.channel_count); + *out_frame_count = audio_buffer->mDataByteSize / outstream->bytes_per_frame; + assert((audio_buffer->mDataByteSize % outstream->bytes_per_frame) == 0); + for (int ch = 0; ch < outstream->layout.channel_count; ch += 1) { + osca->areas[ch].ptr = ((char*)audio_buffer->mData) + outstream->bytes_per_sample * ch; + osca->areas[ch].step = outstream->bytes_per_frame; + } + *out_areas = osca->areas; + } else { + *out_areas = nullptr; + *out_frame_count = 0; + } + return 0; } -static int outstream_end_write_ca(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os, - int frame_count) +static int outstream_end_write_ca(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os, int) { - soundio_panic("TODO"); + SoundIoOutStreamCoreAudio *osca = &os->backend_data.coreaudio; + osca->buffer_index += 1; + return 0; } static int outstream_clear_buffer_ca(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) { - soundio_panic("TODO"); + soundio_panic("TODO clear buffer"); } static int outstream_pause_ca(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os, bool pause) { - soundio_panic("TODO"); + soundio_panic("TODO pause"); } static int instream_open_ca(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) { - soundio_panic("TODO"); + soundio_panic("TODO open"); } static void instream_destroy_ca(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) { - soundio_panic("TODO"); + soundio_panic("TODO destroy"); } static int instream_start_ca(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) { - soundio_panic("TODO"); + soundio_panic("TODO start"); } static int instream_begin_read_ca(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is, SoundIoChannelArea **out_areas, int *frame_count) { - soundio_panic("TODO"); + soundio_panic("TODO begin read"); } static int instream_end_read_ca(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is) { - soundio_panic("TODO"); + soundio_panic("TODO end read"); } static int instream_pause_ca(struct SoundIoPrivate *si, struct SoundIoInStreamPrivate *is, bool pause) { - soundio_panic("TODO"); + soundio_panic("TODO pause"); } diff --git a/src/coreaudio.hpp b/src/coreaudio.hpp index 01fabf5..e01fab2 100644 --- a/src/coreaudio.hpp +++ b/src/coreaudio.hpp @@ -12,11 +12,14 @@ #include "soundio/os.h" #include "atomics.hpp" -#include +#include +#include int soundio_coreaudio_init(struct SoundIoPrivate *si); -struct SoundIoDeviceCoreAudio { }; +struct SoundIoDeviceCoreAudio { + AudioDeviceID device_id; +}; struct SoundIoCoreAudio { SoundIoOsMutex *mutex; @@ -37,8 +40,10 @@ struct SoundIoCoreAudio { }; struct SoundIoOutStreamCoreAudio { - CFStringRef device_uid_string_ref; - AudioDeviceID device_id; + AudioComponentInstance output_instance; + AudioBufferList *io_data; + int buffer_index; + SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS]; }; struct SoundIoInStreamCoreAudio {