Bug 1290425 - Update cubeb to revision 6278ef2f73. r=padenot

This commit is contained in:
Alex Chronopoulos 2016-07-29 13:40:52 +02:00
parent 73a62f5c90
commit ae1e3599d8
10 changed files with 423 additions and 262 deletions

View File

@ -5,4 +5,4 @@ Makefile.in build files for the Mozilla build system.
The cubeb git repository is: git://github.com/kinetiknz/cubeb.git The cubeb git repository is: git://github.com/kinetiknz/cubeb.git
The git commit ID used was 2a5fd74b1122ff4e4c445c1e092932be1273646e. The git commit ID used was fec2a837862fd0feaee1bf3f34bbe9754186b7ac.

View File

@ -261,7 +261,7 @@ alsa_refill_stream(cubeb_stream * stm)
pthread_mutex_lock(&stm->mutex); pthread_mutex_lock(&stm->mutex);
avail = snd_pcm_avail_update(stm->pcm); avail = snd_pcm_avail_update(stm->pcm);
if (avail == -EPIPE) { if (avail < 0) {
snd_pcm_recover(stm->pcm, avail, 1); snd_pcm_recover(stm->pcm, avail, 1);
avail = snd_pcm_avail_update(stm->pcm); avail = snd_pcm_avail_update(stm->pcm);
} }
@ -280,15 +280,10 @@ alsa_refill_stream(cubeb_stream * stm)
/* poll(2) claims this stream is active, so there should be some space /* poll(2) claims this stream is active, so there should be some space
available to write. If avail is still zero here, the stream must be in available to write. If avail is still zero here, the stream must be in
a funky state, so recover and try again. */ a funky state, bail and wait for another wakeup. */
if (avail == 0) { if (avail == 0) {
snd_pcm_recover(stm->pcm, -EPIPE, 1); pthread_mutex_unlock(&stm->mutex);
avail = snd_pcm_avail_update(stm->pcm); return RUNNING;
if (avail <= 0) {
pthread_mutex_unlock(&stm->mutex);
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
return ERROR;
}
} }
p = calloc(1, snd_pcm_frames_to_bytes(stm->pcm, avail)); p = calloc(1, snd_pcm_frames_to_bytes(stm->pcm, avail));
@ -300,6 +295,7 @@ alsa_refill_stream(cubeb_stream * stm)
if (got < 0) { if (got < 0) {
pthread_mutex_unlock(&stm->mutex); pthread_mutex_unlock(&stm->mutex);
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
free(p);
return ERROR; return ERROR;
} }
if (got > 0) { if (got > 0) {
@ -317,7 +313,7 @@ alsa_refill_stream(cubeb_stream * stm)
} }
} }
wrote = snd_pcm_writei(stm->pcm, p, got); wrote = snd_pcm_writei(stm->pcm, p, got);
if (wrote == -EPIPE) { if (wrote < 0) {
snd_pcm_recover(stm->pcm, wrote, 1); snd_pcm_recover(stm->pcm, wrote, 1);
wrote = snd_pcm_writei(stm->pcm, p, got); wrote = snd_pcm_writei(stm->pcm, p, got);
} }
@ -389,6 +385,8 @@ alsa_run(cubeb * ctx)
for (i = 0; i < CUBEB_STREAM_MAX; ++i) { for (i = 0; i < CUBEB_STREAM_MAX; ++i) {
stm = ctx->streams[i]; stm = ctx->streams[i];
/* We can't use snd_pcm_poll_descriptors_revents here because of
https://github.com/kinetiknz/cubeb/issues/135. */
if (stm && stm->state == RUNNING && stm->fds && any_revents(stm->fds, stm->nfds)) { if (stm && stm->state == RUNNING && stm->fds && any_revents(stm->fds, stm->nfds)) {
alsa_set_stream_state(stm, PROCESSING); alsa_set_stream_state(stm, PROCESSING);
pthread_mutex_unlock(&ctx->mutex); pthread_mutex_unlock(&ctx->mutex);
@ -971,7 +969,7 @@ alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) {
/* get a pcm, disabling resampling, so we get a rate the /* get a pcm, disabling resampling, so we get a rate the
* hardware/dmix/pulse/etc. supports. */ * hardware/dmix/pulse/etc. supports. */
r = snd_pcm_open(&pcm, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK | SND_PCM_NO_AUTO_RESAMPLE, 0); r = snd_pcm_open(&pcm, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK, SND_PCM_NO_AUTO_RESAMPLE);
if (r < 0) { if (r < 0) {
return CUBEB_ERROR; return CUBEB_ERROR;
} }

View File

@ -13,6 +13,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <AudioUnit/AudioUnit.h> #include <AudioUnit/AudioUnit.h>
#if !TARGET_OS_IPHONE #if !TARGET_OS_IPHONE
#include <AvailabilityMacros.h>
#include <CoreAudio/AudioHardware.h> #include <CoreAudio/AudioHardware.h>
#include <CoreAudio/HostTime.h> #include <CoreAudio/HostTime.h>
#include <CoreFoundation/CoreFoundation.h> #include <CoreFoundation/CoreFoundation.h>
@ -43,7 +44,7 @@
#define AudioComponentInstanceDispose CloseComponent #define AudioComponentInstanceDispose CloseComponent
#endif #endif
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1080 #if MAC_OS_X_VERSION_MIN_REQUIRED < 101000
typedef UInt32 AudioFormatFlags; typedef UInt32 AudioFormatFlags;
#endif #endif
@ -62,22 +63,31 @@ typedef UInt32 AudioFormatFlags;
#ifdef LOGGING_ENABLED #ifdef LOGGING_ENABLED
#define LOG(...) do { \ #define LOG(...) do { \
fprintf(stderr, __VA_ARGS__); \ fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, "(line: %d)\n", __LINE__); \
} while(0) } while(0)
#else #else
#define LOG(...) #define LOG(...)
#endif #endif
#ifdef LOGGING_ENABLED
#define PRINT_ERROR_CODE(str, r) do { \
fprintf(stderr, "Error %s (line: %d) (%d)\n", str, __LINE__, r); \
} while (0)
#else
#define PRINT_ERROR_CODE(str, r)
#endif
/* Testing empirically, some headsets report a minimal latency that is very /* Testing empirically, some headsets report a minimal latency that is very
* low, but this does not work in practice. Lie and say the minimum is 256 * low, but this does not work in practice. Lie and say the minimum is 256
* frames. */ * frames. */
const uint32_t SAFE_MIN_LATENCY_FRAMES = 256; const uint32_t SAFE_MIN_LATENCY_FRAMES = 256;
const uint32_t SAFE_MAX_LATENCY_FRAMES = 512;
extern cubeb_ops const audiounit_ops; extern cubeb_ops const audiounit_ops;
struct cubeb { struct cubeb {
cubeb_ops const * ops; cubeb_ops const * ops;
pthread_mutex_t mutex; owned_critical_section mutex;
int active_streams; int active_streams;
int limit_streams; int limit_streams;
cubeb_device_collection_changed_callback collection_changed_callback; cubeb_device_collection_changed_callback collection_changed_callback;
@ -162,7 +172,7 @@ struct cubeb_stream {
AudioUnit output_unit; AudioUnit output_unit;
/* Sample rate of input device*/ /* Sample rate of input device*/
Float64 input_hw_rate; Float64 input_hw_rate;
pthread_mutex_t mutex; owned_critical_section mutex;
/* Hold the input samples in every /* Hold the input samples in every
* input callback iteration */ * input callback iteration */
auto_array_wrapper * input_linear_buffer; auto_array_wrapper * input_linear_buffer;
@ -234,7 +244,7 @@ audiounit_render_input(cubeb_stream * stm,
/* Create the AudioBufferList to store input. */ /* Create the AudioBufferList to store input. */
AudioBufferList input_buffer_list; AudioBufferList input_buffer_list;
input_buffer_list.mBuffers[0].mDataByteSize = input_buffer_list.mBuffers[0].mDataByteSize =
stm->input_desc.mBytesPerFrame * stm->input_buffer_frames; stm->input_desc.mBytesPerFrame * input_frames;
input_buffer_list.mBuffers[0].mData = nullptr; input_buffer_list.mBuffers[0].mData = nullptr;
input_buffer_list.mBuffers[0].mNumberChannels = stm->input_desc.mChannelsPerFrame; input_buffer_list.mBuffers[0].mNumberChannels = stm->input_desc.mChannelsPerFrame;
input_buffer_list.mNumberBuffers = 1; input_buffer_list.mNumberBuffers = 1;
@ -248,7 +258,7 @@ audiounit_render_input(cubeb_stream * stm,
&input_buffer_list); &input_buffer_list);
if (r != noErr) { if (r != noErr) {
LOG("Input AudioUnitRender failed with error=%d\n", r); PRINT_ERROR_CODE("AudioUnitRender", r);
audiounit_make_silent(&input_buffer_list.mBuffers[0]); audiounit_make_silent(&input_buffer_list.mBuffers[0]);
return r; return r;
} }
@ -257,7 +267,7 @@ audiounit_render_input(cubeb_stream * stm,
stm->input_linear_buffer->push(input_buffer_list.mBuffers[0].mData, stm->input_linear_buffer->push(input_buffer_list.mBuffers[0].mData,
input_frames * stm->input_desc.mChannelsPerFrame); input_frames * stm->input_desc.mChannelsPerFrame);
LOG("- input: buffers %d, size %d, channels %d, frames %d\n", LOG("- input: buffers %d, size %d, channels %d, frames %d.",
input_buffer_list.mNumberBuffers, input_buffer_list.mNumberBuffers,
input_buffer_list.mBuffers[0].mDataByteSize, input_buffer_list.mBuffers[0].mDataByteSize,
input_buffer_list.mBuffers[0].mNumberChannels, input_buffer_list.mBuffers[0].mNumberChannels,
@ -281,14 +291,13 @@ audiounit_input_callback(void * user_ptr,
cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr); cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr);
long outframes, frames; long outframes, frames;
pthread_mutex_lock(&stm->mutex); auto_lock lock(stm->mutex);
assert(stm->input_unit != NULL); assert(stm->input_unit != NULL);
assert(AU_IN_BUS == bus); assert(AU_IN_BUS == bus);
if (stm->shutdown) { if (stm->shutdown) {
LOG("- input shutdown\n"); LOG("- input shutdown");
pthread_mutex_unlock(&stm->mutex);
return noErr; return noErr;
} }
@ -300,7 +309,6 @@ audiounit_input_callback(void * user_ptr,
// Full Duplex. We'll call data_callback in the AudioUnit output callback. // Full Duplex. We'll call data_callback in the AudioUnit output callback.
if (stm->output_unit != NULL) { if (stm->output_unit != NULL) {
// User callback will be called by output callback // User callback will be called by output callback
pthread_mutex_unlock(&stm->mutex);
return noErr; return noErr;
} }
@ -318,11 +326,9 @@ audiounit_input_callback(void * user_ptr,
if (outframes < 0 || outframes != input_frames) { if (outframes < 0 || outframes != input_frames) {
stm->shutdown = 1; stm->shutdown = 1;
pthread_mutex_unlock(&stm->mutex);
return noErr; return noErr;
} }
pthread_mutex_unlock(&stm->mutex);
return noErr; return noErr;
} }
@ -339,19 +345,18 @@ audiounit_output_callback(void * user_ptr,
cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr); cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr);
LOG("- output(%p): buffers %d, size %d, channels %d, frames %d\n", stm, LOG("- output(%p): buffers %d, size %d, channels %d, frames %d.", stm,
outBufferList->mNumberBuffers, outBufferList->mBuffers[0].mDataByteSize, outBufferList->mNumberBuffers, outBufferList->mBuffers[0].mDataByteSize,
outBufferList->mBuffers[0].mNumberChannels, output_frames); outBufferList->mBuffers[0].mNumberChannels, output_frames);
long outframes = 0, input_frames = 0; long outframes = 0, input_frames = 0;
void * output_buffer = NULL, * input_buffer = NULL; void * output_buffer = NULL, * input_buffer = NULL;
pthread_mutex_lock(&stm->mutex); auto_lock lock(stm->mutex);
if (stm->shutdown) { if (stm->shutdown) {
LOG("- output shutdown\n"); LOG("- output shutdown.");
audiounit_make_silent(&outBufferList->mBuffers[0]); audiounit_make_silent(&outBufferList->mBuffers[0]);
pthread_mutex_unlock(&stm->mutex);
return noErr; return noErr;
} }
@ -364,7 +369,6 @@ audiounit_output_callback(void * user_ptr,
assert(r == 0); assert(r == 0);
} }
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED); stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
pthread_mutex_unlock(&stm->mutex);
audiounit_make_silent(&outBufferList->mBuffers[0]); audiounit_make_silent(&outBufferList->mBuffers[0]);
return noErr; return noErr;
} }
@ -374,14 +378,14 @@ audiounit_output_callback(void * user_ptr,
if (stm->input_unit != NULL) { if (stm->input_unit != NULL) {
/* Output callback came first */ /* Output callback came first */
if (stm->frames_read == 0) { if (stm->frames_read == 0) {
LOG("Output callback came first send silent.\n"); LOG("Output callback came first send silent.");
stm->input_linear_buffer->push_silence(stm->input_buffer_frames * stm->input_linear_buffer->push_silence(stm->input_buffer_frames *
stm->input_desc.mChannelsPerFrame); stm->input_desc.mChannelsPerFrame);
} }
/* Input samples stored previously in input callback. */ /* Input samples stored previously in input callback. */
if (stm->input_linear_buffer->length() == 0) { if (stm->input_linear_buffer->length() == 0) {
/* Do nothing, there should be enough pre-buffered data to consume. */ /* Do nothing, there should be enough pre-buffered data to consume. */
LOG("Input hole. Requested more input than ouput.\n"); LOG("Input hole. Requested more input than ouput.");
} }
// The input buffer // The input buffer
input_buffer = stm->input_linear_buffer->data(); input_buffer = stm->input_linear_buffer->data();
@ -402,7 +406,6 @@ audiounit_output_callback(void * user_ptr,
if (outframes < 0) { if (outframes < 0) {
stm->shutdown = 1; stm->shutdown = 1;
pthread_mutex_unlock(&stm->mutex);
return noErr; return noErr;
} }
@ -413,7 +416,6 @@ audiounit_output_callback(void * user_ptr,
AudioFormatFlags outaff = stm->output_desc.mFormatFlags; AudioFormatFlags outaff = stm->output_desc.mFormatFlags;
float panning = (stm->output_desc.mChannelsPerFrame == 2) ? stm->panning : 0.0f; float panning = (stm->output_desc.mChannelsPerFrame == 2) ? stm->panning : 0.0f;
pthread_mutex_unlock(&stm->mutex);
/* Post process output samples. */ /* Post process output samples. */
if (stm->draining) { if (stm->draining) {
@ -436,7 +438,6 @@ int
audiounit_init(cubeb ** context, char const * context_name) audiounit_init(cubeb ** context, char const * context_name)
{ {
cubeb * ctx; cubeb * ctx;
int r;
*context = NULL; *context = NULL;
@ -446,8 +447,7 @@ audiounit_init(cubeb ** context, char const * context_name)
ctx->ops = &audiounit_ops; ctx->ops = &audiounit_ops;
r = pthread_mutex_init(&ctx->mutex, NULL); ctx->mutex = owned_critical_section();
assert(r == 0);
ctx->active_streams = 0; ctx->active_streams = 0;
@ -489,6 +489,7 @@ audiounit_get_output_device_id(AudioDeviceID * device_id)
&size, &size,
device_id); device_id);
if (r != noErr) { if (r != noErr) {
PRINT_ERROR_CODE("output_device_id", r);
return CUBEB_ERROR; return CUBEB_ERROR;
} }
@ -533,13 +534,13 @@ audiounit_property_listener_callback(AudioObjectID id, UInt32 address_count,
case kAudioHardwarePropertyDefaultOutputDevice: case kAudioHardwarePropertyDefaultOutputDevice:
case kAudioHardwarePropertyDefaultInputDevice: case kAudioHardwarePropertyDefaultInputDevice:
/* fall through */ /* fall through */
case kAudioDevicePropertyDataSource: case kAudioDevicePropertyDataSource: {
pthread_mutex_lock(&stm->mutex); auto_lock lock(stm->mutex);
if (stm->device_changed_callback) { if (stm->device_changed_callback) {
stm->device_changed_callback(stm->user_ptr); stm->device_changed_callback(stm->user_ptr);
}
break;
} }
pthread_mutex_unlock(&stm->mutex);
break;
} }
} }
@ -692,6 +693,7 @@ audiounit_get_acceptable_latency_range(AudioValueRange * latency_range)
}; };
if (audiounit_get_output_device_id(&output_device_id) != CUBEB_OK) { if (audiounit_get_output_device_id(&output_device_id) != CUBEB_OK) {
LOG("Could not get default output device id.");
return CUBEB_ERROR; return CUBEB_ERROR;
} }
@ -705,6 +707,7 @@ audiounit_get_acceptable_latency_range(AudioValueRange * latency_range)
&size, &size,
latency_range); latency_range);
if (r != noErr) { if (r != noErr) {
PRINT_ERROR_CODE("AudioObjectGetPropertyData/buffer size range", r);
return CUBEB_ERROR; return CUBEB_ERROR;
} }
@ -767,6 +770,7 @@ audiounit_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
&size, &size,
&stream_format); &stream_format);
if (r != noErr) { if (r != noErr) {
PRINT_ERROR_CODE("AudioObjectPropertyAddress/StreamFormat", r);
return CUBEB_ERROR; return CUBEB_ERROR;
} }
@ -784,6 +788,7 @@ audiounit_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * la
#else #else
AudioValueRange latency_range; AudioValueRange latency_range;
if (audiounit_get_acceptable_latency_range(&latency_range) != CUBEB_OK) { if (audiounit_get_acceptable_latency_range(&latency_range) != CUBEB_OK) {
LOG("Could not get acceptable latency range.");
return CUBEB_ERROR; return CUBEB_ERROR;
} }
@ -842,14 +847,10 @@ audiounit_destroy(cubeb * ctx)
/* Unregister the callback if necessary. */ /* Unregister the callback if necessary. */
if(ctx->collection_changed_callback) { if(ctx->collection_changed_callback) {
pthread_mutex_lock(&ctx->mutex); auto_lock lock(ctx->mutex);
audiounit_remove_device_listener(ctx); audiounit_remove_device_listener(ctx);
pthread_mutex_unlock(&ctx->mutex);
} }
int r = pthread_mutex_destroy(&ctx->mutex);
assert(r == 0);
delete ctx; delete ctx;
} }
@ -906,6 +907,7 @@ audiounit_create_unit(AudioUnit * unit,
AudioComponent comp; AudioComponent comp;
UInt32 enable; UInt32 enable;
AudioDeviceID devid; AudioDeviceID devid;
OSStatus rv;
desc.componentType = kAudioUnitType_Output; desc.componentType = kAudioUnitType_Output;
desc.componentSubType = CUBEB_AUDIOUNIT_SUBTYPE; desc.componentSubType = CUBEB_AUDIOUNIT_SUBTYPE;
@ -914,24 +916,31 @@ audiounit_create_unit(AudioUnit * unit,
desc.componentFlagsMask = 0; desc.componentFlagsMask = 0;
comp = AudioComponentFindNext(NULL, &desc); comp = AudioComponentFindNext(NULL, &desc);
if (comp == NULL) { if (comp == NULL) {
LOG("Could not find matching audio hardware.");
return CUBEB_ERROR; return CUBEB_ERROR;
} }
if (AudioComponentInstanceNew(comp, unit) != 0) { rv = AudioComponentInstanceNew(comp, unit);
if (rv != noErr) {
PRINT_ERROR_CODE("AudioComponentInstanceNew", rv);
return CUBEB_ERROR; return CUBEB_ERROR;
} }
enable = 1; enable = 1;
if (AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_EnableIO, rv = AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_EnableIO,
is_input ? kAudioUnitScope_Input : kAudioUnitScope_Output, is_input ? kAudioUnitScope_Input : kAudioUnitScope_Output,
is_input ? AU_IN_BUS : AU_OUT_BUS, &enable, sizeof(UInt32)) != noErr) { is_input ? AU_IN_BUS : AU_OUT_BUS, &enable, sizeof(UInt32));
if (rv != noErr) {
PRINT_ERROR_CODE("AudioUnitSetProperty/kAudioOutputUnitProperty_EnableIO", rv);
return CUBEB_ERROR; return CUBEB_ERROR;
} }
enable = 0; enable = 0;
if (AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_EnableIO, rv = AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_EnableIO,
is_input ? kAudioUnitScope_Output : kAudioUnitScope_Input, is_input ? kAudioUnitScope_Output : kAudioUnitScope_Input,
is_input ? AU_OUT_BUS : AU_IN_BUS, &enable, sizeof(UInt32)) != noErr) { is_input ? AU_OUT_BUS : AU_IN_BUS, &enable, sizeof(UInt32));
if (rv != noErr) {
PRINT_ERROR_CODE("AudioUnitSetProperty/kAudioOutputUnitProperty_EnableIO", rv);
return CUBEB_ERROR; return CUBEB_ERROR;
} }
@ -946,6 +955,7 @@ audiounit_create_unit(AudioUnit * unit,
is_input ? AU_IN_BUS : AU_OUT_BUS, is_input ? AU_IN_BUS : AU_OUT_BUS,
&devid, sizeof(AudioDeviceID)); &devid, sizeof(AudioDeviceID));
if (err != noErr) { if (err != noErr) {
PRINT_ERROR_CODE("AudioUnitSetProperty/kAudioOutputUnitProperty_CurrentDevice", rv);
return CUBEB_ERROR; return CUBEB_ERROR;
} }
@ -991,6 +1001,71 @@ audiounit_destroy_input_linear_buffer(cubeb_stream * stream)
delete stream->input_linear_buffer; delete stream->input_linear_buffer;
} }
static uint32_t
audiounit_clamp_latency(cubeb_stream * stm,
uint32_t latency_frames)
{
// For the 1st stream set anything within safe min-max
assert(stm->context->active_streams > 0);
if (stm->context->active_streams == 1) {
return std::max(std::min<uint32_t>(latency_frames, SAFE_MAX_LATENCY_FRAMES),
SAFE_MIN_LATENCY_FRAMES);
}
// If more than one stream operates in parallel
// allow only lower values of latency
int r;
UInt32 output_buffer_size = 0;
UInt32 size = sizeof(output_buffer_size);
if (stm->output_unit) {
r = AudioUnitGetProperty(stm->output_unit,
kAudioDevicePropertyBufferFrameSize,
kAudioUnitScope_Output,
AU_OUT_BUS,
&output_buffer_size,
&size);
if (r != noErr) {
PRINT_ERROR_CODE("AudioUnitGetProperty/output/kAudioDevicePropertyBufferFrameSize", r);
return 0;
}
output_buffer_size = std::max(std::min<uint32_t>(output_buffer_size, SAFE_MAX_LATENCY_FRAMES),
SAFE_MIN_LATENCY_FRAMES);
}
UInt32 input_buffer_size = 0;
if (stm->input_unit) {
r = AudioUnitGetProperty(stm->input_unit,
kAudioDevicePropertyBufferFrameSize,
kAudioUnitScope_Input,
AU_IN_BUS,
&input_buffer_size,
&size);
if (r != noErr) {
PRINT_ERROR_CODE("AudioUnitGetProperty/input/kAudioDevicePropertyBufferFrameSize", r);
return 0;
}
input_buffer_size = std::max(std::min<uint32_t>(input_buffer_size, SAFE_MAX_LATENCY_FRAMES),
SAFE_MIN_LATENCY_FRAMES);
}
// Every following active streams can only set smaller latency
UInt32 upper_latency_limit = 0;
if (input_buffer_size != 0 && output_buffer_size != 0) {
upper_latency_limit = std::min<uint32_t>(input_buffer_size, output_buffer_size);
} else if (input_buffer_size != 0) {
upper_latency_limit = input_buffer_size;
} else if (output_buffer_size != 0) {
upper_latency_limit = output_buffer_size;
} else {
upper_latency_limit = SAFE_MAX_LATENCY_FRAMES;
}
return std::max(std::min<uint32_t>(latency_frames, upper_latency_limit),
SAFE_MIN_LATENCY_FRAMES);
}
static int static int
audiounit_stream_init(cubeb * context, audiounit_stream_init(cubeb * context,
cubeb_stream ** stream, cubeb_stream ** stream,
@ -1015,20 +1090,21 @@ audiounit_stream_init(cubeb * context,
assert(context); assert(context);
*stream = NULL; *stream = NULL;
pthread_mutex_lock(&context->mutex); {
if (context->limit_streams && context->active_streams >= CUBEB_STREAM_MAX) { auto_lock lock(context->mutex);
pthread_mutex_unlock(&context->mutex); if (context->limit_streams && context->active_streams >= CUBEB_STREAM_MAX) {
return CUBEB_ERROR; LOG("Reached the stream limit of %d", CUBEB_STREAM_MAX);
return CUBEB_ERROR;
}
context->active_streams += 1;
} }
context->active_streams += 1;
pthread_mutex_unlock(&context->mutex);
if (input_stream_params != NULL) { if (input_stream_params != NULL) {
r = audiounit_create_unit(&input_unit, true, r = audiounit_create_unit(&input_unit, true,
input_stream_params, input_stream_params,
input_device); input_device);
if (r != CUBEB_OK) { if (r != CUBEB_OK) {
LOG("Create input stream failed\n"); LOG("AudioUnit creation for input failed.");
return r; return r;
} }
} }
@ -1038,7 +1114,7 @@ audiounit_stream_init(cubeb * context,
output_stream_params, output_stream_params,
output_device); output_device);
if (r != CUBEB_OK) { if (r != CUBEB_OK) {
LOG("Create output stream failed\n"); LOG("AudioUnit creation for output failed.");
return r; return r;
} }
} }
@ -1056,13 +1132,7 @@ audiounit_stream_init(cubeb * context,
stm->state_callback = state_callback; stm->state_callback = state_callback;
stm->user_ptr = user_ptr; stm->user_ptr = user_ptr;
stm->device_changed_callback = NULL; stm->device_changed_callback = NULL;
stm->mutex = owned_critical_section();
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
r = pthread_mutex_init(&stm->mutex, &attr);
assert(r == 0);
pthread_mutexattr_destroy(&attr);
/* Init data members where necessary */ /* Init data members where necessary */
stm->hw_latency_frames = UINT64_MAX; stm->hw_latency_frames = UINT64_MAX;
@ -1070,42 +1140,22 @@ audiounit_stream_init(cubeb * context,
/* Silently clamp the latency down to the platform default, because we /* Silently clamp the latency down to the platform default, because we
* synthetize the clock from the callbacks, and we want the clock to update * synthetize the clock from the callbacks, and we want the clock to update
* often. */ * often. */
latency_frames = audiounit_clamp_latency(stm, latency_frames);
UInt32 default_frame_count; assert(latency_frames > 0);
size = sizeof(default_frame_count);
if (stm->output_unit) {
if (AudioUnitGetProperty(stm->output_unit, kAudioDevicePropertyBufferFrameSize,
kAudioUnitScope_Output, 0, &default_frame_count, &size) != 0) {
audiounit_stream_destroy(stm);
return CUBEB_ERROR;
}
} else {
if (AudioUnitGetProperty(stm->input_unit, kAudioDevicePropertyBufferFrameSize,
kAudioUnitScope_Input, 0, &default_frame_count, &size) != 0) {
audiounit_stream_destroy(stm);
return CUBEB_ERROR;
}
}
LOG("Default buffer size: %u frames\n", default_frame_count);
latency_frames = std::max(std::min<uint32_t>(latency_frames,
default_frame_count),
SAFE_MIN_LATENCY_FRAMES);
LOG("Clamped buffer size: %u frames\n", latency_frames);
/* Setup Input Stream! */ /* Setup Input Stream! */
if (input_stream_params != NULL) { if (input_stream_params != NULL) {
/* Get input device sample rate. */ /* Get input device sample rate. */
AudioStreamBasicDescription input_hw_desc; AudioStreamBasicDescription input_hw_desc;
size = sizeof(AudioStreamBasicDescription); size = sizeof(AudioStreamBasicDescription);
if (AudioUnitGetProperty(stm->input_unit, r = AudioUnitGetProperty(stm->input_unit,
kAudioUnitProperty_StreamFormat, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input, kAudioUnitScope_Input,
AU_IN_BUS, AU_IN_BUS,
&input_hw_desc, &input_hw_desc,
&size) != 0) { &size);
if (r != noErr) {
PRINT_ERROR_CODE("AudioUnitGetProperty/input/kAudioUnitProperty_StreamFormat", r);
audiounit_stream_destroy(stm); audiounit_stream_destroy(stm);
return CUBEB_ERROR; return CUBEB_ERROR;
} }
@ -1114,23 +1164,22 @@ audiounit_stream_init(cubeb * context,
/* Set format description according to the input params. */ /* Set format description according to the input params. */
r = audio_stream_desc_init(&stm->input_desc, input_stream_params); r = audio_stream_desc_init(&stm->input_desc, input_stream_params);
if (r != CUBEB_OK) { if (r != CUBEB_OK) {
LOG("Setting format description for input failed.");
audiounit_stream_destroy(stm); audiounit_stream_destroy(stm);
return r; return r;
} }
// Use latency to calculate buffer size // Use latency to set buffer size
if (stm->input_hw_rate == input_stream_params->rate) { stm->input_buffer_frames = latency_frames;
stm->input_buffer_frames = latency_frames; LOG("Input buffer frame count %u.", stm->input_buffer_frames);
} else { r = AudioUnitSetProperty(stm->input_unit,
stm->input_buffer_frames = (latency_frames * stm->input_hw_rate) / input_stream_params->rate;
}
LOG("Calculated input number of frames %u for latency %u\n", stm->input_buffer_frames, latency_frames);
if (AudioUnitSetProperty(stm->input_unit,
kAudioDevicePropertyBufferFrameSize, kAudioDevicePropertyBufferFrameSize,
kAudioUnitScope_Output, kAudioUnitScope_Output,
AU_IN_BUS, AU_IN_BUS,
&stm->input_buffer_frames, &stm->input_buffer_frames,
sizeof(UInt32)) != 0) { sizeof(UInt32));
if (r != noErr) {
PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioDevicePropertyBufferFrameSize", r);
audiounit_stream_destroy(stm); audiounit_stream_destroy(stm);
return CUBEB_ERROR; return CUBEB_ERROR;
} }
@ -1140,23 +1189,27 @@ audiounit_stream_init(cubeb * context,
we will resample inside input callback. */ we will resample inside input callback. */
src_desc.mSampleRate = stm->input_hw_rate; src_desc.mSampleRate = stm->input_hw_rate;
if (AudioUnitSetProperty(stm->input_unit, r = AudioUnitSetProperty(stm->input_unit,
kAudioUnitProperty_StreamFormat, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output, kAudioUnitScope_Output,
AU_IN_BUS, AU_IN_BUS,
&src_desc, &src_desc,
sizeof(AudioStreamBasicDescription)) != 0) { sizeof(AudioStreamBasicDescription));
if (r != noErr) {
PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioUnitProperty_StreamFormat", r);
audiounit_stream_destroy(stm); audiounit_stream_destroy(stm);
return CUBEB_ERROR; return CUBEB_ERROR;
} }
/* Frames per buffer in the input callback. */ /* Frames per buffer in the input callback. */
if (AudioUnitSetProperty(stm->input_unit, r = AudioUnitSetProperty(stm->input_unit,
kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitProperty_MaximumFramesPerSlice,
kAudioUnitScope_Output, kAudioUnitScope_Output,
AU_IN_BUS, AU_IN_BUS,
&stm->input_buffer_frames, &stm->input_buffer_frames,
sizeof(UInt32))) { sizeof(UInt32));
if (r != noErr) {
PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioUnitProperty_MaximumFramesPerSlice", r);
audiounit_stream_destroy(stm); audiounit_stream_destroy(stm);
return CUBEB_ERROR; return CUBEB_ERROR;
} }
@ -1175,22 +1228,26 @@ audiounit_stream_init(cubeb * context,
assert(stm->input_unit != NULL); assert(stm->input_unit != NULL);
aurcbs_in.inputProc = audiounit_input_callback; aurcbs_in.inputProc = audiounit_input_callback;
aurcbs_in.inputProcRefCon = stm; aurcbs_in.inputProcRefCon = stm;
if (AudioUnitSetProperty(stm->input_unit,
r = AudioUnitSetProperty(stm->input_unit,
kAudioOutputUnitProperty_SetInputCallback, kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global, kAudioUnitScope_Global,
AU_OUT_BUS, AU_OUT_BUS,
&aurcbs_in, &aurcbs_in,
sizeof(aurcbs_in)) != 0) { sizeof(aurcbs_in));
if (r != noErr) {
PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioOutputUnitProperty_SetInputCallback", r);
audiounit_stream_destroy(stm); audiounit_stream_destroy(stm);
return CUBEB_ERROR; return CUBEB_ERROR;
} }
LOG("Input audiounit init successfully.\n"); LOG("Input audiounit init successfully.");
} }
/* Setup Output Stream! */ /* Setup Output Stream! */
if (output_stream_params != NULL) { if (output_stream_params != NULL) {
r = audio_stream_desc_init(&stm->output_desc, output_stream_params); r = audio_stream_desc_init(&stm->output_desc, output_stream_params);
if (r != CUBEB_OK) { if (r != CUBEB_OK) {
LOG("Could not initialize the audio stream description.");
audiounit_stream_destroy(stm); audiounit_stream_destroy(stm);
return r; return r;
} }
@ -1199,43 +1256,41 @@ audiounit_stream_init(cubeb * context,
AudioStreamBasicDescription output_hw_desc; AudioStreamBasicDescription output_hw_desc;
size = sizeof(AudioStreamBasicDescription); size = sizeof(AudioStreamBasicDescription);
memset(&output_hw_desc, 0, size); memset(&output_hw_desc, 0, size);
if (AudioUnitGetProperty(stm->output_unit, r = AudioUnitGetProperty(stm->output_unit,
kAudioUnitProperty_StreamFormat, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output, kAudioUnitScope_Output,
AU_OUT_BUS, AU_OUT_BUS,
&output_hw_desc, &output_hw_desc,
&size) != 0) { &size);
if (r != noErr) {
PRINT_ERROR_CODE("AudioUnitGetProperty/output/tkAudioUnitProperty_StreamFormat", r);
audiounit_stream_destroy(stm); audiounit_stream_destroy(stm);
return CUBEB_ERROR; return CUBEB_ERROR;
} }
if (AudioUnitSetProperty(stm->output_unit, r = AudioUnitSetProperty(stm->output_unit,
kAudioUnitProperty_StreamFormat, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input, kAudioUnitScope_Input,
AU_OUT_BUS, AU_OUT_BUS,
&stm->output_desc, &stm->output_desc,
sizeof(AudioStreamBasicDescription)) != 0) { sizeof(AudioStreamBasicDescription));
if (r != noErr) {
PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioUnitProperty_StreamFormat", r);
audiounit_stream_destroy(stm); audiounit_stream_destroy(stm);
return CUBEB_ERROR; return CUBEB_ERROR;
} }
// Use latency to set number of frames in out buffer. Use the
// device sampling rate, internal resampler of audiounit will
// calculate the expected number of frames.
// Use latency to calculate buffer size // Use latency to calculate buffer size
uint32_t output_buffer_frames = 0; uint32_t output_buffer_frames = latency_frames;
if (output_hw_desc.mSampleRate == output_stream_params->rate) { LOG("Output buffer frame count %u.", output_buffer_frames);
output_buffer_frames = latency_frames; r = AudioUnitSetProperty(stm->output_unit,
} else {
output_buffer_frames = (latency_frames * output_hw_desc.mSampleRate) / output_stream_params->rate;
}
LOG("Calculated output number of frames %u for latency %u\n", output_buffer_frames, latency_frames);
if (AudioUnitSetProperty(stm->output_unit,
kAudioDevicePropertyBufferFrameSize, kAudioDevicePropertyBufferFrameSize,
kAudioUnitScope_Input, kAudioUnitScope_Input,
AU_OUT_BUS, AU_OUT_BUS,
&output_buffer_frames, &output_buffer_frames,
sizeof(output_buffer_frames)) != 0) { sizeof(output_buffer_frames));
if (r != noErr) {
PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioDevicePropertyBufferFrameSize", r);
audiounit_stream_destroy(stm); audiounit_stream_destroy(stm);
return CUBEB_ERROR; return CUBEB_ERROR;
} }
@ -1243,16 +1298,18 @@ audiounit_stream_init(cubeb * context,
assert(stm->output_unit != NULL); assert(stm->output_unit != NULL);
aurcbs_out.inputProc = audiounit_output_callback; aurcbs_out.inputProc = audiounit_output_callback;
aurcbs_out.inputProcRefCon = stm; aurcbs_out.inputProcRefCon = stm;
if (AudioUnitSetProperty(stm->output_unit, r = AudioUnitSetProperty(stm->output_unit,
kAudioUnitProperty_SetRenderCallback, kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Global, kAudioUnitScope_Global,
AU_OUT_BUS, AU_OUT_BUS,
&aurcbs_out, &aurcbs_out,
sizeof(aurcbs_out)) != 0) { sizeof(aurcbs_out));
if (r != noErr) {
audiounit_stream_destroy(stm); audiounit_stream_destroy(stm);
PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioUnitProperty_SetRenderCallback", r);
return CUBEB_ERROR; return CUBEB_ERROR;
} }
LOG("Output audiounit init successfully.\n"); LOG("Output audiounit init successfully.");
} }
// Setting the latency doesn't work well for USB headsets (eg. plantronics). // Setting the latency doesn't work well for USB headsets (eg. plantronics).
@ -1329,24 +1386,31 @@ audiounit_stream_init(cubeb * context,
stm->user_ptr, stm->user_ptr,
CUBEB_RESAMPLER_QUALITY_DESKTOP); CUBEB_RESAMPLER_QUALITY_DESKTOP);
if (!stm->resampler) { if (!stm->resampler) {
LOG("Could not create resampler\n"); LOG("Could not create resampler.");
audiounit_stream_destroy(stm); audiounit_stream_destroy(stm);
return CUBEB_ERROR; return CUBEB_ERROR;
} }
if (stm->input_unit != NULL && if (stm->input_unit != NULL) {
AudioUnitInitialize(stm->input_unit) != 0) { r = AudioUnitInitialize(stm->input_unit);
audiounit_stream_destroy(stm); if (r != noErr) {
return CUBEB_ERROR; PRINT_ERROR_CODE("AudioUnitInitialize/input", r);
audiounit_stream_destroy(stm);
return CUBEB_ERROR;
}
} }
if (stm->output_unit != NULL &&
AudioUnitInitialize(stm->output_unit) != 0) { if (stm->output_unit != NULL) {
audiounit_stream_destroy(stm); r = AudioUnitInitialize(stm->output_unit);
return CUBEB_ERROR; if (r != noErr) {
PRINT_ERROR_CODE("AudioUnitInitialize/output", r);
audiounit_stream_destroy(stm);
return CUBEB_ERROR;
}
} }
*stream = stm; *stream = stm;
LOG("Cubeb stream (%p) init successful.\n", stm); LOG("Cubeb stream (%p) init successful.", stm);
return CUBEB_OK; return CUBEB_OK;
} }
@ -1375,13 +1439,11 @@ audiounit_stream_destroy(cubeb_stream * stm)
audiounit_uninstall_device_changed_callback(stm); audiounit_uninstall_device_changed_callback(stm);
#endif #endif
int r = pthread_mutex_destroy(&stm->mutex); {
assert(r == 0); auto_lock lock(stm->context->mutex);
assert(stm->context->active_streams >= 1);
pthread_mutex_lock(&stm->context->mutex); stm->context->active_streams -= 1;
assert(stm->context->active_streams >= 1); }
stm->context->active_streams -= 1;
pthread_mutex_unlock(&stm->context->mutex);
delete stm; delete stm;
} }
@ -1389,9 +1451,12 @@ audiounit_stream_destroy(cubeb_stream * stm)
static int static int
audiounit_stream_start(cubeb_stream * stm) audiounit_stream_start(cubeb_stream * stm)
{ {
pthread_mutex_lock(&stm->context->mutex); {
stm->shutdown = 0; auto_lock lock(stm->mutex);
stm->draining = 0; stm->shutdown = 0;
stm->draining = 0;
}
OSStatus r; OSStatus r;
if (stm->input_unit != NULL) { if (stm->input_unit != NULL) {
r = AudioOutputUnitStart(stm->input_unit); r = AudioOutputUnitStart(stm->input_unit);
@ -1402,16 +1467,18 @@ audiounit_stream_start(cubeb_stream * stm)
assert(r == 0); assert(r == 0);
} }
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED); stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
LOG("Cubeb stream (%p) started successfully.\n", stm); LOG("Cubeb stream (%p) started successfully.", stm);
pthread_mutex_unlock(&stm->context->mutex);
return CUBEB_OK; return CUBEB_OK;
} }
static int static int
audiounit_stream_stop(cubeb_stream * stm) audiounit_stream_stop(cubeb_stream * stm)
{ {
pthread_mutex_lock(&stm->context->mutex); {
stm->shutdown = 1; auto_lock lock(stm->mutex);
stm->shutdown = 1;
}
OSStatus r; OSStatus r;
if (stm->input_unit != NULL) { if (stm->input_unit != NULL) {
r = AudioOutputUnitStop(stm->input_unit); r = AudioOutputUnitStop(stm->input_unit);
@ -1422,17 +1489,16 @@ audiounit_stream_stop(cubeb_stream * stm)
assert(r == 0); assert(r == 0);
} }
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED); stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
LOG("Cubeb stream (%p) stopped successfully.\n", stm); LOG("Cubeb stream (%p) stopped successfully.", stm);
pthread_mutex_unlock(&stm->context->mutex);
return CUBEB_OK; return CUBEB_OK;
} }
static int static int
audiounit_stream_get_position(cubeb_stream * stm, uint64_t * position) audiounit_stream_get_position(cubeb_stream * stm, uint64_t * position)
{ {
pthread_mutex_lock(&stm->mutex); auto_lock lock(stm->mutex);
*position = stm->frames_played; *position = stm->frames_played;
pthread_mutex_unlock(&stm->mutex);
return CUBEB_OK; return CUBEB_OK;
} }
@ -1443,7 +1509,7 @@ audiounit_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
//TODO //TODO
return CUBEB_ERROR_NOT_SUPPORTED; return CUBEB_ERROR_NOT_SUPPORTED;
#else #else
pthread_mutex_lock(&stm->mutex); auto_lock lock(stm->mutex);
if (stm->hw_latency_frames == UINT64_MAX) { if (stm->hw_latency_frames == UINT64_MAX) {
UInt32 size; UInt32 size;
uint32_t device_latency_frames, device_safety_offset; uint32_t device_latency_frames, device_safety_offset;
@ -1463,7 +1529,6 @@ audiounit_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
r = audiounit_get_output_device_id(&output_device_id); r = audiounit_get_output_device_id(&output_device_id);
if (r != noErr) { if (r != noErr) {
pthread_mutex_unlock(&stm->mutex);
return CUBEB_ERROR; return CUBEB_ERROR;
} }
@ -1475,7 +1540,7 @@ audiounit_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
&unit_latency_sec, &unit_latency_sec,
&size); &size);
if (r != noErr) { if (r != noErr) {
pthread_mutex_unlock(&stm->mutex); PRINT_ERROR_CODE("AudioUnitGetProperty/kAudioUnitProperty_Latency", r);
return CUBEB_ERROR; return CUBEB_ERROR;
} }
@ -1487,7 +1552,7 @@ audiounit_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
&size, &size,
&device_latency_frames); &device_latency_frames);
if (r != noErr) { if (r != noErr) {
pthread_mutex_unlock(&stm->mutex); PRINT_ERROR_CODE("AudioUnitGetPropertyData/latency_frames", r);
return CUBEB_ERROR; return CUBEB_ERROR;
} }
@ -1499,7 +1564,7 @@ audiounit_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
&size, &size,
&device_safety_offset); &device_safety_offset);
if (r != noErr) { if (r != noErr) {
pthread_mutex_unlock(&stm->mutex); PRINT_ERROR_CODE("AudioUnitGetPropertyData/safety_offset", r);
return CUBEB_ERROR; return CUBEB_ERROR;
} }
@ -1511,7 +1576,6 @@ audiounit_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
} }
*latency = stm->hw_latency_frames + stm->current_latency_frames; *latency = stm->hw_latency_frames + stm->current_latency_frames;
pthread_mutex_unlock(&stm->mutex);
return CUBEB_OK; return CUBEB_OK;
#endif #endif
@ -1527,6 +1591,7 @@ int audiounit_stream_set_volume(cubeb_stream * stm, float volume)
0, volume, 0); 0, volume, 0);
if (r != noErr) { if (r != noErr) {
PRINT_ERROR_CODE("AudioUnitSetParameter/kHALOutputParam_Volume", r);
return CUBEB_ERROR; return CUBEB_ERROR;
} }
return CUBEB_OK; return CUBEB_OK;
@ -1538,9 +1603,10 @@ int audiounit_stream_set_panning(cubeb_stream * stm, float panning)
return CUBEB_ERROR_INVALID_PARAMETER; return CUBEB_ERROR_INVALID_PARAMETER;
} }
pthread_mutex_lock(&stm->mutex); {
stm->panning = panning; auto_lock lock(stm->mutex);
pthread_mutex_unlock(&stm->mutex); stm->panning = panning;
}
return CUBEB_OK; return CUBEB_OK;
} }
@ -1614,7 +1680,7 @@ int audiounit_stream_get_current_device(cubeb_stream * stm,
size = sizeof(UInt32); size = sizeof(UInt32);
r = AudioObjectGetPropertyData(input_device_id, &datasource_address_input, 0, NULL, &size, &data); r = AudioObjectGetPropertyData(input_device_id, &datasource_address_input, 0, NULL, &size, &data);
if (r != noErr) { if (r != noErr) {
LOG("Error when getting device !\n"); LOG("Error when getting device !");
size = 0; size = 0;
data = 0; data = 0;
} }
@ -1653,7 +1719,8 @@ int audiounit_stream_register_device_changed_callback(cubeb_stream * stream,
* Current implementation requires unregister before register a new cb. */ * Current implementation requires unregister before register a new cb. */
assert(!stream->device_changed_callback); assert(!stream->device_changed_callback);
pthread_mutex_lock(&stream->mutex); auto_lock lock(stream->mutex);
stream->device_changed_callback = device_changed_callback; stream->device_changed_callback = device_changed_callback;
int r = CUBEB_OK; int r = CUBEB_OK;
#if !TARGET_OS_IPHONE #if !TARGET_OS_IPHONE
@ -1663,7 +1730,6 @@ int audiounit_stream_register_device_changed_callback(cubeb_stream * stream,
r = audiounit_uninstall_device_changed_callback(stream); r = audiounit_uninstall_device_changed_callback(stream);
} }
#endif #endif
pthread_mutex_unlock(&stream->mutex);
return r; return r;
} }
@ -2028,10 +2094,10 @@ audiounit_collection_changed_callback(AudioObjectID inObjectID,
void * inClientData) void * inClientData)
{ {
cubeb * context = static_cast<cubeb *>(inClientData); cubeb * context = static_cast<cubeb *>(inClientData);
pthread_mutex_lock(&context->mutex); auto_lock lock(context->mutex);
if (context->collection_changed_callback == NULL) { if (context->collection_changed_callback == NULL) {
/* Listener removed while waiting in mutex, abort. */ /* Listener removed while waiting in mutex, abort. */
pthread_mutex_unlock(&context->mutex);
return noErr; return noErr;
} }
@ -2045,7 +2111,6 @@ audiounit_collection_changed_callback(AudioObjectID inObjectID,
audiounit_equal_arrays(devices, context->devtype_device_array, new_number_of_devices)) { audiounit_equal_arrays(devices, context->devtype_device_array, new_number_of_devices)) {
/* Device changed for the other scope, ignore. */ /* Device changed for the other scope, ignore. */
delete [] devices; delete [] devices;
pthread_mutex_unlock(&context->mutex);
return noErr; return noErr;
} }
/* Device on desired scope changed, reset counter and array. */ /* Device on desired scope changed, reset counter and array. */
@ -2056,7 +2121,6 @@ audiounit_collection_changed_callback(AudioObjectID inObjectID,
} }
context->collection_changed_callback(context, context->collection_changed_user_ptr); context->collection_changed_callback(context, context->collection_changed_user_ptr);
pthread_mutex_unlock(&context->mutex);
return noErr; return noErr;
} }
@ -2130,7 +2194,7 @@ int audiounit_register_device_collection_changed(cubeb * context,
void * user_ptr) void * user_ptr)
{ {
OSStatus ret; OSStatus ret;
pthread_mutex_lock(&context->mutex); auto_lock lock(context->mutex);
if (collection_changed_callback) { if (collection_changed_callback) {
ret = audiounit_add_device_listener(context, devtype, ret = audiounit_add_device_listener(context, devtype,
collection_changed_callback, collection_changed_callback,
@ -2138,7 +2202,6 @@ int audiounit_register_device_collection_changed(cubeb * context,
} else { } else {
ret = audiounit_remove_device_listener(context); ret = audiounit_remove_device_listener(context);
} }
pthread_mutex_unlock(&context->mutex);
return (ret == noErr) ? CUBEB_OK : CUBEB_ERROR; return (ret == noErr) ? CUBEB_OK : CUBEB_ERROR;
} }

View File

@ -287,10 +287,10 @@ sndio_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
} }
static int static int
sndio_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms) sndio_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
{ {
// XXX Not yet implemented. // XXX Not yet implemented.
*latency = 2048; *latency_frames = 2048;
return CUBEB_OK; return CUBEB_OK;
} }

View File

@ -11,6 +11,11 @@
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
#if defined(WIN32)
#include "cubeb_utils_win.h"
#else
#include "cubeb_utils_unix.h"
#endif
/** Similar to memcpy, but accounts for the size of an element. */ /** Similar to memcpy, but accounts for the size of an element. */
template<typename T> template<typename T>
@ -189,4 +194,18 @@ private:
size_t length_; size_t length_;
}; };
struct auto_lock {
auto_lock(owned_critical_section & lock)
: lock(lock)
{
lock.enter();
}
~auto_lock()
{
lock.leave();
}
private:
owned_critical_section & lock;
};
#endif /* CUBEB_UTILS */ #endif /* CUBEB_UTILS */

View File

@ -0,0 +1,85 @@
/*
* Copyright © 2016 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#if !defined(CUBEB_UTILS_UNIX)
#define CUBEB_UTILS_UNIX
#include <pthread.h>
#include <errno.h>
#include <stdio.h>
/* This wraps a critical section to track the owner in debug mode. */
class owned_critical_section
{
public:
owned_critical_section()
{
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
#ifdef DEBUG
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
#else
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
#endif
#ifdef DEBUG
int r =
#endif
pthread_mutex_init(&mutex, &attr);
#ifdef DEBUG
assert(r == 0);
#endif
pthread_mutexattr_destroy(&attr);
}
~owned_critical_section()
{
#ifdef DEBUG
int r =
#endif
pthread_mutex_destroy(&mutex);
#ifdef DEBUG
assert(r == 0);
#endif
}
void enter()
{
#ifdef DEBUG
int r =
#endif
pthread_mutex_lock(&mutex);
#ifdef DEBUG
assert(r == 0 && "Deadlock");
#endif
}
void leave()
{
#ifdef DEBUG
int r =
#endif
pthread_mutex_unlock(&mutex);
#ifdef DEBUG
assert(r == 0 && "Unlocking unlocked mutex");
#endif
}
void assert_current_thread_owns()
{
#ifdef DEBUG
int r = pthread_mutex_lock(&mutex);
assert(r == EDEADLK);
#endif
}
private:
pthread_mutex_t mutex;
};
#endif /* CUBEB_UTILS_UNIX */

View File

@ -0,0 +1,67 @@
/*
* Copyright © 2016 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#if !defined(CUBEB_UTILS_WIN)
#define CUBEB_UTILS_WIN
#include <windows.h>
#include "cubeb-internal.h"
/* This wraps a critical section to track the owner in debug mode, adapted from
NSPR and http://blogs.msdn.com/b/oldnewthing/archive/2013/07/12/10433554.aspx */
class owned_critical_section
{
public:
owned_critical_section()
#ifdef DEBUG
: owner(0)
#endif
{
InitializeCriticalSection(&critical_section);
}
~owned_critical_section()
{
DeleteCriticalSection(&critical_section);
}
void enter()
{
EnterCriticalSection(&critical_section);
#ifdef DEBUG
XASSERT(owner != GetCurrentThreadId() && "recursive locking");
owner = GetCurrentThreadId();
#endif
}
void leave()
{
#ifdef DEBUG
/* GetCurrentThreadId cannot return 0: it is not a the valid thread id */
owner = 0;
#endif
LeaveCriticalSection(&critical_section);
}
/* This is guaranteed to have the good behaviour if it succeeds. The behaviour
is undefined otherwise. */
void assert_current_thread_owns()
{
#ifdef DEBUG
/* This implies owner != 0, because GetCurrentThreadId cannot return 0. */
XASSERT(owner == GetCurrentThreadId());
#endif
}
private:
CRITICAL_SECTION critical_section;
#ifdef DEBUG
DWORD owner;
#endif
};
#endif /* CUBEB_UTILS_WIN */

View File

@ -79,73 +79,6 @@ void SafeRelease(T * ptr)
} }
} }
/* This wraps a critical section to track the owner in debug mode, adapted from
NSPR and http://blogs.msdn.com/b/oldnewthing/archive/2013/07/12/10433554.aspx */
class owned_critical_section
{
public:
owned_critical_section()
#ifdef DEBUG
: owner(0)
#endif
{
InitializeCriticalSection(&critical_section);
}
~owned_critical_section()
{
DeleteCriticalSection(&critical_section);
}
void enter()
{
EnterCriticalSection(&critical_section);
#ifdef DEBUG
XASSERT(owner != GetCurrentThreadId() && "recursive locking");
owner = GetCurrentThreadId();
#endif
}
void leave()
{
#ifdef DEBUG
/* GetCurrentThreadId cannot return 0: it is not a the valid thread id */
owner = 0;
#endif
LeaveCriticalSection(&critical_section);
}
/* This is guaranteed to have the good behaviour if it succeeds. The behaviour
is undefined otherwise. */
void assert_current_thread_owns()
{
#ifdef DEBUG
/* This implies owner != 0, because GetCurrentThreadId cannot return 0. */
XASSERT(owner == GetCurrentThreadId());
#endif
}
private:
CRITICAL_SECTION critical_section;
#ifdef DEBUG
DWORD owner;
#endif
};
struct auto_lock {
auto_lock(owned_critical_section * lock)
: lock(lock)
{
lock->enter();
}
~auto_lock()
{
lock->leave();
}
private:
owned_critical_section * lock;
};
struct auto_com { struct auto_com {
auto_com() { auto_com() {
result = CoInitializeEx(NULL, COINIT_MULTITHREADED); result = CoInitializeEx(NULL, COINIT_MULTITHREADED);
@ -280,7 +213,7 @@ struct cubeb_stream
/* The lock protects all members that are touched by the render thread or /* The lock protects all members that are touched by the render thread or
change during a device reset, including: audio_clock, audio_stream_volume, change during a device reset, including: audio_clock, audio_stream_volume,
client, frames_written, mix_params, total_frames_written, prev_position. */ client, frames_written, mix_params, total_frames_written, prev_position. */
owned_critical_section * stream_reset_lock; owned_critical_section stream_reset_lock;
/* Maximum number of frames that can be passed down in a callback. */ /* Maximum number of frames that can be passed down in a callback. */
uint32_t input_buffer_frame_count; uint32_t input_buffer_frame_count;
/* Maximum number of frames that can be requested in a callback. */ /* Maximum number of frames that can be requested in a callback. */
@ -1063,7 +996,7 @@ HRESULT get_default_endpoint(IMMDevice ** device, EDataFlow direction)
double double
current_stream_delay(cubeb_stream * stm) current_stream_delay(cubeb_stream * stm)
{ {
stm->stream_reset_lock->assert_current_thread_owns(); stm->stream_reset_lock.assert_current_thread_owns();
/* If the default audio endpoint went away during playback and we weren't /* If the default audio endpoint went away during playback and we weren't
able to configure a new one, it's possible the caller may call this able to configure a new one, it's possible the caller may call this
@ -1097,7 +1030,7 @@ current_stream_delay(cubeb_stream * stm)
int int
stream_set_volume(cubeb_stream * stm, float volume) stream_set_volume(cubeb_stream * stm, float volume)
{ {
stm->stream_reset_lock->assert_current_thread_owns(); stm->stream_reset_lock.assert_current_thread_owns();
if (!stm->audio_stream_volume) { if (!stm->audio_stream_volume) {
return CUBEB_ERROR; return CUBEB_ERROR;
@ -1451,7 +1384,7 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm,
WAVEFORMATEX * mix_format; WAVEFORMATEX * mix_format;
HRESULT hr; HRESULT hr;
stm->stream_reset_lock->assert_current_thread_owns(); stm->stream_reset_lock.assert_current_thread_owns();
if (devid) { if (devid) {
std::unique_ptr<const wchar_t> id; std::unique_ptr<const wchar_t> id;
@ -1552,7 +1485,7 @@ int setup_wasapi_stream(cubeb_stream * stm)
HRESULT hr; HRESULT hr;
int rv; int rv;
stm->stream_reset_lock->assert_current_thread_owns(); stm->stream_reset_lock.assert_current_thread_owns();
auto_com com; auto_com com;
if (!com.ok()) { if (!com.ok()) {
@ -1708,7 +1641,7 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
stm->latency = latency_frames; stm->latency = latency_frames;
stm->volume = 1.0; stm->volume = 1.0;
stm->stream_reset_lock = new owned_critical_section(); stm->stream_reset_lock = owned_critical_section();
stm->reconfigure_event = CreateEvent(NULL, 0, 0, NULL); stm->reconfigure_event = CreateEvent(NULL, 0, 0, NULL);
if (!stm->reconfigure_event) { if (!stm->reconfigure_event) {
@ -1761,7 +1694,7 @@ void close_wasapi_stream(cubeb_stream * stm)
{ {
XASSERT(stm); XASSERT(stm);
stm->stream_reset_lock->assert_current_thread_owns(); stm->stream_reset_lock.assert_current_thread_owns();
SafeRelease(stm->output_client); SafeRelease(stm->output_client);
stm->output_client = NULL; stm->output_client = NULL;
@ -1805,8 +1738,6 @@ void wasapi_stream_destroy(cubeb_stream * stm)
close_wasapi_stream(stm); close_wasapi_stream(stm);
} }
delete stm->stream_reset_lock;
free(stm); free(stm);
} }

View File

@ -18,10 +18,6 @@
#include "TestHarness.h" #include "TestHarness.h"
#endif #endif
#if (defined(_WIN32) || defined(__WIN32__))
#define __func__ __FUNCTION__
#endif
#define ARRAY_LENGTH(_x) (sizeof(_x) / sizeof(_x[0])) #define ARRAY_LENGTH(_x) (sizeof(_x) / sizeof(_x[0]))
#define BEGIN_TEST fprintf(stderr, "START %s\n", __func__) #define BEGIN_TEST fprintf(stderr, "START %s\n", __func__)
#define END_TEST fprintf(stderr, "END %s\n", __func__) #define END_TEST fprintf(stderr, "END %s\n", __func__)

View File

@ -25,6 +25,8 @@ cp $1/src/cubeb_resampler_internal.h src
cp $1/src/cubeb_ring_array.h src cp $1/src/cubeb_ring_array.h src
cp $1/src/cubeb_sndio.c src cp $1/src/cubeb_sndio.c src
cp $1/src/cubeb_utils.h src cp $1/src/cubeb_utils.h src
cp $1/src/cubeb_utils_unix.h src
cp $1/src/cubeb_utils_win.h src
cp $1/src/cubeb_wasapi.cpp src cp $1/src/cubeb_wasapi.cpp src
cp $1/src/cubeb_winmm.c src cp $1/src/cubeb_winmm.c src
cp $1/test/common.h tests/common.h cp $1/test/common.h tests/common.h