mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 00:01:50 +00:00
Bug 1290425 - Update cubeb to revision 6278ef2f73. r=padenot
This commit is contained in:
parent
73a62f5c90
commit
ae1e3599d8
@ -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.
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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 */
|
||||||
|
85
media/libcubeb/src/cubeb_utils_unix.h
Normal file
85
media/libcubeb/src/cubeb_utils_unix.h
Normal 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 */
|
67
media/libcubeb/src/cubeb_utils_win.h
Normal file
67
media/libcubeb/src/cubeb_utils_win.h
Normal 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 */
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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__)
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user