зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1285541 - Update cubeb to revision e2d399. r=padenot,achronop
Patches by padenot and achronop. MozReview-Commit-ID: G00OFgHdKne --HG-- rename : media/libcubeb/src/cubeb_audiounit.c => media/libcubeb/src/cubeb_audiounit.cpp
This commit is contained in:
Родитель
b6fefb7013
Коммит
dd64e90915
|
@ -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 b8aebef33a096640d6cbd7a6fa568bccef1b339c.
|
||||
The git commit ID used was e2d3996866fe86e52200cb71044ea4c199555834.
|
||||
|
|
|
@ -32,10 +32,10 @@ extern "C" {
|
|||
cubeb_init(&app_ctx, "Example Application");
|
||||
int rv;
|
||||
int rate;
|
||||
int latency_ms;
|
||||
int latency_frames;
|
||||
uint64_t ts;
|
||||
|
||||
rv = cubeb_get_min_latency(app_ctx, output_params, &latency_ms);
|
||||
rv = cubeb_get_min_latency(app_ctx, output_params, &latency_frames);
|
||||
if (rv != CUBEB_OK) {
|
||||
fprintf(stderr, "Could not get minimum latency");
|
||||
return rv;
|
||||
|
@ -61,7 +61,7 @@ extern "C" {
|
|||
rv = cubeb_stream_init(app_ctx, &stm, "Example Stream 1",
|
||||
NULL, input_params,
|
||||
NULL, output_params,
|
||||
latency_ms,
|
||||
latency_frames,
|
||||
data_cb, state_cb,
|
||||
NULL);
|
||||
if (rv != CUBEB_OK) {
|
||||
|
@ -284,8 +284,8 @@ typedef struct {
|
|||
unsigned int max_rate; /**< Maximum sample rate supported. */
|
||||
unsigned int min_rate; /**< Minimum sample rate supported. */
|
||||
|
||||
unsigned int latency_lo_ms; /**< Lowest possible latency in milliseconds. */
|
||||
unsigned int latency_hi_ms; /**< Higest possible latency in milliseconds. */
|
||||
unsigned int latency_lo; /**< Lowest possible latency in frames. */
|
||||
unsigned int latency_hi; /**< Higest possible latency in frames. */
|
||||
} cubeb_device_info;
|
||||
|
||||
/** Device collection. */
|
||||
|
@ -363,19 +363,20 @@ char const * cubeb_get_backend_id(cubeb * context);
|
|||
@retval CUBEB_ERROR */
|
||||
int cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels);
|
||||
|
||||
/** Get the minimal latency value, in milliseconds, that is guaranteed to work
|
||||
/** Get the minimal latency value, in frames, that is guaranteed to work
|
||||
when creating a stream for the specified sample rate. This is platform,
|
||||
hardware and backend dependant.
|
||||
@param context A pointer to the cubeb context.
|
||||
@param params On some backends, the minimum achievable latency depends on
|
||||
the characteristics of the stream.
|
||||
@param latency_ms The latency value, in ms, to pass to cubeb_stream_init.
|
||||
@param latency_frames The latency value, in frames, to pass to
|
||||
cubeb_stream_init.
|
||||
@retval CUBEB_OK
|
||||
@retval CUBEB_ERROR_INVALID_PARAMETER
|
||||
@retval CUBEB_ERROR_NOT_SUPPORTED */
|
||||
int cubeb_get_min_latency(cubeb * context,
|
||||
cubeb_stream_params params,
|
||||
uint32_t * latency_ms);
|
||||
uint32_t * latency_frames);
|
||||
|
||||
/** Get the preferred sample rate for this backend: this is hardware and
|
||||
platform dependant, and can avoid resampling, and/or trigger fastpaths.
|
||||
|
@ -404,8 +405,8 @@ void cubeb_destroy(cubeb * context);
|
|||
default output device is used.
|
||||
@param output_stream_params Parameters for the output side of the stream, or
|
||||
NULL if this stream is input only.
|
||||
@param latency Approximate stream latency in milliseconds. Valid range
|
||||
is [1, 2000].
|
||||
@param latency Stream latency in frames. Valid range
|
||||
is [1, 96000].
|
||||
@param data_callback Will be called to preroll data before playback is
|
||||
started by cubeb_stream_start.
|
||||
@param state_callback A pointer to a state callback.
|
||||
|
@ -422,7 +423,7 @@ int cubeb_stream_init(cubeb * context,
|
|||
cubeb_stream_params * input_stream_params,
|
||||
cubeb_devid output_device,
|
||||
cubeb_stream_params * output_stream_params,
|
||||
unsigned int latency,
|
||||
unsigned int latency_frames,
|
||||
cubeb_data_callback data_callback,
|
||||
cubeb_state_callback state_callback,
|
||||
void * user_ptr);
|
||||
|
|
|
@ -107,7 +107,7 @@ validate_stream_params(cubeb_stream_params * input_stream_params,
|
|||
int
|
||||
validate_latency(int latency)
|
||||
{
|
||||
if (latency < 1 || latency > 2000) {
|
||||
if (latency < 1 || latency > 96000) {
|
||||
return CUBEB_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
return CUBEB_OK;
|
||||
|
|
|
@ -774,7 +774,7 @@ alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
|
|||
cubeb_stream_params * input_stream_params,
|
||||
cubeb_devid output_device,
|
||||
cubeb_stream_params * output_stream_params,
|
||||
unsigned int latency,
|
||||
unsigned int latency_frames,
|
||||
cubeb_data_callback data_callback, cubeb_state_callback state_callback,
|
||||
void * user_ptr)
|
||||
{
|
||||
|
@ -782,6 +782,8 @@ alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
|
|||
int r;
|
||||
snd_pcm_format_t format;
|
||||
snd_pcm_uframes_t period_size;
|
||||
int latency_us = 0;
|
||||
|
||||
|
||||
assert(ctx && stream);
|
||||
|
||||
|
@ -845,16 +847,19 @@ alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
|
|||
r = snd_pcm_nonblock(stm->pcm, 1);
|
||||
assert(r == 0);
|
||||
|
||||
latency_us = latency_frames * 1e6 / stm->params.rate;
|
||||
|
||||
/* Ugly hack: the PA ALSA plugin allows buffer configurations that can't
|
||||
possibly work. See https://bugzilla.mozilla.org/show_bug.cgi?id=761274.
|
||||
Only resort to this hack if the handle_underrun workaround failed. */
|
||||
if (!ctx->local_config && ctx->is_pa) {
|
||||
latency = latency < 500 ? 500 : latency;
|
||||
const int min_latency = 5e5;
|
||||
latency_us = latency_us < min_latency ? min_latency: latency_us;
|
||||
}
|
||||
|
||||
r = snd_pcm_set_params(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED,
|
||||
stm->params.channels, stm->params.rate, 1,
|
||||
latency * 1000);
|
||||
latency_us);
|
||||
if (r < 0) {
|
||||
alsa_stream_destroy(stm);
|
||||
return CUBEB_ERROR_INVALID_FORMAT;
|
||||
|
@ -999,11 +1004,11 @@ alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) {
|
|||
}
|
||||
|
||||
static int
|
||||
alsa_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms)
|
||||
alsa_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
|
||||
{
|
||||
/* This is found to be an acceptable minimum, even on a super low-end
|
||||
/* 40ms is found to be an acceptable minimum, even on a super low-end
|
||||
* machine. */
|
||||
*latency_ms = 40;
|
||||
*latency_frames = 40 * params.rate / 1000;
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
|
|
@ -250,9 +250,6 @@ audiotrack_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * l
|
|||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
/* Convert to milliseconds. */
|
||||
*latency_ms = *latency_ms * 1000 / params.rate;
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
#endif
|
||||
#include "cubeb_resampler.h"
|
||||
#include "cubeb_ring_array.h"
|
||||
#include "cubeb_utils.h"
|
||||
#include <algorithm>
|
||||
|
||||
#if !defined(kCFCoreFoundationVersionNumber10_7)
|
||||
/* From CoreFoundation CFBase.h */
|
||||
|
@ -65,10 +67,10 @@ typedef UInt32 AudioFormatFlags;
|
|||
#define LOG(...)
|
||||
#endif
|
||||
|
||||
static struct cubeb_ops const audiounit_ops;
|
||||
extern cubeb_ops const audiounit_ops;
|
||||
|
||||
struct cubeb {
|
||||
struct cubeb_ops const * ops;
|
||||
cubeb_ops const * ops;
|
||||
pthread_mutex_t mutex;
|
||||
int active_streams;
|
||||
int limit_streams;
|
||||
|
@ -80,6 +82,65 @@ struct cubeb {
|
|||
AudioObjectID * devtype_device_array;
|
||||
};
|
||||
|
||||
class auto_array_wrapper
|
||||
{
|
||||
public:
|
||||
explicit auto_array_wrapper(auto_array<float> * ar)
|
||||
: float_ar(ar)
|
||||
, short_ar(nullptr)
|
||||
{assert((float_ar && !short_ar) || (!float_ar && short_ar));}
|
||||
|
||||
explicit auto_array_wrapper(auto_array<short> * ar)
|
||||
: float_ar(nullptr)
|
||||
, short_ar(ar)
|
||||
{assert((float_ar && !short_ar) || (!float_ar && short_ar));}
|
||||
|
||||
~auto_array_wrapper() {
|
||||
assert((float_ar && !short_ar) || (!float_ar && short_ar));
|
||||
delete float_ar;
|
||||
delete short_ar;
|
||||
}
|
||||
|
||||
void push(void * elements, size_t length){
|
||||
assert((float_ar && !short_ar) || (!float_ar && short_ar));
|
||||
if (float_ar)
|
||||
return float_ar->push(static_cast<float*>(elements), length);
|
||||
return short_ar->push(static_cast<short*>(elements), length);
|
||||
}
|
||||
|
||||
size_t length() const {
|
||||
assert((float_ar && !short_ar) || (!float_ar && short_ar));
|
||||
if (float_ar)
|
||||
return float_ar->length();
|
||||
return short_ar->length();
|
||||
}
|
||||
|
||||
void push_silence(size_t length) {
|
||||
assert((float_ar && !short_ar) || (!float_ar && short_ar));
|
||||
if (float_ar)
|
||||
return float_ar->push_silence(length);
|
||||
return short_ar->push_silence(length);
|
||||
}
|
||||
|
||||
bool pop(void * elements, size_t length) {
|
||||
assert((float_ar && !short_ar) || (!float_ar && short_ar));
|
||||
if (float_ar)
|
||||
return float_ar->pop(static_cast<float*>(elements), length);
|
||||
return short_ar->pop(static_cast<short*>(elements), length);
|
||||
}
|
||||
|
||||
void * data() const {
|
||||
assert((float_ar && !short_ar) || (!float_ar && short_ar));
|
||||
if (float_ar)
|
||||
return float_ar->data();
|
||||
return short_ar->data();
|
||||
}
|
||||
|
||||
private:
|
||||
auto_array<float> * float_ar;
|
||||
auto_array<short> * short_ar;
|
||||
};
|
||||
|
||||
struct cubeb_stream {
|
||||
cubeb * context;
|
||||
cubeb_data_callback data_callback;
|
||||
|
@ -98,7 +159,7 @@ struct cubeb_stream {
|
|||
pthread_mutex_t mutex;
|
||||
/* Hold the input samples in every
|
||||
* input callback iteration */
|
||||
ring_array input_buffer_array;
|
||||
auto_array_wrapper * input_linear_buffer;
|
||||
/* Frames on input buffer */
|
||||
uint32_t input_buffer_frames;
|
||||
/* Frame counters */
|
||||
|
@ -158,38 +219,18 @@ audiounit_make_silent(AudioBuffer * ioData)
|
|||
}
|
||||
|
||||
static OSStatus
|
||||
audiounit_input_callback(void * user_ptr,
|
||||
AudioUnitRenderActionFlags * flags,
|
||||
AudioTimeStamp const * tstamp,
|
||||
UInt32 bus,
|
||||
UInt32 input_frames,
|
||||
AudioBufferList * bufs)
|
||||
audiounit_render_input(cubeb_stream * stm,
|
||||
AudioUnitRenderActionFlags * flags,
|
||||
AudioTimeStamp const * tstamp,
|
||||
UInt32 bus,
|
||||
UInt32 input_frames)
|
||||
{
|
||||
cubeb_stream * stm = user_ptr;
|
||||
long outframes, frames;
|
||||
|
||||
pthread_mutex_lock(&stm->mutex);
|
||||
|
||||
assert(stm->input_unit != NULL);
|
||||
assert(AU_IN_BUS == bus);
|
||||
|
||||
if (stm->shutdown) {
|
||||
pthread_mutex_unlock(&stm->mutex);
|
||||
return noErr;
|
||||
}
|
||||
|
||||
/* Get next store buffer from ring array */
|
||||
AudioBuffer * input_buffer = ring_array_get_free_buffer(&stm->input_buffer_array);
|
||||
if (input_buffer == NULL) {
|
||||
LOG("input: Ring array is full drop one buffer\n");
|
||||
ring_array_get_data_buffer(&stm->input_buffer_array);
|
||||
|
||||
input_buffer = ring_array_get_free_buffer(&stm->input_buffer_array);
|
||||
assert(input_buffer);
|
||||
}
|
||||
|
||||
/* Create the AudioBufferList to store input. */
|
||||
AudioBufferList input_buffer_list;
|
||||
input_buffer_list.mBuffers[0] = *input_buffer;
|
||||
input_buffer_list.mBuffers[0].mDataByteSize =
|
||||
stm->input_desc.mBytesPerFrame * stm->input_buffer_frames;
|
||||
input_buffer_list.mBuffers[0].mData = nullptr;
|
||||
input_buffer_list.mBuffers[0].mNumberChannels = stm->input_desc.mChannelsPerFrame;
|
||||
input_buffer_list.mNumberBuffers = 1;
|
||||
|
||||
/* Render input samples */
|
||||
|
@ -199,21 +240,57 @@ audiounit_input_callback(void * user_ptr,
|
|||
bus,
|
||||
input_frames,
|
||||
&input_buffer_list);
|
||||
|
||||
if (r != noErr) {
|
||||
LOG("Input AudioUnitRender failed with error=%d", r);
|
||||
audiounit_make_silent(input_buffer);
|
||||
LOG("Input AudioUnitRender failed with error=%d\n", r);
|
||||
audiounit_make_silent(&input_buffer_list.mBuffers[0]);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Copy input data in linear buffer. */
|
||||
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",
|
||||
input_buffer_list.mNumberBuffers,
|
||||
input_buffer_list.mBuffers[0].mDataByteSize,
|
||||
input_buffer_list.mBuffers[0].mNumberChannels,
|
||||
input_frames);
|
||||
|
||||
/* Advance input frame counter. */
|
||||
assert(input_frames > 0);
|
||||
stm->frames_read += input_frames;
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
static OSStatus
|
||||
audiounit_input_callback(void * user_ptr,
|
||||
AudioUnitRenderActionFlags * flags,
|
||||
AudioTimeStamp const * tstamp,
|
||||
UInt32 bus,
|
||||
UInt32 input_frames,
|
||||
AudioBufferList * bufs)
|
||||
{
|
||||
cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr);
|
||||
long outframes, frames;
|
||||
|
||||
pthread_mutex_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);
|
||||
return noErr;
|
||||
}
|
||||
|
||||
OSStatus r = audiounit_render_input(stm, flags, tstamp, bus, input_frames);
|
||||
if (r != noErr) {
|
||||
return r;
|
||||
}
|
||||
|
||||
// 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
|
||||
|
@ -224,13 +301,14 @@ audiounit_input_callback(void * user_ptr,
|
|||
/* Input only. Call the user callback through resampler.
|
||||
Resampler will deliver input buffer in the correct rate. */
|
||||
frames = input_frames;
|
||||
input_buffer = ring_array_get_data_buffer(&stm->input_buffer_array);
|
||||
assert(input_buffer && "fetch buffer is null in the input");
|
||||
assert(input_frames <= stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame);
|
||||
outframes = cubeb_resampler_fill(stm->resampler,
|
||||
input_buffer->mData,
|
||||
stm->input_linear_buffer->data(),
|
||||
&frames,
|
||||
NULL,
|
||||
0);
|
||||
// Reset input buffer
|
||||
stm->input_linear_buffer->pop(nullptr, frames * stm->input_desc.mChannelsPerFrame);
|
||||
|
||||
if (outframes < 0 || outframes != input_frames) {
|
||||
stm->shutdown = 1;
|
||||
|
@ -253,7 +331,7 @@ audiounit_output_callback(void * user_ptr,
|
|||
assert(AU_OUT_BUS == bus);
|
||||
assert(outBufferList->mNumberBuffers == 1);
|
||||
|
||||
cubeb_stream * stm = user_ptr;
|
||||
cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr);
|
||||
|
||||
LOG("- output(%p): buffers %d, size %d, channels %d, frames %d\n", stm,
|
||||
outBufferList->mNumberBuffers, outBufferList->mBuffers[0].mDataByteSize,
|
||||
|
@ -265,6 +343,7 @@ audiounit_output_callback(void * user_ptr,
|
|||
pthread_mutex_lock(&stm->mutex);
|
||||
|
||||
if (stm->shutdown) {
|
||||
LOG("- output shutdown\n");
|
||||
audiounit_make_silent(&outBufferList->mBuffers[0]);
|
||||
pthread_mutex_unlock(&stm->mutex);
|
||||
return noErr;
|
||||
|
@ -286,33 +365,22 @@ audiounit_output_callback(void * user_ptr,
|
|||
/* Get output buffer. */
|
||||
output_buffer = outBufferList->mBuffers[0].mData;
|
||||
/* If Full duplex get also input buffer */
|
||||
AudioBuffer * input_aud_buf = NULL;
|
||||
if (stm->input_unit != NULL) {
|
||||
/* Output callback came first */
|
||||
if (stm->frames_read == 0) {
|
||||
LOG("Output callback came first send silent.\n");
|
||||
audiounit_make_silent(&outBufferList->mBuffers[0]);
|
||||
pthread_mutex_unlock(&stm->mutex);
|
||||
return noErr;
|
||||
stm->input_linear_buffer->push_silence(stm->input_buffer_frames *
|
||||
stm->input_desc.mChannelsPerFrame);
|
||||
}
|
||||
/* Input samples stored previously in input callback. */
|
||||
input_aud_buf = ring_array_get_data_buffer(&stm->input_buffer_array);
|
||||
if (input_aud_buf == NULL) {
|
||||
LOG("Requested more output than input. "
|
||||
"This is either a hole or we are after a stream stop and input thread stopped before output\n");
|
||||
/* Provide silent input. Other than that we could provide silent output and exit without
|
||||
* calling user callback. I do not prefer it because the user loose the control of
|
||||
* the output. Also resampler loose frame counting and produce less frame than
|
||||
* expected at some point in the future breaking an assert. */
|
||||
|
||||
/* Avoid here to allocate new memory since we are inside callback. Use the existing
|
||||
* allocated buffers since the ring array is empty and the buffer is not used. */
|
||||
input_aud_buf = ring_array_get_dummy_buffer(&stm->input_buffer_array);
|
||||
audiounit_make_silent(input_aud_buf);
|
||||
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");
|
||||
}
|
||||
input_buffer = input_aud_buf->mData;
|
||||
input_frames = stm->input_buffer_frames;
|
||||
assert(stm->frames_read > 0);
|
||||
// The input buffer
|
||||
input_buffer = stm->input_linear_buffer->data();
|
||||
// Number of input frames in the buffer
|
||||
input_frames = stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame;
|
||||
}
|
||||
|
||||
/* Call user callback through resampler. */
|
||||
|
@ -322,9 +390,8 @@ audiounit_output_callback(void * user_ptr,
|
|||
output_buffer,
|
||||
output_frames);
|
||||
|
||||
/* Cleanup the input buffer to make sure that we have fresh data. */
|
||||
if (input_buffer) {
|
||||
audiounit_make_silent(input_aud_buf);
|
||||
stm->input_linear_buffer->pop(nullptr, input_frames * stm->input_desc.mChannelsPerFrame);
|
||||
}
|
||||
|
||||
if (outframes < 0) {
|
||||
|
@ -358,7 +425,8 @@ audiounit_output_callback(void * user_ptr,
|
|||
return noErr;
|
||||
}
|
||||
|
||||
/*static*/ int
|
||||
extern "C" {
|
||||
int
|
||||
audiounit_init(cubeb ** context, char const * context_name)
|
||||
{
|
||||
cubeb * ctx;
|
||||
|
@ -366,8 +434,9 @@ audiounit_init(cubeb ** context, char const * context_name)
|
|||
|
||||
*context = NULL;
|
||||
|
||||
ctx = calloc(1, sizeof(*ctx));
|
||||
ctx = new cubeb;
|
||||
assert(ctx);
|
||||
PodZero(ctx, 1);
|
||||
|
||||
ctx->ops = &audiounit_ops;
|
||||
|
||||
|
@ -385,6 +454,7 @@ audiounit_init(cubeb ** context, char const * context_name)
|
|||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
}
|
||||
|
||||
static char const *
|
||||
audiounit_get_backend_id(cubeb * ctx)
|
||||
|
@ -700,7 +770,7 @@ audiounit_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
|
|||
}
|
||||
|
||||
static int
|
||||
audiounit_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms)
|
||||
audiounit_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
|
||||
{
|
||||
#if TARGET_OS_IPHONE
|
||||
//TODO: [[AVAudioSession sharedInstance] inputLatency]
|
||||
|
@ -711,7 +781,10 @@ audiounit_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * la
|
|||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
*latency_ms = (latency_range.mMinimum * 1000 + params.rate - 1) / params.rate;
|
||||
/* 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. */
|
||||
*latency_frames = std::max<int>(latency_range.mMinimum, 256);
|
||||
#endif
|
||||
|
||||
return CUBEB_OK;
|
||||
|
@ -750,7 +823,7 @@ audiounit_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
|
|||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
*rate = (uint32_t)fsamplerate;
|
||||
*rate = static_cast<uint32_t>(fsamplerate);
|
||||
#endif
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
@ -773,7 +846,7 @@ audiounit_destroy(cubeb * ctx)
|
|||
int r = pthread_mutex_destroy(&ctx->mutex);
|
||||
assert(r == 0);
|
||||
|
||||
free(ctx);
|
||||
delete ctx;
|
||||
}
|
||||
|
||||
static void audiounit_stream_destroy(cubeb_stream * stm);
|
||||
|
@ -862,7 +935,7 @@ audiounit_create_unit(AudioUnit * unit,
|
|||
devid = audiounit_get_default_device_id(is_input ? CUBEB_DEVICE_TYPE_INPUT
|
||||
: CUBEB_DEVICE_TYPE_OUTPUT);
|
||||
} else {
|
||||
devid = (AudioDeviceID)device;
|
||||
devid = reinterpret_cast<intptr_t>(device);
|
||||
}
|
||||
int err = AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_CurrentDevice,
|
||||
kAudioUnitScope_Global,
|
||||
|
@ -876,21 +949,42 @@ audiounit_create_unit(AudioUnit * unit,
|
|||
}
|
||||
|
||||
static int
|
||||
audiounit_init_input_buffer_array(cubeb_stream * stream, uint32_t capacity)
|
||||
audiounit_init_input_linear_buffer(cubeb_stream * stream, uint32_t capacity)
|
||||
{
|
||||
int r = ring_array_init(&stream->input_buffer_array,
|
||||
capacity,
|
||||
stream->input_desc.mBytesPerFrame,
|
||||
stream->input_desc.mChannelsPerFrame,
|
||||
stream->input_buffer_frames);
|
||||
if (stream->input_desc.mFormatFlags == kAudioFormatFlagIsSignedInteger) {
|
||||
stream->input_linear_buffer = new auto_array_wrapper(
|
||||
new auto_array<short>(capacity *
|
||||
stream->input_buffer_frames *
|
||||
stream->input_desc.mChannelsPerFrame) );
|
||||
} else {
|
||||
stream->input_linear_buffer = new auto_array_wrapper(
|
||||
new auto_array<float>(capacity *
|
||||
stream->input_buffer_frames *
|
||||
stream->input_desc.mChannelsPerFrame) );
|
||||
}
|
||||
|
||||
return r;
|
||||
if (!stream->input_linear_buffer) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
assert(stream->input_linear_buffer->length() == 0);
|
||||
|
||||
// Pre-buffer silence if needed
|
||||
if (capacity != 1) {
|
||||
size_t silence_size = stream->input_buffer_frames *
|
||||
stream->input_desc.mChannelsPerFrame;
|
||||
stream->input_linear_buffer->push_silence(silence_size);
|
||||
|
||||
assert(stream->input_linear_buffer->length() == silence_size);
|
||||
}
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
audiounit_destroy_input_buffer_array(cubeb_stream * stream)
|
||||
audiounit_destroy_input_linear_buffer(cubeb_stream * stream)
|
||||
{
|
||||
ring_array_destroy(&stream->input_buffer_array);
|
||||
delete stream->input_linear_buffer;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -901,7 +995,7 @@ audiounit_stream_init(cubeb * context,
|
|||
cubeb_stream_params * input_stream_params,
|
||||
cubeb_devid output_device,
|
||||
cubeb_stream_params * output_stream_params,
|
||||
unsigned int latency,
|
||||
unsigned int latency_frames,
|
||||
cubeb_data_callback data_callback,
|
||||
cubeb_state_callback state_callback,
|
||||
void * user_ptr)
|
||||
|
@ -945,8 +1039,9 @@ audiounit_stream_init(cubeb * context,
|
|||
}
|
||||
}
|
||||
|
||||
stm = calloc(1, sizeof(cubeb_stream));
|
||||
stm = new cubeb_stream;
|
||||
assert(stm);
|
||||
PodZero(stm, 1);
|
||||
|
||||
/* These could be different in the future if we have both
|
||||
* full-duplex stream and different devices for input vs output. */
|
||||
|
@ -968,29 +1063,32 @@ audiounit_stream_init(cubeb * context,
|
|||
/* Init data members where necessary */
|
||||
stm->hw_latency_frames = UINT64_MAX;
|
||||
|
||||
/* 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::min<uint32_t>(latency_frames, default_frame_count);
|
||||
|
||||
/* Setup Input Stream! */
|
||||
if (input_stream_params != NULL) {
|
||||
size = sizeof(UInt32);
|
||||
if (AudioUnitGetProperty(stm->input_unit,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
kAudioUnitScope_Input,
|
||||
AU_IN_BUS,
|
||||
&stm->input_buffer_frames,
|
||||
&size) != 0) {
|
||||
audiounit_stream_destroy(stm);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
if (AudioUnitSetProperty(stm->input_unit,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
kAudioUnitScope_Output,
|
||||
AU_IN_BUS,
|
||||
&stm->input_buffer_frames,
|
||||
size) != 0) {
|
||||
audiounit_stream_destroy(stm);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
/* Get input device sample rate. */
|
||||
AudioStreamBasicDescription input_hw_desc;
|
||||
size = sizeof(AudioStreamBasicDescription);
|
||||
|
@ -1012,6 +1110,23 @@ audiounit_stream_init(cubeb * context,
|
|||
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,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
kAudioUnitScope_Output,
|
||||
AU_IN_BUS,
|
||||
&stm->input_buffer_frames,
|
||||
sizeof(UInt32)) != 0) {
|
||||
audiounit_stream_destroy(stm);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
AudioStreamBasicDescription src_desc = stm->input_desc;
|
||||
/* Input AudioUnit must be configured with device's sample rate.
|
||||
we will resample inside input callback. */
|
||||
|
@ -1044,7 +1159,7 @@ audiounit_stream_init(cubeb * context,
|
|||
// Full-duplex increase capacity
|
||||
array_capacity = 8;
|
||||
}
|
||||
if (audiounit_init_input_buffer_array(stm, array_capacity) != CUBEB_OK) {
|
||||
if (audiounit_init_input_linear_buffer(stm, array_capacity) != CUBEB_OK) {
|
||||
audiounit_stream_destroy(stm);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
@ -1072,6 +1187,20 @@ audiounit_stream_init(cubeb * context,
|
|||
return r;
|
||||
}
|
||||
|
||||
/* Get output device sample rate. */
|
||||
AudioStreamBasicDescription output_hw_desc;
|
||||
size = sizeof(AudioStreamBasicDescription);
|
||||
memset(&output_hw_desc, 0, size);
|
||||
if (AudioUnitGetProperty(stm->output_unit,
|
||||
kAudioUnitProperty_StreamFormat,
|
||||
kAudioUnitScope_Output,
|
||||
AU_OUT_BUS,
|
||||
&output_hw_desc,
|
||||
&size) != 0) {
|
||||
audiounit_stream_destroy(stm);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
if (AudioUnitSetProperty(stm->output_unit,
|
||||
kAudioUnitProperty_StreamFormat,
|
||||
kAudioUnitScope_Input,
|
||||
|
@ -1082,6 +1211,27 @@ audiounit_stream_init(cubeb * context,
|
|||
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,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
kAudioUnitScope_Input,
|
||||
AU_OUT_BUS,
|
||||
&output_buffer_frames,
|
||||
sizeof(output_buffer_frames)) != 0) {
|
||||
audiounit_stream_destroy(stm);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
assert(stm->output_unit != NULL);
|
||||
aurcbs_out.inputProc = audiounit_output_callback;
|
||||
aurcbs_out.inputProcRefCon = stm;
|
||||
|
@ -1100,7 +1250,7 @@ audiounit_stream_init(cubeb * context,
|
|||
// Setting the latency doesn't work well for USB headsets (eg. plantronics).
|
||||
// Keep the default latency for now.
|
||||
#if 0
|
||||
buffer_size = latency / 1000.0 * ss.mSampleRate;
|
||||
buffer_size = latency;
|
||||
|
||||
/* Get the range of latency this particular device can work with, and clamp
|
||||
* the requested latency to this acceptable range. */
|
||||
|
@ -1203,7 +1353,7 @@ audiounit_stream_destroy(cubeb_stream * stm)
|
|||
AudioComponentInstanceDispose(stm->input_unit);
|
||||
}
|
||||
|
||||
audiounit_destroy_input_buffer_array(stm);
|
||||
audiounit_destroy_input_linear_buffer(stm);
|
||||
|
||||
if (stm->output_unit != NULL) {
|
||||
AudioOutputUnitStop(stm->output_unit);
|
||||
|
@ -1225,7 +1375,7 @@ audiounit_stream_destroy(cubeb_stream * stm)
|
|||
stm->context->active_streams -= 1;
|
||||
pthread_mutex_unlock(&stm->context->mutex);
|
||||
|
||||
free(stm);
|
||||
delete stm;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -1347,7 +1497,7 @@ audiounit_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
|
|||
|
||||
/* This part is fixed and depend on the stream parameter and the hardware. */
|
||||
stm->hw_latency_frames =
|
||||
(uint32_t)(unit_latency_sec * stm->output_desc.mSampleRate)
|
||||
static_cast<uint32_t>(unit_latency_sec * stm->output_desc.mSampleRate)
|
||||
+ device_latency_frames
|
||||
+ device_safety_offset;
|
||||
}
|
||||
|
@ -1419,10 +1569,11 @@ int audiounit_stream_get_current_device(cubeb_stream * stm,
|
|||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
*device = malloc(sizeof(cubeb_device));
|
||||
*device = new cubeb_device;
|
||||
if (!*device) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
PodZero(*device, 1);
|
||||
|
||||
size = sizeof(UInt32);
|
||||
/* This fails with some USB headset, so simply return an empty string. */
|
||||
|
@ -1434,12 +1585,7 @@ int audiounit_stream_get_current_device(cubeb_stream * stm,
|
|||
data = 0;
|
||||
}
|
||||
|
||||
(*device)->output_name = malloc(size + 1);
|
||||
if (!(*device)->output_name) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
(*device)->output_name = malloc(size + 1);
|
||||
(*device)->output_name = new char[size + 1];
|
||||
if (!(*device)->output_name) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
@ -1465,7 +1611,7 @@ int audiounit_stream_get_current_device(cubeb_stream * stm,
|
|||
data = 0;
|
||||
}
|
||||
|
||||
(*device)->input_name = malloc(size + 1);
|
||||
(*device)->input_name = new char[size + 1];
|
||||
if (!(*device)->input_name) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
@ -1486,9 +1632,9 @@ int audiounit_stream_get_current_device(cubeb_stream * stm,
|
|||
int audiounit_stream_device_destroy(cubeb_stream * stream,
|
||||
cubeb_device * device)
|
||||
{
|
||||
free(device->output_name);
|
||||
free(device->input_name);
|
||||
free(device);
|
||||
delete [] device->output_name;
|
||||
delete [] device->input_name;
|
||||
delete device;
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
|
@ -1528,17 +1674,17 @@ audiounit_get_devices(AudioObjectID ** devices, uint32_t * count)
|
|||
return ret;
|
||||
}
|
||||
|
||||
*count = (uint32_t)(size / sizeof(AudioObjectID));
|
||||
*count = static_cast<uint32_t>(size / sizeof(AudioObjectID));
|
||||
if (size >= sizeof(AudioObjectID)) {
|
||||
if (*devices != NULL) {
|
||||
free(*devices);
|
||||
delete [] (*devices);
|
||||
}
|
||||
*devices = malloc(size);
|
||||
memset(*devices, 0, size);
|
||||
*devices = new AudioObjectID[*count];
|
||||
PodZero(*devices, *count);
|
||||
|
||||
ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &adr, 0, NULL, &size, (void *)*devices);
|
||||
if (ret != noErr) {
|
||||
free(*devices);
|
||||
delete [] (*devices);
|
||||
*devices = NULL;
|
||||
}
|
||||
} else {
|
||||
|
@ -1560,10 +1706,10 @@ audiounit_strref_to_cstr_utf8(CFStringRef strref)
|
|||
|
||||
len = CFStringGetLength(strref);
|
||||
size = CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8);
|
||||
ret = malloc(size);
|
||||
ret = new char[size];
|
||||
|
||||
if (!CFStringGetCString(strref, ret, size, kCFStringEncodingUTF8)) {
|
||||
free(ret);
|
||||
delete [] ret;
|
||||
ret = NULL;
|
||||
}
|
||||
|
||||
|
@ -1580,7 +1726,7 @@ audiounit_get_channel_count(AudioObjectID devid, AudioObjectPropertyScope scope)
|
|||
adr.mSelector = kAudioDevicePropertyStreamConfiguration;
|
||||
|
||||
if (AudioObjectGetPropertyDataSize(devid, &adr, 0, NULL, &size) == noErr && size > 0) {
|
||||
AudioBufferList * list = alloca(size);
|
||||
AudioBufferList * list = static_cast<AudioBufferList *>(alloca(size));
|
||||
if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, list) == noErr) {
|
||||
for (i = 0; i < list->mNumberBuffers; i++)
|
||||
ret += list->mBuffers[i].mNumberChannels;
|
||||
|
@ -1611,7 +1757,7 @@ audiounit_get_available_samplerate(AudioObjectID devid, AudioObjectPropertyScope
|
|||
if (AudioObjectHasProperty(devid, &adr) &&
|
||||
AudioObjectGetPropertyDataSize(devid, &adr, 0, NULL, &size) == noErr) {
|
||||
uint32_t i, count = size / sizeof(AudioValueRange);
|
||||
AudioValueRange * ranges = malloc(size);
|
||||
AudioValueRange * ranges = new AudioValueRange[count];
|
||||
range.mMinimum = 9999999999.0;
|
||||
range.mMaximum = 0.0;
|
||||
if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, ranges) == noErr) {
|
||||
|
@ -1622,9 +1768,9 @@ audiounit_get_available_samplerate(AudioObjectID devid, AudioObjectPropertyScope
|
|||
range.mMinimum = ranges[i].mMinimum;
|
||||
}
|
||||
}
|
||||
free(ranges);
|
||||
*max = (uint32_t)range.mMaximum;
|
||||
*min = (uint32_t)range.mMinimum;
|
||||
delete [] ranges;
|
||||
*max = static_cast<uint32_t>(range.mMaximum);
|
||||
*min = static_cast<uint32_t>(range.mMinimum);
|
||||
} else {
|
||||
*min = *max = 0;
|
||||
}
|
||||
|
@ -1683,7 +1829,8 @@ audiounit_create_device_from_hwdev(AudioObjectID devid, cubeb_device_type type)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
ret = calloc(1, sizeof(cubeb_device_info));
|
||||
ret = new cubeb_device_info;
|
||||
PodZero(ret, 1);
|
||||
|
||||
size = sizeof(CFStringRef);
|
||||
adr.mSelector = kAudioDevicePropertyDeviceUID;
|
||||
|
@ -1733,7 +1880,7 @@ audiounit_create_device_from_hwdev(AudioObjectID devid, cubeb_device_type type)
|
|||
CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE;
|
||||
|
||||
ret->max_channels = ch;
|
||||
ret->format = CUBEB_DEVICE_FMT_ALL; /* CoreAudio supports All! */
|
||||
ret->format = (cubeb_device_fmt)CUBEB_DEVICE_FMT_ALL; /* CoreAudio supports All! */
|
||||
/* kAudioFormatFlagsAudioUnitCanonical is deprecated, prefer floating point */
|
||||
ret->default_format = CUBEB_DEVICE_FMT_F32NE;
|
||||
audiounit_get_available_samplerate(devid, adr.mScope,
|
||||
|
@ -1744,11 +1891,11 @@ audiounit_create_device_from_hwdev(AudioObjectID devid, cubeb_device_type type)
|
|||
adr.mSelector = kAudioDevicePropertyBufferFrameSizeRange;
|
||||
size = sizeof(AudioValueRange);
|
||||
if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &range) == noErr) {
|
||||
ret->latency_lo_ms = ((latency + range.mMinimum) * 1000) / ret->default_rate;
|
||||
ret->latency_hi_ms = ((latency + range.mMaximum) * 1000) / ret->default_rate;
|
||||
ret->latency_lo = latency + range.mMinimum;
|
||||
ret->latency_hi = latency + range.mMaximum;
|
||||
} else {
|
||||
ret->latency_lo_ms = 10; /* Default to 10ms */
|
||||
ret->latency_hi_ms = 100; /* Default to 100ms */
|
||||
ret->latency_lo = 10 * ret->default_rate / 1000; /* Default to 10ms */
|
||||
ret->latency_hi = 100 * ret->default_rate / 1000; /* Default to 100ms */
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -1766,8 +1913,8 @@ audiounit_enumerate_devices(cubeb * context, cubeb_device_type type,
|
|||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
*collection = malloc(sizeof(cubeb_device_collection) +
|
||||
sizeof(cubeb_device_info*) * (hwdevcount > 0 ? hwdevcount - 1 : 0));
|
||||
*collection = static_cast<cubeb_device_collection *>(malloc(sizeof(cubeb_device_collection) +
|
||||
sizeof(cubeb_device_info*) * (hwdevcount > 0 ? hwdevcount - 1 : 0)));
|
||||
(*collection)->count = 0;
|
||||
|
||||
if (hwdevcount > 0) {
|
||||
|
@ -1788,7 +1935,7 @@ audiounit_enumerate_devices(cubeb * context, cubeb_device_type type,
|
|||
}
|
||||
}
|
||||
|
||||
free(hwdevs);
|
||||
delete [] hwdevs;
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
@ -1825,7 +1972,7 @@ audiounit_get_devices_of_type(cubeb_device_type devtype, AudioObjectID ** devid_
|
|||
|
||||
if (devtype == (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) {
|
||||
if (devid_array) {
|
||||
*devid_array = calloc(count, sizeof(AudioObjectID));
|
||||
*devid_array = new AudioObjectID[count];
|
||||
assert(*devid_array);
|
||||
memcpy(*devid_array, &devices, count * sizeof(AudioObjectID));
|
||||
}
|
||||
|
@ -1847,7 +1994,7 @@ audiounit_get_devices_of_type(cubeb_device_type devtype, AudioObjectID ** devid_
|
|||
}
|
||||
|
||||
if (devid_array && dev_count > 0) {
|
||||
*devid_array = calloc(dev_count, sizeof(AudioObjectID));
|
||||
*devid_array = static_cast<AudioObjectID *>(calloc(dev_count, sizeof(AudioObjectID)));
|
||||
assert(*devid_array);
|
||||
memcpy(*devid_array, &devices_in_scope, dev_count * sizeof(AudioObjectID));
|
||||
}
|
||||
|
@ -1872,7 +2019,7 @@ audiounit_collection_changed_callback(AudioObjectID inObjectID,
|
|||
const AudioObjectPropertyAddress * inAddresses,
|
||||
void * inClientData)
|
||||
{
|
||||
cubeb * context = inClientData;
|
||||
cubeb * context = static_cast<cubeb *>(inClientData);
|
||||
pthread_mutex_lock(&context->mutex);
|
||||
if (context->collection_changed_callback == NULL) {
|
||||
/* Listener removed while waiting in mutex, abort. */
|
||||
|
@ -1889,14 +2036,14 @@ audiounit_collection_changed_callback(AudioObjectID inObjectID,
|
|||
if (context->devtype_device_count == new_number_of_devices &&
|
||||
audiounit_equal_arrays(devices, context->devtype_device_array, new_number_of_devices)) {
|
||||
/* Device changed for the other scope, ignore. */
|
||||
free(devices);
|
||||
delete [] devices;
|
||||
pthread_mutex_unlock(&context->mutex);
|
||||
return noErr;
|
||||
}
|
||||
/* Device on desired scope changed, reset counter and array. */
|
||||
context->devtype_device_count = new_number_of_devices;
|
||||
/* Free the old array before replace. */
|
||||
free(context->devtype_device_array);
|
||||
delete [] context->devtype_device_array;
|
||||
context->devtype_device_array = devices;
|
||||
}
|
||||
|
||||
|
@ -1957,12 +2104,12 @@ audiounit_remove_device_listener(cubeb * context)
|
|||
context);
|
||||
if (ret == noErr) {
|
||||
/* Reset all values. */
|
||||
context->collection_changed_devtype = 0;
|
||||
context->collection_changed_devtype = CUBEB_DEVICE_TYPE_UNKNOWN;
|
||||
context->collection_changed_callback = NULL;
|
||||
context->collection_changed_user_ptr = NULL;
|
||||
context->devtype_device_count = 0;
|
||||
if (context->devtype_device_array) {
|
||||
free(context->devtype_device_array);
|
||||
delete [] context->devtype_device_array;
|
||||
context->devtype_device_array = NULL;
|
||||
}
|
||||
}
|
||||
|
@ -1987,24 +2134,24 @@ int audiounit_register_device_collection_changed(cubeb * context,
|
|||
return (ret == noErr) ? CUBEB_OK : CUBEB_ERROR;
|
||||
}
|
||||
|
||||
static struct cubeb_ops const audiounit_ops = {
|
||||
.init = audiounit_init,
|
||||
.get_backend_id = audiounit_get_backend_id,
|
||||
.get_max_channel_count = audiounit_get_max_channel_count,
|
||||
.get_min_latency = audiounit_get_min_latency,
|
||||
.get_preferred_sample_rate = audiounit_get_preferred_sample_rate,
|
||||
.enumerate_devices = audiounit_enumerate_devices,
|
||||
.destroy = audiounit_destroy,
|
||||
.stream_init = audiounit_stream_init,
|
||||
.stream_destroy = audiounit_stream_destroy,
|
||||
.stream_start = audiounit_stream_start,
|
||||
.stream_stop = audiounit_stream_stop,
|
||||
.stream_get_position = audiounit_stream_get_position,
|
||||
.stream_get_latency = audiounit_stream_get_latency,
|
||||
.stream_set_volume = audiounit_stream_set_volume,
|
||||
.stream_set_panning = audiounit_stream_set_panning,
|
||||
.stream_get_current_device = audiounit_stream_get_current_device,
|
||||
.stream_device_destroy = audiounit_stream_device_destroy,
|
||||
.stream_register_device_changed_callback = audiounit_stream_register_device_changed_callback,
|
||||
.register_device_collection_changed = audiounit_register_device_collection_changed
|
||||
cubeb_ops const audiounit_ops = {
|
||||
/*.init =*/ audiounit_init,
|
||||
/*.get_backend_id =*/ audiounit_get_backend_id,
|
||||
/*.get_max_channel_count =*/ audiounit_get_max_channel_count,
|
||||
/*.get_min_latency =*/ audiounit_get_min_latency,
|
||||
/*.get_preferred_sample_rate =*/ audiounit_get_preferred_sample_rate,
|
||||
/*.enumerate_devices =*/ audiounit_enumerate_devices,
|
||||
/*.destroy =*/ audiounit_destroy,
|
||||
/*.stream_init =*/ audiounit_stream_init,
|
||||
/*.stream_destroy =*/ audiounit_stream_destroy,
|
||||
/*.stream_start =*/ audiounit_stream_start,
|
||||
/*.stream_stop =*/ audiounit_stream_stop,
|
||||
/*.stream_get_position =*/ audiounit_stream_get_position,
|
||||
/*.stream_get_latency =*/ audiounit_stream_get_latency,
|
||||
/*.stream_set_volume =*/ audiounit_stream_set_volume,
|
||||
/*.stream_set_panning =*/ audiounit_stream_set_panning,
|
||||
/*.stream_get_current_device =*/ audiounit_stream_get_current_device,
|
||||
/*.stream_device_destroy =*/ audiounit_stream_device_destroy,
|
||||
/*.stream_register_device_changed_callback =*/ audiounit_stream_register_device_changed_callback,
|
||||
/*.register_device_collection_changed =*/ audiounit_register_device_collection_changed
|
||||
};
|
|
@ -85,8 +85,8 @@ extern "C"
|
|||
}
|
||||
static char const * cbjack_get_backend_id(cubeb * context);
|
||||
static int cbjack_get_max_channel_count(cubeb * ctx, uint32_t * max_channels);
|
||||
static int cbjack_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms);
|
||||
static int cbjack_get_latency(cubeb_stream * stm, unsigned int * latency_ms);
|
||||
static int cbjack_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames);
|
||||
static int cbjack_get_latency(cubeb_stream * stm, unsigned int * latency_frames);
|
||||
static int cbjack_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate);
|
||||
static void cbjack_destroy(cubeb * context);
|
||||
static void cbjack_interleave_capture(cubeb_stream * stream, float **in, jack_nframes_t nframes, bool format_mismatch);
|
||||
|
@ -102,7 +102,7 @@ static int cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char cons
|
|||
cubeb_stream_params * input_stream_params,
|
||||
cubeb_devid output_device,
|
||||
cubeb_stream_params * output_stream_params,
|
||||
unsigned int latency,
|
||||
unsigned int latency_frames,
|
||||
cubeb_data_callback data_callback,
|
||||
cubeb_state_callback state_callback,
|
||||
void * user_ptr);
|
||||
|
@ -307,10 +307,8 @@ cbjack_graph_order_callback(void * arg)
|
|||
max_latency = 128;
|
||||
}
|
||||
|
||||
if (cbjack_get_preferred_sample_rate(ctx, &rate) == CUBEB_ERROR)
|
||||
ctx->jack_latency = (max_latency * 1000) / 48000;
|
||||
else
|
||||
ctx->jack_latency = (max_latency * 1000) / rate;
|
||||
ctx->jack_latency = max_latency;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -705,7 +703,7 @@ cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_
|
|||
cubeb_stream_params * input_stream_params,
|
||||
cubeb_devid output_device,
|
||||
cubeb_stream_params * output_stream_params,
|
||||
unsigned int latency,
|
||||
unsigned int latency_frames,
|
||||
cubeb_data_callback data_callback,
|
||||
cubeb_state_callback state_callback,
|
||||
void * user_ptr)
|
||||
|
@ -999,8 +997,8 @@ cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,
|
|||
context->devinfo[i]->min_rate = rate;
|
||||
context->devinfo[i]->max_rate = rate;
|
||||
context->devinfo[i]->default_rate = rate;
|
||||
context->devinfo[i]->latency_lo_ms = 1;
|
||||
context->devinfo[i]->latency_hi_ms = 10;
|
||||
context->devinfo[i]->latency_lo = 0;
|
||||
context->devinfo[i]->latency_hi = 0;
|
||||
i++;
|
||||
}
|
||||
|
||||
|
@ -1020,8 +1018,8 @@ cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,
|
|||
context->devinfo[i]->min_rate = rate;
|
||||
context->devinfo[i]->max_rate = rate;
|
||||
context->devinfo[i]->default_rate = rate;
|
||||
context->devinfo[i]->latency_lo_ms = 1;
|
||||
context->devinfo[i]->latency_hi_ms = 10;
|
||||
context->devinfo[i]->latency_lo = 0;
|
||||
context->devinfo[i]->latency_hi = 0;
|
||||
i++;
|
||||
}
|
||||
|
||||
|
|
|
@ -425,7 +425,7 @@ opensl_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
|
|||
}
|
||||
|
||||
static int
|
||||
opensl_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms)
|
||||
opensl_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
|
||||
{
|
||||
/* https://android.googlesource.com/platform/ndk.git/+/master/docs/opensles/index.html
|
||||
* We don't want to deal with JNI here (and we don't have Java on b2g anyways),
|
||||
|
@ -475,7 +475,7 @@ opensl_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * laten
|
|||
/* To get a fast track in Android's mixer, we need to be at the native
|
||||
* samplerate, which is device dependant. Some devices might be able to
|
||||
* resample when playing a fast track, but it's pretty rare. */
|
||||
*latency_ms = NBUFS * primary_buffer_size / (primary_sampling_rate / 1000);
|
||||
*latency_frames = NBUFS * primary_buffer_size;
|
||||
|
||||
dlclose(libmedia);
|
||||
|
||||
|
@ -502,7 +502,7 @@ opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name
|
|||
cubeb_stream_params * input_stream_params,
|
||||
cubeb_devid output_device,
|
||||
cubeb_stream_params * output_stream_params,
|
||||
unsigned int latency,
|
||||
unsigned int latency_frames,
|
||||
cubeb_data_callback data_callback, cubeb_state_callback state_callback,
|
||||
void * user_ptr)
|
||||
{
|
||||
|
@ -517,11 +517,6 @@ opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name
|
|||
|
||||
*stream = NULL;
|
||||
|
||||
if (output_stream_params->channels < 1 || output_stream_params->channels > 32 ||
|
||||
latency < 1 || latency > 2000) {
|
||||
return CUBEB_ERROR_INVALID_FORMAT;
|
||||
}
|
||||
|
||||
SLDataFormat_PCM format;
|
||||
|
||||
format.formatType = SL_DATAFORMAT_PCM;
|
||||
|
@ -554,7 +549,7 @@ opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name
|
|||
stm->user_ptr = user_ptr;
|
||||
|
||||
stm->inputrate = output_stream_params->rate;
|
||||
stm->latency = latency;
|
||||
stm->latency = latency_frames;
|
||||
stm->stream_type = output_stream_params->stream_type;
|
||||
stm->framesize = output_stream_params->channels * sizeof(int16_t);
|
||||
stm->lastPosition = -1;
|
||||
|
@ -594,11 +589,11 @@ opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name
|
|||
if (get_android_version() >= ANDROID_VERSION_MARSHMALLOW) {
|
||||
// Reset preferred samping rate to trigger fallback to native sampling rate.
|
||||
preferred_sampling_rate = 0;
|
||||
if (opensl_get_min_latency(ctx, *output_stream_params, &latency) != CUBEB_OK) {
|
||||
if (opensl_get_min_latency(ctx, *output_stream_params, &latency_frames) != CUBEB_OK) {
|
||||
// Default to AudioFlinger's advertised fast track latency of 10ms.
|
||||
latency = 10;
|
||||
latency_frames = 440;
|
||||
}
|
||||
stm->latency = latency;
|
||||
stm->latency = latency_frames;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -627,7 +622,7 @@ opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name
|
|||
|
||||
stm->outputrate = preferred_sampling_rate;
|
||||
stm->bytespersec = stm->outputrate * stm->framesize;
|
||||
stm->queuebuf_len = (stm->bytespersec * latency) / (1000 * NBUFS);
|
||||
stm->queuebuf_len = stm->framesize * latency_frames / NBUFS;
|
||||
// round up to the next multiple of stm->framesize, if needed.
|
||||
if (stm->queuebuf_len % stm->framesize) {
|
||||
stm->queuebuf_len += stm->framesize - (stm->queuebuf_len % stm->framesize);
|
||||
|
|
|
@ -11,4 +11,12 @@
|
|||
*
|
||||
* This has to be called only once per process, so it is in a separate header
|
||||
* for easy integration in other code bases. */
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void cubeb_set_coreaudio_notification_runloop();
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -582,10 +582,10 @@ pulse_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
|
|||
}
|
||||
|
||||
static int
|
||||
pulse_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms)
|
||||
pulse_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
|
||||
{
|
||||
// According to PulseAudio developers, this is a safe minimum.
|
||||
*latency_ms = 25;
|
||||
*latency_frames = 25 * params.rate / 1000;
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
@ -670,12 +670,12 @@ create_pa_stream(cubeb_stream * stm,
|
|||
}
|
||||
|
||||
static pa_buffer_attr
|
||||
set_buffering_attribute(unsigned int latency, pa_sample_spec * sample_spec)
|
||||
set_buffering_attribute(unsigned int latency_frames, pa_sample_spec * sample_spec)
|
||||
{
|
||||
pa_buffer_attr battr;
|
||||
battr.maxlength = -1;
|
||||
battr.prebuf = -1;
|
||||
battr.tlength = WRAP(pa_usec_to_bytes)(latency * PA_USEC_PER_MSEC, sample_spec);
|
||||
battr.tlength = latency_frames * WRAP(pa_frame_size)(sample_spec);
|
||||
battr.minreq = battr.tlength / 4;
|
||||
battr.fragsize = battr.minreq;
|
||||
|
||||
|
@ -693,7 +693,7 @@ pulse_stream_init(cubeb * context,
|
|||
cubeb_stream_params * input_stream_params,
|
||||
cubeb_devid output_device,
|
||||
cubeb_stream_params * output_stream_params,
|
||||
unsigned int latency,
|
||||
unsigned int latency_frames,
|
||||
cubeb_data_callback data_callback,
|
||||
cubeb_state_callback state_callback,
|
||||
void * user_ptr)
|
||||
|
@ -734,7 +734,7 @@ pulse_stream_init(cubeb * context,
|
|||
WRAP(pa_stream_set_state_callback)(stm->output_stream, stream_state_callback, stm);
|
||||
WRAP(pa_stream_set_write_callback)(stm->output_stream, stream_write_callback, stm);
|
||||
|
||||
battr = set_buffering_attribute(latency, &stm->output_sample_spec);
|
||||
battr = set_buffering_attribute(latency_frames, &stm->output_sample_spec);
|
||||
WRAP(pa_stream_connect_playback)(stm->output_stream,
|
||||
output_device,
|
||||
&battr,
|
||||
|
@ -757,7 +757,7 @@ pulse_stream_init(cubeb * context,
|
|||
WRAP(pa_stream_set_state_callback)(stm->input_stream, stream_state_callback, stm);
|
||||
WRAP(pa_stream_set_read_callback)(stm->input_stream, stream_read_callback, stm);
|
||||
|
||||
battr = set_buffering_attribute(latency, &stm->input_sample_spec);
|
||||
battr = set_buffering_attribute(latency_frames, &stm->input_sample_spec);
|
||||
WRAP(pa_stream_connect_record)(stm->input_stream,
|
||||
input_device,
|
||||
&battr,
|
||||
|
@ -1056,8 +1056,8 @@ pulse_sink_info_cb(pa_context * context, const pa_sink_info * info,
|
|||
devinfo->max_rate = PA_RATE_MAX;
|
||||
devinfo->default_rate = info->sample_spec.rate;
|
||||
|
||||
devinfo->latency_lo_ms = 25;
|
||||
devinfo->latency_hi_ms = 400;
|
||||
devinfo->latency_lo = 0;
|
||||
devinfo->latency_hi = 0;
|
||||
|
||||
pulse_ensure_dev_list_data_list_size (list_data);
|
||||
list_data->devinfo[list_data->count++] = devinfo;
|
||||
|
@ -1116,8 +1116,8 @@ pulse_source_info_cb(pa_context * context, const pa_source_info * info,
|
|||
devinfo->max_rate = PA_RATE_MAX;
|
||||
devinfo->default_rate = info->sample_spec.rate;
|
||||
|
||||
devinfo->latency_lo_ms = 1;
|
||||
devinfo->latency_hi_ms = 10;
|
||||
devinfo->latency_lo = 0;
|
||||
devinfo->latency_hi = 0;
|
||||
|
||||
pulse_ensure_dev_list_data_list_size (list_data);
|
||||
list_data->devinfo[list_data->count++] = devinfo;
|
||||
|
|
|
@ -263,7 +263,7 @@ public:
|
|||
* number of output frames will be exactly equal. */
|
||||
uint32_t input_needed_for_output(uint32_t output_frame_count)
|
||||
{
|
||||
return (uint32_t)ceilf((output_frame_count - samples_to_frames(resampling_in_buffer.length()))
|
||||
return (uint32_t)ceilf((output_frame_count - samples_to_frames(resampling_out_buffer.length()))
|
||||
* resampling_ratio);
|
||||
|
||||
}
|
||||
|
@ -332,7 +332,7 @@ private:
|
|||
/** Additional latency inserted into the pipeline for synchronisation. */
|
||||
uint32_t additional_latency;
|
||||
/** When `input_buffer` is called, this allows tracking the number of samples
|
||||
that where in the buffer. */
|
||||
that were in the buffer. */
|
||||
uint32_t leftover_samples;
|
||||
};
|
||||
|
||||
|
|
|
@ -8,9 +8,7 @@
|
|||
#ifndef CUBEB_RING_ARRAY_H
|
||||
#define CUBEB_RING_ARRAY_H
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
#include "cubeb_utils.h"
|
||||
|
||||
/** Ring array of pointers is used to hold buffers. In case that
|
||||
asynchronous producer/consumer callbacks do not arrive in a
|
||||
|
@ -34,10 +32,11 @@ single_audiobuffer_init(AudioBuffer * buffer,
|
|||
assert(bytesPerFrame > 0 && channelsPerFrame && frames > 0);
|
||||
|
||||
size_t size = bytesPerFrame * frames;
|
||||
buffer->mData = calloc(1, size);
|
||||
buffer->mData = operator new(size);
|
||||
if (buffer->mData == NULL) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
PodZero(static_cast<char*>(buffer->mData), size);
|
||||
|
||||
buffer->mNumberChannels = channelsPerFrame;
|
||||
buffer->mDataByteSize = size;
|
||||
|
@ -64,7 +63,8 @@ ring_array_init(ring_array * ra,
|
|||
ra->tail = 0;
|
||||
ra->count = 0;
|
||||
|
||||
ra->buffer_array = calloc(ra->capacity, sizeof(AudioBuffer));
|
||||
ra->buffer_array = new AudioBuffer[ra->capacity];
|
||||
PodZero(ra->buffer_array, ra->capacity);
|
||||
if (ra->buffer_array == NULL) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
@ -92,10 +92,10 @@ ring_array_destroy(ring_array * ra)
|
|||
}
|
||||
for (unsigned int i = 0; i < ra->capacity; ++i) {
|
||||
if (ra->buffer_array[i].mData) {
|
||||
free(ra->buffer_array[i].mData);
|
||||
operator delete(ra->buffer_array[i].mData);
|
||||
}
|
||||
}
|
||||
free(ra->buffer_array);
|
||||
delete [] ra->buffer_array;
|
||||
}
|
||||
|
||||
/** Get the allocated buffer to be stored with fresh data.
|
||||
|
@ -111,7 +111,7 @@ ring_array_get_free_buffer(ring_array * ra)
|
|||
}
|
||||
|
||||
assert(ra->count == 0 || (ra->tail + ra->count) % ra->capacity != ra->tail);
|
||||
void * ret = &ra->buffer_array[(ra->tail + ra->count) % ra->capacity];
|
||||
AudioBuffer * ret = &ra->buffer_array[(ra->tail + ra->count) % ra->capacity];
|
||||
|
||||
++ra->count;
|
||||
assert(ra->count <= ra->capacity);
|
||||
|
@ -131,7 +131,7 @@ ring_array_get_data_buffer(ring_array * ra)
|
|||
if (ra->count == 0) {
|
||||
return NULL;
|
||||
}
|
||||
void * ret = &ra->buffer_array[ra->tail];
|
||||
AudioBuffer * ret = &ra->buffer_array[ra->tail];
|
||||
|
||||
ra->tail = (ra->tail + 1) % ra->capacity;
|
||||
assert(ra->tail < ra->capacity);
|
||||
|
@ -156,8 +156,4 @@ ring_array_get_dummy_buffer(ring_array * ra)
|
|||
return &ra->buffer_array[0];
|
||||
}
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //CUBEB_RING_ARRAY_H
|
||||
|
|
|
@ -177,7 +177,7 @@ sndio_stream_init(cubeb * context,
|
|||
cubeb_stream_params * input_stream_params,
|
||||
cubeb_devid output_device,
|
||||
cubeb_stream_params * output_stream_params,
|
||||
unsigned int latency,
|
||||
unsigned int latency_frames,
|
||||
cubeb_data_callback data_callback,
|
||||
cubeb_state_callback state_callback,
|
||||
void *user_ptr)
|
||||
|
@ -222,7 +222,7 @@ sndio_stream_init(cubeb * context,
|
|||
}
|
||||
wpar.rate = output_stream_params->rate;
|
||||
wpar.pchan = output_stream_params->channels;
|
||||
wpar.appbufsz = latency * wpar.rate / 1000;
|
||||
wpar.appbufsz = latency_frames;
|
||||
if (!sio_setpar(s->hdl, &wpar) || !sio_getpar(s->hdl, &rpar)) {
|
||||
sio_close(s->hdl);
|
||||
free(s);
|
||||
|
@ -290,7 +290,7 @@ static int
|
|||
sndio_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms)
|
||||
{
|
||||
// XXX Not yet implemented.
|
||||
*latency_ms = 40;
|
||||
*latency = 2048;
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
|
|
@ -62,23 +62,6 @@ DEFINE_PROPERTYKEY(PKEY_Device_InstanceId, 0x78c34fc8, 0x104a, 0x4aca, 0x9e
|
|||
(sizeof(array_) / sizeof(array_[0]))
|
||||
|
||||
namespace {
|
||||
uint32_t
|
||||
ms_to_hns(uint32_t ms)
|
||||
{
|
||||
return ms * 10000;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
hns_to_ms(REFERENCE_TIME hns)
|
||||
{
|
||||
return static_cast<uint32_t>(hns / 10000);
|
||||
}
|
||||
|
||||
double
|
||||
hns_to_s(REFERENCE_TIME hns)
|
||||
{
|
||||
return static_cast<double>(hns) / 10000000;
|
||||
}
|
||||
|
||||
void
|
||||
SafeRelease(HANDLE handle)
|
||||
|
@ -240,7 +223,7 @@ struct cubeb_stream
|
|||
/* The input and output device, or NULL for default. */
|
||||
cubeb_devid input_device;
|
||||
cubeb_devid output_device;
|
||||
/* The latency initially requested for this stream. */
|
||||
/* The latency initially requested for this stream, in frames. */
|
||||
unsigned latency;
|
||||
cubeb_state_callback state_callback;
|
||||
cubeb_data_callback data_callback;
|
||||
|
@ -438,6 +421,50 @@ double stream_to_mix_samplerate_ratio(cubeb_stream_params & stream, cubeb_stream
|
|||
return double(stream.rate) / mixer.rate;
|
||||
}
|
||||
|
||||
|
||||
uint32_t
|
||||
get_rate(cubeb_stream * stm)
|
||||
{
|
||||
return has_input(stm) ? stm->input_stream_params.rate
|
||||
: stm->output_stream_params.rate;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ms_to_hns(uint32_t ms)
|
||||
{
|
||||
return ms * 10000;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
hns_to_ms(REFERENCE_TIME hns)
|
||||
{
|
||||
return static_cast<uint32_t>(hns / 10000);
|
||||
}
|
||||
|
||||
double
|
||||
hns_to_s(REFERENCE_TIME hns)
|
||||
{
|
||||
return static_cast<double>(hns) / 10000000;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
hns_to_frames(cubeb_stream * stm, REFERENCE_TIME hns)
|
||||
{
|
||||
return hns_to_ms(hns * get_rate(stm)) / 1000;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
hns_to_frames(uint32_t rate, REFERENCE_TIME hns)
|
||||
{
|
||||
return hns_to_ms(hns * rate) / 1000;
|
||||
}
|
||||
|
||||
REFERENCE_TIME
|
||||
frames_to_hns(cubeb_stream * stm, uint32_t frames)
|
||||
{
|
||||
return frames * 1000 / get_rate(stm);
|
||||
}
|
||||
|
||||
/* Upmix function, copies a mono channel into L and R */
|
||||
template<typename T>
|
||||
void
|
||||
|
@ -1244,7 +1271,7 @@ wasapi_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
|
|||
}
|
||||
|
||||
int
|
||||
wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms)
|
||||
wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
|
||||
{
|
||||
HRESULT hr;
|
||||
IAudioClient * client;
|
||||
|
@ -1287,7 +1314,8 @@ wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * laten
|
|||
/* According to the docs, the best latency we can achieve is by synchronizing
|
||||
the stream and the engine.
|
||||
http://msdn.microsoft.com/en-us/library/windows/desktop/dd370871%28v=vs.85%29.aspx */
|
||||
*latency_ms = hns_to_ms(default_period);
|
||||
|
||||
*latency_frames = hns_to_frames(params.rate, default_period);
|
||||
|
||||
SafeRelease(client);
|
||||
|
||||
|
@ -1476,7 +1504,7 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm,
|
|||
hr = (*audio_client)->Initialize(AUDCLNT_SHAREMODE_SHARED,
|
||||
AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
|
||||
AUDCLNT_STREAMFLAGS_NOPERSIST,
|
||||
ms_to_hns(stm->latency),
|
||||
frames_to_hns(stm, stm->latency),
|
||||
0,
|
||||
mix_format,
|
||||
NULL);
|
||||
|
@ -1642,7 +1670,7 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
|
|||
cubeb_stream_params * input_stream_params,
|
||||
cubeb_devid output_device,
|
||||
cubeb_stream_params * output_stream_params,
|
||||
unsigned int latency, cubeb_data_callback data_callback,
|
||||
unsigned int latency_frames, cubeb_data_callback data_callback,
|
||||
cubeb_state_callback state_callback, void * user_ptr)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
@ -1652,7 +1680,7 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
|
|||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
XASSERT(context && stream);
|
||||
XASSERT(context && stream && (input_stream_params || output_stream_params));
|
||||
|
||||
if (output_stream_params && output_stream_params->format != CUBEB_SAMPLE_FLOAT32NE ||
|
||||
input_stream_params && input_stream_params->format != CUBEB_SAMPLE_FLOAT32NE) {
|
||||
|
@ -1676,7 +1704,8 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
|
|||
stm->output_stream_params = *output_stream_params;
|
||||
stm->output_device = output_device;
|
||||
}
|
||||
stm->latency = latency;
|
||||
|
||||
stm->latency = latency_frames;
|
||||
stm->volume = 1.0;
|
||||
|
||||
stm->stream_reset_lock = new owned_critical_section();
|
||||
|
@ -1734,8 +1763,6 @@ void close_wasapi_stream(cubeb_stream * stm)
|
|||
|
||||
stm->stream_reset_lock->assert_current_thread_owns();
|
||||
|
||||
XASSERT(stm->output_client || stm->input_client);
|
||||
|
||||
SafeRelease(stm->output_client);
|
||||
stm->output_client = NULL;
|
||||
SafeRelease(stm->input_client);
|
||||
|
@ -1947,8 +1974,7 @@ int wasapi_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
|
|||
if (FAILED(hr)) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
double latency_s = hns_to_s(latency_hns);
|
||||
*latency = static_cast<uint32_t>(latency_s * stm->output_stream_params.rate);
|
||||
*latency = hns_to_frames(stm, latency_hns);
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
@ -2141,11 +2167,11 @@ wasapi_create_device(IMMDeviceEnumerator * enumerator, IMMDevice * dev)
|
|||
|
||||
if (SUCCEEDED(dev->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&client)) &&
|
||||
SUCCEEDED(client->GetDevicePeriod(&def_period, &min_period))) {
|
||||
ret->latency_lo_ms = hns_to_ms(min_period);
|
||||
ret->latency_hi_ms = hns_to_ms(def_period);
|
||||
ret->latency_lo = hns_to_frames(ret->default_rate, min_period);
|
||||
ret->latency_hi = hns_to_frames(ret->default_rate, def_period);
|
||||
} else {
|
||||
ret->latency_lo_ms = 0;
|
||||
ret->latency_hi_ms = 0;
|
||||
ret->latency_lo = 0;
|
||||
ret->latency_hi = 0;
|
||||
}
|
||||
SafeRelease(client);
|
||||
|
||||
|
|
|
@ -89,7 +89,7 @@ struct cubeb {
|
|||
PSLIST_HEADER work;
|
||||
CRITICAL_SECTION lock;
|
||||
unsigned int active_streams;
|
||||
unsigned int minimum_latency;
|
||||
unsigned int minimum_latency_ms;
|
||||
};
|
||||
|
||||
struct cubeb_stream {
|
||||
|
@ -336,7 +336,7 @@ winmm_init(cubeb ** context, char const * context_name)
|
|||
InitializeCriticalSection(&ctx->lock);
|
||||
ctx->active_streams = 0;
|
||||
|
||||
ctx->minimum_latency = calculate_minimum_latency();
|
||||
ctx->minimum_latency_ms = calculate_minimum_latency();
|
||||
|
||||
*context = ctx;
|
||||
|
||||
|
@ -384,7 +384,7 @@ winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n
|
|||
cubeb_stream_params * input_stream_params,
|
||||
cubeb_devid output_device,
|
||||
cubeb_stream_params * output_stream_params,
|
||||
unsigned int latency,
|
||||
unsigned int latency_frames,
|
||||
cubeb_data_callback data_callback,
|
||||
cubeb_state_callback state_callback,
|
||||
void * user_ptr)
|
||||
|
@ -467,11 +467,13 @@ winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n
|
|||
stm->user_ptr = user_ptr;
|
||||
stm->written = 0;
|
||||
|
||||
if (latency < context->minimum_latency) {
|
||||
latency = context->minimum_latency;
|
||||
uint32_t latency_ms = latency_frames * 1000 / output_stream_params->rate;
|
||||
|
||||
if (latency_ms < context->minimum_latency_ms) {
|
||||
latency_ms = context->minimum_latency_ms;
|
||||
}
|
||||
|
||||
bufsz = (size_t) (stm->params.rate / 1000.0 * latency * bytes_per_frame(stm->params) / NBUFS);
|
||||
bufsz = (size_t) (stm->params.rate / 1000.0 * latency_ms * bytes_per_frame(stm->params) / NBUFS);
|
||||
if (bufsz % bytes_per_frame(stm->params) != 0) {
|
||||
bufsz += bytes_per_frame(stm->params) - (bufsz % bytes_per_frame(stm->params));
|
||||
}
|
||||
|
@ -600,7 +602,7 @@ static int
|
|||
winmm_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency)
|
||||
{
|
||||
// 100ms minimum, if we are not in a bizarre configuration.
|
||||
*latency = ctx->minimum_latency;
|
||||
*latency = ctx->minimum_latency_ms * params.rate / 1000;
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
@ -854,8 +856,8 @@ winmm_create_device_from_outcaps2(LPWAVEOUTCAPS2A caps, UINT devid)
|
|||
&ret->format, &ret->default_format);
|
||||
|
||||
/* Hardcoed latency estimates... */
|
||||
ret->latency_lo_ms = 100;
|
||||
ret->latency_hi_ms = 200;
|
||||
ret->latency_lo = 100 * ret->default_rate / 1000;
|
||||
ret->latency_hi = 200 * ret->default_rate / 1000;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -885,8 +887,8 @@ winmm_create_device_from_outcaps(LPWAVEOUTCAPSA caps, UINT devid)
|
|||
&ret->format, &ret->default_format);
|
||||
|
||||
/* Hardcoed latency estimates... */
|
||||
ret->latency_lo_ms = 100;
|
||||
ret->latency_hi_ms = 200;
|
||||
ret->latency_lo = 100 * ret->default_rate / 1000;
|
||||
ret->latency_hi = 200 * ret->default_rate / 1000;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -935,8 +937,8 @@ winmm_create_device_from_incaps2(LPWAVEINCAPS2A caps, UINT devid)
|
|||
&ret->format, &ret->default_format);
|
||||
|
||||
/* Hardcoed latency estimates... */
|
||||
ret->latency_lo_ms = 100;
|
||||
ret->latency_hi_ms = 200;
|
||||
ret->latency_lo = 100 * ret->default_rate / 1000;
|
||||
ret->latency_hi = 200 * ret->default_rate / 1000;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -966,8 +968,8 @@ winmm_create_device_from_incaps(LPWAVEINCAPSA caps, UINT devid)
|
|||
&ret->format, &ret->default_format);
|
||||
|
||||
/* Hardcoed latency estimates... */
|
||||
ret->latency_lo_ms = 100;
|
||||
ret->latency_hi_ms = 200;
|
||||
ret->latency_lo = 100 * ret->default_rate / 1000;
|
||||
ret->latency_hi = 200 * ret->default_rate / 1000;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -161,7 +161,7 @@ int run_test(int num_channels, int sampling_rate, int is_float)
|
|||
}
|
||||
|
||||
r = cubeb_stream_init(ctx, &stream, "test tone", NULL, NULL, NULL, ¶ms,
|
||||
100, is_float ? data_cb_float : data_cb_short, state_cb, synth);
|
||||
4096, is_float ? data_cb_float : data_cb_short, state_cb, synth);
|
||||
if (r != CUBEB_OK) {
|
||||
fprintf(stderr, "Error initializing cubeb stream: %d\n", r);
|
||||
goto cleanup;
|
||||
|
@ -213,7 +213,8 @@ int run_panning_volume_test(int is_float)
|
|||
}
|
||||
|
||||
r = cubeb_stream_init(ctx, &stream, "test tone", NULL, NULL, NULL, ¶ms,
|
||||
100, is_float ? data_cb_float : data_cb_short, state_cb, synth);
|
||||
4096, is_float ? data_cb_float : data_cb_short,
|
||||
state_cb, synth);
|
||||
if (r != CUBEB_OK) {
|
||||
fprintf(stderr, "Error initializing cubeb stream: %d\n", r);
|
||||
goto cleanup;
|
||||
|
|
|
@ -101,7 +101,7 @@ int main(int argc, char *argv[])
|
|||
cubeb_stream_params output_params;
|
||||
int r;
|
||||
user_state stream_state = { false };
|
||||
uint32_t latency_ms = 0;
|
||||
uint32_t latency_frames = 0;
|
||||
|
||||
r = cubeb_init(&ctx, "Cubeb duplex example");
|
||||
if (r != CUBEB_OK) {
|
||||
|
@ -123,7 +123,7 @@ int main(int argc, char *argv[])
|
|||
output_params.rate = 48000;
|
||||
output_params.channels = 2;
|
||||
|
||||
r = cubeb_get_min_latency(ctx, output_params, &latency_ms);
|
||||
r = cubeb_get_min_latency(ctx, output_params, &latency_frames);
|
||||
|
||||
if (r != CUBEB_OK) {
|
||||
fprintf(stderr, "Could not get minimal latency\n");
|
||||
|
@ -132,7 +132,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
r = cubeb_stream_init(ctx, &stream, "Cubeb duplex",
|
||||
NULL, &input_params, NULL, &output_params,
|
||||
latency_ms, data_cb, state_cb, &stream_state);
|
||||
latency_frames, data_cb, state_cb, &stream_state);
|
||||
if (r != CUBEB_OK) {
|
||||
fprintf(stderr, "Error initializing cubeb stream\n");
|
||||
return r;
|
||||
|
|
|
@ -21,7 +21,7 @@ int main(int argc, char * argv[])
|
|||
int r;
|
||||
uint32_t max_channels;
|
||||
uint32_t preferred_rate;
|
||||
uint32_t latency_ms;
|
||||
uint32_t latency_frames;
|
||||
|
||||
LOG("latency_test start");
|
||||
r = cubeb_init(&ctx, "Cubeb audio test");
|
||||
|
@ -47,10 +47,10 @@ int main(int argc, char * argv[])
|
|||
preferred_rate,
|
||||
max_channels
|
||||
};
|
||||
r = cubeb_get_min_latency(ctx, params, &latency_ms);
|
||||
r = cubeb_get_min_latency(ctx, params, &latency_frames);
|
||||
assert(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED);
|
||||
if (r == CUBEB_OK) {
|
||||
assert(latency_ms > 0 && "Invalid minimal latency.");
|
||||
assert(latency_frames > 0 && "Invalid minimal latency.");
|
||||
LOG("cubeb_get_min_latency ok");
|
||||
}
|
||||
|
||||
|
|
|
@ -106,7 +106,7 @@ int main(int argc, char *argv[])
|
|||
params.channels = 1;
|
||||
|
||||
r = cubeb_stream_init(ctx, &stream, "Cubeb record (mono)", NULL, ¶ms, NULL, nullptr,
|
||||
250, data_cb, state_cb, &stream_state);
|
||||
4096, data_cb, state_cb, &stream_state);
|
||||
if (r != CUBEB_OK) {
|
||||
fprintf(stderr, "Error initializing cubeb stream\n");
|
||||
return r;
|
||||
|
|
|
@ -26,8 +26,8 @@
|
|||
#define BEGIN_TEST fprintf(stderr, "START %s\n", __func__)
|
||||
#define END_TEST fprintf(stderr, "END %s\n", __func__)
|
||||
|
||||
#define STREAM_LATENCY 100
|
||||
#define STREAM_RATE 44100
|
||||
#define STREAM_LATENCY 100 * STREAM_RATE / 1000
|
||||
#define STREAM_CHANNELS 1
|
||||
#if (defined(_WIN32) || defined(__WIN32__))
|
||||
#define STREAM_FORMAT CUBEB_SAMPLE_FLOAT32LE
|
||||
|
|
|
@ -128,7 +128,7 @@ int main(int argc, char *argv[])
|
|||
user_data->position = 0;
|
||||
|
||||
r = cubeb_stream_init(ctx, &stream, "Cubeb tone (mono)", NULL, NULL, NULL, ¶ms,
|
||||
250, data_cb, state_cb, user_data);
|
||||
4096, data_cb, state_cb, user_data);
|
||||
if (r != CUBEB_OK) {
|
||||
fprintf(stderr, "Error initializing cubeb stream\n");
|
||||
return r;
|
||||
|
|
Загрузка…
Ссылка в новой задаче