From 6b2c610ec860d1859d206d66327c952e9d4b0316 Mon Sep 17 00:00:00 2001 From: Alex Chronopoulos Date: Fri, 4 Mar 2016 18:12:28 +0200 Subject: [PATCH 01/17] Implement full-duplex for audiounit backend. --- .gitignore | 2 + Makefile.am | 6 +- include/cubeb/cubeb.h | 2 +- src/cubeb.c | 1 + src/cubeb_audiounit.c | 825 +++++++++++++++++++++++++-------- src/cubeb_pulse.c | 4 +- src/cubeb_resampler.cpp | 21 - src/cubeb_resampler_internal.h | 21 +- src/cubeb_ring_array.h | 133 ++++++ test/test_resampler.cpp | 14 +- test/test_ring_array.c | 70 +++ 11 files changed, 874 insertions(+), 225 deletions(-) create mode 100644 src/cubeb_ring_array.h create mode 100644 test/test_ring_array.c diff --git a/.gitignore b/.gitignore index c8d9152..2f9c85a 100644 --- a/.gitignore +++ b/.gitignore @@ -63,3 +63,5 @@ include/cubeb/cubeb-stdint.h test-suite.log test/test_sanity.log test/test_sanity.trs +test/test_ring_array +test/test_ring_array.exe diff --git a/Makefile.am b/Makefile.am index 7707c31..880bffa 100644 --- a/Makefile.am +++ b/Makefile.am @@ -83,6 +83,7 @@ check_PROGRAMS = test/test_sanity \ test/test_resampler \ test/test_record \ test/test_utils \ + test/test_ring_array\ $(NULL) test_test_sanity_SOURCES = test/test_sanity.cpp @@ -101,7 +102,7 @@ test_test_devices_SOURCES = test/test_devices.cpp test_test_devices_LDADD = -lm src/libcubeb.la $(platform_lib) test_test_resampler_SOURCES = test/test_resampler.cpp -test_test_resampler_LDADD = -lm src/libcubeb.la $(platform_lib) src/cubeb_resampler.o +test_test_resampler_LDADD = -lm src/libcubeb.la $(platform_lib) src/cubeb_resampler.o src/speex/resample.lo test_test_duplex_SOURCES = test/test_duplex.cpp test_test_duplex_LDADD = -lm src/libcubeb.la $(platform_lib) @@ -111,6 +112,9 @@ test_test_record_LDADD = -lm src/libcubeb.la $(platform_lib) test_test_utils_SOURCES = test/test_utils.cpp +test_test_ring_array_SOURCES = test/test_ring_array.c +test_test_ring_array_LDADD = -lpthread + TESTS = $(check_PROGRAMS) dist-hook: diff --git a/include/cubeb/cubeb.h b/include/cubeb/cubeb.h index d1db0c9..1662c3d 100644 --- a/include/cubeb/cubeb.h +++ b/include/cubeb/cubeb.h @@ -221,7 +221,7 @@ typedef enum { * `cubeb_enumerate_devices`, and must be destroyed using * `cubeb_device_info_destroy`. */ typedef struct { - cubeb_devid devid; /**< Device identifier handle. */ + cubeb_devid devid; /**< Device identifier handle. Always fresh allocate and copy it's value. */ char * device_id; /**< Device identifier which might be presented in a UI. */ char * friendly_name; /**< Friendly device name which might be presented in a UI. */ char * group_id; /**< Two devices have the same group identifier if they belong to the same physical device; for example a headset and microphone. */ diff --git a/src/cubeb.c b/src/cubeb.c index 5f5e92b..8c5dfc0 100644 --- a/src/cubeb.c +++ b/src/cubeb.c @@ -425,6 +425,7 @@ int cubeb_device_collection_destroy(cubeb_device_collection * collection) int cubeb_device_info_destroy(cubeb_device_info * info) { + free(info->devid); free(info->device_id); free(info->friendly_name); free(info->group_id); diff --git a/src/cubeb_audiounit.c b/src/cubeb_audiounit.c index ed7c2ed..db38628 100644 --- a/src/cubeb_audiounit.c +++ b/src/cubeb_audiounit.c @@ -16,16 +16,17 @@ #include #include #include -#else +#endif #include #include -#endif #include "cubeb/cubeb.h" #include "cubeb-internal.h" #include "cubeb_panner.h" #if !TARGET_OS_IPHONE #include "cubeb_osx_run_loop.h" #endif +#include "cubeb_resampler.h" +#include "cubeb_ring_array.h" #if !defined(kCFCoreFoundationVersionNumber10_7) /* From CoreFoundation CFBase.h */ @@ -33,11 +34,42 @@ #endif #if !TARGET_OS_IPHONE && MAC_OS_X_VERSION_MIN_REQUIRED < 1060 -#define MACOSX_LESS_THAN_106 +#define AudioComponent Component +#define AudioComponentDescription ComponentDescription +#define AudioComponentFindNext FindNextComponent +#define AudioComponentInstanceNew OpenAComponent +#define AudioComponentInstanceDispose CloseComponent #endif #define CUBEB_STREAM_MAX 8 -#define NBUFS 4 + +#define AU_OUT_BUS 0 +#define AU_IN_BUS 1 + +#if TARGET_OS_IPHONE +#define CUBEB_AUDIOUNIT_SUBTYPE kAudioUnitSubType_RemoteIO +#else +#define CUBEB_AUDIOUNIT_SUBTYPE kAudioUnitSubType_HALOutput +#endif + +//#define LOGGING_ENABLED +#ifdef LOGGING_ENABLED +#define LOG(...) do { \ + fprintf(stderr, __VA_ARGS__); \ + } while(0) +#else +#define LOG(...) +#endif + +#ifdef DEBUG +#define ASSERT_LOCKED(mutex) \ +do { \ + int rv = pthread_mutex_lock(&mutex); \ + assert(rv == EDEADLK); \ +} while (0); +#else +#define ASSERT_LOCKED(mutex) +#endif static struct cubeb_ops const audiounit_ops; @@ -50,20 +82,36 @@ struct cubeb { struct cubeb_stream { cubeb * context; - AudioUnit unit; cubeb_data_callback data_callback; cubeb_state_callback state_callback; cubeb_device_changed_callback device_changed_callback; + /* User pointer of data_callback */ void * user_ptr; - AudioStreamBasicDescription sample_spec; + /* Format descriptions */ + AudioStreamBasicDescription input_desc; + AudioStreamBasicDescription output_desc; + /* I/O AudioUnits */ + AudioUnit input_unit; + AudioUnit output_unit; + /* Sample rate of input device*/ + Float64 input_hw_rate; pthread_mutex_t mutex; + /* Hold the input samples in every + * input callback iteration */ + AudioBufferList input_buffer_list[RING_ARRAY_CAPACITY]; + ring_array input_buffer_list_array; + /* Frames on input buffer */ + uint32_t input_buffer_frames; + /* Frame counters */ uint64_t frames_played; uint64_t frames_queued; + uint64_t frames_read; int shutdown; int draining; uint64_t current_latency_frames; uint64_t hw_latency_frames; float panning; + cubeb_resampler * resampler; }; #if TARGET_OS_IPHONE @@ -101,70 +149,209 @@ audiotimestamp_to_latency(AudioTimeStamp const * tstamp, cubeb_stream * stream) uint64_t pres = AudioConvertHostTimeToNanos(tstamp->mHostTime); uint64_t now = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); - return ((pres - now) * stream->sample_spec.mSampleRate) / 1000000000LL; + return ((pres - now) * stream->output_desc.mSampleRate) / 1000000000LL; } static OSStatus -audiounit_output_callback(void * user_ptr, AudioUnitRenderActionFlags * flags, - AudioTimeStamp const * tstamp, UInt32 bus, UInt32 nframes, - AudioBufferList * bufs) +audiounit_input_callback(void * user_ptr, + AudioUnitRenderActionFlags * flags, + AudioTimeStamp const * tstamp, + UInt32 bus, + UInt32 input_frames, + AudioBufferList * bufs) { - cubeb_stream * stm; - unsigned char * buf; - long got; - OSStatus r; - float panning; + cubeb_stream * stream = user_ptr; + long outframes, frames; + void * input_buffer = NULL; - assert(bufs->mNumberBuffers == 1); - buf = bufs->mBuffers[0].mData; + pthread_mutex_lock(&stream->mutex); + ASSERT_LOCKED(stream->mutex); - stm = user_ptr; + assert(stream->input_unit != NULL); + assert(AU_IN_BUS == bus); - pthread_mutex_lock(&stm->mutex); + if (stream->shutdown) { + pthread_mutex_unlock(&stream->mutex); + return noErr; + } - stm->current_latency_frames = audiotimestamp_to_latency(tstamp, stm); - panning = stm->panning; + /* Get next store buffer from ring array */ + AudioBufferList * store_input_buffer_list = ring_array_store_buffer(&stream->input_buffer_list_array); + if (store_input_buffer_list == NULL) { + LOG("input: Ring array is full drop one buffer\n"); + ring_array_fetch_buffer(&stream->input_buffer_list_array); - if (stm->draining || stm->shutdown) { - pthread_mutex_unlock(&stm->mutex); - if (stm->draining) { - r = AudioOutputUnitStop(stm->unit); - assert(r == 0); - stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED); + store_input_buffer_list = ring_array_store_buffer(&stream->input_buffer_list_array); + assert(store_input_buffer_list); + } + /* Render input samples */ + OSStatus r = AudioUnitRender(stream->input_unit, + flags, + tstamp, + bus, + input_frames, + store_input_buffer_list); + assert(r == noErr); + LOG("- input: buffers %d, size %d, channels %d, frames %d\n", store_input_buffer_list->mNumberBuffers, + store_input_buffer_list->mBuffers[0].mDataByteSize, store_input_buffer_list->mBuffers[0].mNumberChannels, input_frames); + + assert(input_frames > 0); + stream->frames_read += input_frames; + + // Full Duplex. We'll call data_callback in the AudioUnit output callback. + if (stream->output_unit != NULL) { + // User callback will be called by output callback + pthread_mutex_unlock(&stream->mutex); + return noErr; + } + + /* Input only. Call the user callback through resampler. + Resampler will deliver input buffer in the correct rate. */ + frames = input_frames; + AudioBufferList * fetch_input_buffer_list = ring_array_fetch_buffer(&stream->input_buffer_list_array); + assert(fetch_input_buffer_list && "fetch buffer is null in the input"); + input_buffer = fetch_input_buffer_list->mBuffers[0].mData; + outframes = cubeb_resampler_fill(stream->resampler, + input_buffer, + &frames, + NULL, + 0); + + if (outframes < 0 || outframes != input_frames) { + stream->shutdown = 1; + pthread_mutex_unlock(&stream->mutex); + return noErr; + } + + pthread_mutex_unlock(&stream->mutex); + return noErr; +} + +static void +audiounit_make_silent(AudioBufferList * ioData) +{ + for(UInt32 i = 0; imNumberBuffers;i++) { + memset(ioData->mBuffers[i].mData, 0, ioData->mBuffers[i].mDataByteSize); + } +} + +static OSStatus +audiounit_output_callback(void * user_ptr, + AudioUnitRenderActionFlags * flags, + AudioTimeStamp const * tstamp, + UInt32 bus, + UInt32 output_frames, + AudioBufferList * outBufferList) +{ + assert(AU_OUT_BUS == bus); + assert(outBufferList->mNumberBuffers == 1); + + LOG("- output: buffers %d, size %d, channels %d, frames %d\n", + outBufferList->mNumberBuffers, outBufferList->mBuffers[0].mDataByteSize, + outBufferList->mBuffers[0].mNumberChannels, output_frames); + + cubeb_stream * stream = user_ptr; + long outframes = 0, input_frames = 0; + void * output_buffer = NULL, * input_buffer = NULL; + + pthread_mutex_lock(&stream->mutex); + ASSERT_LOCKED(stream->mutex); + + if (stream->shutdown) { + audiounit_make_silent(outBufferList); + pthread_mutex_unlock(&stream->mutex); + return noErr; + } + + stream->current_latency_frames = audiotimestamp_to_latency(tstamp, stream); + if (stream->draining) { + OSStatus r = AudioOutputUnitStop(stream->output_unit); + assert(r == 0); + if (stream->input_unit) { + r = AudioOutputUnitStop(stream->input_unit); + assert(r==0); } + stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_DRAINED); + pthread_mutex_unlock(&stream->mutex); + audiounit_make_silent(outBufferList); + return noErr; + } + /* Get output buffer. */ + output_buffer = outBufferList->mBuffers[0].mData; + /* If Full duplex get also input buffer */ + AudioBufferList * fetch_input_buffer_list = NULL; + if (stream->input_unit != NULL) { + /* Output callback came first */ + if (stream->frames_read == 0) { + audiounit_make_silent(outBufferList); + pthread_mutex_unlock(&stream->mutex); + return noErr; + } + /* Input samples stored previously in input callback. */ + fetch_input_buffer_list = ring_array_fetch_buffer(&stream->input_buffer_list_array); + if (fetch_input_buffer_list == NULL) { + LOG("Requested more output than input. " + "This is either a hole or we are after a stream stop and input thread stopped before output\n"); + /* Provide silent input. Other than that we could provide silent output and exit without + * calling user callback. I do not prefer it because the user loose the control of + * the output. Also resampler loose frame counting and produce less frame than + * expected at some point in the future breaking an assert. */ + + /* Avoid here to allocate new memory since we are inside callback. Use the existing + * allocated buffers since the ring array is empty and the buffer is not used. */ + fetch_input_buffer_list = &stream->input_buffer_list[0]; + audiounit_make_silent(fetch_input_buffer_list); + } + input_buffer = fetch_input_buffer_list->mBuffers[0].mData; + input_frames = stream->input_buffer_frames; + assert(stream->frames_read > 0); + } + + /* Call user callback through resampler. */ + outframes = cubeb_resampler_fill(stream->resampler, + input_buffer, + &input_frames, + output_buffer, + output_frames); + + /* Cleanup the input buffer to make sure that we have fresh data. */ + if (input_buffer) { + audiounit_make_silent(fetch_input_buffer_list); + } + + + if (outframes < 0) { + stream->shutdown = 1; + pthread_mutex_unlock(&stream->mutex); return noErr; } - pthread_mutex_unlock(&stm->mutex); - got = stm->data_callback(stm, stm->user_ptr, NULL, buf, nframes); - pthread_mutex_lock(&stm->mutex); - if (got < 0) { - /* XXX handle this case. */ - assert(false); - pthread_mutex_unlock(&stm->mutex); - return noErr; + AudioFormatFlags outaff; + float panning; + size_t outbpf; + + outbpf = stream->output_desc.mBytesPerFrame; + stream->draining = (outframes < output_frames); + stream->frames_played = stream->frames_queued; + stream->frames_queued += outframes; + + outaff = stream->output_desc.mFormatFlags; + panning = (stream->output_desc.mChannelsPerFrame == 2) ? stream->panning : 0.0f; + pthread_mutex_unlock(&stream->mutex); + + /* Post process output samples. */ + if (stream->draining) { + /* Clear missing frames (silence) */ + memset(output_buffer + outframes * outbpf, 0, (output_frames - outframes) * outbpf); } - - if ((UInt32) got < nframes) { - size_t got_bytes = got * stm->sample_spec.mBytesPerFrame; - size_t rem_bytes = (nframes - got) * stm->sample_spec.mBytesPerFrame; - - stm->draining = 1; - - memset(buf + got_bytes, 0, rem_bytes); + /* Pan stereo. */ + if (panning != 0.0f) { + if (outaff & kAudioFormatFlagIsFloat) { + cubeb_pan_stereo_buffer_float((float*)output_buffer, outframes, panning); + } else if (outaff & kAudioFormatFlagIsSignedInteger) { + cubeb_pan_stereo_buffer_int((short*)output_buffer, outframes, panning); + } } - - stm->frames_played = stm->frames_queued; - stm->frames_queued += got; - pthread_mutex_unlock(&stm->mutex); - - if (stm->sample_spec.mChannelsPerFrame == 2) { - if (stm->sample_spec.mFormatFlags & kAudioFormatFlagIsFloat) - cubeb_pan_stereo_buffer_float((float*)buf, got, panning); - else if (stm->sample_spec.mFormatFlags & kAudioFormatFlagIsSignedInteger) - cubeb_pan_stereo_buffer_int((short*)buf, got, panning); - } - return noErr; } @@ -538,73 +725,186 @@ audiounit_destroy(cubeb * ctx) static void audiounit_stream_destroy(cubeb_stream * stm); static int -audiounit_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name, - cubeb_devid input_device, - cubeb_stream_params * input_stream_params, - cubeb_devid output_device, - cubeb_stream_params * output_stream_params, - unsigned int latency, - cubeb_data_callback data_callback, cubeb_state_callback state_callback, - void * user_ptr) +audio_stream_desc_init (AudioStreamBasicDescription * ss, + const cubeb_stream_params * stream_params) { - AudioStreamBasicDescription ss; -#if MACOSX_LESS_THAN_106 - ComponentDescription desc; - Component comp; -#else - AudioComponentDescription desc; - AudioComponent comp; -#endif - cubeb_stream * stm; - AURenderCallbackStruct input; - unsigned int buffer_size, default_buffer_size; - OSStatus r; - UInt32 size; - AudioValueRange latency_range; + memset(ss, 0, sizeof(AudioStreamBasicDescription)); - assert(!input_stream_params && "not supported"); - if (input_device || output_device) { - /* Device selection not yet implemented. */ - return CUBEB_ERROR_DEVICE_UNAVAILABLE; - } - - assert(context); - *stream = NULL; - - memset(&ss, 0, sizeof(ss)); - ss.mFormatFlags = 0; - - switch (output_stream_params->format) { + switch (stream_params->format) { case CUBEB_SAMPLE_S16LE: - ss.mBitsPerChannel = 16; - ss.mFormatFlags |= kAudioFormatFlagIsSignedInteger; + ss->mBitsPerChannel = 16; + ss->mFormatFlags = kAudioFormatFlagIsSignedInteger; break; case CUBEB_SAMPLE_S16BE: - ss.mBitsPerChannel = 16; - ss.mFormatFlags |= kAudioFormatFlagIsSignedInteger | + ss->mBitsPerChannel = 16; + ss->mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsBigEndian; break; case CUBEB_SAMPLE_FLOAT32LE: - ss.mBitsPerChannel = 32; - ss.mFormatFlags |= kAudioFormatFlagIsFloat; + ss->mBitsPerChannel = 32; + ss->mFormatFlags = kAudioFormatFlagIsFloat; break; case CUBEB_SAMPLE_FLOAT32BE: - ss.mBitsPerChannel = 32; - ss.mFormatFlags |= kAudioFormatFlagIsFloat | + ss->mBitsPerChannel = 32; + ss->mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsBigEndian; break; default: return CUBEB_ERROR_INVALID_FORMAT; } - ss.mFormatID = kAudioFormatLinearPCM; - ss.mFormatFlags |= kLinearPCMFormatFlagIsPacked; - ss.mSampleRate = output_stream_params->rate; - ss.mChannelsPerFrame = output_stream_params->channels; + ss->mFormatID = kAudioFormatLinearPCM; + ss->mFormatFlags |= kLinearPCMFormatFlagIsPacked; + ss->mSampleRate = stream_params->rate; + ss->mChannelsPerFrame = stream_params->channels; - ss.mBytesPerFrame = (ss.mBitsPerChannel / 8) * ss.mChannelsPerFrame; - ss.mFramesPerPacket = 1; - ss.mBytesPerPacket = ss.mBytesPerFrame * ss.mFramesPerPacket; + ss->mBytesPerFrame = (ss->mBitsPerChannel / 8) * ss->mChannelsPerFrame; + ss->mFramesPerPacket = 1; + ss->mBytesPerPacket = ss->mBytesPerFrame * ss->mFramesPerPacket; + + return CUBEB_OK; +} + +static int +audiounit_create_unit(AudioUnit * unit, + bool is_input, + const cubeb_stream_params * stream_params, + cubeb_devid device) +{ + AudioComponentDescription desc; + AudioComponent comp; + UInt32 enable; + AudioDeviceID devid; + + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = CUBEB_AUDIOUNIT_SUBTYPE; + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + comp = AudioComponentFindNext(NULL, &desc); + if (comp == NULL) { + return CUBEB_ERROR; + } + + if (AudioComponentInstanceNew(comp, unit) != 0) { + return CUBEB_ERROR; + } + + enable = 1; + if (AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_EnableIO, + is_input ? kAudioUnitScope_Input : kAudioUnitScope_Output, + is_input ? AU_IN_BUS : AU_OUT_BUS, &enable, sizeof(UInt32)) != noErr) { + return CUBEB_ERROR; + } + + enable = 0; + if (AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_EnableIO, + is_input ? kAudioUnitScope_Output : kAudioUnitScope_Input, + is_input ? AU_OUT_BUS : AU_IN_BUS, &enable, sizeof(UInt32)) != noErr) { + return CUBEB_ERROR; + } + + if (device == NULL) { + devid = audiounit_get_default_device_id (is_input ? CUBEB_DEVICE_TYPE_INPUT + : CUBEB_DEVICE_TYPE_OUTPUT); + } else { + devid = *(AudioDeviceID *)device; + } + int err = AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_CurrentDevice, + kAudioUnitScope_Global, + is_input ? AU_IN_BUS : AU_OUT_BUS, + &devid, sizeof(AudioDeviceID)); + if (err != noErr) { + return CUBEB_ERROR; + } + + return CUBEB_OK; +} + +static int +audiounit_buflst_init_single_buffer (AudioBufferList * buflst, + const AudioStreamBasicDescription * desc, + uint32_t frames) +{ + size_t size = desc->mBytesPerFrame * frames; + + if ((buflst->mBuffers[0].mData = malloc(size)) == NULL) { + return CUBEB_ERROR; + } + + buflst->mBuffers[0].mNumberChannels = desc->mChannelsPerFrame; + buflst->mBuffers[0].mDataByteSize = size; + buflst->mNumberBuffers = 1; + + return CUBEB_OK; +} + +static int +audiounit_init_input_buffer_list_array(cubeb_stream * stream) +{ + /* Init ring array. */ + ring_array_init(&stream->input_buffer_list_array); + /* Initialize buffer list. */ + memset(&stream->input_buffer_list, 0, sizeof(stream->input_buffer_list)); + /* Create input descriptor. */ + AudioStreamBasicDescription src_desc = stream->input_desc; + src_desc.mSampleRate = stream->input_hw_rate; + /* Set data to ring array. */ + for (int i = 0; i < RING_ARRAY_CAPACITY; ++i) { + /* Allocate the data (AudioBufferList here). */ + if (audiounit_buflst_init_single_buffer(&stream->input_buffer_list[i], + &src_desc, + stream->input_buffer_frames) != CUBEB_OK) { + return CUBEB_ERROR; + } + /* Set the data in the array. */ + if (ring_array_set_data(&stream->input_buffer_list_array, + &stream->input_buffer_list[i], i) == NULL) { + return CUBEB_ERROR; + } + } + return CUBEB_OK; +} + +static void +audiounit_destroy_input_buffer_list_array(cubeb_stream * stream) +{ + /* Destroy the data in the array first*/ + for (int i = 0; i < RING_ARRAY_CAPACITY; ++i) { + if (stream->input_buffer_list[i].mBuffers[0].mData) { + free(stream->input_buffer_list[i].mBuffers[0].mData); + } + } + ring_array_destroy(&stream->input_buffer_list_array); +} + +static int +audiounit_stream_init(cubeb * context, + cubeb_stream ** stream, + char const * stream_name, + cubeb_devid input_device, + cubeb_stream_params * input_stream_params, + cubeb_devid output_device, + cubeb_stream_params * output_stream_params, + unsigned int latency, + cubeb_data_callback data_callback, + cubeb_state_callback state_callback, + void * user_ptr) +{ + cubeb_stream * stm; + AudioUnit input_unit; + AudioUnit output_unit; + int ret; + AURenderCallbackStruct aurcbs_in; + AURenderCallbackStruct aurcbs_out; + UInt32 size; +#if 0 + unsigned int buffer_size, default_buffer_size; + AudioValueRange latency_range; +#endif + + assert(context); + *stream = NULL; pthread_mutex_lock(&context->mutex); if (context->limit_streams && context->active_streams >= CUBEB_STREAM_MAX) { @@ -614,65 +914,176 @@ audiounit_stream_init(cubeb * context, cubeb_stream ** stream, char const * stre context->active_streams += 1; pthread_mutex_unlock(&context->mutex); - desc.componentType = kAudioUnitType_Output; - desc.componentSubType = -#if TARGET_OS_IPHONE - kAudioUnitSubType_RemoteIO; -#else - kAudioUnitSubType_DefaultOutput; -#endif - desc.componentManufacturer = kAudioUnitManufacturer_Apple; - desc.componentFlags = 0; - desc.componentFlagsMask = 0; -#if MACOSX_LESS_THAN_106 - comp = FindNextComponent(NULL, &desc); -#else - comp = AudioComponentFindNext(NULL, &desc); -#endif - assert(comp); + if (input_stream_params != NULL) { + if ((ret = audiounit_create_unit (&input_unit, true, + input_stream_params, input_device)) != CUBEB_OK) + return ret; + } - stm = calloc(1, sizeof(*stm)); + if (output_stream_params != NULL) { + if ((ret = audiounit_create_unit (&output_unit, false, + output_stream_params, output_device)) != CUBEB_OK) + return ret; + } + + stm = calloc(1, sizeof(cubeb_stream)); assert(stm); + /* These could be different in the future if we have both + * full-duplex stream and different devices for input vs output. */ + stm->input_unit = (input_stream_params != NULL) ? input_unit : NULL; + stm->output_unit = (output_stream_params != NULL) ? output_unit : NULL; stm->context = context; stm->data_callback = data_callback; stm->state_callback = state_callback; stm->user_ptr = user_ptr; stm->device_changed_callback = NULL; - stm->sample_spec = ss; - pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); +#ifdef DEBUG + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); +#else pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - r = pthread_mutex_init(&stm->mutex, &attr); +#endif + ret = pthread_mutex_init(&stm->mutex, &attr); + assert(0 == ret); pthread_mutexattr_destroy(&attr); - assert(r == 0); + stm->draining = false; + stm->shutdown = 0; stm->frames_played = 0; stm->frames_queued = 0; + stm->frames_read = 0; stm->current_latency_frames = 0; stm->hw_latency_frames = UINT64_MAX; -#if MACOSX_LESS_THAN_106 - r = OpenAComponent(comp, &stm->unit); -#else - r = AudioComponentInstanceNew(comp, &stm->unit); -#endif - if (r != 0) { - audiounit_stream_destroy(stm); - return CUBEB_ERROR; + /* Setup Input Stream! */ + if (input_stream_params != NULL) { + + size = sizeof(UInt32); + if (AudioUnitGetProperty(stm->input_unit, + kAudioDevicePropertyBufferFrameSize, + kAudioUnitScope_Input, + AU_IN_BUS, + &stm->input_buffer_frames, + &size) != 0) { + audiounit_stream_destroy(stm); + return CUBEB_ERROR; + } + + if (AudioUnitSetProperty(stm->input_unit, + kAudioDevicePropertyBufferFrameSize, + kAudioUnitScope_Output, + AU_IN_BUS, + &stm->input_buffer_frames, + size) != 0) { + audiounit_stream_destroy(stm); + return CUBEB_ERROR; + } + + /* Get input device sample rate. */ + AudioStreamBasicDescription input_hw_desc; + size = sizeof(AudioStreamBasicDescription); + if (AudioUnitGetProperty(stm->input_unit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, + AU_IN_BUS, + &input_hw_desc, + &size) !=0) { + audiounit_stream_destroy(stm); + return CUBEB_ERROR; + } + stm->input_hw_rate = input_hw_desc.mSampleRate; + + /* Set format description according to the input params. */ + if ((ret = audio_stream_desc_init(&stm->input_desc, input_stream_params)) != CUBEB_OK) { + audiounit_stream_destroy(stm); + return ret; + } + + AudioStreamBasicDescription src_desc = stm->input_desc; + /* Input AudioUnit must be configured with device's sample rate. */ + if (src_desc.mSampleRate != stm->input_hw_rate) { + /* Set the sample rate of the device we will resample inside input callback. */ + src_desc.mSampleRate = stm->input_hw_rate; + } + + if (AudioUnitSetProperty(stm->input_unit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, + AU_IN_BUS, + &src_desc, + sizeof(AudioStreamBasicDescription)) !=0) { + audiounit_stream_destroy(stm); + return CUBEB_ERROR; + } + + /* Frames per buffer in the input callback. */ + if (AudioUnitSetProperty(stm->input_unit, + kAudioUnitProperty_MaximumFramesPerSlice, + kAudioUnitScope_Output, + AU_IN_BUS, + &stm->input_buffer_frames, + sizeof(UInt32))) { + audiounit_stream_destroy(stm); + return CUBEB_ERROR; + } + + if (audiounit_init_input_buffer_list_array(stm) != CUBEB_OK) { + audiounit_stream_destroy(stm); + return CUBEB_ERROR; + } + + assert(stm->input_unit != NULL); + aurcbs_in.inputProc = audiounit_input_callback; + aurcbs_in.inputProcRefCon = stm; + if (AudioUnitSetProperty(stm->input_unit, + kAudioOutputUnitProperty_SetInputCallback, + kAudioUnitScope_Global, + AU_OUT_BUS, + &aurcbs_in, + sizeof(aurcbs_in)) != 0) { + audiounit_stream_destroy(stm); + return CUBEB_ERROR; + } } - input.inputProc = audiounit_output_callback; - input.inputProcRefCon = stm; - r = AudioUnitSetProperty(stm->unit, kAudioUnitProperty_SetRenderCallback, - kAudioUnitScope_Global, 0, &input, sizeof(input)); - if (r != 0) { - audiounit_stream_destroy(stm); - return CUBEB_ERROR; + /* Setup Output Stream! */ + if (output_stream_params != NULL) { + + if ((ret = audio_stream_desc_init(&stm->output_desc, output_stream_params)) != CUBEB_OK) { + audiounit_stream_destroy(stm); + return ret; + } + + if (AudioUnitSetProperty(stm->output_unit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, + AU_OUT_BUS, + &stm->output_desc, + sizeof(AudioStreamBasicDescription)) != 0) { + audiounit_stream_destroy(stm); + return CUBEB_ERROR; + } + + assert(stm->output_unit != NULL); + aurcbs_out.inputProc = audiounit_output_callback; + aurcbs_out.inputProcRefCon = stm; + if (AudioUnitSetProperty(stm->output_unit, + kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Global, + AU_OUT_BUS, + &aurcbs_out, + sizeof(aurcbs_out)) != 0) { + audiounit_stream_destroy(stm); + return CUBEB_ERROR; + } } + // Setting the latency doesn't work well for USB headsets (eg. plantronics). + // Keep the default latency for now. +#if 0 buffer_size = latency / 1000.0 * ss.mSampleRate; /* Get the range of latency this particular device can work with, and clamp @@ -694,42 +1105,67 @@ audiounit_stream_init(cubeb * context, cubeb_stream ** stream, char const * stre * set it. Otherwise, use the default latency. **/ size = sizeof(default_buffer_size); - r = AudioUnitGetProperty(stm->unit, kAudioDevicePropertyBufferFrameSize, - kAudioUnitScope_Output, 0, &default_buffer_size, &size); - - if (r != 0) { + if (AudioUnitGetProperty(stm->output_unit, kAudioDevicePropertyBufferFrameSize, + kAudioUnitScope_Output, 0, &default_buffer_size, &size) != 0) { audiounit_stream_destroy(stm); return CUBEB_ERROR; } + + if (buffer_size < default_buffer_size) { + /* Set the maximum number of frame that the render callback will ask for, + * effectively setting the latency of the stream. This is process-wide. */ + if (AudioUnitSetProperty(stm->output_unit, kAudioDevicePropertyBufferFrameSize, + kAudioUnitScope_Output, 0, &buffer_size, sizeof(buffer_size)) != 0) { + audiounit_stream_destroy(stm); + return CUBEB_ERROR; + } + } #else // TARGET_OS_IPHONE //TODO: [[AVAudioSession sharedInstance] inputLatency] // http://stackoverflow.com/questions/13157523/kaudiodevicepropertybufferframesize-replacement-for-ios #endif - - // Setting the latency doesn't work well for USB headsets (eg. plantronics). - // Keep the default latency for now. -#if 0 - if (buffer_size < default_buffer_size) { - /* Set the maximum number of frame that the render callback will ask for, - * effectively setting the latency of the stream. This is process-wide. */ - r = AudioUnitSetProperty(stm->unit, kAudioDevicePropertyBufferFrameSize, - kAudioUnitScope_Output, 0, &buffer_size, sizeof(buffer_size)); - if (r != 0) { - audiounit_stream_destroy(stm); - return CUBEB_ERROR; - } - } #endif - r = AudioUnitSetProperty(stm->unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, - 0, &ss, sizeof(ss)); - if (r != 0) { - audiounit_stream_destroy(stm); + /* We use a resampler because input AudioUnit operates + * reliable only in the capture device sample rate. + * Resampler will convert it to the user sample rate + * and deliver it to the callback. */ + int32_t target_sample_rate; + if (input_stream_params) { + target_sample_rate = input_stream_params->rate; + } else { + assert(output_stream_params); + target_sample_rate = output_stream_params->rate; + } + + cubeb_stream_params input_unconverted_params; + if (input_stream_params) { + input_unconverted_params = *input_stream_params; + /* Use the rate of the input device. */ + input_unconverted_params.rate = stm->input_hw_rate; + } + + /* Create resampler. Output params are unchanged + * because we do not need conversion on the output. */ + stm->resampler = cubeb_resampler_create(stm, + input_stream_params ? &input_unconverted_params : NULL, + output_stream_params, + target_sample_rate, + stm->data_callback, + stm->user_ptr, + CUBEB_RESAMPLER_QUALITY_DESKTOP); + if (!stm->resampler) { + LOG("Could not get a resampler\n"); return CUBEB_ERROR; } - r = AudioUnitInitialize(stm->unit); - if (r != 0) { + if (stm->input_unit != NULL && + AudioUnitInitialize(stm->input_unit) != 0) { + audiounit_stream_destroy(stm); + return CUBEB_ERROR; + } + if (stm->output_unit != NULL && + AudioUnitInitialize(stm->output_unit) != 0) { audiounit_stream_destroy(stm); return CUBEB_ERROR; } @@ -748,25 +1184,29 @@ audiounit_stream_init(cubeb * context, cubeb_stream ** stream, char const * stre static void audiounit_stream_destroy(cubeb_stream * stm) { - int r; - stm->shutdown = 1; - if (stm->unit) { - AudioOutputUnitStop(stm->unit); - AudioUnitUninitialize(stm->unit); -#if MACOSX_LESS_THAN_106 - CloseComponent(stm->unit); -#else - AudioComponentInstanceDispose(stm->unit); -#endif + if (stm->input_unit != NULL) { + AudioOutputUnitStop(stm->input_unit); + AudioUnitUninitialize(stm->input_unit); + AudioComponentInstanceDispose(stm->input_unit); + stm->input_unit = NULL; + } + + audiounit_destroy_input_buffer_list_array(stm); + + if (stm->output_unit != NULL) { + AudioOutputUnitStop(stm->output_unit); + AudioUnitUninitialize(stm->output_unit); + AudioComponentInstanceDispose(stm->output_unit); + stm->output_unit = NULL; } #if !TARGET_OS_IPHONE audiounit_uninstall_device_changed_callback(stm); #endif - r = pthread_mutex_destroy(&stm->mutex); + int r = pthread_mutex_destroy(&stm->mutex); assert(r == 0); pthread_mutex_lock(&stm->context->mutex); @@ -781,8 +1221,14 @@ static int audiounit_stream_start(cubeb_stream * stm) { OSStatus r; - r = AudioOutputUnitStart(stm->unit); - assert(r == 0); + if (stm->input_unit != NULL) { + r = AudioOutputUnitStart(stm->input_unit); + assert(r == 0); + } + if (stm->output_unit != NULL) { + r = AudioOutputUnitStart(stm->output_unit); + assert(r == 0); + } stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED); return CUBEB_OK; } @@ -791,8 +1237,14 @@ static int audiounit_stream_stop(cubeb_stream * stm) { OSStatus r; - r = AudioOutputUnitStop(stm->unit); - assert(r == 0); + if (stm->input_unit != NULL) { + r = AudioOutputUnitStop(stm->input_unit); + assert(r == 0); + } + if (stm->output_unit != NULL) { + r = AudioOutputUnitStop(stm->output_unit); + assert(r == 0); + } stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED); return CUBEB_OK; } @@ -839,7 +1291,7 @@ audiounit_stream_get_latency(cubeb_stream * stm, uint32_t * latency) } size = sizeof(unit_latency_sec); - r = AudioUnitGetProperty(stm->unit, + r = AudioUnitGetProperty(stm->output_unit, kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0, @@ -876,7 +1328,7 @@ audiounit_stream_get_latency(cubeb_stream * stm, uint32_t * latency) /* This part is fixed and depend on the stream parameter and the hardware. */ stm->hw_latency_frames = - (uint32_t)(unit_latency_sec * stm->sample_spec.mSampleRate) + (uint32_t)(unit_latency_sec * stm->output_desc.mSampleRate) + device_latency_frames + device_safety_offset; } @@ -892,7 +1344,7 @@ int audiounit_stream_set_volume(cubeb_stream * stm, float volume) { OSStatus r; - r = AudioUnitSetParameter(stm->unit, + r = AudioUnitSetParameter(stm->output_unit, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, volume, 0); @@ -905,7 +1357,7 @@ int audiounit_stream_set_volume(cubeb_stream * stm, float volume) int audiounit_stream_set_panning(cubeb_stream * stm, float panning) { - if (stm->sample_spec.mChannelsPerFrame > 2) { + if (stm->output_desc.mChannelsPerFrame > 2) { return CUBEB_ERROR_INVALID_PARAMETER; } @@ -989,7 +1441,7 @@ int audiounit_stream_get_current_device(cubeb_stream * stm, size = sizeof(UInt32); r = AudioObjectGetPropertyData(input_device_id, &datasource_address_input, 0, NULL, &size, &data); if (r != noErr) { - printf("Error when getting device !\n"); + LOG("Error when getting device !\n"); size = 0; data = 0; } @@ -1196,7 +1648,8 @@ audiounit_create_device_from_hwdev(AudioObjectID devid, cubeb_device_type type) adr.mSelector = kAudioDevicePropertyDeviceUID; if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &str) == noErr && str != NULL) { ret->device_id = audiounit_strref_to_cstr_utf8(str); - ret->devid = (cubeb_devid)ret->device_id; + ret->devid = malloc(sizeof(UInt32)); + memcpy(ret->devid, &devid, sizeof(UInt32)); ret->group_id = strdup(ret->device_id); CFRelease(str); } diff --git a/src/cubeb_pulse.c b/src/cubeb_pulse.c index f523bef..033ce89 100644 --- a/src/cubeb_pulse.c +++ b/src/cubeb_pulse.c @@ -1036,7 +1036,7 @@ pulse_sink_info_cb(pa_context * context, const pa_sink_info * info, devinfo = calloc(1, sizeof(cubeb_device_info)); devinfo->device_id = strdup(info->name); - devinfo->devid = (cubeb_devid)devinfo->device_id; + devinfo->devid = strdup(devinfo->device_id); devinfo->friendly_name = strdup(info->description); prop = WRAP(pa_proplist_gets)(info->proplist, "sysfs.path"); if (prop) @@ -1096,7 +1096,7 @@ pulse_source_info_cb(pa_context * context, const pa_source_info * info, devinfo = calloc(1, sizeof(cubeb_device_info)); devinfo->device_id = strdup(info->name); - devinfo->devid = (cubeb_devid)devinfo->device_id; + devinfo->devid = strdup(devinfo->device_id); devinfo->friendly_name = strdup(info->description); prop = WRAP(pa_proplist_gets)(info->proplist, "sysfs.path"); if (prop) diff --git a/src/cubeb_resampler.cpp b/src/cubeb_resampler.cpp index 45b9b5c..6f9ba92 100644 --- a/src/cubeb_resampler.cpp +++ b/src/cubeb_resampler.cpp @@ -33,27 +33,6 @@ to_speex_quality(cubeb_resampler_quality q) } } -template -cubeb_resampler_speex_one_way::cubeb_resampler_speex_one_way(uint32_t channels, - uint32_t source_rate, - uint32_t target_rate, - int quality) - : processor(channels) - , resampling_ratio(static_cast(source_rate) / target_rate) - , additional_latency(0) -{ - int r; - speex_resampler = speex_resampler_init(channels, source_rate, - target_rate, quality, &r); - assert(r == RESAMPLER_ERR_SUCCESS && "resampler allocation failure"); -} - -template -cubeb_resampler_speex_one_way::~cubeb_resampler_speex_one_way() -{ - speex_resampler_destroy(speex_resampler); -} - long noop_resampler::fill(void * input_buffer, long * input_frames_count, void * output_buffer, long output_frames) { diff --git a/src/cubeb_resampler_internal.h b/src/cubeb_resampler_internal.h index c845d91..eed16c4 100644 --- a/src/cubeb_resampler_internal.h +++ b/src/cubeb_resampler_internal.h @@ -1,5 +1,5 @@ /* - * Copyright © 2016 Mozilla Foundation + * Copyright © 2016 Mozilla Foundation * * This program is made available under an ISC-style license. See the * accompanying file LICENSE for details. @@ -32,9 +32,6 @@ namespace std int to_speex_quality(cubeb_resampler_quality q); -template -class cubeb_resampler_speex_one_way; - struct cubeb_resampler { virtual long fill(void * input_buffer, long * input_frames_count, void * output_buffer, long frames_needed) = 0; @@ -155,10 +152,22 @@ public: cubeb_resampler_speex_one_way(uint32_t channels, uint32_t source_rate, uint32_t target_rate, - int quality); + int quality) + : processor(channels) + , resampling_ratio(static_cast(source_rate) / target_rate) + , additional_latency(0) + { + int r; + speex_resampler = speex_resampler_init(channels, source_rate, + target_rate, quality, &r); + assert(r == RESAMPLER_ERR_SUCCESS && "resampler allocation failure"); + } /** Destructor, deallocate the resampler */ - virtual ~cubeb_resampler_speex_one_way(); + virtual ~cubeb_resampler_speex_one_way() + { + speex_resampler_destroy(speex_resampler); + } /** Sometimes, it is necessary to add latency on one way of a two-way * resampler so that the stream are synchronized. This must be called only on diff --git a/src/cubeb_ring_array.h b/src/cubeb_ring_array.h new file mode 100644 index 0000000..93498ec --- /dev/null +++ b/src/cubeb_ring_array.h @@ -0,0 +1,133 @@ +/* + * Copyright © 2016 Mozilla Foundation + * + * This program is made available under an ISC-style license. See the + * accompanying file LICENSE for details. + */ + +#ifndef CUBEB_RING_ARRAY_H +#define CUBEB_RING_ARRAY_H + +#if defined(__cplusplus) +extern "C" { +#endif + +/** Ring array of pointers is used to hold buffers. In case that + asynchronus producer/consumer callbacks do not arrive in a + repeated order the ring array stores the buffers and fetch + them in the correct order. */ +#define RING_ARRAY_CAPACITY 8 + +typedef struct { + void* pointer_array[RING_ARRAY_CAPACITY]; /**< Array that hold pointers of the allocated space for the buffers. */ + unsigned int tail; /**< Index of the last element (first to deliver). */ + int count; /**< Number of elements in the array. */ + unsigned int capacity; /**< Total length of the array. */ + pthread_mutex_t mutex; /**< Mutex to synchronize store/fetch. */ +} ring_array; + +/** Initialize the ring array. + @param ra The ring_array pointer of allocated structure. + @retval 0 on success. */ +int +ring_array_init(ring_array * ra) +{ + ra->capacity = RING_ARRAY_CAPACITY; + ra->tail = 0; + ra->count = 0; + memset(ra->pointer_array, 0, sizeof(ra->pointer_array)); + + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT); + int ret = pthread_mutex_init(&ra->mutex, &attr); + assert(0 == ret); + pthread_mutexattr_destroy(&attr); + + assert(ra->pointer_array[0] == NULL); + + return ret; +} + +/** Set the allocated space to store the data. + This must be done before store/fetch. + @param ra The ring_array pointer. + @param data Pointer to allocated space of buffers. + @param index Index between 0 and capacity-1 to store the allocated data buffer. + @retval The data pointer on success or NULL on failure. */ +void * +ring_array_set_data(ring_array * ra, void * data, unsigned int index) +{ + if (index < ra->capacity) { + ra->pointer_array[index] = data; + return data; + } + return NULL; +} + +/** Destroy the ring array. + @param ra The ring_array pointer.*/ +void +ring_array_destroy(ring_array * ra) +{ + pthread_mutex_destroy(&ra->mutex); +} + +/** Get the allocated buffer to be stored with fresh data. + @param ra The ring_array pointer. + @retval Pointer of the allocated space to be stored with fresh data or NULL if full. */ +void * +ring_array_store_buffer(ring_array * ra) +{ + int rv = pthread_mutex_lock(&ra->mutex); + assert(rv == 0); + + assert(ra->pointer_array[0] != NULL); + + if (ra->count == (int)ra->capacity) { + pthread_mutex_unlock(&ra->mutex); + return NULL; + } + + assert(ra->count == 0 || (ra->tail + ra->count) % ra->capacity != ra->tail); + void * ret = ra->pointer_array[(ra->tail + ra->count) % ra->capacity]; + + ++ra->count; + assert(ra->count <= (int)ra->capacity); + + pthread_mutex_unlock(&ra->mutex); + return ret; +} + +/** Get the next available buffer with data. + @param ra The ring_array pointer. + @retval Pointer of the next in order data buffer or NULL if empty. */ +void * +ring_array_fetch_buffer(ring_array * ra) +{ + int rv = pthread_mutex_lock(&ra->mutex); + assert(rv == 0); + + assert(ra->pointer_array[0] != NULL); + + if (ra->count == 0) { + pthread_mutex_unlock(&ra->mutex); + return NULL; + } + void * ret = ra->pointer_array[ra->tail]; + + ra->tail = (ra->tail + 1) % ra->capacity; + assert(ra->tail < ra->capacity); + + ra->count--; + assert(ra->count >= 0); + + pthread_mutex_unlock(&ra->mutex); + return ret; +} + +#if defined(__cplusplus) +} +#endif + +#endif //CUBEB_RING_ARRAY_H diff --git a/test/test_resampler.cpp b/test/test_resampler.cpp index 5a5cd69..c4d9e51 100644 --- a/test/test_resampler.cpp +++ b/test/test_resampler.cpp @@ -1,5 +1,5 @@ /* - * Copyright © 2016 Mozilla Foundation + * Copyright © 2016 Mozilla Foundation * * This program is made available under an ISC-style license. See the * accompanying file LICENSE for details. @@ -8,9 +8,6 @@ #define OUTSIDE_SPEEX #define RANDOM_PREFIX speex -#include "cubeb/cubeb.h" -#include "cubeb_utils.h" -#include "cubeb_resampler.h" #include "cubeb_resampler_internal.h" #include #include @@ -312,9 +309,10 @@ bool array_fuzzy_equal(const auto_array& lhs, const auto_array& rhs, T eps for (uint32_t i = 0; i < len; i++) { if (abs(lhs.at(i) - rhs.at(i)) > epsi) { - std::cout << "not fuzzy equal at index " << i - << "lhs: " << lhs.at(i) << " rhs: " << rhs.at(i) - << "delta: " << abs(lhs.at(i) - rhs.at(i)) << std::endl; + std::cout << "not fuzzy equal at index: " << i + << " lhs: " << lhs.at(i) << " rhs: " << rhs.at(i) + << " delta: " << abs(lhs.at(i) - rhs.at(i)) + << " epsilon: "<< epsi << std::endl; return false; } } @@ -430,7 +428,7 @@ void test_resamplers_duplex() for (uint32_t source_rate_output = 0; source_rate_output < array_size(sample_rates); source_rate_output++) { for (uint32_t dest_rate = 0; dest_rate < array_size(sample_rates); dest_rate++) { for (uint32_t chunk_duration = min_chunks; chunk_duration < max_chunks; chunk_duration+=chunk_increment) { - printf("input chanenls:%d output_channels:%d input_rate:%d" + printf("input chanenls:%d output_channels:%d input_rate:%d " "output_rate:%d target_rate:%d chunk_ms:%d\n", input_channels, output_channels, sample_rates[source_rate_input], diff --git a/test/test_ring_array.c b/test/test_ring_array.c new file mode 100644 index 0000000..c2cdfc8 --- /dev/null +++ b/test/test_ring_array.c @@ -0,0 +1,70 @@ +#include +#include +#include + +#include "cubeb_ring_array.h" + +int test_ring_array() +{ + ring_array ra; + ring_array_init(&ra); + int data[RING_ARRAY_CAPACITY] ;// {1,2,3,4,5,6,7,8}; + void * p_data = NULL; + + for (int i =0; i < RING_ARRAY_CAPACITY; ++i) { + data[i] = i; // in case RING_ARRAY_CAPACITY change value + p_data = ring_array_set_data(&ra, &data[i], i); + assert(p_data); + assert(p_data == &data[i]); + } + + p_data = NULL; + /* Get store buffers*/ + for (int i =0; i < RING_ARRAY_CAPACITY; ++i) { + p_data = ring_array_store_buffer(&ra); + assert(p_data == &data[i]); + } + /*Now array is full extra store should give NULL*/ + assert(NULL == ring_array_store_buffer(&ra)); + /* Get fetch buffers*/ + for (int i =0; i < RING_ARRAY_CAPACITY; ++i) { + p_data = ring_array_fetch_buffer(&ra); + assert(p_data == &data[i]); + } + /*Now array is empty extra fetch should give NULL*/ + assert(NULL == ring_array_fetch_buffer(&ra)); + + p_data = NULL; + /* Repeated store fetch should can go for ever*/ + for (int i =0; i < 2*RING_ARRAY_CAPACITY; ++i) { + p_data = ring_array_store_buffer(&ra); + assert(p_data); + assert(ring_array_fetch_buffer(&ra) == p_data); + } + + p_data = NULL; + /* Verify/modify buffer data*/ + for (int i =0; i < RING_ARRAY_CAPACITY; ++i) { + p_data = ring_array_store_buffer(&ra); + assert(p_data); + assert(*((int*)p_data) == data[i]); + (*((int*)p_data))++; // Modify data + } + for (int i =0; i < RING_ARRAY_CAPACITY; ++i) { + p_data = ring_array_fetch_buffer(&ra); + assert(p_data); + assert(*((int*)p_data) == i+1); // Verify modified data + } + + ring_array_destroy(&ra); + + return 0; +} + + +int main() +{ + test_ring_array(); + return 0; +} + From 91200251d997f34b3fad222c29dcbca877bcaff6 Mon Sep 17 00:00:00 2001 From: Alex Chronopoulos Date: Mon, 7 Mar 2016 13:35:35 +0200 Subject: [PATCH 02/17] Formating, typo and local var rebaming. --- src/cubeb_audiounit.c | 182 +++++++++++++++++++++-------------------- src/cubeb_ring_array.h | 4 +- test/test_ring_array.c | 12 +-- 3 files changed, 103 insertions(+), 95 deletions(-) diff --git a/src/cubeb_audiounit.c b/src/cubeb_audiounit.c index db38628..729aabc 100644 --- a/src/cubeb_audiounit.c +++ b/src/cubeb_audiounit.c @@ -160,32 +160,32 @@ audiounit_input_callback(void * user_ptr, UInt32 input_frames, AudioBufferList * bufs) { - cubeb_stream * stream = user_ptr; + cubeb_stream * stm = user_ptr; long outframes, frames; void * input_buffer = NULL; - pthread_mutex_lock(&stream->mutex); - ASSERT_LOCKED(stream->mutex); + pthread_mutex_lock(&stm->mutex); + ASSERT_LOCKED(stm->mutex); - assert(stream->input_unit != NULL); + assert(stm->input_unit != NULL); assert(AU_IN_BUS == bus); - if (stream->shutdown) { - pthread_mutex_unlock(&stream->mutex); + if (stm->shutdown) { + pthread_mutex_unlock(&stm->mutex); return noErr; } /* Get next store buffer from ring array */ - AudioBufferList * store_input_buffer_list = ring_array_store_buffer(&stream->input_buffer_list_array); + AudioBufferList * store_input_buffer_list = ring_array_store_buffer(&stm->input_buffer_list_array); if (store_input_buffer_list == NULL) { LOG("input: Ring array is full drop one buffer\n"); - ring_array_fetch_buffer(&stream->input_buffer_list_array); + ring_array_fetch_buffer(&stm->input_buffer_list_array); - store_input_buffer_list = ring_array_store_buffer(&stream->input_buffer_list_array); + store_input_buffer_list = ring_array_store_buffer(&stm->input_buffer_list_array); assert(store_input_buffer_list); } /* Render input samples */ - OSStatus r = AudioUnitRender(stream->input_unit, + OSStatus r = AudioUnitRender(stm->input_unit, flags, tstamp, bus, @@ -196,41 +196,41 @@ audiounit_input_callback(void * user_ptr, store_input_buffer_list->mBuffers[0].mDataByteSize, store_input_buffer_list->mBuffers[0].mNumberChannels, input_frames); assert(input_frames > 0); - stream->frames_read += input_frames; + stm->frames_read += input_frames; // Full Duplex. We'll call data_callback in the AudioUnit output callback. - if (stream->output_unit != NULL) { + if (stm->output_unit != NULL) { // User callback will be called by output callback - pthread_mutex_unlock(&stream->mutex); + pthread_mutex_unlock(&stm->mutex); return noErr; } /* Input only. Call the user callback through resampler. Resampler will deliver input buffer in the correct rate. */ frames = input_frames; - AudioBufferList * fetch_input_buffer_list = ring_array_fetch_buffer(&stream->input_buffer_list_array); + AudioBufferList * fetch_input_buffer_list = ring_array_fetch_buffer(&stm->input_buffer_list_array); assert(fetch_input_buffer_list && "fetch buffer is null in the input"); input_buffer = fetch_input_buffer_list->mBuffers[0].mData; - outframes = cubeb_resampler_fill(stream->resampler, + outframes = cubeb_resampler_fill(stm->resampler, input_buffer, &frames, NULL, 0); if (outframes < 0 || outframes != input_frames) { - stream->shutdown = 1; - pthread_mutex_unlock(&stream->mutex); + stm->shutdown = 1; + pthread_mutex_unlock(&stm->mutex); return noErr; } - pthread_mutex_unlock(&stream->mutex); + pthread_mutex_unlock(&stm->mutex); return noErr; } static void audiounit_make_silent(AudioBufferList * ioData) { - for(UInt32 i = 0; imNumberBuffers;i++) { + for(UInt32 i = 0; i < ioData->mNumberBuffers; i++) { memset(ioData->mBuffers[i].mData, 0, ioData->mBuffers[i].mDataByteSize); } } @@ -250,29 +250,29 @@ audiounit_output_callback(void * user_ptr, outBufferList->mNumberBuffers, outBufferList->mBuffers[0].mDataByteSize, outBufferList->mBuffers[0].mNumberChannels, output_frames); - cubeb_stream * stream = user_ptr; + cubeb_stream * stm = user_ptr; long outframes = 0, input_frames = 0; void * output_buffer = NULL, * input_buffer = NULL; - pthread_mutex_lock(&stream->mutex); - ASSERT_LOCKED(stream->mutex); + pthread_mutex_lock(&stm->mutex); + ASSERT_LOCKED(stm->mutex); - if (stream->shutdown) { + if (stm->shutdown) { audiounit_make_silent(outBufferList); - pthread_mutex_unlock(&stream->mutex); + pthread_mutex_unlock(&stm->mutex); return noErr; } - stream->current_latency_frames = audiotimestamp_to_latency(tstamp, stream); - if (stream->draining) { - OSStatus r = AudioOutputUnitStop(stream->output_unit); + stm->current_latency_frames = audiotimestamp_to_latency(tstamp, stm); + if (stm->draining) { + OSStatus r = AudioOutputUnitStop(stm->output_unit); assert(r == 0); - if (stream->input_unit) { - r = AudioOutputUnitStop(stream->input_unit); - assert(r==0); + if (stm->input_unit) { + r = AudioOutputUnitStop(stm->input_unit); + assert(r == 0); } - stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_DRAINED); - pthread_mutex_unlock(&stream->mutex); + stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED); + pthread_mutex_unlock(&stm->mutex); audiounit_make_silent(outBufferList); return noErr; } @@ -280,15 +280,15 @@ audiounit_output_callback(void * user_ptr, output_buffer = outBufferList->mBuffers[0].mData; /* If Full duplex get also input buffer */ AudioBufferList * fetch_input_buffer_list = NULL; - if (stream->input_unit != NULL) { + if (stm->input_unit != NULL) { /* Output callback came first */ - if (stream->frames_read == 0) { + if (stm->frames_read == 0) { audiounit_make_silent(outBufferList); - pthread_mutex_unlock(&stream->mutex); + pthread_mutex_unlock(&stm->mutex); return noErr; } /* Input samples stored previously in input callback. */ - fetch_input_buffer_list = ring_array_fetch_buffer(&stream->input_buffer_list_array); + fetch_input_buffer_list = ring_array_fetch_buffer(&stm->input_buffer_list_array); if (fetch_input_buffer_list == NULL) { LOG("Requested more output than input. " "This is either a hole or we are after a stream stop and input thread stopped before output\n"); @@ -299,16 +299,16 @@ audiounit_output_callback(void * user_ptr, /* Avoid here to allocate new memory since we are inside callback. Use the existing * allocated buffers since the ring array is empty and the buffer is not used. */ - fetch_input_buffer_list = &stream->input_buffer_list[0]; + fetch_input_buffer_list = &stm->input_buffer_list[0]; audiounit_make_silent(fetch_input_buffer_list); } input_buffer = fetch_input_buffer_list->mBuffers[0].mData; - input_frames = stream->input_buffer_frames; - assert(stream->frames_read > 0); + input_frames = stm->input_buffer_frames; + assert(stm->frames_read > 0); } /* Call user callback through resampler. */ - outframes = cubeb_resampler_fill(stream->resampler, + outframes = cubeb_resampler_fill(stm->resampler, input_buffer, &input_frames, output_buffer, @@ -319,10 +319,9 @@ audiounit_output_callback(void * user_ptr, audiounit_make_silent(fetch_input_buffer_list); } - if (outframes < 0) { - stream->shutdown = 1; - pthread_mutex_unlock(&stream->mutex); + stm->shutdown = 1; + pthread_mutex_unlock(&stm->mutex); return noErr; } @@ -330,17 +329,17 @@ audiounit_output_callback(void * user_ptr, float panning; size_t outbpf; - outbpf = stream->output_desc.mBytesPerFrame; - stream->draining = (outframes < output_frames); - stream->frames_played = stream->frames_queued; - stream->frames_queued += outframes; + outbpf = stm->output_desc.mBytesPerFrame; + stm->draining = outframes < output_frames; + stm->frames_played = stm->frames_queued; + stm->frames_queued += outframes; - outaff = stream->output_desc.mFormatFlags; - panning = (stream->output_desc.mChannelsPerFrame == 2) ? stream->panning : 0.0f; - pthread_mutex_unlock(&stream->mutex); + outaff = stm->output_desc.mFormatFlags; + panning = (stm->output_desc.mChannelsPerFrame == 2) ? stm->panning : 0.0f; + pthread_mutex_unlock(&stm->mutex); /* Post process output samples. */ - if (stream->draining) { + if (stm->draining) { /* Clear missing frames (silence) */ memset(output_buffer + outframes * outbpf, 0, (output_frames - outframes) * outbpf); } @@ -597,12 +596,13 @@ audiounit_get_default_device_id(cubeb_device_type type) AudioDeviceID devid; UInt32 size; - if (type == CUBEB_DEVICE_TYPE_OUTPUT) + if (type == CUBEB_DEVICE_TYPE_OUTPUT) { adr.mSelector = kAudioHardwarePropertyDefaultOutputDevice; - else if (type == CUBEB_DEVICE_TYPE_INPUT) + } else if (type == CUBEB_DEVICE_TYPE_INPUT) { adr.mSelector = kAudioHardwarePropertyDefaultInputDevice; - else + } else { return kAudioObjectUnknown; + } size = sizeof(AudioDeviceID); if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &adr, 0, NULL, &size, &devid) != noErr) { @@ -725,8 +725,8 @@ audiounit_destroy(cubeb * ctx) static void audiounit_stream_destroy(cubeb_stream * stm); static int -audio_stream_desc_init (AudioStreamBasicDescription * ss, - const cubeb_stream_params * stream_params) +audio_stream_desc_init(AudioStreamBasicDescription * ss, + const cubeb_stream_params * stream_params) { memset(ss, 0, sizeof(AudioStreamBasicDescription)); @@ -805,10 +805,10 @@ audiounit_create_unit(AudioUnit * unit, } if (device == NULL) { - devid = audiounit_get_default_device_id (is_input ? CUBEB_DEVICE_TYPE_INPUT - : CUBEB_DEVICE_TYPE_OUTPUT); + devid = audiounit_get_default_device_id(is_input ? CUBEB_DEVICE_TYPE_INPUT + : CUBEB_DEVICE_TYPE_OUTPUT); } else { - devid = *(AudioDeviceID *)device; + devid = *(AudioDeviceID*)device; } int err = AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, @@ -822,9 +822,9 @@ audiounit_create_unit(AudioUnit * unit, } static int -audiounit_buflst_init_single_buffer (AudioBufferList * buflst, - const AudioStreamBasicDescription * desc, - uint32_t frames) +audiounit_buflst_init_single_buffer(AudioBufferList * buflst, + const AudioStreamBasicDescription * desc, + uint32_t frames) { size_t size = desc->mBytesPerFrame * frames; @@ -894,7 +894,7 @@ audiounit_stream_init(cubeb * context, cubeb_stream * stm; AudioUnit input_unit; AudioUnit output_unit; - int ret; + int r; AURenderCallbackStruct aurcbs_in; AURenderCallbackStruct aurcbs_out; UInt32 size; @@ -915,15 +915,15 @@ audiounit_stream_init(cubeb * context, pthread_mutex_unlock(&context->mutex); if (input_stream_params != NULL) { - if ((ret = audiounit_create_unit (&input_unit, true, + if ((r = audiounit_create_unit(&input_unit, true, input_stream_params, input_device)) != CUBEB_OK) - return ret; + return r; } if (output_stream_params != NULL) { - if ((ret = audiounit_create_unit (&output_unit, false, + if ((r = audiounit_create_unit(&output_unit, false, output_stream_params, output_device)) != CUBEB_OK) - return ret; + return r; } stm = calloc(1, sizeof(cubeb_stream)); @@ -931,7 +931,7 @@ audiounit_stream_init(cubeb * context, /* These could be different in the future if we have both * full-duplex stream and different devices for input vs output. */ - stm->input_unit = (input_stream_params != NULL) ? input_unit : NULL; + stm->input_unit = (input_stream_params != NULL) ? input_unit : NULL; stm->output_unit = (output_stream_params != NULL) ? output_unit : NULL; stm->context = context; stm->data_callback = data_callback; @@ -946,8 +946,8 @@ audiounit_stream_init(cubeb * context, #else pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); #endif - ret = pthread_mutex_init(&stm->mutex, &attr); - assert(0 == ret); + r = pthread_mutex_init(&stm->mutex, &attr); + assert(r == 0); pthread_mutexattr_destroy(&attr); stm->draining = false; @@ -960,7 +960,6 @@ audiounit_stream_init(cubeb * context, /* Setup Input Stream! */ if (input_stream_params != NULL) { - size = sizeof(UInt32); if (AudioUnitGetProperty(stm->input_unit, kAudioDevicePropertyBufferFrameSize, @@ -990,16 +989,16 @@ audiounit_stream_init(cubeb * context, kAudioUnitScope_Input, AU_IN_BUS, &input_hw_desc, - &size) !=0) { + &size) != 0) { audiounit_stream_destroy(stm); return CUBEB_ERROR; } stm->input_hw_rate = input_hw_desc.mSampleRate; /* Set format description according to the input params. */ - if ((ret = audio_stream_desc_init(&stm->input_desc, input_stream_params)) != CUBEB_OK) { + if ((r = audio_stream_desc_init(&stm->input_desc, input_stream_params)) != CUBEB_OK) { audiounit_stream_destroy(stm); - return ret; + return r; } AudioStreamBasicDescription src_desc = stm->input_desc; @@ -1014,7 +1013,7 @@ audiounit_stream_init(cubeb * context, kAudioUnitScope_Output, AU_IN_BUS, &src_desc, - sizeof(AudioStreamBasicDescription)) !=0) { + sizeof(AudioStreamBasicDescription)) != 0) { audiounit_stream_destroy(stm); return CUBEB_ERROR; } @@ -1051,10 +1050,9 @@ audiounit_stream_init(cubeb * context, /* Setup Output Stream! */ if (output_stream_params != NULL) { - - if ((ret = audio_stream_desc_init(&stm->output_desc, output_stream_params)) != CUBEB_OK) { + if ((r = audio_stream_desc_init(&stm->output_desc, output_stream_params)) != CUBEB_OK) { audiounit_stream_destroy(stm); - return ret; + return r; } if (AudioUnitSetProperty(stm->output_unit, @@ -1493,12 +1491,15 @@ audiounit_get_devices(AudioObjectID ** devices, uint32_t * count) kAudioObjectPropertyElementMaster }; ret = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &adr, 0, NULL, &size); - if (ret != noErr) + if (ret != noErr) { return ret; + } *count = (uint32_t)(size / sizeof(AudioObjectID)); if (size >= sizeof(AudioObjectID)) { - if (*devices != NULL) free(*devices); + if (*devices != NULL) { + free(*devices); + } *devices = malloc(size); memset(*devices, 0, size); @@ -1515,11 +1516,13 @@ audiounit_get_devices(AudioObjectID ** devices, uint32_t * count) } static char * -audiounit_strref_to_cstr_utf8(CFStringRef strref) { +audiounit_strref_to_cstr_utf8(CFStringRef strref) +{ CFIndex len, size; char * ret; - if (strref == NULL) + if (strref == NULL) { return NULL; + } len = CFStringGetLength(strref); size = CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8); @@ -1563,8 +1566,9 @@ audiounit_get_available_samplerate(AudioObjectID devid, AudioObjectPropertyScope if (AudioObjectHasProperty(devid, &adr)) { UInt32 size = sizeof(Float64); Float64 fvalue = 0.0; - if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &fvalue) == noErr) + if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &fvalue) == noErr) { *def = fvalue; + } } adr.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; @@ -1603,8 +1607,9 @@ audiounit_get_device_presentation_latency(AudioObjectID devid, AudioObjectProper adr.mSelector = kAudioDevicePropertyLatency; size = sizeof(UInt32); - if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &dev) != noErr) + if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &dev) != noErr) { dev = 0; + } adr.mSelector = kAudioDevicePropertyStreams; size = sizeof(sid); @@ -1616,8 +1621,9 @@ audiounit_get_device_presentation_latency(AudioObjectID devid, AudioObjectProper adr.mSelector = kAudioDevicePropertySafetyOffset; size = sizeof(UInt32); - if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &offset) != noErr) + if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &offset) != noErr) { offset = 0; + } return dev + stream + offset; } @@ -1639,8 +1645,9 @@ audiounit_create_device_from_hwdev(AudioObjectID devid, cubeb_device_type type) return NULL; } - if ((ch = audiounit_get_channel_count(devid, adr.mScope)) == 0) + if ((ch = audiounit_get_channel_count(devid, adr.mScope)) == 0) { return NULL; + } ret = calloc(1, sizeof(cubeb_device_info)); @@ -1722,8 +1729,9 @@ audiounit_enumerate_devices(cubeb * context, cubeb_device_type type, uint32_t i, hwdevcount = 0; OSStatus err; - if ((err = audiounit_get_devices(&hwdevs, &hwdevcount)) != noErr) + if ((err = audiounit_get_devices(&hwdevs, &hwdevcount)) != noErr) { return CUBEB_ERROR; + } *collection = malloc(sizeof(cubeb_device_collection) + sizeof(cubeb_device_info*) * (hwdevcount > 0 ? hwdevcount - 1 : 0)); diff --git a/src/cubeb_ring_array.h b/src/cubeb_ring_array.h index 93498ec..adf96a2 100644 --- a/src/cubeb_ring_array.h +++ b/src/cubeb_ring_array.h @@ -13,7 +13,7 @@ extern "C" { #endif /** Ring array of pointers is used to hold buffers. In case that - asynchronus producer/consumer callbacks do not arrive in a + asynchronous producer/consumer callbacks do not arrive in a repeated order the ring array stores the buffers and fetch them in the correct order. */ #define RING_ARRAY_CAPACITY 8 @@ -41,7 +41,7 @@ ring_array_init(ring_array * ra) pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT); int ret = pthread_mutex_init(&ra->mutex, &attr); - assert(0 == ret); + assert(ret == 0); pthread_mutexattr_destroy(&attr); assert(ra->pointer_array[0] == NULL); diff --git a/test/test_ring_array.c b/test/test_ring_array.c index c2cdfc8..a9c9f7c 100644 --- a/test/test_ring_array.c +++ b/test/test_ring_array.c @@ -11,7 +11,7 @@ int test_ring_array() int data[RING_ARRAY_CAPACITY] ;// {1,2,3,4,5,6,7,8}; void * p_data = NULL; - for (int i =0; i < RING_ARRAY_CAPACITY; ++i) { + for (int i = 0; i < RING_ARRAY_CAPACITY; ++i) { data[i] = i; // in case RING_ARRAY_CAPACITY change value p_data = ring_array_set_data(&ra, &data[i], i); assert(p_data); @@ -20,14 +20,14 @@ int test_ring_array() p_data = NULL; /* Get store buffers*/ - for (int i =0; i < RING_ARRAY_CAPACITY; ++i) { + for (int i = 0; i < RING_ARRAY_CAPACITY; ++i) { p_data = ring_array_store_buffer(&ra); assert(p_data == &data[i]); } /*Now array is full extra store should give NULL*/ assert(NULL == ring_array_store_buffer(&ra)); /* Get fetch buffers*/ - for (int i =0; i < RING_ARRAY_CAPACITY; ++i) { + for (int i = 0; i < RING_ARRAY_CAPACITY; ++i) { p_data = ring_array_fetch_buffer(&ra); assert(p_data == &data[i]); } @@ -36,7 +36,7 @@ int test_ring_array() p_data = NULL; /* Repeated store fetch should can go for ever*/ - for (int i =0; i < 2*RING_ARRAY_CAPACITY; ++i) { + for (int i = 0; i < 2*RING_ARRAY_CAPACITY; ++i) { p_data = ring_array_store_buffer(&ra); assert(p_data); assert(ring_array_fetch_buffer(&ra) == p_data); @@ -44,13 +44,13 @@ int test_ring_array() p_data = NULL; /* Verify/modify buffer data*/ - for (int i =0; i < RING_ARRAY_CAPACITY; ++i) { + for (int i = 0; i < RING_ARRAY_CAPACITY; ++i) { p_data = ring_array_store_buffer(&ra); assert(p_data); assert(*((int*)p_data) == data[i]); (*((int*)p_data))++; // Modify data } - for (int i =0; i < RING_ARRAY_CAPACITY; ++i) { + for (int i = 0; i < RING_ARRAY_CAPACITY; ++i) { p_data = ring_array_fetch_buffer(&ra); assert(p_data); assert(*((int*)p_data) == i+1); // Verify modified data From ce30446ba5f7fa9cb3f8b918455e4ead692ee1a0 Mon Sep 17 00:00:00 2001 From: Alex Chronopoulos Date: Mon, 7 Mar 2016 15:59:24 +0200 Subject: [PATCH 03/17] Remove mutex assert and reword devid comment --- include/cubeb/cubeb.h | 2 +- src/cubeb_audiounit.c | 16 ---------------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/include/cubeb/cubeb.h b/include/cubeb/cubeb.h index 1662c3d..1140782 100644 --- a/include/cubeb/cubeb.h +++ b/include/cubeb/cubeb.h @@ -221,7 +221,7 @@ typedef enum { * `cubeb_enumerate_devices`, and must be destroyed using * `cubeb_device_info_destroy`. */ typedef struct { - cubeb_devid devid; /**< Device identifier handle. Always fresh allocate and copy it's value. */ + cubeb_devid devid; /**< Device identifier handle. Required deep copy of the value. */ char * device_id; /**< Device identifier which might be presented in a UI. */ char * friendly_name; /**< Friendly device name which might be presented in a UI. */ char * group_id; /**< Two devices have the same group identifier if they belong to the same physical device; for example a headset and microphone. */ diff --git a/src/cubeb_audiounit.c b/src/cubeb_audiounit.c index 729aabc..bccd323 100644 --- a/src/cubeb_audiounit.c +++ b/src/cubeb_audiounit.c @@ -61,16 +61,6 @@ #define LOG(...) #endif -#ifdef DEBUG -#define ASSERT_LOCKED(mutex) \ -do { \ - int rv = pthread_mutex_lock(&mutex); \ - assert(rv == EDEADLK); \ -} while (0); -#else -#define ASSERT_LOCKED(mutex) -#endif - static struct cubeb_ops const audiounit_ops; struct cubeb { @@ -165,7 +155,6 @@ audiounit_input_callback(void * user_ptr, void * input_buffer = NULL; pthread_mutex_lock(&stm->mutex); - ASSERT_LOCKED(stm->mutex); assert(stm->input_unit != NULL); assert(AU_IN_BUS == bus); @@ -255,7 +244,6 @@ audiounit_output_callback(void * user_ptr, void * output_buffer = NULL, * input_buffer = NULL; pthread_mutex_lock(&stm->mutex); - ASSERT_LOCKED(stm->mutex); if (stm->shutdown) { audiounit_make_silent(outBufferList); @@ -941,11 +929,7 @@ audiounit_stream_init(cubeb * context, pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); -#ifdef DEBUG - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); -#else pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); -#endif r = pthread_mutex_init(&stm->mutex, &attr); assert(r == 0); pthread_mutexattr_destroy(&attr); From 6c782af0e76b9bc6c05c4c63c765f71ff6ff0488 Mon Sep 17 00:00:00 2001 From: Alex Chronopoulos Date: Mon, 7 Mar 2016 18:30:39 +0200 Subject: [PATCH 04/17] Apply various review comments. --- src/cubeb_audiounit.c | 56 ++++++++++++++++--------------------------- 1 file changed, 20 insertions(+), 36 deletions(-) diff --git a/src/cubeb_audiounit.c b/src/cubeb_audiounit.c index bccd323..06af732 100644 --- a/src/cubeb_audiounit.c +++ b/src/cubeb_audiounit.c @@ -152,7 +152,6 @@ audiounit_input_callback(void * user_ptr, { cubeb_stream * stm = user_ptr; long outframes, frames; - void * input_buffer = NULL; pthread_mutex_lock(&stm->mutex); @@ -199,9 +198,8 @@ audiounit_input_callback(void * user_ptr, frames = input_frames; AudioBufferList * fetch_input_buffer_list = ring_array_fetch_buffer(&stm->input_buffer_list_array); assert(fetch_input_buffer_list && "fetch buffer is null in the input"); - input_buffer = fetch_input_buffer_list->mBuffers[0].mData; outframes = cubeb_resampler_fill(stm->resampler, - input_buffer, + fetch_input_buffer_list->mBuffers[0].mData, &frames, NULL, 0); @@ -313,17 +311,13 @@ audiounit_output_callback(void * user_ptr, return noErr; } - AudioFormatFlags outaff; - float panning; - size_t outbpf; - - outbpf = stm->output_desc.mBytesPerFrame; + size_t outbpf = stm->output_desc.mBytesPerFrame; stm->draining = outframes < output_frames; stm->frames_played = stm->frames_queued; stm->frames_queued += outframes; - outaff = stm->output_desc.mFormatFlags; - panning = (stm->output_desc.mChannelsPerFrame == 2) ? stm->panning : 0.0f; + AudioFormatFlags outaff = stm->output_desc.mFormatFlags; + float panning = (stm->output_desc.mChannelsPerFrame == 2) ? stm->panning : 0.0f; pthread_mutex_unlock(&stm->mutex); /* Post process output samples. */ @@ -716,8 +710,6 @@ static int audio_stream_desc_init(AudioStreamBasicDescription * ss, const cubeb_stream_params * stream_params) { - memset(ss, 0, sizeof(AudioStreamBasicDescription)); - switch (stream_params->format) { case CUBEB_SAMPLE_S16LE: ss->mBitsPerChannel = 16; @@ -750,6 +742,8 @@ audio_stream_desc_init(AudioStreamBasicDescription * ss, ss->mFramesPerPacket = 1; ss->mBytesPerPacket = ss->mBytesPerFrame * ss->mFramesPerPacket; + ss->mReserved = 0; + return CUBEB_OK; } @@ -886,10 +880,6 @@ audiounit_stream_init(cubeb * context, AURenderCallbackStruct aurcbs_in; AURenderCallbackStruct aurcbs_out; UInt32 size; -#if 0 - unsigned int buffer_size, default_buffer_size; - AudioValueRange latency_range; -#endif assert(context); *stream = NULL; @@ -902,15 +892,15 @@ audiounit_stream_init(cubeb * context, context->active_streams += 1; pthread_mutex_unlock(&context->mutex); - if (input_stream_params != NULL) { - if ((r = audiounit_create_unit(&input_unit, true, - input_stream_params, input_device)) != CUBEB_OK) + if (input_stream_params != NULL && + (r = audiounit_create_unit(&input_unit, true, + input_stream_params, input_device)) != CUBEB_OK) { return r; } - if (output_stream_params != NULL) { - if ((r = audiounit_create_unit(&output_unit, false, - output_stream_params, output_device)) != CUBEB_OK) + if (output_stream_params != NULL && + (r = audiounit_create_unit(&output_unit, false, + output_stream_params, output_device)) != CUBEB_OK) { return r; } @@ -934,12 +924,7 @@ audiounit_stream_init(cubeb * context, assert(r == 0); pthread_mutexattr_destroy(&attr); - stm->draining = false; - stm->shutdown = 0; - stm->frames_played = 0; - stm->frames_queued = 0; - stm->frames_read = 0; - stm->current_latency_frames = 0; + /* Init data members where necessary */ stm->hw_latency_frames = UINT64_MAX; /* Setup Input Stream! */ @@ -986,11 +971,9 @@ audiounit_stream_init(cubeb * context, } AudioStreamBasicDescription src_desc = stm->input_desc; - /* Input AudioUnit must be configured with device's sample rate. */ - if (src_desc.mSampleRate != stm->input_hw_rate) { - /* Set the sample rate of the device we will resample inside input callback. */ - src_desc.mSampleRate = stm->input_hw_rate; - } + /* Input AudioUnit must be configured with device's sample rate. + we will resample inside input callback. */ + src_desc.mSampleRate = stm->input_hw_rate; if (AudioUnitSetProperty(stm->input_unit, kAudioUnitProperty_StreamFormat, @@ -1112,7 +1095,7 @@ audiounit_stream_init(cubeb * context, * reliable only in the capture device sample rate. * Resampler will convert it to the user sample rate * and deliver it to the callback. */ - int32_t target_sample_rate; + uint32_t target_sample_rate; if (input_stream_params) { target_sample_rate = input_stream_params->rate; } else { @@ -1138,6 +1121,7 @@ audiounit_stream_init(cubeb * context, CUBEB_RESAMPLER_QUALITY_DESKTOP); if (!stm->resampler) { LOG("Could not get a resampler\n"); + audiounit_stream_destroy(stm); return CUBEB_ERROR; } @@ -1172,7 +1156,6 @@ audiounit_stream_destroy(cubeb_stream * stm) AudioOutputUnitStop(stm->input_unit); AudioUnitUninitialize(stm->input_unit); AudioComponentInstanceDispose(stm->input_unit); - stm->input_unit = NULL; } audiounit_destroy_input_buffer_list_array(stm); @@ -1181,9 +1164,10 @@ audiounit_stream_destroy(cubeb_stream * stm) AudioOutputUnitStop(stm->output_unit); AudioUnitUninitialize(stm->output_unit); AudioComponentInstanceDispose(stm->output_unit); - stm->output_unit = NULL; } + cubeb_resampler_destroy(stm->resampler); + #if !TARGET_OS_IPHONE audiounit_uninstall_device_changed_callback(stm); #endif From b08fd093d70bdbeb6520ed8112caefa22060a055 Mon Sep 17 00:00:00 2001 From: Alex Chronopoulos Date: Mon, 7 Mar 2016 18:31:32 +0200 Subject: [PATCH 05/17] Remove synchronization from ring array --- src/cubeb_ring_array.h | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/src/cubeb_ring_array.h b/src/cubeb_ring_array.h index adf96a2..642c1cb 100644 --- a/src/cubeb_ring_array.h +++ b/src/cubeb_ring_array.h @@ -23,7 +23,6 @@ typedef struct { unsigned int tail; /**< Index of the last element (first to deliver). */ int count; /**< Number of elements in the array. */ unsigned int capacity; /**< Total length of the array. */ - pthread_mutex_t mutex; /**< Mutex to synchronize store/fetch. */ } ring_array; /** Initialize the ring array. @@ -37,16 +36,9 @@ ring_array_init(ring_array * ra) ra->count = 0; memset(ra->pointer_array, 0, sizeof(ra->pointer_array)); - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT); - int ret = pthread_mutex_init(&ra->mutex, &attr); - assert(ret == 0); - pthread_mutexattr_destroy(&attr); - assert(ra->pointer_array[0] == NULL); - return ret; + return 0; } /** Set the allocated space to store the data. @@ -70,7 +62,6 @@ ring_array_set_data(ring_array * ra, void * data, unsigned int index) void ring_array_destroy(ring_array * ra) { - pthread_mutex_destroy(&ra->mutex); } /** Get the allocated buffer to be stored with fresh data. @@ -79,13 +70,8 @@ ring_array_destroy(ring_array * ra) void * ring_array_store_buffer(ring_array * ra) { - int rv = pthread_mutex_lock(&ra->mutex); - assert(rv == 0); - assert(ra->pointer_array[0] != NULL); - if (ra->count == (int)ra->capacity) { - pthread_mutex_unlock(&ra->mutex); return NULL; } @@ -95,7 +81,6 @@ ring_array_store_buffer(ring_array * ra) ++ra->count; assert(ra->count <= (int)ra->capacity); - pthread_mutex_unlock(&ra->mutex); return ret; } @@ -105,13 +90,9 @@ ring_array_store_buffer(ring_array * ra) void * ring_array_fetch_buffer(ring_array * ra) { - int rv = pthread_mutex_lock(&ra->mutex); - assert(rv == 0); - assert(ra->pointer_array[0] != NULL); if (ra->count == 0) { - pthread_mutex_unlock(&ra->mutex); return NULL; } void * ret = ra->pointer_array[ra->tail]; @@ -122,7 +103,6 @@ ring_array_fetch_buffer(ring_array * ra) ra->count--; assert(ra->count >= 0); - pthread_mutex_unlock(&ra->mutex); return ret; } From 634698acfd31601a38c6489d8ebe31459fb8c9e7 Mon Sep 17 00:00:00 2001 From: Alex Chronopoulos Date: Mon, 7 Mar 2016 18:38:36 +0200 Subject: [PATCH 06/17] Change ring_array_set_data return value. --- src/cubeb_audiounit.c | 6 ++---- src/cubeb_ring_array.h | 14 +++++--------- test/test_ring_array.c | 5 +---- 3 files changed, 8 insertions(+), 17 deletions(-) diff --git a/src/cubeb_audiounit.c b/src/cubeb_audiounit.c index 06af732..f9ab34d 100644 --- a/src/cubeb_audiounit.c +++ b/src/cubeb_audiounit.c @@ -840,10 +840,8 @@ audiounit_init_input_buffer_list_array(cubeb_stream * stream) return CUBEB_ERROR; } /* Set the data in the array. */ - if (ring_array_set_data(&stream->input_buffer_list_array, - &stream->input_buffer_list[i], i) == NULL) { - return CUBEB_ERROR; - } + ring_array_set_data(&stream->input_buffer_list_array, + &stream->input_buffer_list[i], i); } return CUBEB_OK; } diff --git a/src/cubeb_ring_array.h b/src/cubeb_ring_array.h index 642c1cb..86aee6d 100644 --- a/src/cubeb_ring_array.h +++ b/src/cubeb_ring_array.h @@ -42,19 +42,15 @@ ring_array_init(ring_array * ra) } /** Set the allocated space to store the data. - This must be done before store/fetch. + This must be called before store/fetch. @param ra The ring_array pointer. @param data Pointer to allocated space of buffers. - @param index Index between 0 and capacity-1 to store the allocated data buffer. - @retval The data pointer on success or NULL on failure. */ -void * + @param index Index between 0 and capacity-1 to store the allocated data buffer. */ +void ring_array_set_data(ring_array * ra, void * data, unsigned int index) { - if (index < ra->capacity) { - ra->pointer_array[index] = data; - return data; - } - return NULL; + assert(index < ra->capacity); + ra->pointer_array[index] = data; } /** Destroy the ring array. diff --git a/test/test_ring_array.c b/test/test_ring_array.c index a9c9f7c..78aaf3c 100644 --- a/test/test_ring_array.c +++ b/test/test_ring_array.c @@ -13,12 +13,9 @@ int test_ring_array() for (int i = 0; i < RING_ARRAY_CAPACITY; ++i) { data[i] = i; // in case RING_ARRAY_CAPACITY change value - p_data = ring_array_set_data(&ra, &data[i], i); - assert(p_data); - assert(p_data == &data[i]); + ring_array_set_data(&ra, &data[i], i); } - p_data = NULL; /* Get store buffers*/ for (int i = 0; i < RING_ARRAY_CAPACITY; ++i) { p_data = ring_array_store_buffer(&ra); From 86d669178b59b82c6384d8b411c4a7e2b52e860e Mon Sep 17 00:00:00 2001 From: Alex Chronopoulos Date: Mon, 7 Mar 2016 19:10:17 +0200 Subject: [PATCH 07/17] Change the ambiguous fetch/store names in ring array methods --- src/cubeb_audiounit.c | 32 ++++++++++++++++---------------- src/cubeb_ring_array.h | 6 +++--- test/test_ring_array.c | 16 ++++++++-------- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/cubeb_audiounit.c b/src/cubeb_audiounit.c index f9ab34d..ed5211e 100644 --- a/src/cubeb_audiounit.c +++ b/src/cubeb_audiounit.c @@ -164,13 +164,13 @@ audiounit_input_callback(void * user_ptr, } /* Get next store buffer from ring array */ - AudioBufferList * store_input_buffer_list = ring_array_store_buffer(&stm->input_buffer_list_array); - if (store_input_buffer_list == NULL) { + AudioBufferList * input_buffer_list = ring_array_get_next_free_buffer(&stm->input_buffer_list_array); + if (input_buffer_list == NULL) { LOG("input: Ring array is full drop one buffer\n"); - ring_array_fetch_buffer(&stm->input_buffer_list_array); + ring_array_get_first_data_buffer(&stm->input_buffer_list_array); - store_input_buffer_list = ring_array_store_buffer(&stm->input_buffer_list_array); - assert(store_input_buffer_list); + input_buffer_list = ring_array_get_next_free_buffer(&stm->input_buffer_list_array); + assert(input_buffer_list); } /* Render input samples */ OSStatus r = AudioUnitRender(stm->input_unit, @@ -178,7 +178,7 @@ audiounit_input_callback(void * user_ptr, tstamp, bus, input_frames, - store_input_buffer_list); + input_buffer_list); assert(r == noErr); LOG("- input: buffers %d, size %d, channels %d, frames %d\n", store_input_buffer_list->mNumberBuffers, store_input_buffer_list->mBuffers[0].mDataByteSize, store_input_buffer_list->mBuffers[0].mNumberChannels, input_frames); @@ -196,10 +196,10 @@ audiounit_input_callback(void * user_ptr, /* Input only. Call the user callback through resampler. Resampler will deliver input buffer in the correct rate. */ frames = input_frames; - AudioBufferList * fetch_input_buffer_list = ring_array_fetch_buffer(&stm->input_buffer_list_array); - assert(fetch_input_buffer_list && "fetch buffer is null in the input"); + input_buffer_list = ring_array_get_first_data_buffer(&stm->input_buffer_list_array); + assert(input_buffer_list && "fetch buffer is null in the input"); outframes = cubeb_resampler_fill(stm->resampler, - fetch_input_buffer_list->mBuffers[0].mData, + input_buffer_list->mBuffers[0].mData, &frames, NULL, 0); @@ -265,7 +265,7 @@ audiounit_output_callback(void * user_ptr, /* Get output buffer. */ output_buffer = outBufferList->mBuffers[0].mData; /* If Full duplex get also input buffer */ - AudioBufferList * fetch_input_buffer_list = NULL; + AudioBufferList * input_buffer_list = NULL; if (stm->input_unit != NULL) { /* Output callback came first */ if (stm->frames_read == 0) { @@ -274,8 +274,8 @@ audiounit_output_callback(void * user_ptr, return noErr; } /* Input samples stored previously in input callback. */ - fetch_input_buffer_list = ring_array_fetch_buffer(&stm->input_buffer_list_array); - if (fetch_input_buffer_list == NULL) { + input_buffer_list = ring_array_get_first_data_buffer(&stm->input_buffer_list_array); + if (input_buffer_list == NULL) { LOG("Requested more output than input. " "This is either a hole or we are after a stream stop and input thread stopped before output\n"); /* Provide silent input. Other than that we could provide silent output and exit without @@ -285,10 +285,10 @@ audiounit_output_callback(void * user_ptr, /* Avoid here to allocate new memory since we are inside callback. Use the existing * allocated buffers since the ring array is empty and the buffer is not used. */ - fetch_input_buffer_list = &stm->input_buffer_list[0]; - audiounit_make_silent(fetch_input_buffer_list); + input_buffer_list = &stm->input_buffer_list[0]; + audiounit_make_silent(input_buffer_list); } - input_buffer = fetch_input_buffer_list->mBuffers[0].mData; + input_buffer = input_buffer_list->mBuffers[0].mData; input_frames = stm->input_buffer_frames; assert(stm->frames_read > 0); } @@ -302,7 +302,7 @@ audiounit_output_callback(void * user_ptr, /* Cleanup the input buffer to make sure that we have fresh data. */ if (input_buffer) { - audiounit_make_silent(fetch_input_buffer_list); + audiounit_make_silent(input_buffer_list); } if (outframes < 0) { diff --git a/src/cubeb_ring_array.h b/src/cubeb_ring_array.h index 86aee6d..3dd9e2a 100644 --- a/src/cubeb_ring_array.h +++ b/src/cubeb_ring_array.h @@ -42,7 +42,7 @@ ring_array_init(ring_array * ra) } /** Set the allocated space to store the data. - This must be called before store/fetch. + This must be called after init and before the get operation buffer. @param ra The ring_array pointer. @param data Pointer to allocated space of buffers. @param index Index between 0 and capacity-1 to store the allocated data buffer. */ @@ -64,7 +64,7 @@ ring_array_destroy(ring_array * ra) @param ra The ring_array pointer. @retval Pointer of the allocated space to be stored with fresh data or NULL if full. */ void * -ring_array_store_buffer(ring_array * ra) +ring_array_get_next_free_buffer(ring_array * ra) { assert(ra->pointer_array[0] != NULL); if (ra->count == (int)ra->capacity) { @@ -84,7 +84,7 @@ ring_array_store_buffer(ring_array * ra) @param ra The ring_array pointer. @retval Pointer of the next in order data buffer or NULL if empty. */ void * -ring_array_fetch_buffer(ring_array * ra) +ring_array_get_first_data_buffer(ring_array * ra) { assert(ra->pointer_array[0] != NULL); diff --git a/test/test_ring_array.c b/test/test_ring_array.c index 78aaf3c..7fc0276 100644 --- a/test/test_ring_array.c +++ b/test/test_ring_array.c @@ -18,37 +18,37 @@ int test_ring_array() /* Get store buffers*/ for (int i = 0; i < RING_ARRAY_CAPACITY; ++i) { - p_data = ring_array_store_buffer(&ra); + p_data = ring_array_get_next_free_buffer(&ra); assert(p_data == &data[i]); } /*Now array is full extra store should give NULL*/ - assert(NULL == ring_array_store_buffer(&ra)); + assert(NULL == ring_array_get_next_free_buffer(&ra)); /* Get fetch buffers*/ for (int i = 0; i < RING_ARRAY_CAPACITY; ++i) { - p_data = ring_array_fetch_buffer(&ra); + p_data = ring_array_get_first_data_buffer(&ra); assert(p_data == &data[i]); } /*Now array is empty extra fetch should give NULL*/ - assert(NULL == ring_array_fetch_buffer(&ra)); + assert(NULL == ring_array_get_first_data_buffer(&ra)); p_data = NULL; /* Repeated store fetch should can go for ever*/ for (int i = 0; i < 2*RING_ARRAY_CAPACITY; ++i) { - p_data = ring_array_store_buffer(&ra); + p_data = ring_array_get_next_free_buffer(&ra); assert(p_data); - assert(ring_array_fetch_buffer(&ra) == p_data); + assert(ring_array_get_first_data_buffer(&ra) == p_data); } p_data = NULL; /* Verify/modify buffer data*/ for (int i = 0; i < RING_ARRAY_CAPACITY; ++i) { - p_data = ring_array_store_buffer(&ra); + p_data = ring_array_get_next_free_buffer(&ra); assert(p_data); assert(*((int*)p_data) == data[i]); (*((int*)p_data))++; // Modify data } for (int i = 0; i < RING_ARRAY_CAPACITY; ++i) { - p_data = ring_array_fetch_buffer(&ra); + p_data = ring_array_get_first_data_buffer(&ra); assert(p_data); assert(*((int*)p_data) == i+1); // Verify modified data } From a2269f494e778f3b3fb218a0fc11e93589965e4e Mon Sep 17 00:00:00 2001 From: Alex Chronopoulos Date: Mon, 7 Mar 2016 20:57:40 +0200 Subject: [PATCH 08/17] Separate the assignment from the test --- src/cubeb_audiounit.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/cubeb_audiounit.c b/src/cubeb_audiounit.c index ed5211e..a96d88f 100644 --- a/src/cubeb_audiounit.c +++ b/src/cubeb_audiounit.c @@ -810,7 +810,8 @@ audiounit_buflst_init_single_buffer(AudioBufferList * buflst, { size_t size = desc->mBytesPerFrame * frames; - if ((buflst->mBuffers[0].mData = malloc(size)) == NULL) { + buflst->mBuffers[0].mData = malloc(size); + if (buflst->mBuffers[0].mData == NULL) { return CUBEB_ERROR; } @@ -890,16 +891,22 @@ audiounit_stream_init(cubeb * context, context->active_streams += 1; pthread_mutex_unlock(&context->mutex); - if (input_stream_params != NULL && - (r = audiounit_create_unit(&input_unit, true, - input_stream_params, input_device)) != CUBEB_OK) { + if (input_stream_params != NULL) { + r = audiounit_create_unit(&input_unit, true, + input_stream_params, + input_device); + if (r != CUBEB_OK) { return r; + } } - if (output_stream_params != NULL && - (r = audiounit_create_unit(&output_unit, false, - output_stream_params, output_device)) != CUBEB_OK) { + if (output_stream_params != NULL) { + r = audiounit_create_unit(&output_unit, false, + output_stream_params, + output_device); + if (r != CUBEB_OK) { return r; + } } stm = calloc(1, sizeof(cubeb_stream)); @@ -963,7 +970,8 @@ audiounit_stream_init(cubeb * context, stm->input_hw_rate = input_hw_desc.mSampleRate; /* Set format description according to the input params. */ - if ((r = audio_stream_desc_init(&stm->input_desc, input_stream_params)) != CUBEB_OK) { + r = audio_stream_desc_init(&stm->input_desc, input_stream_params); + if (r != CUBEB_OK) { audiounit_stream_destroy(stm); return r; } @@ -1015,7 +1023,8 @@ audiounit_stream_init(cubeb * context, /* Setup Output Stream! */ if (output_stream_params != NULL) { - if ((r = audio_stream_desc_init(&stm->output_desc, output_stream_params)) != CUBEB_OK) { + r = audio_stream_desc_init(&stm->output_desc, output_stream_params); + if (r != CUBEB_OK) { audiounit_stream_destroy(stm); return r; } @@ -1248,7 +1257,6 @@ audiounit_stream_get_latency(cubeb_stream * stm, uint32_t * latency) }; r = audiounit_get_output_device_id(&output_device_id); - if (r != noErr) { pthread_mutex_unlock(&stm->mutex); return CUBEB_ERROR; @@ -1611,7 +1619,8 @@ audiounit_create_device_from_hwdev(AudioObjectID devid, cubeb_device_type type) return NULL; } - if ((ch = audiounit_get_channel_count(devid, adr.mScope)) == 0) { + ch = audiounit_get_channel_count(devid, adr.mScope); + if (ch == 0) { return NULL; } From d2679cf56ed648e3460ac2a65ce5705219705b8d Mon Sep 17 00:00:00 2001 From: Alex Chronopoulos Date: Mon, 7 Mar 2016 22:44:46 +0200 Subject: [PATCH 09/17] Use AudioBuffer instead of AudioBufferList --- src/cubeb_audiounit.c | 95 +++++++++++++++++++++++-------------------- 1 file changed, 50 insertions(+), 45 deletions(-) diff --git a/src/cubeb_audiounit.c b/src/cubeb_audiounit.c index a96d88f..bc2ccd1 100644 --- a/src/cubeb_audiounit.c +++ b/src/cubeb_audiounit.c @@ -88,8 +88,8 @@ struct cubeb_stream { pthread_mutex_t mutex; /* Hold the input samples in every * input callback iteration */ - AudioBufferList input_buffer_list[RING_ARRAY_CAPACITY]; - ring_array input_buffer_list_array; + AudioBuffer input_buffer[RING_ARRAY_CAPACITY]; + ring_array input_buffer_array; /* Frames on input buffer */ uint32_t input_buffer_frames; /* Frame counters */ @@ -164,24 +164,32 @@ audiounit_input_callback(void * user_ptr, } /* Get next store buffer from ring array */ - AudioBufferList * input_buffer_list = ring_array_get_next_free_buffer(&stm->input_buffer_list_array); - if (input_buffer_list == NULL) { + AudioBuffer * input_buffer = ring_array_get_next_free_buffer(&stm->input_buffer_array); + if (input_buffer == NULL) { LOG("input: Ring array is full drop one buffer\n"); - ring_array_get_first_data_buffer(&stm->input_buffer_list_array); + ring_array_get_first_data_buffer(&stm->input_buffer_array); - input_buffer_list = ring_array_get_next_free_buffer(&stm->input_buffer_list_array); - assert(input_buffer_list); + input_buffer = ring_array_get_next_free_buffer(&stm->input_buffer_array); + assert(input_buffer); } + + AudioBufferList input_buffer_list; + input_buffer_list.mBuffers[0] = *input_buffer; + input_buffer_list.mNumberBuffers = 1; + /* Render input samples */ OSStatus r = AudioUnitRender(stm->input_unit, flags, tstamp, bus, input_frames, - input_buffer_list); + &input_buffer_list); assert(r == noErr); - LOG("- input: buffers %d, size %d, channels %d, frames %d\n", store_input_buffer_list->mNumberBuffers, - store_input_buffer_list->mBuffers[0].mDataByteSize, store_input_buffer_list->mBuffers[0].mNumberChannels, input_frames); + LOG("- input: buffers %d, size %d, channels %d, frames %d\n", + input_buffer_list.mNumberBuffers, + input_buffer_list.mBuffers[0].mDataByteSize, + input_buffer_list.mBuffers[0].mNumberChannels, + input_frames); assert(input_frames > 0); stm->frames_read += input_frames; @@ -196,10 +204,10 @@ audiounit_input_callback(void * user_ptr, /* Input only. Call the user callback through resampler. Resampler will deliver input buffer in the correct rate. */ frames = input_frames; - input_buffer_list = ring_array_get_first_data_buffer(&stm->input_buffer_list_array); - assert(input_buffer_list && "fetch buffer is null in the input"); + input_buffer = ring_array_get_first_data_buffer(&stm->input_buffer_array); + assert(input_buffer && "fetch buffer is null in the input"); outframes = cubeb_resampler_fill(stm->resampler, - input_buffer_list->mBuffers[0].mData, + input_buffer->mData, &frames, NULL, 0); @@ -215,11 +223,9 @@ audiounit_input_callback(void * user_ptr, } static void -audiounit_make_silent(AudioBufferList * ioData) +audiounit_make_silent(AudioBuffer * ioData) { - for(UInt32 i = 0; i < ioData->mNumberBuffers; i++) { - memset(ioData->mBuffers[i].mData, 0, ioData->mBuffers[i].mDataByteSize); - } + memset(ioData->mData, 0, ioData->mDataByteSize); } static OSStatus @@ -244,7 +250,7 @@ audiounit_output_callback(void * user_ptr, pthread_mutex_lock(&stm->mutex); if (stm->shutdown) { - audiounit_make_silent(outBufferList); + audiounit_make_silent(&outBufferList->mBuffers[0]); pthread_mutex_unlock(&stm->mutex); return noErr; } @@ -259,23 +265,23 @@ audiounit_output_callback(void * user_ptr, } stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED); pthread_mutex_unlock(&stm->mutex); - audiounit_make_silent(outBufferList); + audiounit_make_silent(&outBufferList->mBuffers[0]); return noErr; } /* Get output buffer. */ output_buffer = outBufferList->mBuffers[0].mData; /* If Full duplex get also input buffer */ - AudioBufferList * input_buffer_list = NULL; + AudioBuffer * input_aud_buf = NULL; if (stm->input_unit != NULL) { /* Output callback came first */ if (stm->frames_read == 0) { - audiounit_make_silent(outBufferList); + audiounit_make_silent(&outBufferList->mBuffers[0]); pthread_mutex_unlock(&stm->mutex); return noErr; } /* Input samples stored previously in input callback. */ - input_buffer_list = ring_array_get_first_data_buffer(&stm->input_buffer_list_array); - if (input_buffer_list == NULL) { + input_aud_buf = ring_array_get_first_data_buffer(&stm->input_buffer_array); + if (input_aud_buf == NULL) { LOG("Requested more output than input. " "This is either a hole or we are after a stream stop and input thread stopped before output\n"); /* Provide silent input. Other than that we could provide silent output and exit without @@ -285,10 +291,10 @@ audiounit_output_callback(void * user_ptr, /* Avoid here to allocate new memory since we are inside callback. Use the existing * allocated buffers since the ring array is empty and the buffer is not used. */ - input_buffer_list = &stm->input_buffer_list[0]; - audiounit_make_silent(input_buffer_list); + input_aud_buf = &stm->input_buffer[0]; + audiounit_make_silent(input_aud_buf); } - input_buffer = input_buffer_list->mBuffers[0].mData; + input_buffer = input_aud_buf->mData; input_frames = stm->input_buffer_frames; assert(stm->frames_read > 0); } @@ -302,7 +308,7 @@ audiounit_output_callback(void * user_ptr, /* Cleanup the input buffer to make sure that we have fresh data. */ if (input_buffer) { - audiounit_make_silent(input_buffer_list); + audiounit_make_silent(input_aud_buf); } if (outframes < 0) { @@ -804,59 +810,58 @@ audiounit_create_unit(AudioUnit * unit, } static int -audiounit_buflst_init_single_buffer(AudioBufferList * buflst, +audiounit_buflst_init_single_buffer(AudioBuffer * buffer, const AudioStreamBasicDescription * desc, uint32_t frames) { size_t size = desc->mBytesPerFrame * frames; - buflst->mBuffers[0].mData = malloc(size); - if (buflst->mBuffers[0].mData == NULL) { + buffer->mData = malloc(size); + if (buffer->mData == NULL) { return CUBEB_ERROR; } - buflst->mBuffers[0].mNumberChannels = desc->mChannelsPerFrame; - buflst->mBuffers[0].mDataByteSize = size; - buflst->mNumberBuffers = 1; + buffer->mNumberChannels = desc->mChannelsPerFrame; + buffer->mDataByteSize = size; return CUBEB_OK; } static int -audiounit_init_input_buffer_list_array(cubeb_stream * stream) +audiounit_init_input_buffer_array(cubeb_stream * stream) { /* Init ring array. */ - ring_array_init(&stream->input_buffer_list_array); + ring_array_init(&stream->input_buffer_array); /* Initialize buffer list. */ - memset(&stream->input_buffer_list, 0, sizeof(stream->input_buffer_list)); + memset(&stream->input_buffer, 0, sizeof(stream->input_buffer)); /* Create input descriptor. */ AudioStreamBasicDescription src_desc = stream->input_desc; src_desc.mSampleRate = stream->input_hw_rate; /* Set data to ring array. */ for (int i = 0; i < RING_ARRAY_CAPACITY; ++i) { /* Allocate the data (AudioBufferList here). */ - if (audiounit_buflst_init_single_buffer(&stream->input_buffer_list[i], + if (audiounit_buflst_init_single_buffer(&stream->input_buffer[i], &src_desc, stream->input_buffer_frames) != CUBEB_OK) { return CUBEB_ERROR; } /* Set the data in the array. */ - ring_array_set_data(&stream->input_buffer_list_array, - &stream->input_buffer_list[i], i); + ring_array_set_data(&stream->input_buffer_array, + &stream->input_buffer[i], i); } return CUBEB_OK; } static void -audiounit_destroy_input_buffer_list_array(cubeb_stream * stream) +audiounit_destroy_input_buffer_array(cubeb_stream * stream) { /* Destroy the data in the array first*/ for (int i = 0; i < RING_ARRAY_CAPACITY; ++i) { - if (stream->input_buffer_list[i].mBuffers[0].mData) { - free(stream->input_buffer_list[i].mBuffers[0].mData); + if (stream->input_buffer[i].mData) { + free(stream->input_buffer[i].mData); } } - ring_array_destroy(&stream->input_buffer_list_array); + ring_array_destroy(&stream->input_buffer_array); } static int @@ -1002,7 +1007,7 @@ audiounit_stream_init(cubeb * context, return CUBEB_ERROR; } - if (audiounit_init_input_buffer_list_array(stm) != CUBEB_OK) { + if (audiounit_init_input_buffer_array(stm) != CUBEB_OK) { audiounit_stream_destroy(stm); return CUBEB_ERROR; } @@ -1165,7 +1170,7 @@ audiounit_stream_destroy(cubeb_stream * stm) AudioComponentInstanceDispose(stm->input_unit); } - audiounit_destroy_input_buffer_list_array(stm); + audiounit_destroy_input_buffer_array(stm); if (stm->output_unit != NULL) { AudioOutputUnitStop(stm->output_unit); From 23e3202d7d1c550506466b707b3c126b328306c8 Mon Sep 17 00:00:00 2001 From: Alex Chronopoulos Date: Tue, 8 Mar 2016 13:26:50 +0200 Subject: [PATCH 10/17] Fix after rebase, expected NULL instead of point to 0 int. --- src/cubeb_audiounit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cubeb_audiounit.c b/src/cubeb_audiounit.c index bc2ccd1..0a1bf77 100644 --- a/src/cubeb_audiounit.c +++ b/src/cubeb_audiounit.c @@ -302,7 +302,7 @@ audiounit_output_callback(void * user_ptr, /* Call user callback through resampler. */ outframes = cubeb_resampler_fill(stm->resampler, input_buffer, - &input_frames, + input_buffer ? &input_frames : NULL, output_buffer, output_frames); From cc322a9cca227b46814c42ca18e6016cf7f6881c Mon Sep 17 00:00:00 2001 From: Alex Chronopoulos Date: Wed, 9 Mar 2016 13:59:07 +0200 Subject: [PATCH 11/17] Change count to uint. --- src/cubeb_ring_array.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cubeb_ring_array.h b/src/cubeb_ring_array.h index 3dd9e2a..53f03e3 100644 --- a/src/cubeb_ring_array.h +++ b/src/cubeb_ring_array.h @@ -21,7 +21,7 @@ extern "C" { typedef struct { void* pointer_array[RING_ARRAY_CAPACITY]; /**< Array that hold pointers of the allocated space for the buffers. */ unsigned int tail; /**< Index of the last element (first to deliver). */ - int count; /**< Number of elements in the array. */ + unsigned int count; /**< Number of elements in the array. */ unsigned int capacity; /**< Total length of the array. */ } ring_array; @@ -67,7 +67,7 @@ void * ring_array_get_next_free_buffer(ring_array * ra) { assert(ra->pointer_array[0] != NULL); - if (ra->count == (int)ra->capacity) { + if (ra->count == ra->capacity) { return NULL; } @@ -75,7 +75,7 @@ ring_array_get_next_free_buffer(ring_array * ra) void * ret = ra->pointer_array[(ra->tail + ra->count) % ra->capacity]; ++ra->count; - assert(ra->count <= (int)ra->capacity); + assert(ra->count <= ra->capacity); return ret; } @@ -96,8 +96,8 @@ ring_array_get_first_data_buffer(ring_array * ra) ra->tail = (ra->tail + 1) % ra->capacity; assert(ra->tail < ra->capacity); - ra->count--; - assert(ra->count >= 0); + assert(ra->count > 0); + --ra->count; return ret; } From 3d8a4e0e3ffb798a515c5ee3db1bece583fc05ca Mon Sep 17 00:00:00 2001 From: Alex Chronopoulos Date: Wed, 9 Mar 2016 15:12:02 +0200 Subject: [PATCH 12/17] Remove deep copy and free of devid. --- include/cubeb/cubeb.h | 2 +- src/cubeb.c | 5 ++++- src/cubeb_audiounit.c | 3 +-- src/cubeb_pulse.c | 4 ++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/include/cubeb/cubeb.h b/include/cubeb/cubeb.h index 1140782..d1db0c9 100644 --- a/include/cubeb/cubeb.h +++ b/include/cubeb/cubeb.h @@ -221,7 +221,7 @@ typedef enum { * `cubeb_enumerate_devices`, and must be destroyed using * `cubeb_device_info_destroy`. */ typedef struct { - cubeb_devid devid; /**< Device identifier handle. Required deep copy of the value. */ + cubeb_devid devid; /**< Device identifier handle. */ char * device_id; /**< Device identifier which might be presented in a UI. */ char * friendly_name; /**< Friendly device name which might be presented in a UI. */ char * group_id; /**< Two devices have the same group identifier if they belong to the same physical device; for example a headset and microphone. */ diff --git a/src/cubeb.c b/src/cubeb.c index 8c5dfc0..266b41e 100644 --- a/src/cubeb.c +++ b/src/cubeb.c @@ -425,7 +425,10 @@ int cubeb_device_collection_destroy(cubeb_device_collection * collection) int cubeb_device_info_destroy(cubeb_device_info * info) { - free(info->devid); + if (info == NULL) { + return CUBEB_ERROR_INVALID_PARAMETER; + } + free(info->device_id); free(info->friendly_name); free(info->group_id); diff --git a/src/cubeb_audiounit.c b/src/cubeb_audiounit.c index 0a1bf77..d0abbba 100644 --- a/src/cubeb_audiounit.c +++ b/src/cubeb_audiounit.c @@ -1635,8 +1635,7 @@ audiounit_create_device_from_hwdev(AudioObjectID devid, cubeb_device_type type) adr.mSelector = kAudioDevicePropertyDeviceUID; if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &str) == noErr && str != NULL) { ret->device_id = audiounit_strref_to_cstr_utf8(str); - ret->devid = malloc(sizeof(UInt32)); - memcpy(ret->devid, &devid, sizeof(UInt32)); + ret->devid = (cubeb_devid)(size_t)devid; ret->group_id = strdup(ret->device_id); CFRelease(str); } diff --git a/src/cubeb_pulse.c b/src/cubeb_pulse.c index 033ce89..3f95e9a 100644 --- a/src/cubeb_pulse.c +++ b/src/cubeb_pulse.c @@ -1036,7 +1036,7 @@ pulse_sink_info_cb(pa_context * context, const pa_sink_info * info, devinfo = calloc(1, sizeof(cubeb_device_info)); devinfo->device_id = strdup(info->name); - devinfo->devid = strdup(devinfo->device_id); + devinfo->devid = devinfo->device_id; devinfo->friendly_name = strdup(info->description); prop = WRAP(pa_proplist_gets)(info->proplist, "sysfs.path"); if (prop) @@ -1096,7 +1096,7 @@ pulse_source_info_cb(pa_context * context, const pa_source_info * info, devinfo = calloc(1, sizeof(cubeb_device_info)); devinfo->device_id = strdup(info->name); - devinfo->devid = strdup(devinfo->device_id); + devinfo->devid = devinfo->device_id; devinfo->friendly_name = strdup(info->description); prop = WRAP(pa_proplist_gets)(info->proplist, "sysfs.path"); if (prop) From 7cce388bbc2f71238b34c425a921a4aee393951c Mon Sep 17 00:00:00 2001 From: Alex Chronopoulos Date: Wed, 9 Mar 2016 18:01:56 +0200 Subject: [PATCH 13/17] Change buffer init method signature --- src/cubeb_audiounit.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/cubeb_audiounit.c b/src/cubeb_audiounit.c index d0abbba..d248447 100644 --- a/src/cubeb_audiounit.c +++ b/src/cubeb_audiounit.c @@ -810,18 +810,19 @@ audiounit_create_unit(AudioUnit * unit, } static int -audiounit_buflst_init_single_buffer(AudioBuffer * buffer, - const AudioStreamBasicDescription * desc, - uint32_t frames) +audiounit_init_audio_buffer(AudioBuffer * buffer, + uint32_t bytesPerFrame, + uint32_t channelsPerFrame, + uint32_t frames) { - size_t size = desc->mBytesPerFrame * frames; + size_t size = bytesPerFrame * frames; - buffer->mData = malloc(size); + buffer->mData = calloc(1, size); if (buffer->mData == NULL) { return CUBEB_ERROR; } - buffer->mNumberChannels = desc->mChannelsPerFrame; + buffer->mNumberChannels = channelsPerFrame; buffer->mDataByteSize = size; return CUBEB_OK; @@ -840,9 +841,10 @@ audiounit_init_input_buffer_array(cubeb_stream * stream) /* Set data to ring array. */ for (int i = 0; i < RING_ARRAY_CAPACITY; ++i) { /* Allocate the data (AudioBufferList here). */ - if (audiounit_buflst_init_single_buffer(&stream->input_buffer[i], - &src_desc, - stream->input_buffer_frames) != CUBEB_OK) { + if (audiounit_init_audio_buffer(&stream->input_buffer[i], + src_desc.mBytesPerFrame, + src_desc.mChannelsPerFrame, + stream->input_buffer_frames) != CUBEB_OK) { return CUBEB_ERROR; } /* Set the data in the array. */ From 55f11f238c47a169790ed18643f34db753caf79f Mon Sep 17 00:00:00 2001 From: Alex Chronopoulos Date: Wed, 9 Mar 2016 21:20:13 +0200 Subject: [PATCH 14/17] Merge AudioBuffer to ring array. --- src/cubeb_audiounit.c | 65 ++++++------------------------ src/cubeb_ring_array.h | 89 ++++++++++++++++++++++++++++++------------ test/test_ring_array.c | 42 +++++++++++--------- 3 files changed, 99 insertions(+), 97 deletions(-) diff --git a/src/cubeb_audiounit.c b/src/cubeb_audiounit.c index d248447..070f06c 100644 --- a/src/cubeb_audiounit.c +++ b/src/cubeb_audiounit.c @@ -88,7 +88,6 @@ struct cubeb_stream { pthread_mutex_t mutex; /* Hold the input samples in every * input callback iteration */ - AudioBuffer input_buffer[RING_ARRAY_CAPACITY]; ring_array input_buffer_array; /* Frames on input buffer */ uint32_t input_buffer_frames; @@ -164,12 +163,12 @@ audiounit_input_callback(void * user_ptr, } /* Get next store buffer from ring array */ - AudioBuffer * input_buffer = ring_array_get_next_free_buffer(&stm->input_buffer_array); + AudioBuffer * input_buffer = ring_array_get_free_buffer(&stm->input_buffer_array); if (input_buffer == NULL) { LOG("input: Ring array is full drop one buffer\n"); - ring_array_get_first_data_buffer(&stm->input_buffer_array); + ring_array_get_data_buffer(&stm->input_buffer_array); - input_buffer = ring_array_get_next_free_buffer(&stm->input_buffer_array); + input_buffer = ring_array_get_free_buffer(&stm->input_buffer_array); assert(input_buffer); } @@ -204,7 +203,7 @@ audiounit_input_callback(void * user_ptr, /* Input only. Call the user callback through resampler. Resampler will deliver input buffer in the correct rate. */ frames = input_frames; - input_buffer = ring_array_get_first_data_buffer(&stm->input_buffer_array); + input_buffer = ring_array_get_data_buffer(&stm->input_buffer_array); assert(input_buffer && "fetch buffer is null in the input"); outframes = cubeb_resampler_fill(stm->resampler, input_buffer->mData, @@ -280,7 +279,7 @@ audiounit_output_callback(void * user_ptr, return noErr; } /* Input samples stored previously in input callback. */ - input_aud_buf = ring_array_get_first_data_buffer(&stm->input_buffer_array); + input_aud_buf = ring_array_get_data_buffer(&stm->input_buffer_array); if (input_aud_buf == NULL) { LOG("Requested more output than input. " "This is either a hole or we are after a stream stop and input thread stopped before output\n"); @@ -291,7 +290,7 @@ audiounit_output_callback(void * user_ptr, /* Avoid here to allocate new memory since we are inside callback. Use the existing * allocated buffers since the ring array is empty and the buffer is not used. */ - input_aud_buf = &stm->input_buffer[0]; + input_aud_buf = ring_array_get_dummy_buffer(&stm->input_buffer_array); audiounit_make_silent(input_aud_buf); } input_buffer = input_aud_buf->mData; @@ -809,60 +808,20 @@ audiounit_create_unit(AudioUnit * unit, return CUBEB_OK; } -static int -audiounit_init_audio_buffer(AudioBuffer * buffer, - uint32_t bytesPerFrame, - uint32_t channelsPerFrame, - uint32_t frames) -{ - size_t size = bytesPerFrame * frames; - - buffer->mData = calloc(1, size); - if (buffer->mData == NULL) { - return CUBEB_ERROR; - } - - buffer->mNumberChannels = channelsPerFrame; - buffer->mDataByteSize = size; - - return CUBEB_OK; -} - static int audiounit_init_input_buffer_array(cubeb_stream * stream) { - /* Init ring array. */ - ring_array_init(&stream->input_buffer_array); - /* Initialize buffer list. */ - memset(&stream->input_buffer, 0, sizeof(stream->input_buffer)); - /* Create input descriptor. */ - AudioStreamBasicDescription src_desc = stream->input_desc; - src_desc.mSampleRate = stream->input_hw_rate; - /* Set data to ring array. */ - for (int i = 0; i < RING_ARRAY_CAPACITY; ++i) { - /* Allocate the data (AudioBufferList here). */ - if (audiounit_init_audio_buffer(&stream->input_buffer[i], - src_desc.mBytesPerFrame, - src_desc.mChannelsPerFrame, - stream->input_buffer_frames) != CUBEB_OK) { - return CUBEB_ERROR; - } - /* Set the data in the array. */ - ring_array_set_data(&stream->input_buffer_array, - &stream->input_buffer[i], i); - } - return CUBEB_OK; + int r = ring_array_init(&stream->input_buffer_array, + stream->input_desc.mBytesPerFrame, + stream->input_desc.mChannelsPerFrame, + stream->input_buffer_frames); + + return r; } static void audiounit_destroy_input_buffer_array(cubeb_stream * stream) { - /* Destroy the data in the array first*/ - for (int i = 0; i < RING_ARRAY_CAPACITY; ++i) { - if (stream->input_buffer[i].mData) { - free(stream->input_buffer[i].mData); - } - } ring_array_destroy(&stream->input_buffer_array); } diff --git a/src/cubeb_ring_array.h b/src/cubeb_ring_array.h index 53f03e3..d9e739e 100644 --- a/src/cubeb_ring_array.h +++ b/src/cubeb_ring_array.h @@ -19,38 +19,58 @@ extern "C" { #define RING_ARRAY_CAPACITY 8 typedef struct { - void* pointer_array[RING_ARRAY_CAPACITY]; /**< Array that hold pointers of the allocated space for the buffers. */ + AudioBuffer buffer_array[RING_ARRAY_CAPACITY]; /**< Array that hold pointers of the allocated space for the buffers. */ unsigned int tail; /**< Index of the last element (first to deliver). */ unsigned int count; /**< Number of elements in the array. */ unsigned int capacity; /**< Total length of the array. */ } ring_array; +static int +single_audiobuffer_init(AudioBuffer * buffer, + uint32_t bytesPerFrame, + uint32_t channelsPerFrame, + uint32_t frames) +{ + assert(buffer); + assert(bytesPerFrame > 0 && channelsPerFrame && frames > 0); + + size_t size = bytesPerFrame * frames; + buffer->mData = calloc(1, size); + if (buffer->mData == NULL) { + return CUBEB_ERROR; + } + + buffer->mNumberChannels = channelsPerFrame; + buffer->mDataByteSize = size; + + return CUBEB_OK; +} + /** Initialize the ring array. @param ra The ring_array pointer of allocated structure. @retval 0 on success. */ int -ring_array_init(ring_array * ra) +ring_array_init(ring_array * ra, + uint32_t bytesPerFrame, + uint32_t channelsPerFrame, + uint32_t framesPerBuffer) { + assert(ra); ra->capacity = RING_ARRAY_CAPACITY; ra->tail = 0; ra->count = 0; - memset(ra->pointer_array, 0, sizeof(ra->pointer_array)); + memset(ra->buffer_array, 0, sizeof(ra->buffer_array)); - assert(ra->pointer_array[0] == NULL); + for (unsigned int i = 0; i < ra->capacity; ++i) { + if (single_audiobuffer_init(&ra->buffer_array[i], + bytesPerFrame, + channelsPerFrame, + framesPerBuffer) != CUBEB_OK) { + return CUBEB_ERROR; + } + } - return 0; -} - -/** Set the allocated space to store the data. - This must be called after init and before the get operation buffer. - @param ra The ring_array pointer. - @param data Pointer to allocated space of buffers. - @param index Index between 0 and capacity-1 to store the allocated data buffer. */ -void -ring_array_set_data(ring_array * ra, void * data, unsigned int index) -{ - assert(index < ra->capacity); - ra->pointer_array[index] = data; + return CUBEB_OK; } /** Destroy the ring array. @@ -58,21 +78,27 @@ ring_array_set_data(ring_array * ra, void * data, unsigned int index) void ring_array_destroy(ring_array * ra) { + assert(ra); + for (unsigned int i = 0; i < ra->capacity; ++i) { + if (ra->buffer_array[i].mData) { + free(ra->buffer_array[i].mData); + } + } } /** Get the allocated buffer to be stored with fresh data. @param ra The ring_array pointer. @retval Pointer of the allocated space to be stored with fresh data or NULL if full. */ -void * -ring_array_get_next_free_buffer(ring_array * ra) +AudioBuffer * +ring_array_get_free_buffer(ring_array * ra) { - assert(ra->pointer_array[0] != NULL); + assert(ra->buffer_array[0].mData != NULL); if (ra->count == ra->capacity) { return NULL; } assert(ra->count == 0 || (ra->tail + ra->count) % ra->capacity != ra->tail); - void * ret = ra->pointer_array[(ra->tail + ra->count) % ra->capacity]; + void * ret = &ra->buffer_array[(ra->tail + ra->count) % ra->capacity]; ++ra->count; assert(ra->count <= ra->capacity); @@ -83,15 +109,15 @@ ring_array_get_next_free_buffer(ring_array * ra) /** Get the next available buffer with data. @param ra The ring_array pointer. @retval Pointer of the next in order data buffer or NULL if empty. */ -void * -ring_array_get_first_data_buffer(ring_array * ra) +AudioBuffer * +ring_array_get_data_buffer(ring_array * ra) { - assert(ra->pointer_array[0] != NULL); + assert(ra->buffer_array[0].mData != NULL); if (ra->count == 0) { return NULL; } - void * ret = ra->pointer_array[ra->tail]; + void * ret = &ra->buffer_array[ra->tail]; ra->tail = (ra->tail + 1) % ra->capacity; assert(ra->tail < ra->capacity); @@ -102,6 +128,19 @@ ring_array_get_first_data_buffer(ring_array * ra) return ret; } +/** When array is empty get the first allocated buffer in the array. + @param ra The ring_array pointer. + @retval If arrays is empty, pointer of the allocated space else NULL. */ +AudioBuffer * +ring_array_get_dummy_buffer(ring_array * ra) +{ + assert(ra->capacity > 0); + if (ra->count > 0) { + return NULL; + } + return &ra->buffer_array[0]; +} + #if defined(__cplusplus) } #endif diff --git a/test/test_ring_array.c b/test/test_ring_array.c index 7fc0276..b6f0e63 100644 --- a/test/test_ring_array.c +++ b/test/test_ring_array.c @@ -1,56 +1,60 @@ -#include #include #include +#include +#include +#include "cubeb/cubeb.h" #include "cubeb_ring_array.h" int test_ring_array() { ring_array ra; - ring_array_init(&ra); - int data[RING_ARRAY_CAPACITY] ;// {1,2,3,4,5,6,7,8}; - void * p_data = NULL; + ring_array_init(&ra, sizeof(int), 1, 1); + int verify_data[RING_ARRAY_CAPACITY] ;// {1,2,3,4,5,6,7,8}; + AudioBuffer * p_data = NULL; for (int i = 0; i < RING_ARRAY_CAPACITY; ++i) { - data[i] = i; // in case RING_ARRAY_CAPACITY change value - ring_array_set_data(&ra, &data[i], i); + verify_data[i] = i; // in case RING_ARRAY_CAPACITY change value + *(int*)ra.buffer_array[i].mData = i; + assert(ra.buffer_array[i].mDataByteSize == 1 * sizeof(int)); + assert(ra.buffer_array[i].mNumberChannels == 1); } /* Get store buffers*/ for (int i = 0; i < RING_ARRAY_CAPACITY; ++i) { - p_data = ring_array_get_next_free_buffer(&ra); - assert(p_data == &data[i]); + p_data = ring_array_get_free_buffer(&ra); + assert(p_data && *(int*)p_data->mData == verify_data[i]); } /*Now array is full extra store should give NULL*/ - assert(NULL == ring_array_get_next_free_buffer(&ra)); + assert(NULL == ring_array_get_free_buffer(&ra)); /* Get fetch buffers*/ for (int i = 0; i < RING_ARRAY_CAPACITY; ++i) { - p_data = ring_array_get_first_data_buffer(&ra); - assert(p_data == &data[i]); + p_data = ring_array_get_data_buffer(&ra); + assert(p_data && *(int*)p_data->mData == verify_data[i]); } /*Now array is empty extra fetch should give NULL*/ - assert(NULL == ring_array_get_first_data_buffer(&ra)); + assert(NULL == ring_array_get_data_buffer(&ra)); p_data = NULL; /* Repeated store fetch should can go for ever*/ for (int i = 0; i < 2*RING_ARRAY_CAPACITY; ++i) { - p_data = ring_array_get_next_free_buffer(&ra); + p_data = ring_array_get_free_buffer(&ra); assert(p_data); - assert(ring_array_get_first_data_buffer(&ra) == p_data); + assert(ring_array_get_data_buffer(&ra) == p_data); } p_data = NULL; /* Verify/modify buffer data*/ for (int i = 0; i < RING_ARRAY_CAPACITY; ++i) { - p_data = ring_array_get_next_free_buffer(&ra); + p_data = ring_array_get_free_buffer(&ra); assert(p_data); - assert(*((int*)p_data) == data[i]); - (*((int*)p_data))++; // Modify data + assert(*((int*)p_data->mData) == verify_data[i]); + (*((int*)p_data->mData))++; // Modify data } for (int i = 0; i < RING_ARRAY_CAPACITY; ++i) { - p_data = ring_array_get_first_data_buffer(&ra); + p_data = ring_array_get_data_buffer(&ra); assert(p_data); - assert(*((int*)p_data) == i+1); // Verify modified data + assert(*((int*)p_data->mData) == verify_data[i]+1); // Verify modified data } ring_array_destroy(&ra); From 038bd2bf106c1ab3f87c69dba118945797d77890 Mon Sep 17 00:00:00 2001 From: Alex Chronopoulos Date: Wed, 9 Mar 2016 21:52:10 +0200 Subject: [PATCH 15/17] Dynamic capacity on ring array. --- src/cubeb_audiounit.c | 11 +++++++++-- src/cubeb_ring_array.h | 29 ++++++++++++++++++++++------- test/test_ring_array.c | 23 ++++++++++++++--------- 3 files changed, 45 insertions(+), 18 deletions(-) diff --git a/src/cubeb_audiounit.c b/src/cubeb_audiounit.c index 070f06c..e9dc62a 100644 --- a/src/cubeb_audiounit.c +++ b/src/cubeb_audiounit.c @@ -809,9 +809,10 @@ audiounit_create_unit(AudioUnit * unit, } static int -audiounit_init_input_buffer_array(cubeb_stream * stream) +audiounit_init_input_buffer_array(cubeb_stream * stream, uint32_t capacity) { int r = ring_array_init(&stream->input_buffer_array, + capacity, stream->input_desc.mBytesPerFrame, stream->input_desc.mChannelsPerFrame, stream->input_buffer_frames); @@ -968,7 +969,13 @@ audiounit_stream_init(cubeb * context, return CUBEB_ERROR; } - if (audiounit_init_input_buffer_array(stm) != CUBEB_OK) { + // Input only capacity + unsigned int array_capacity = 1; + if (output_stream_params) { + // Full-duplex increase capacity + array_capacity = 8; + } + if (audiounit_init_input_buffer_array(stm, array_capacity) != CUBEB_OK) { audiounit_stream_destroy(stm); return CUBEB_ERROR; } diff --git a/src/cubeb_ring_array.h b/src/cubeb_ring_array.h index d9e739e..047c158 100644 --- a/src/cubeb_ring_array.h +++ b/src/cubeb_ring_array.h @@ -16,13 +16,12 @@ extern "C" { asynchronous producer/consumer callbacks do not arrive in a repeated order the ring array stores the buffers and fetch them in the correct order. */ -#define RING_ARRAY_CAPACITY 8 typedef struct { - AudioBuffer buffer_array[RING_ARRAY_CAPACITY]; /**< Array that hold pointers of the allocated space for the buffers. */ - unsigned int tail; /**< Index of the last element (first to deliver). */ - unsigned int count; /**< Number of elements in the array. */ - unsigned int capacity; /**< Total length of the array. */ + AudioBuffer * buffer_array; /**< Array that hold pointers of the allocated space for the buffers. */ + unsigned int tail; /**< Index of the last element (first to deliver). */ + unsigned int count; /**< Number of elements in the array. */ + unsigned int capacity; /**< Total length of the array. */ } ring_array; static int @@ -51,15 +50,24 @@ single_audiobuffer_init(AudioBuffer * buffer, @retval 0 on success. */ int ring_array_init(ring_array * ra, + uint32_t capacity, uint32_t bytesPerFrame, uint32_t channelsPerFrame, uint32_t framesPerBuffer) { assert(ra); - ra->capacity = RING_ARRAY_CAPACITY; + if (capacity == 0 || bytesPerFrame == 0 || + channelsPerFrame == 0 || framesPerBuffer == 0) { + return CUBEB_ERROR_INVALID_PARAMETER; + } + ra->capacity = capacity; ra->tail = 0; ra->count = 0; - memset(ra->buffer_array, 0, sizeof(ra->buffer_array)); + + ra->buffer_array = calloc(ra->capacity, sizeof(AudioBuffer)); + if (ra->buffer_array == NULL) { + return CUBEB_ERROR; + } for (unsigned int i = 0; i < ra->capacity; ++i) { if (single_audiobuffer_init(&ra->buffer_array[i], @@ -79,11 +87,15 @@ void ring_array_destroy(ring_array * ra) { assert(ra); + if (ra->buffer_array == NULL){ + return; + } for (unsigned int i = 0; i < ra->capacity; ++i) { if (ra->buffer_array[i].mData) { free(ra->buffer_array[i].mData); } } + free(ra->buffer_array); } /** Get the allocated buffer to be stored with fresh data. @@ -92,6 +104,7 @@ ring_array_destroy(ring_array * ra) AudioBuffer * ring_array_get_free_buffer(ring_array * ra) { + assert(ra && ra->buffer_array); assert(ra->buffer_array[0].mData != NULL); if (ra->count == ra->capacity) { return NULL; @@ -112,6 +125,7 @@ ring_array_get_free_buffer(ring_array * ra) AudioBuffer * ring_array_get_data_buffer(ring_array * ra) { + assert(ra && ra->buffer_array); assert(ra->buffer_array[0].mData != NULL); if (ra->count == 0) { @@ -134,6 +148,7 @@ ring_array_get_data_buffer(ring_array * ra) AudioBuffer * ring_array_get_dummy_buffer(ring_array * ra) { + assert(ra && ra->buffer_array); assert(ra->capacity > 0); if (ra->count > 0) { return NULL; diff --git a/test/test_ring_array.c b/test/test_ring_array.c index b6f0e63..f8b30a5 100644 --- a/test/test_ring_array.c +++ b/test/test_ring_array.c @@ -9,26 +9,31 @@ int test_ring_array() { ring_array ra; - ring_array_init(&ra, sizeof(int), 1, 1); - int verify_data[RING_ARRAY_CAPACITY] ;// {1,2,3,4,5,6,7,8}; + + assert(ring_array_init(&ra, 0, 0, 1, 1) == CUBEB_ERROR_INVALID_PARAMETER); + assert(ring_array_init(&ra, 1, 0, 0, 1) == CUBEB_ERROR_INVALID_PARAMETER); + + unsigned int capacity = 8; + ring_array_init(&ra, capacity, sizeof(int), 1, 1); + int verify_data[capacity] ;// {1,2,3,4,5,6,7,8}; AudioBuffer * p_data = NULL; - for (int i = 0; i < RING_ARRAY_CAPACITY; ++i) { - verify_data[i] = i; // in case RING_ARRAY_CAPACITY change value + for (unsigned int i = 0; i < capacity; ++i) { + verify_data[i] = i; // in case capacity change value *(int*)ra.buffer_array[i].mData = i; assert(ra.buffer_array[i].mDataByteSize == 1 * sizeof(int)); assert(ra.buffer_array[i].mNumberChannels == 1); } /* Get store buffers*/ - for (int i = 0; i < RING_ARRAY_CAPACITY; ++i) { + for (unsigned int i = 0; i < capacity; ++i) { p_data = ring_array_get_free_buffer(&ra); assert(p_data && *(int*)p_data->mData == verify_data[i]); } /*Now array is full extra store should give NULL*/ assert(NULL == ring_array_get_free_buffer(&ra)); /* Get fetch buffers*/ - for (int i = 0; i < RING_ARRAY_CAPACITY; ++i) { + for (unsigned int i = 0; i < capacity; ++i) { p_data = ring_array_get_data_buffer(&ra); assert(p_data && *(int*)p_data->mData == verify_data[i]); } @@ -37,7 +42,7 @@ int test_ring_array() p_data = NULL; /* Repeated store fetch should can go for ever*/ - for (int i = 0; i < 2*RING_ARRAY_CAPACITY; ++i) { + for (unsigned int i = 0; i < 2*capacity; ++i) { p_data = ring_array_get_free_buffer(&ra); assert(p_data); assert(ring_array_get_data_buffer(&ra) == p_data); @@ -45,13 +50,13 @@ int test_ring_array() p_data = NULL; /* Verify/modify buffer data*/ - for (int i = 0; i < RING_ARRAY_CAPACITY; ++i) { + for (unsigned int i = 0; i < capacity; ++i) { p_data = ring_array_get_free_buffer(&ra); assert(p_data); assert(*((int*)p_data->mData) == verify_data[i]); (*((int*)p_data->mData))++; // Modify data } - for (int i = 0; i < RING_ARRAY_CAPACITY; ++i) { + for (unsigned int i = 0; i < capacity; ++i) { p_data = ring_array_get_data_buffer(&ra); assert(p_data); assert(*((int*)p_data->mData) == verify_data[i]+1); // Verify modified data From 271c4467dec97352c1a8f85cbf5acb4a1ef7646d Mon Sep 17 00:00:00 2001 From: Alex Chronopoulos Date: Wed, 9 Mar 2016 23:30:45 +0200 Subject: [PATCH 16/17] Remove pthread link flag fot test ring array --- Makefile.am | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index 880bffa..ed3f4ce 100644 --- a/Makefile.am +++ b/Makefile.am @@ -113,7 +113,6 @@ test_test_record_LDADD = -lm src/libcubeb.la $(platform_lib) test_test_utils_SOURCES = test/test_utils.cpp test_test_ring_array_SOURCES = test/test_ring_array.c -test_test_ring_array_LDADD = -lpthread TESTS = $(check_PROGRAMS) From 45e379229d5fb00dc14275649cc251475a662d54 Mon Sep 17 00:00:00 2001 From: Alex Chronopoulos Date: Wed, 9 Mar 2016 23:41:27 +0200 Subject: [PATCH 17/17] Exclude from testing apple specific test. --- test/test_ring_array.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/test_ring_array.c b/test/test_ring_array.c index f8b30a5..a4d72d9 100644 --- a/test/test_ring_array.c +++ b/test/test_ring_array.c @@ -1,3 +1,5 @@ +#ifdef __APPLE__ + #include #include #include @@ -74,3 +76,11 @@ int main() return 0; } +#else + +int main() +{ + return 0; +} + +#endif