Large diffs are not rendered by default.

@@ -96,6 +96,8 @@ static int cbjack_stream_device_destroy(cubeb_stream * stream,
static int cbjack_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const device);
static int cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,
cubeb_device_collection * collection);
static int cbjack_device_collection_destroy(cubeb * context,
cubeb_device_collection * collection);
static int cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
@@ -119,12 +121,13 @@ static struct cubeb_ops const cbjack_ops = {
.get_preferred_sample_rate = cbjack_get_preferred_sample_rate,
.get_preferred_channel_layout = NULL,
.enumerate_devices = cbjack_enumerate_devices,
.device_collection_destroy = cubeb_utils_default_device_collection_destroy,
.device_collection_destroy = cbjack_device_collection_destroy,
.destroy = cbjack_destroy,
.stream_init = cbjack_stream_init,
.stream_destroy = cbjack_stream_destroy,
.stream_start = cbjack_stream_start,
.stream_stop = cbjack_stream_stop,
.stream_reset_default_device = NULL,
.stream_get_position = cbjack_stream_get_position,
.stream_get_latency = cbjack_get_latency,
.stream_set_volume = cbjack_stream_set_volume,
@@ -973,6 +976,9 @@ cbjack_stream_device_destroy(cubeb_stream * /*stream*/,
return CUBEB_OK;
}

#define JACK_DEFAULT_IN "JACK capture"
#define JACK_DEFAULT_OUT "JACK playback"

static int
cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,
cubeb_device_collection * collection)
@@ -982,20 +988,20 @@ cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,

uint32_t rate;
cbjack_get_preferred_sample_rate(context, &rate);
const char * j_in = "JACK capture";
const char * j_out = "JACK playback";

cubeb_device_info * devices = new cubeb_device_info[2];
reinterpret_cast<cubeb_device_info *>(calloc(2, sizeof(cubeb_device_info)));
if (!devices)
return CUBEB_ERROR;
PodZero(devices, 2);
collection->count = 0;

if (type & CUBEB_DEVICE_TYPE_OUTPUT) {
cubeb_device_info * cur = &devices[collection->count];
cur->device_id = strdup(j_out);
cur->device_id = JACK_DEFAULT_OUT;
cur->devid = (cubeb_devid) cur->device_id;
cur->friendly_name = strdup(j_out);
cur->group_id = strdup(j_out);
cur->vendor_name = strdup(j_out);
cur->friendly_name = JACK_DEFAULT_OUT;
cur->group_id = JACK_DEFAULT_OUT;
cur->vendor_name = JACK_DEFAULT_OUT;
cur->type = CUBEB_DEVICE_TYPE_OUTPUT;
cur->state = CUBEB_DEVICE_STATE_ENABLED;
cur->preferred = CUBEB_DEVICE_PREF_ALL;
@@ -1012,11 +1018,11 @@ cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,

if (type & CUBEB_DEVICE_TYPE_INPUT) {
cubeb_device_info * cur = &devices[collection->count];
cur->device_id = strdup(j_in);
cur->device_id = JACK_DEFAULT_IN;
cur->devid = (cubeb_devid) cur->device_id;
cur->friendly_name = strdup(j_in);
cur->group_id = strdup(j_in);
cur->vendor_name = strdup(j_in);
cur->friendly_name = JACK_DEFAULT_IN;
cur->group_id = JACK_DEFAULT_IN;
cur->vendor_name = JACK_DEFAULT_IN;
cur->type = CUBEB_DEVICE_TYPE_INPUT;
cur->state = CUBEB_DEVICE_STATE_ENABLED;
cur->preferred = CUBEB_DEVICE_PREF_ALL;
@@ -1035,3 +1041,12 @@ cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,

return CUBEB_OK;
}

static int
cbjack_device_collection_destroy(cubeb * /*ctx*/,
cubeb_device_collection * collection)
{
XASSERT(collection);
delete [] collection->device;
return CUBEB_OK;
}
@@ -351,6 +351,7 @@ static struct cubeb_ops const kai_ops = {
/*.stream_destroy =*/ kai_stream_destroy,
/*.stream_start =*/ kai_stream_start,
/*.stream_stop =*/ kai_stream_stop,
/*.stream_reset_default_device =*/ NULL,
/*.stream_get_position =*/ kai_stream_get_position,
/*.stream_get_latency = */ kai_stream_get_latency,
/*.stream_set_volume =*/ kai_stream_set_volume,
@@ -95,6 +95,12 @@ class cubeb_async_logger
}
}).detach();
}
// Tell the underlying queue the producer thread has changed, so it does not
// assert in debug. This should be called with the thread stopped.
void reset_producer_thread()
{
msg_queue.reset_thread_ids();
}
private:
#ifndef _WIN32
const struct timespec sleep_for = {
@@ -128,3 +134,11 @@ void cubeb_async_log(char const * fmt, ...)
cubeb_async_logger::get().push(msg);
va_end(args);
}

void cubeb_async_log_reset_threads()
{
if (!g_cubeb_log_callback) {
return;
}
cubeb_async_logger::get().reset_producer_thread();
}
@@ -23,6 +23,7 @@ extern "C" {
extern cubeb_log_level g_cubeb_log_level;
extern cubeb_log_callback g_cubeb_log_callback PRINTF_FORMAT(1, 2);
void cubeb_async_log(const char * fmt, ...);
void cubeb_async_log_reset_threads();

#ifdef __cplusplus
}
@@ -299,7 +299,7 @@ mix_remap(long inframes,
T * out, unsigned long out_len,
cubeb_channel_layout in_layout, cubeb_channel_layout out_layout)
{
assert(in_layout != out_layout);
assert(in_layout != out_layout && inframes >= 0);

// We might overwrite the data before we copied them to the mapped index
// (e.g. upmixing from stereo to 2F1 or mapping [L, R] to [R, L])
@@ -325,15 +325,16 @@ mix_remap(long inframes,
return false;
}

for (unsigned long i = 0, out_index = 0; i < inframes * in_channels; i += in_channels, out_index += out_channels) {
for (unsigned long i = 0, out_index = 0; i < (unsigned long)inframes * in_channels; i += in_channels, out_index += out_channels) {
for (unsigned int j = 0; j < out_channels; ++j) {
cubeb_channel channel = CHANNEL_INDEX_TO_ORDER[out_layout][j];
uint32_t channel_mask = 1 << channel;
int channel_index = CHANNEL_ORDER_TO_INDEX[in_layout][channel];
assert(channel_index >= -1);
assert(out_index + j < out_len);
if (in_layout_mask & channel_mask) {
assert(i + channel_index < in_len);
assert(channel_index != -1);
assert(i + channel_index < in_len);
out[out_index + j] = in[i + channel_index];
} else {
assert(channel_index == -1);
@@ -353,13 +354,13 @@ downmix_fallback(long inframes,
T * out, unsigned long out_len,
unsigned int in_channels, unsigned int out_channels)
{
assert(in_channels >= out_channels);
assert(in_channels >= out_channels && inframes >= 0);

if (in_channels == out_channels && in == out) {
return;
}

for (unsigned long i = 0, out_index = 0; i < inframes * in_channels; i += in_channels, out_index += out_channels) {
for (unsigned long i = 0, out_index = 0; i < (unsigned long)inframes * in_channels; i += in_channels, out_index += out_channels) {
for (unsigned int j = 0; j < out_channels; ++j) {
assert(i + j < in_len && out_index + j < out_len);
out[out_index + j] = in[i + j];
@@ -416,7 +417,7 @@ mono_to_stereo(long insamples, T const * in, unsigned long in_len,
T * out, unsigned long out_len, unsigned int out_channels)
{
for (long i = 0, j = 0; i < insamples; ++i, j += out_channels) {
assert(i < in_len && j + 1 < out_len);
assert((unsigned long)i < in_len && (unsigned long)j + 1 < out_len);
out[j] = out[j + 1] = in[i];
}
}
@@ -460,7 +461,7 @@ cubeb_upmix(long inframes,
/* Put silence in remaining channels. */
for (long i = 0, o = 0; i < inframes; ++i, o += out_channels) {
for (unsigned int j = 2; j < out_channels; ++j) {
assert(o + j < out_len);
assert((unsigned long)o + j < out_len);
out[o + j] = 0.0;
}
}
@@ -490,7 +491,8 @@ cubeb_should_downmix(cubeb_stream_params const * stream, cubeb_stream_params con
bool
cubeb_should_mix(cubeb_stream_params const * stream, cubeb_stream_params const * mixer)
{
return cubeb_should_upmix(stream, mixer) || cubeb_should_downmix(stream, mixer);
return stream->layout != CUBEB_LAYOUT_UNDEFINED &&
(cubeb_should_upmix(stream, mixer) || cubeb_should_downmix(stream, mixer));
}

struct cubeb_mixer {
@@ -559,11 +561,11 @@ void cubeb_mixer_destroy(cubeb_mixer * mixer)

void cubeb_mixer_mix(cubeb_mixer * mixer, long frames,
void * input_buffer, unsigned long input_buffer_length,
void * output_buffer, unsigned long outputput_buffer_length,
void * output_buffer, unsigned long output_buffer_length,
cubeb_stream_params const * stream_params,
cubeb_stream_params const * mixer_params)
{
assert(mixer);
mixer->mix(frames, input_buffer, input_buffer_length, output_buffer, outputput_buffer_length,
mixer->mix(frames, input_buffer, input_buffer_length, output_buffer, output_buffer_length,
stream_params, mixer_params);
}
@@ -79,7 +79,7 @@ cubeb_mixer * cubeb_mixer_create(cubeb_sample_format format,
void cubeb_mixer_destroy(cubeb_mixer * mixer);
void cubeb_mixer_mix(cubeb_mixer * mixer, long frames,
void * input_buffer, unsigned long input_buffer_length,
void * output_buffer, unsigned long outputput_buffer_length,
void * output_buffer, unsigned long output_buffer_length,
cubeb_stream_params const * stream_params,
cubeb_stream_params const * mixer_params);

@@ -105,7 +105,6 @@ struct cubeb_stream {
/* Flag indicating draining. Synchronized
* by stream::mutex lock. */
int draining;
cubeb_stream_type stream_type;
/* Flags to determine in/out.*/
uint32_t input_enabled;
uint32_t output_enabled;
@@ -187,6 +186,29 @@ opensl_set_draining(cubeb_stream * stm, int value)
stm->draining = value;
}

static void
opensl_notify_drained(cubeb_stream * stm)
{
assert(stm);
int r = pthread_mutex_lock(&stm->mutex);
assert(r == 0);
int draining = opensl_get_draining(stm);
r = pthread_mutex_unlock(&stm->mutex);
assert(r == 0);
if (draining) {
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
if (stm->play) {
LOG("stop player in play_callback");
r = opensl_stop_player(stm);
assert(r == CUBEB_OK);
}
if (stm->recorderItf) {
r = opensl_stop_recorder(stm);
assert(r == CUBEB_OK);
}
}
}

static uint32_t
opensl_get_shutdown(cubeb_stream * stm)
{
@@ -217,24 +239,7 @@ play_callback(SLPlayItf caller, void * user_ptr, SLuint32 event)
assert(stm);
switch (event) {
case SL_PLAYEVENT_HEADATMARKER:
{
int r = pthread_mutex_lock(&stm->mutex);
assert(r == 0);
draining = opensl_get_draining(stm);
r = pthread_mutex_unlock(&stm->mutex);
assert(r == 0);
if (draining) {
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
if (stm->play) {
r = opensl_stop_player(stm);
assert(r == CUBEB_OK);
}
if (stm->recorderItf) {
r = opensl_stop_recorder(stm);
assert(r == CUBEB_OK);
}
}
}
opensl_notify_drained(stm);
break;
default:
break;
@@ -330,9 +335,16 @@ bufferqueue_callback(SLBufferQueueItf caller, void * user_ptr)
opensl_set_draining(stm, 1);
r = pthread_mutex_unlock(&stm->mutex);
assert(r == 0);
// Use SL_PLAYEVENT_HEADATMARKER event from slPlayCallback of SLPlayItf
// to make sure all the data has been processed.
(*stm->play)->SetMarkerPosition(stm->play, (SLmillisecond)written_duration);

if (written_duration == 0) {
// since we didn't write any sample, it's not possible to reach the marker
// time and trigger the callback. We should initiative notify drained.
opensl_notify_drained(stm);
} else {
// Use SL_PLAYEVENT_HEADATMARKER event from slPlayCallback of SLPlayItf
// to make sure all the data has been processed.
(*stm->play)->SetMarkerPosition(stm->play, (SLmillisecond)written_duration);
}
return;
}
}
@@ -586,31 +598,6 @@ player_fullduplex_callback(SLBufferQueueItf caller, void * user_ptr)
TIMESTAMP("EXIT");
}

#if defined(__ANDROID__)
static SLuint32
convert_stream_type_to_sl_stream(cubeb_stream_type stream_type)
{
switch(stream_type) {
case CUBEB_STREAM_TYPE_SYSTEM:
return SL_ANDROID_STREAM_SYSTEM;
case CUBEB_STREAM_TYPE_MUSIC:
return SL_ANDROID_STREAM_MEDIA;
case CUBEB_STREAM_TYPE_NOTIFICATION:
return SL_ANDROID_STREAM_NOTIFICATION;
case CUBEB_STREAM_TYPE_ALARM:
return SL_ANDROID_STREAM_ALARM;
case CUBEB_STREAM_TYPE_VOICE_CALL:
return SL_ANDROID_STREAM_VOICE;
case CUBEB_STREAM_TYPE_RING:
return SL_ANDROID_STREAM_RING;
case CUBEB_STREAM_TYPE_SYSTEM_ENFORCED:
return SL_ANDROID_STREAM_SYSTEM_ENFORCED;
default:
return 0xFFFFFFFF;
}
}
#endif

static void opensl_destroy(cubeb * ctx);

#if defined(__ANDROID__)
@@ -899,7 +886,7 @@ opensl_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * laten
if (get_primary_output_frame_count) {
primary_buffer_size = get_primary_output_frame_count();
} else {
if (get_output_frame_count(&primary_buffer_size, params.stream_type) != 0) {
if (get_output_frame_count(&primary_buffer_size, AUDIO_STREAM_TYPE_MUSIC) != 0) {
return CUBEB_ERROR;
}
}
@@ -1127,7 +1114,6 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
assert(params);

stm->inputrate = params->rate;
stm->stream_type = params->stream_type;
stm->framesize = params->channels * sizeof(int16_t);
stm->lastPosition = -1;
stm->lastPositionTimeStamp = 0;
@@ -1228,29 +1214,6 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
assert(stm->queuebuf[i]);
}

#if defined(__ANDROID__)
SLuint32 stream_type = convert_stream_type_to_sl_stream(params->stream_type);
if (stream_type != 0xFFFFFFFF) {
SLAndroidConfigurationItf playerConfig;
res = (*stm->playerObj)->GetInterface(stm->playerObj,
stm->context->SL_IID_ANDROIDCONFIGURATION,
&playerConfig);
if (res != SL_RESULT_SUCCESS) {
LOG("Failed to get android configuration interface. Error code: %lu", res);
return CUBEB_ERROR;
}

res = (*playerConfig)->SetConfiguration(playerConfig,
SL_ANDROID_KEY_STREAM_TYPE,
&stream_type,
sizeof(SLint32));
if (res != SL_RESULT_SUCCESS) {
LOG("Failed to set android configuration interface. Error code: %lu", res);
return CUBEB_ERROR;
}
}
#endif

res = (*stm->playerObj)->Realize(stm->playerObj, SL_BOOLEAN_FALSE);
if (res != SL_RESULT_SUCCESS) {
LOG("Failed to realize player object. Error code: %lu", res);
@@ -1654,7 +1617,7 @@ opensl_stream_get_position(cubeb_stream * stm, uint64_t * position)

samplerate = stm->inputrate;

r = stm->context->get_output_latency(&mixer_latency, stm->stream_type);
r = stm->context->get_output_latency(&mixer_latency, AUDIO_STREAM_TYPE_MUSIC);
if (r) {
return CUBEB_ERROR;
}
@@ -1690,7 +1653,7 @@ opensl_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
uint32_t mixer_latency; // The latency returned by AudioFlinger is in ms.

/* audio_stream_type_t is an int, so this is okay. */
r = stm->context->get_output_latency(&mixer_latency, stm->stream_type);
r = stm->context->get_output_latency(&mixer_latency, AUDIO_STREAM_TYPE_MUSIC);
if (r) {
return CUBEB_ERROR;
}
@@ -1746,6 +1709,7 @@ static struct cubeb_ops const opensl_ops = {
.stream_destroy = opensl_stream_destroy,
.stream_start = opensl_stream_start,
.stream_stop = opensl_stream_stop,
.stream_reset_default_device = NULL,
.stream_get_position = opensl_stream_get_position,
.stream_get_latency = opensl_stream_get_latency,
.stream_set_volume = opensl_stream_set_volume,
@@ -13,7 +13,7 @@
#include "cubeb/cubeb.h"
#include "cubeb-internal.h"
#include "cubeb_mixer.h"
#include "cubeb_utils.h"
#include "cubeb_strings.h"
#include <stdio.h>

#ifdef DISABLE_LIBPULSE_DLOPEN
@@ -102,6 +102,7 @@ struct cubeb {
int error;
cubeb_device_collection_changed_callback collection_changed_callback;
void * collection_changed_user_ptr;
cubeb_strings * device_ids;
};

struct cubeb_stream {
@@ -127,6 +128,24 @@ enum cork_state {
NOTIFY = 1 << 1
};

static int
intern_device_id(cubeb * ctx, char const ** id)
{
char const * interned;

assert(ctx);
assert(id);

interned = cubeb_strings_intern(ctx->device_ids, *id);
if (!interned) {
return CUBEB_ERROR;
}

*id = interned;

return CUBEB_OK;
}

static void
sink_info_callback(pa_context * context, const pa_sink_info * info, int eol, void * u)
{
@@ -608,6 +627,10 @@ pulse_init(cubeb ** context, char const * context_name)

ctx->ops = &pulse_ops;
ctx->libpulse = libpulse;
if (cubeb_strings_init(&ctx->device_ids) != CUBEB_OK) {
pulse_destroy(ctx);
return CUBEB_ERROR;
}

ctx->mainloop = WRAP(pa_threaded_mainloop_new)();
ctx->default_sink_info = NULL;
@@ -630,7 +653,6 @@ pulse_init(cubeb ** context, char const * context_name)
WRAP(pa_operation_unref)(o);
}
WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop);
assert(ctx->default_sink_info);

*context = ctx;

@@ -650,6 +672,9 @@ pulse_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
(void)ctx;
assert(ctx && max_channels);

if (!ctx->default_sink_info)
return CUBEB_ERROR;

*max_channels = ctx->default_sink_info->channel_map.channels;

return CUBEB_OK;
@@ -661,6 +686,9 @@ pulse_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
assert(ctx && rate);
(void)ctx;

if (!ctx->default_sink_info)
return CUBEB_ERROR;

*rate = ctx->default_sink_info->sample_spec.rate;

return CUBEB_OK;
@@ -672,6 +700,9 @@ pulse_get_preferred_channel_layout(cubeb * ctx, cubeb_channel_layout * layout)
assert(ctx && layout);
(void)ctx;

if (!ctx->default_sink_info)
return CUBEB_ERROR;

*layout = channel_map_to_layout(&ctx->default_sink_info->channel_map);

return CUBEB_OK;
@@ -717,6 +748,10 @@ pulse_destroy(cubeb * ctx)
WRAP(pa_threaded_mainloop_free)(ctx->mainloop);
}

if (ctx->device_ids) {
cubeb_strings_destroy(ctx->device_ids);
}

if (ctx->libpulse) {
dlclose(ctx->libpulse);
}
@@ -751,8 +786,9 @@ create_pa_stream(cubeb_stream * stm,
{
assert(stm && stream_params);
assert(&stm->input_stream == pa_stm || (&stm->output_stream == pa_stm &&
stream_params->layout != CUBEB_LAYOUT_UNDEFINED &&
CUBEB_CHANNEL_LAYOUT_MAPS[stream_params->layout].channels == stream_params->channels));
(stream_params->layout == CUBEB_LAYOUT_UNDEFINED ||
(stream_params->layout != CUBEB_LAYOUT_UNDEFINED &&
CUBEB_CHANNEL_LAYOUT_MAPS[stream_params->layout].channels == stream_params->channels))));
*pa_stm = NULL;
pa_sample_spec ss;
ss.format = to_pulse_format(stream_params->format);
@@ -1049,6 +1085,7 @@ pulse_stream_set_volume(cubeb_stream * stm, float volume)
pa_volume_t vol;
pa_cvolume cvol;
const pa_sample_spec * ss;
cubeb * ctx;

if (!stm->output_stream) {
return CUBEB_ERROR;
@@ -1058,7 +1095,9 @@ pulse_stream_set_volume(cubeb_stream * stm, float volume)

/* if the pulse daemon is configured to use flat volumes,
* apply our own gain instead of changing the input volume on the sink. */
if (stm->context->default_sink_info->flags & PA_SINK_FLAT_VOLUME) {
ctx = stm->context;
if (ctx->default_sink_info &&
(ctx->default_sink_info->flags & PA_SINK_FLAT_VOLUME)) {
stm->volume = volume;
} else {
ss = WRAP(pa_stream_get_sample_spec)(stm->output_stream);
@@ -1068,16 +1107,16 @@ pulse_stream_set_volume(cubeb_stream * stm, float volume)

index = WRAP(pa_stream_get_index)(stm->output_stream);

op = WRAP(pa_context_set_sink_input_volume)(stm->context->context,
op = WRAP(pa_context_set_sink_input_volume)(ctx->context,
index, &cvol, volume_success,
stm);
if (op) {
operation_wait(stm->context, stm->output_stream, op);
operation_wait(ctx, stm->output_stream, op);
WRAP(pa_operation_unref)(op);
}
}

WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop);

return CUBEB_OK;
}
@@ -1202,18 +1241,30 @@ pulse_sink_info_cb(pa_context * context, const pa_sink_info * info,
{
pulse_dev_list_data * list_data = user_data;
cubeb_device_info * devinfo;
const char * prop;
char const * prop = NULL;
char const * device_id = NULL;

(void)context;

if (eol || info == NULL)
if (eol) {
WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0);
return;
}

if (info == NULL)
return;

device_id = info->name;
if (intern_device_id(list_data->context, &device_id) != CUBEB_OK) {
assert(false);
return;
}

pulse_ensure_dev_list_data_list_size(list_data);
devinfo = &list_data->devinfo[list_data->count];
memset(devinfo, 0, sizeof(cubeb_device_info));

devinfo->device_id = strdup(info->name);
devinfo->device_id = device_id;
devinfo->devid = (cubeb_devid) devinfo->device_id;
devinfo->friendly_name = strdup(info->description);
prop = WRAP(pa_proplist_gets)(info->proplist, "sysfs.path");
@@ -1239,8 +1290,6 @@ pulse_sink_info_cb(pa_context * context, const pa_sink_info * info,
devinfo->latency_hi = 0;

list_data->count += 1;

WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0);
}

static cubeb_device_state
@@ -1264,18 +1313,27 @@ pulse_source_info_cb(pa_context * context, const pa_source_info * info,
{
pulse_dev_list_data * list_data = user_data;
cubeb_device_info * devinfo;
const char * prop;
char const * prop = NULL;
char const * device_id = NULL;

(void)context;

if (eol)
if (eol) {
WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0);
return;
}

device_id = info->name;
if (intern_device_id(list_data->context, &device_id) != CUBEB_OK) {
assert(false);
return;
}

pulse_ensure_dev_list_data_list_size(list_data);
devinfo = &list_data->devinfo[list_data->count];
memset(devinfo, 0, sizeof(cubeb_device_info));

devinfo->device_id = strdup(info->name);
devinfo->device_id = device_id;
devinfo->devid = (cubeb_devid) devinfo->device_id;
devinfo->friendly_name = strdup(info->description);
prop = WRAP(pa_proplist_gets)(info->proplist, "sysfs.path");
@@ -1301,7 +1359,6 @@ pulse_source_info_cb(pa_context * context, const pa_source_info * info,
devinfo->latency_hi = 0;

list_data->count += 1;
WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0);
}

static void
@@ -1313,8 +1370,10 @@ pulse_server_info_cb(pa_context * c, const pa_server_info * i, void * userdata)

free(list_data->default_sink_name);
free(list_data->default_source_name);
list_data->default_sink_name = strdup(i->default_sink_name);
list_data->default_source_name = strdup(i->default_source_name);
list_data->default_sink_name =
i->default_sink_name ? strdup(i->default_sink_name) : NULL;
list_data->default_source_name =
i->default_source_name ? strdup(i->default_source_name) : NULL;

WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0);
}
@@ -1363,6 +1422,21 @@ pulse_enumerate_devices(cubeb * context, cubeb_device_type type,
return CUBEB_OK;
}

static int
pulse_device_collection_destroy(cubeb * ctx, cubeb_device_collection * collection)
{
size_t n;

for (n = 0; n < collection->count; n++) {
free((void *) collection->device[n].friendly_name);
free((void *) collection->device[n].vendor_name);
free((void *) collection->device[n].group_id);
}

free(collection->device);
return CUBEB_OK;
}

static int
pulse_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const device)
{
@@ -1492,12 +1566,13 @@ static struct cubeb_ops const pulse_ops = {
.get_preferred_sample_rate = pulse_get_preferred_sample_rate,
.get_preferred_channel_layout = pulse_get_preferred_channel_layout,
.enumerate_devices = pulse_enumerate_devices,
.device_collection_destroy = cubeb_utils_default_device_collection_destroy,
.device_collection_destroy = pulse_device_collection_destroy,
.destroy = pulse_destroy,
.stream_init = pulse_stream_init,
.stream_destroy = pulse_stream_destroy,
.stream_start = pulse_stream_start,
.stream_stop = pulse_stream_stop,
.stream_reset_default_device = NULL,
.stream_get_position = pulse_stream_get_position,
.stream_get_latency = pulse_stream_get_latency,
.stream_set_volume = pulse_stream_set_volume,
@@ -35,15 +35,22 @@ to_speex_quality(cubeb_resampler_quality q)
}
}

uint32_t min_buffered_audio_frame(uint32_t sample_rate)
{
return sample_rate / 20;
}

template<typename T>
passthrough_resampler<T>::passthrough_resampler(cubeb_stream * s,
cubeb_data_callback cb,
void * ptr,
uint32_t input_channels)
uint32_t input_channels,
uint32_t sample_rate)
: processor(input_channels)
, stream(s)
, data_callback(cb)
, user_ptr(ptr)
, sample_rate(sample_rate)
{
}

@@ -73,6 +80,7 @@ long passthrough_resampler<T>::fill(void * input_buffer, long * input_frames_cou
if (input_buffer) {
internal_input_buffer.pop(nullptr, frames_to_samples(output_frames));
*input_frames_count = output_frames;
drop_audio_if_needed();
}

return rv;
@@ -242,9 +250,15 @@ ::fill_internal_duplex(T * in_buffer, long * input_frames_count,

output_processor->written(got);

input_processor->drop_audio_if_needed();

/* Process the output. If not enough frames have been returned from the
* callback, drain the processors. */
return output_processor->output(out_buffer, output_frames_needed);
got = output_processor->output(out_buffer, output_frames_needed);

output_processor->drop_audio_if_needed();

return got;
}

/* Resampler C API */
@@ -39,6 +39,13 @@ MOZ_END_STD_NAMESPACE

/* This header file contains the internal C++ API of the resamplers, for testing. */

// When dropping audio input frames to prevent building
// an input delay, this function returns the number of frames
// to keep in the buffer.
// @parameter sample_rate The sample rate of the stream.
// @return A number of frames to keep.
uint32_t min_buffered_audio_frame(uint32_t sample_rate);

int to_speex_quality(cubeb_resampler_quality q);

struct cubeb_resampler {
@@ -75,7 +82,8 @@ class passthrough_resampler : public cubeb_resampler
passthrough_resampler(cubeb_stream * s,
cubeb_data_callback cb,
void * ptr,
uint32_t input_channels);
uint32_t input_channels,
uint32_t sample_rate);

virtual long fill(void * input_buffer, long * input_frames_count,
void * output_buffer, long output_frames);
@@ -85,13 +93,23 @@ class passthrough_resampler : public cubeb_resampler
return 0;
}

void drop_audio_if_needed()
{
uint32_t to_keep = min_buffered_audio_frame(sample_rate);
uint32_t available = samples_to_frames(internal_input_buffer.length());
if (available > to_keep) {
internal_input_buffer.pop(nullptr, frames_to_samples(available - to_keep));
}
}

private:
cubeb_stream * const stream;
const cubeb_data_callback data_callback;
void * const user_ptr;
/* This allows to buffer some input to account for the fact that we buffer
* some inputs. */
auto_array<T> internal_input_buffer;
uint32_t sample_rate;
};

/** Bidirectional resampler, can resample an input and an output stream, or just
@@ -164,6 +182,7 @@ class cubeb_resampler_speex_one_way : public processor {
int quality)
: processor(channels)
, resampling_ratio(static_cast<float>(source_rate) / target_rate)
, source_rate(source_rate)
, additional_latency(0)
, leftover_samples(0)
{
@@ -296,6 +315,16 @@ class cubeb_resampler_speex_one_way : public processor {
resampling_in_buffer.set_length(leftover_samples +
frames_to_samples(written_frames));
}

void drop_audio_if_needed()
{
// Keep at most 100ms buffered.
uint32_t available = samples_to_frames(resampling_in_buffer.length());
uint32_t to_keep = min_buffered_audio_frame(source_rate);
if (available > to_keep) {
resampling_in_buffer.pop(nullptr, frames_to_samples(available - to_keep));
}
}
private:
/** Wrapper for the speex resampling functions to have a typed
* interface. */
@@ -332,6 +361,7 @@ class cubeb_resampler_speex_one_way : public processor {
SpeexResamplerState * speex_resampler;
/** Source rate / target rate. */
const float resampling_ratio;
const uint32_t source_rate;
/** Storage for the input frames, to be resampled. Also contains
* any unresampled frames after resampling. */
auto_array<T> resampling_in_buffer;
@@ -350,11 +380,13 @@ class delay_line : public processor {
public:
/** Constructor
* @parameter frames the number of frames of delay.
* @parameter channels the number of channels of this delay line. */
delay_line(uint32_t frames, uint32_t channels)
* @parameter channels the number of channels of this delay line.
* @parameter sample_rate sample-rate of the audio going through this delay line */
delay_line(uint32_t frames, uint32_t channels, uint32_t sample_rate)
: processor(channels)
, length(frames)
, leftover_samples(0)
, sample_rate(sample_rate)
{
/* Fill the delay line with some silent frames to add latency. */
delay_input_buffer.push_silence(frames * channels);
@@ -444,6 +476,15 @@ class delay_line : public processor {
{
return length;
}

void drop_audio_if_needed()
{
size_t available = samples_to_frames(delay_input_buffer.length());
uint32_t to_keep = min_buffered_audio_frame(sample_rate);
if (available > to_keep) {
delay_input_buffer.pop(nullptr, frames_to_samples(available - to_keep));
}
}
private:
/** The length, in frames, of this delay line */
uint32_t length;
@@ -455,6 +496,7 @@ class delay_line : public processor {
/** The output buffer. This is only ever used if using the ::output with a
* single argument. */
auto_array<T> delay_output_buffer;
uint32_t sample_rate;
};

/** This sits behind the C API and is more typed. */
@@ -485,7 +527,8 @@ cubeb_resampler_create_internal(cubeb_stream * stream,
(output_params && !input_params && (output_params->rate == target_rate))) {
return new passthrough_resampler<T>(stream, callback,
user_ptr,
input_params ? input_params->channels : 0);
input_params ? input_params->channels : 0,
target_rate);
}

/* Determine if we need to resampler one or both directions, and create the
@@ -517,13 +560,15 @@ cubeb_resampler_create_internal(cubeb_stream * stream,
* other direction so that the streams are synchronized. */
if (input_resampler && !output_resampler && input_params && output_params) {
output_delay.reset(new delay_line<T>(input_resampler->latency(),
output_params->channels));
output_params->channels,
output_params->rate));
if (!output_delay) {
return NULL;
}
} else if (output_resampler && !input_resampler && input_params && output_params) {
input_delay.reset(new delay_line<T>(output_resampler->latency(),
input_params->channels));
input_params->channels,
output_params->rate));
if (!input_delay) {
return NULL;
}
@@ -226,6 +226,17 @@ class ring_buffer_base
{
return storage_capacity() - 1;
}
/**
* Reset the consumer and producer thread identifier, in case the thread are
* being changed. This has to be externally synchronized. This is no-op when
* asserts are disabled.
*/
void reset_thread_ids()
{
#ifndef NDEBUG
consumer_id = producer_id = std::thread::id();
#endif
}
private:
/** Return true if the ring buffer is empty.
*
@@ -376,6 +376,7 @@ static struct cubeb_ops const sndio_ops = {
.stream_destroy = sndio_stream_destroy,
.stream_start = sndio_stream_start,
.stream_stop = sndio_stream_stop,
.stream_reset_default_device = NULL,
.stream_get_position = sndio_stream_get_position,
.stream_get_latency = sndio_stream_get_latency,
.stream_set_volume = sndio_stream_set_volume,
@@ -0,0 +1,155 @@
/*
* Copyright © 2011 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/

#include "cubeb_strings.h"

#include <assert.h>
#include <stdlib.h>
#include <string.h>

#define CUBEB_STRINGS_INLINE_COUNT 4

struct cubeb_strings {
uint32_t size;
uint32_t count;
char ** data;
char * small_store[CUBEB_STRINGS_INLINE_COUNT];
};

int
cubeb_strings_init(cubeb_strings ** strings)
{
cubeb_strings* strs = NULL;

if (!strings) {
return CUBEB_ERROR;
}

strs = calloc(1, sizeof(cubeb_strings));
assert(strs);

if (!strs) {
return CUBEB_ERROR;
}

strs->size = sizeof(strs->small_store) / sizeof(strs->small_store[0]);
strs->count = 0;
strs->data = strs->small_store;

*strings = strs;

return CUBEB_OK;
}

void
cubeb_strings_destroy(cubeb_strings * strings)
{
char ** sp = NULL;
char ** se = NULL;

if (!strings) {
return;
}

sp = strings->data;
se = sp + strings->count;

for ( ; sp != se; sp++) {
if (*sp) {
free(*sp);
}
}

if (strings->data != strings->small_store) {
free(strings->data);
}

free(strings);
}

/** Look for string in string storage.
@param strings Opaque pointer to interned string storage.
@param s String to look up.
@retval Read-only string or NULL if not found. */
static char const *
cubeb_strings_lookup(cubeb_strings * strings, char const * s)
{
char ** sp = NULL;
char ** se = NULL;

if (!strings || !s) {
return NULL;
}

sp = strings->data;
se = sp + strings->count;

for ( ; sp != se; sp++) {
if (*sp && strcmp(*sp, s) == 0) {
return *sp;
}
}

return NULL;
}

static char const *
cubeb_strings_push(cubeb_strings * strings, char const * s)
{
char * is = NULL;

if (strings->count == strings->size) {
char ** new_data;
uint32_t value_size = sizeof(char const *);
uint32_t new_size = strings->size * 2;
if (!new_size || value_size > (uint32_t)-1 / new_size) {
// overflow
return NULL;
}

if (strings->small_store == strings->data) {
// First time heap allocation.
new_data = malloc(new_size * value_size);
if (new_data) {
memcpy(new_data, strings->small_store, sizeof(strings->small_store));
}
} else {
new_data = realloc(strings->data, new_size * value_size);
}

if (!new_data) {
// out of memory
return NULL;
}

strings->size = new_size;
strings->data = new_data;
}

is = strdup(s);
strings->data[strings->count++] = is;

return is;
}

char const *
cubeb_strings_intern(cubeb_strings * strings, char const * s)
{
char const * is = NULL;

if (!strings || !s) {
return NULL;
}

is = cubeb_strings_lookup(strings, s);
if (is) {
return is;
}

return cubeb_strings_push(strings, s);
}

@@ -0,0 +1,44 @@
/*
* Copyright © 2011 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/

#ifndef CUBEB_STRINGS_H
#define CUBEB_STRINGS_H

#include "cubeb/cubeb.h"

#if defined(__cplusplus)
extern "C" {
#endif

/** Opaque handle referencing interned string storage. */
typedef struct cubeb_strings cubeb_strings;

/** Initialize an interned string structure.
@param strings An out param where an opaque pointer to the
interned string storage will be returned.
@retval CUBEB_OK in case of success.
@retval CUBEB_ERROR in case of error. */
CUBEB_EXPORT int cubeb_strings_init(cubeb_strings ** strings);

/** Destroy an interned string structure freeing all associated memory.
@param strings An opaque pointer to the interned string storage to
destroy. */
CUBEB_EXPORT void cubeb_strings_destroy(cubeb_strings * strings);

/** Add string to internal storage.
@param strings Opaque pointer to interned string storage.
@param s String to add to storage.
@retval CUBEB_OK
@retval CUBEB_ERROR
*/
CUBEB_EXPORT char const * cubeb_strings_intern(cubeb_strings * strings, char const * s);

#if defined(__cplusplus)
}
#endif

#endif // !CUBEB_STRINGS_H

This file was deleted.

@@ -17,7 +17,7 @@
#include <assert.h>
#include <mutex>
#include <type_traits>
#if defined(WIN32)
#if defined(_WIN32)
#include "cubeb_utils_win.h"
#else
#include "cubeb_utils_unix.h"
@@ -336,17 +336,4 @@ struct auto_array_wrapper_impl : public auto_array_wrapper {
using auto_lock = std::lock_guard<owned_critical_section>;
#endif // __cplusplus

// C language helpers

#ifdef __cplusplus
extern "C" {
#endif

int cubeb_utils_default_device_collection_destroy(cubeb * context,
cubeb_device_collection * collection);

#ifdef __cplusplus
}
#endif

#endif /* CUBEB_UTILS */

Large diffs are not rendered by default.

@@ -19,7 +19,6 @@
#include <math.h>
#include "cubeb/cubeb.h"
#include "cubeb-internal.h"
#include "cubeb_utils.h"

/* This is missing from the MinGW headers. Use a safe fallback. */
#if !defined(MEMORY_ALLOCATION_ALIGNMENT)
@@ -1017,6 +1016,26 @@ winmm_enumerate_devices(cubeb * context, cubeb_device_type type,
return CUBEB_OK;
}

static int
winmm_device_collection_destroy(cubeb * ctx,
cubeb_device_collection * collection)
{
uint32_t i;
XASSERT(collection);

(void) ctx;

for (i = 0; i < collection->count; i++) {
free((void *) collection->device[i].device_id);
free((void *) collection->device[i].friendly_name);
free((void *) collection->device[i].group_id);
free((void *) collection->device[i].vendor_name);
}

free(collection->device);
return CUBEB_OK;
}

static struct cubeb_ops const winmm_ops = {
/*.init =*/ winmm_init,
/*.get_backend_id =*/ winmm_get_backend_id,
@@ -1025,12 +1044,13 @@ static struct cubeb_ops const winmm_ops = {
/*.get_preferred_sample_rate =*/ winmm_get_preferred_sample_rate,
/*.get_preferred_channel_layout =*/ NULL,
/*.enumerate_devices =*/ winmm_enumerate_devices,
/*.device_collection_destroy =*/ cubeb_utils_default_device_collection_destroy,
/*.device_collection_destroy =*/ winmm_device_collection_destroy,
/*.destroy =*/ winmm_destroy,
/*.stream_init =*/ winmm_stream_init,
/*.stream_destroy =*/ winmm_stream_destroy,
/*.stream_start =*/ winmm_stream_start,
/*.stream_stop =*/ winmm_stream_stop,
/*.stream_reset_default_device =*/ NULL,
/*.stream_get_position =*/ winmm_stream_get_position,
/*.stream_get_latency = */ winmm_stream_get_latency,
/*.stream_set_volume =*/ winmm_stream_set_volume,
@@ -798,11 +798,10 @@ EXPORT SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels, spx_u

EXPORT SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, spx_uint32_t ratio_num, spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err)
{
spx_uint32_t i;
SpeexResamplerState *st;
int filter_err;

if (quality > 10 || quality < 0)
if (nb_channels == 0 || ratio_num == 0 || ratio_den == 0 || quality > 10 || quality < 0)
{
if (err)
*err = RESAMPLER_ERR_INVALID_ARG;
@@ -1111,6 +1110,10 @@ EXPORT int speex_resampler_set_rate_frac(SpeexResamplerState *st, spx_uint32_t r
spx_uint32_t fact;
spx_uint32_t old_den;
spx_uint32_t i;

if (ratio_num == 0 || ratio_den == 0)
return RESAMPLER_ERR_INVALID_ARG;

if (st->in_rate == in_rate && st->out_rate == out_rate && st->num_rate == ratio_num && st->den_rate == ratio_den)
return RESAMPLER_ERR_SUCCESS;

@@ -56,7 +56,7 @@ bool CubebStream::Start()
}

u32 minimum_latency = 0;
if (cubeb_get_min_latency(m_ctx.get(), params, &minimum_latency) != CUBEB_OK)
if (cubeb_get_min_latency(m_ctx.get(), &params, &minimum_latency) != CUBEB_OK)
ERROR_LOG(AUDIO, "Error getting minimum latency");
INFO_LOG(AUDIO, "Minimum latency: %i frames", minimum_latency);

@@ -80,7 +80,7 @@ void CEXIMic::StreamStart()
params.layout = CUBEB_LAYOUT_MONO;

u32 minimum_latency;
if (cubeb_get_min_latency(m_cubeb_ctx.get(), params, &minimum_latency) != CUBEB_OK)
if (cubeb_get_min_latency(m_cubeb_ctx.get(), &params, &minimum_latency) != CUBEB_OK)
{
WARN_LOG(EXPANSIONINTERFACE, "Error getting minimum latency");
}