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 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);
avail = snd_pcm_avail_update(stm->pcm);
if (avail == -EPIPE) {
if (avail < 0) {
snd_pcm_recover(stm->pcm, avail, 1);
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
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) {
snd_pcm_recover(stm->pcm, -EPIPE, 1);
avail = snd_pcm_avail_update(stm->pcm);
if (avail <= 0) {
pthread_mutex_unlock(&stm->mutex);
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
return ERROR;
}
pthread_mutex_unlock(&stm->mutex);
return RUNNING;
}
p = calloc(1, snd_pcm_frames_to_bytes(stm->pcm, avail));
@ -300,6 +295,7 @@ alsa_refill_stream(cubeb_stream * stm)
if (got < 0) {
pthread_mutex_unlock(&stm->mutex);
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
free(p);
return ERROR;
}
if (got > 0) {
@ -317,7 +313,7 @@ alsa_refill_stream(cubeb_stream * stm)
}
}
wrote = snd_pcm_writei(stm->pcm, p, got);
if (wrote == -EPIPE) {
if (wrote < 0) {
snd_pcm_recover(stm->pcm, wrote, 1);
wrote = snd_pcm_writei(stm->pcm, p, got);
}
@ -389,6 +385,8 @@ alsa_run(cubeb * ctx)
for (i = 0; i < CUBEB_STREAM_MAX; ++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)) {
alsa_set_stream_state(stm, PROCESSING);
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
* 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) {
return CUBEB_ERROR;
}

View File

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

View File

@ -11,6 +11,11 @@
#include <stdint.h>
#include <string.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. */
template<typename T>
@ -189,4 +194,18 @@ private:
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 */

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 {
auto_com() {
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
change during a device reset, including: audio_clock, audio_stream_volume,
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. */
uint32_t input_buffer_frame_count;
/* Maximum number of frames that can be requested in a callback. */
@ -1063,7 +996,7 @@ HRESULT get_default_endpoint(IMMDevice ** device, EDataFlow direction)
double
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
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
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) {
return CUBEB_ERROR;
@ -1451,7 +1384,7 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm,
WAVEFORMATEX * mix_format;
HRESULT hr;
stm->stream_reset_lock->assert_current_thread_owns();
stm->stream_reset_lock.assert_current_thread_owns();
if (devid) {
std::unique_ptr<const wchar_t> id;
@ -1552,7 +1485,7 @@ int setup_wasapi_stream(cubeb_stream * stm)
HRESULT hr;
int rv;
stm->stream_reset_lock->assert_current_thread_owns();
stm->stream_reset_lock.assert_current_thread_owns();
auto_com com;
if (!com.ok()) {
@ -1708,7 +1641,7 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
stm->latency = latency_frames;
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);
if (!stm->reconfigure_event) {
@ -1761,7 +1694,7 @@ void close_wasapi_stream(cubeb_stream * stm)
{
XASSERT(stm);
stm->stream_reset_lock->assert_current_thread_owns();
stm->stream_reset_lock.assert_current_thread_owns();
SafeRelease(stm->output_client);
stm->output_client = NULL;
@ -1805,8 +1738,6 @@ void wasapi_stream_destroy(cubeb_stream * stm)
close_wasapi_stream(stm);
}
delete stm->stream_reset_lock;
free(stm);
}

View File

@ -18,10 +18,6 @@
#include "TestHarness.h"
#endif
#if (defined(_WIN32) || defined(__WIN32__))
#define __func__ __FUNCTION__
#endif
#define ARRAY_LENGTH(_x) (sizeof(_x) / sizeof(_x[0]))
#define BEGIN_TEST fprintf(stderr, "START %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_sndio.c 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_winmm.c src
cp $1/test/common.h tests/common.h