From 279fbfd34c92e6bc57e5deacb7ac0fe203bd0a8f Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Thu, 16 Jun 2016 14:52:51 +0100 Subject: [PATCH] Bug 1280280 - Update cubeb to 073c9f011114. r=kinetik MozReview-Commit-ID: 8mARVNKAG7P --- media/libcubeb/README_MOZILLA | 2 +- media/libcubeb/src/cubeb.c | 1 + media/libcubeb/src/cubeb_jack.cpp | 1038 +++++++++++++++++ media/libcubeb/src/cubeb_resampler.cpp | 21 +- media/libcubeb/src/cubeb_resampler_internal.h | 8 +- media/libcubeb/src/cubeb_wasapi.cpp | 33 +- media/libcubeb/src/cubeb_winmm.c | 27 +- media/libcubeb/tests/test_latency.cpp | 2 +- media/libcubeb/tests/test_sanity.cpp | 32 +- 9 files changed, 1139 insertions(+), 25 deletions(-) create mode 100644 media/libcubeb/src/cubeb_jack.cpp diff --git a/media/libcubeb/README_MOZILLA b/media/libcubeb/README_MOZILLA index 26fc468a2f84..4cbcd2e45785 100644 --- a/media/libcubeb/README_MOZILLA +++ b/media/libcubeb/README_MOZILLA @@ -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 2b3d6dc8c4353c38d3e9e2bdc9a688d6d632ac19. +The git commit ID used was 073c9f011114fe4208b4aa49e99e33cde1deb6f1. diff --git a/media/libcubeb/src/cubeb.c b/media/libcubeb/src/cubeb.c index 9b5ba02b2935..2fc3860d7c5d 100644 --- a/media/libcubeb/src/cubeb.c +++ b/media/libcubeb/src/cubeb.c @@ -66,6 +66,7 @@ int validate_stream_params(cubeb_stream_params * input_stream_params, cubeb_stream_params * output_stream_params) { + XASSERT(input_stream_params || output_stream_params); if (output_stream_params) { if (output_stream_params->rate < 1000 || output_stream_params->rate > 192000 || output_stream_params->channels < 1 || output_stream_params->channels > 8) { diff --git a/media/libcubeb/src/cubeb_jack.cpp b/media/libcubeb/src/cubeb_jack.cpp new file mode 100644 index 000000000000..265dc79f5c6e --- /dev/null +++ b/media/libcubeb/src/cubeb_jack.cpp @@ -0,0 +1,1038 @@ +/* + * Copyright © 2012 David Richards + * Copyright © 2013 Sebastien Alaiwan + * Copyright © 2016 Damien Zammit + * + * This program is made available under an ISC-style license. See the + * accompanying file LICENSE for details. + */ +#define _DEFAULT_SOURCE +#define _BSD_SOURCE +#define _POSIX_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cubeb/cubeb.h" +#include "cubeb-internal.h" +#include "cubeb_resampler.h" + +#include +#include + +#define JACK_API_VISIT(X) \ + X(jack_activate) \ + X(jack_client_close) \ + X(jack_client_open) \ + X(jack_connect) \ + X(jack_free) \ + X(jack_get_ports) \ + X(jack_get_sample_rate) \ + X(jack_get_xrun_delayed_usecs) \ + X(jack_get_buffer_size) \ + X(jack_port_get_buffer) \ + X(jack_port_name) \ + X(jack_port_register) \ + X(jack_port_unregister) \ + X(jack_port_get_latency_range) \ + X(jack_set_process_callback) \ + X(jack_set_xrun_callback) \ + X(jack_set_graph_order_callback) + +#define IMPORT_FUNC(x) static decltype(x) * api_##x; +JACK_API_VISIT(IMPORT_FUNC); + +static const int MAX_STREAMS = 16; +static const int MAX_CHANNELS = 8; +static const int FIFO_SIZE = 4096 * sizeof(float); + +enum devstream { + NONE = 0, + IN_ONLY, + OUT_ONLY, + DUPLEX, +}; + +static void +s16ne_to_float(float * dst, const int16_t * src, size_t n) +{ + for (size_t i = 0; i < n; i++) + *(dst++) = (float)((float)*(src++) / 32767.0f); +} + +static void +float_to_s16ne(int16_t * dst, float * src, size_t n) +{ + for (size_t i = 0; i < n; i++) { + if (*src > 1.f) *src = 1.f; + if (*src < -1.f) *src = -1.f; + *(dst++) = (int16_t)((int16_t)(*(src++) * 32767)); + } +} + +extern "C" +{ +/*static*/ int jack_init (cubeb ** context, char const * context_name); +} +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_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); +static void cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream, short **bufs_in, float **bufs_out, jack_nframes_t nframes); +static void cbjack_deinterleave_playback_refill_float(cubeb_stream * stream, float **bufs_in, float **bufs_out, jack_nframes_t nframes); +static int cbjack_stream_device_destroy(cubeb_stream * stream, + cubeb_device * device); +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_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name, + cubeb_devid input_device, + cubeb_stream_params * input_stream_params, + cubeb_devid output_device, + cubeb_stream_params * output_stream_params, + unsigned int latency, + cubeb_data_callback data_callback, + cubeb_state_callback state_callback, + void * user_ptr); +static void cbjack_stream_destroy(cubeb_stream * stream); +static int cbjack_stream_start(cubeb_stream * stream); +static int cbjack_stream_stop(cubeb_stream * stream); +static int cbjack_stream_get_position(cubeb_stream * stream, uint64_t * position); +static int cbjack_stream_set_volume(cubeb_stream * stm, float volume); + +static struct cubeb_ops const cbjack_ops = { + .init = jack_init, + .get_backend_id = cbjack_get_backend_id, + .get_max_channel_count = cbjack_get_max_channel_count, + .get_min_latency = cbjack_get_min_latency, + .get_preferred_sample_rate = cbjack_get_preferred_sample_rate, + .enumerate_devices = cbjack_enumerate_devices, + .destroy = cbjack_destroy, + .stream_init = cbjack_stream_init, + .stream_destroy = cbjack_stream_destroy, + .stream_start = cbjack_stream_start, + .stream_stop = cbjack_stream_stop, + .stream_get_position = cbjack_stream_get_position, + .stream_get_latency = cbjack_get_latency, + .stream_set_volume = cbjack_stream_set_volume, + .stream_set_panning = NULL, + .stream_get_current_device = cbjack_stream_get_current_device, + .stream_device_destroy = cbjack_stream_device_destroy, + .stream_register_device_changed_callback = NULL, + .register_device_collection_changed = NULL +}; + +struct cubeb_stream { + cubeb * context; + + /**< Mutex for each stream */ + pthread_mutex_t mutex; + + bool in_use; /**< Set to false iff the stream is free */ + bool ports_ready; /**< Set to true iff the JACK ports are ready */ + + cubeb_data_callback data_callback; + cubeb_state_callback state_callback; + void * user_ptr; + cubeb_stream_params in_params; + cubeb_stream_params out_params; + + cubeb_resampler * resampler; + + uint64_t position; + bool pause; + float ratio; + enum devstream devs; + char stream_name[256]; + jack_port_t * output_ports[MAX_CHANNELS]; + jack_port_t * input_ports[MAX_CHANNELS]; + float volume; +}; + +struct cubeb { + struct cubeb_ops const * ops; + void * libjack; + + /**< Mutex for whole context */ + pthread_mutex_t mutex; + + /**< Audio buffers, converted to float */ + float in_float_interleaved_buffer[FIFO_SIZE * MAX_CHANNELS]; + float out_float_interleaved_buffer[FIFO_SIZE * MAX_CHANNELS]; + + /**< Audio buffer, at the sampling rate of the output */ + float in_resampled_interleaved_buffer_float[FIFO_SIZE * MAX_CHANNELS * 3]; + int16_t in_resampled_interleaved_buffer_s16ne[FIFO_SIZE * MAX_CHANNELS * 3]; + float out_resampled_interleaved_buffer_float[FIFO_SIZE * MAX_CHANNELS * 3]; + int16_t out_resampled_interleaved_buffer_s16ne[FIFO_SIZE * MAX_CHANNELS * 3]; + + cubeb_stream streams[MAX_STREAMS]; + unsigned int active_streams; + + cubeb_device_info * devinfo[2]; + cubeb_device_collection_changed_callback collection_changed_callback; + + bool active; + unsigned int jack_sample_rate; + unsigned int jack_latency; + unsigned int jack_xruns; + unsigned int jack_buffer_size; + unsigned int fragment_size; + unsigned int output_bytes_per_frame; + jack_client_t * jack_client; +}; + +static int +load_jack_lib(cubeb * context) +{ +#ifdef __APPLE__ + context->libjack = dlopen("libjack.0.dylib", RTLD_LAZY); + context->libjack = dlopen("/usr/local/lib/libjack.0.dylib", RTLD_LAZY); +#elif defined(__WIN32__) +# ifdef _WIN64 + context->libjack = LoadLibrary("libjack64.dll"); +# else + context->libjack = LoadLibrary("libjack.dll"); +# endif +#else + context->libjack = dlopen("libjack.so.0", RTLD_LAZY); +#endif + if (!context->libjack) { + return CUBEB_ERROR; + } + +#define LOAD(x) \ + { \ + api_##x = (decltype(x)*)dlsym(context->libjack, #x); \ + if (!api_##x) { \ + dlclose(context->libjack); \ + return CUBEB_ERROR; \ + } \ + } + + JACK_API_VISIT(LOAD); +#undef LOAD + + return CUBEB_OK; +} + +static void +cbjack_connect_ports (cubeb_stream * stream) +{ + const char ** phys_in_ports = api_jack_get_ports (stream->context->jack_client, + NULL, NULL, + JackPortIsInput + | JackPortIsPhysical); + const char ** phys_out_ports = api_jack_get_ports (stream->context->jack_client, + NULL, NULL, + JackPortIsOutput + | JackPortIsPhysical); + + if (*phys_in_ports == NULL) { + goto skipplayback; + } + + // Connect outputs to playback + for (unsigned int c = 0; c < stream->out_params.channels && phys_in_ports[c] != NULL; c++) { + const char *src_port = api_jack_port_name (stream->output_ports[c]); + + api_jack_connect (stream->context->jack_client, src_port, phys_in_ports[c]); + } + +skipplayback: + if (*phys_out_ports == NULL) { + goto end; + } + // Connect inputs to capture + for (unsigned int c = 0; c < stream->in_params.channels && phys_out_ports[c] != NULL; c++) { + const char *src_port = api_jack_port_name (stream->input_ports[c]); + + api_jack_connect (stream->context->jack_client, phys_out_ports[c], src_port); + } +end: + api_jack_free(phys_out_ports); + api_jack_free(phys_in_ports); +} + +static int +cbjack_xrun_callback(void * arg) +{ + cubeb * ctx = (cubeb *)arg; + + float delay = api_jack_get_xrun_delayed_usecs(ctx->jack_client); + int fragments = (int)ceilf( ((delay / 1000000.0) * ctx->jack_sample_rate ) + / (float)(ctx->jack_buffer_size) ); + ctx->jack_xruns += fragments; + return 0; +} + +static int +cbjack_graph_order_callback(void * arg) +{ + cubeb * ctx = (cubeb *)arg; + int i; + uint32_t rate; + + jack_latency_range_t latency_range; + jack_nframes_t port_latency, max_latency = 0; + + for (int j = 0; j < MAX_STREAMS; j++) { + cubeb_stream *stm = &ctx->streams[j]; + + if (!stm->in_use) + continue; + if (!stm->ports_ready) + continue; + + for (i = 0; i < (int)stm->out_params.channels; ++i) { + api_jack_port_get_latency_range(stm->output_ports[i], JackPlaybackLatency, &latency_range); + port_latency = latency_range.max; + if (port_latency > max_latency) + max_latency = port_latency; + } + /* Cap minimum latency to 128 frames */ + if (max_latency < 128) + 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; + return 0; +} + +static int +cbjack_process(jack_nframes_t nframes, void * arg) +{ + cubeb * ctx = (cubeb *)arg; + int t_jack_xruns = ctx->jack_xruns; + int i; + + for (int j = 0; j < MAX_STREAMS; j++) { + cubeb_stream *stm = &ctx->streams[j]; + float *bufs_out[stm->out_params.channels]; + float *bufs_in[stm->in_params.channels]; + + if (!stm->in_use) + continue; + + // handle xruns by skipping audio that should have been played + for (i = 0; i < t_jack_xruns; i++) { + stm->position += ctx->fragment_size * stm->ratio; + } + ctx->jack_xruns -= t_jack_xruns; + + if (!stm->ports_ready) + continue; + + if (stm->devs & OUT_ONLY) { + // get jack output buffers + for (i = 0; i < (int)stm->out_params.channels; i++) + bufs_out[i] = (float*)api_jack_port_get_buffer(stm->output_ports[i], nframes); + } + if (stm->devs & IN_ONLY) { + // get jack input buffers + for (i = 0; i < (int)stm->in_params.channels; i++) + bufs_in[i] = (float*)api_jack_port_get_buffer(stm->input_ports[i], nframes); + } + if (stm->pause) { + // paused, play silence on output + if (stm->devs & OUT_ONLY) { + for (unsigned int c = 0; c < stm->out_params.channels; c++) { + float* buffer_out = bufs_out[c]; + for (long f = 0; f < nframes; f++) { + buffer_out[f] = 0.f; + } + } + } + if (stm->devs & IN_ONLY) { + // paused, capture silence + for (unsigned int c = 0; c < stm->in_params.channels; c++) { + float* buffer_in = bufs_in[c]; + for (long f = 0; f < nframes; f++) { + buffer_in[f] = 0.f; + } + } + } + } else { + + // try to lock stream mutex + if (pthread_mutex_trylock(&stm->mutex) == 0) { + + int16_t *in_s16ne = stm->context->in_resampled_interleaved_buffer_s16ne; + float *in_float = stm->context->in_resampled_interleaved_buffer_float; + + // unpaused, play audio + if (stm->devs == DUPLEX) { + if (stm->out_params.format == CUBEB_SAMPLE_S16NE) { + cbjack_interleave_capture(stm, bufs_in, nframes, true); + cbjack_deinterleave_playback_refill_s16ne(stm, &in_s16ne, bufs_out, nframes); + } else if (stm->out_params.format == CUBEB_SAMPLE_FLOAT32NE) { + cbjack_interleave_capture(stm, bufs_in, nframes, false); + cbjack_deinterleave_playback_refill_float(stm, &in_float, bufs_out, nframes); + } + } else if (stm->devs == IN_ONLY) { + if (stm->in_params.format == CUBEB_SAMPLE_S16NE) { + cbjack_interleave_capture(stm, bufs_in, nframes, true); + cbjack_deinterleave_playback_refill_s16ne(stm, &in_s16ne, nullptr, nframes); + } else if (stm->in_params.format == CUBEB_SAMPLE_FLOAT32NE) { + cbjack_interleave_capture(stm, bufs_in, nframes, false); + cbjack_deinterleave_playback_refill_float(stm, &in_float, nullptr, nframes); + } + } else if (stm->devs == OUT_ONLY) { + if (stm->out_params.format == CUBEB_SAMPLE_S16NE) { + cbjack_deinterleave_playback_refill_s16ne(stm, nullptr, bufs_out, nframes); + } else if (stm->out_params.format == CUBEB_SAMPLE_FLOAT32NE) { + cbjack_deinterleave_playback_refill_float(stm, nullptr, bufs_out, nframes); + } + } + // unlock stream mutex + pthread_mutex_unlock(&stm->mutex); + + } else { + // could not lock mutex + // output silence + if (stm->devs & OUT_ONLY) { + for (unsigned int c = 0; c < stm->out_params.channels; c++) { + float* buffer_out = bufs_out[c]; + for (long f = 0; f < nframes; f++) { + buffer_out[f] = 0.f; + } + } + } + if (stm->devs & IN_ONLY) { + // capture silence + for (unsigned int c = 0; c < stm->in_params.channels; c++) { + float* buffer_in = bufs_in[c]; + for (long f = 0; f < nframes; f++) { + buffer_in[f] = 0.f; + } + } + } + } + } + } + return 0; +} + + +static void +cbjack_deinterleave_playback_refill_float(cubeb_stream * stream, float ** in, float ** bufs_out, jack_nframes_t nframes) +{ + float * out_interleaved_buffer = nullptr; + + float * inptr = (in != NULL) ? *in : nullptr; + float * outptr = (bufs_out != NULL) ? *bufs_out : nullptr; + + long needed_frames = (bufs_out != NULL) ? nframes : 0; + long done_frames = 0; + long input_frames_count = (in != NULL) ? nframes : 0; + + + done_frames = cubeb_resampler_fill(stream->resampler, + inptr, + &input_frames_count, + (bufs_out != NULL) ? stream->context->out_resampled_interleaved_buffer_float : NULL, + needed_frames); + + out_interleaved_buffer = stream->context->out_resampled_interleaved_buffer_float; + + if (outptr) { + // convert interleaved output buffers to contiguous buffers + for (unsigned int c = 0; c < stream->out_params.channels; c++) { + float* buffer = bufs_out[c]; + for (long f = 0; f < done_frames; f++) { + buffer[f] = out_interleaved_buffer[(f * stream->out_params.channels) + c] * stream->volume; + } + if (done_frames < needed_frames) { + // draining + for (long f = done_frames; f < needed_frames; f++) { + buffer[f] = 0.f; + } + } + if (done_frames == 0) { + // stop, but first zero out the existing buffer + for (long f = 0; f < needed_frames; f++) { + buffer[f] = 0.f; + } + } + } + } + + if (done_frames >= 0 && done_frames < needed_frames) { + // set drained + stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_DRAINED); + // stop stream + cbjack_stream_stop(stream); + } + if (done_frames > 0 && done_frames <= needed_frames) { + // advance stream position + stream->position += done_frames * stream->ratio; + } + if (done_frames < 0 || done_frames > needed_frames) { + // stream error + stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_ERROR); + } +} + +static void +cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream, short ** in, float ** bufs_out, jack_nframes_t nframes) +{ + float * out_interleaved_buffer = nullptr; + + short * inptr = (in != NULL) ? *in : nullptr; + float * outptr = (bufs_out != NULL) ? *bufs_out : nullptr; + + long needed_frames = (bufs_out != NULL) ? nframes : 0; + long done_frames = 0; + long input_frames_count = (in != NULL) ? nframes : 0; + + done_frames = cubeb_resampler_fill(stream->resampler, + inptr, + &input_frames_count, + (bufs_out != NULL) ? stream->context->out_resampled_interleaved_buffer_s16ne : NULL, + needed_frames); + + s16ne_to_float(stream->context->out_resampled_interleaved_buffer_float, stream->context->out_resampled_interleaved_buffer_s16ne, done_frames * stream->out_params.channels); + + out_interleaved_buffer = stream->context->out_resampled_interleaved_buffer_float; + + if (outptr) { + // convert interleaved output buffers to contiguous buffers + for (unsigned int c = 0; c < stream->out_params.channels; c++) { + float* buffer = bufs_out[c]; + for (long f = 0; f < done_frames; f++) { + buffer[f] = out_interleaved_buffer[(f * stream->out_params.channels) + c] * stream->volume; + } + if (done_frames < needed_frames) { + // draining + for (long f = done_frames; f < needed_frames; f++) { + buffer[f] = 0.f; + } + } + if (done_frames == 0) { + // stop, but first zero out the existing buffer + for (long f = 0; f < needed_frames; f++) { + buffer[f] = 0.f; + } + } + } + } + + if (done_frames >= 0 && done_frames < needed_frames) { + // set drained + stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_DRAINED); + // stop stream + cbjack_stream_stop(stream); + } + if (done_frames > 0 && done_frames <= needed_frames) { + // advance stream position + stream->position += done_frames * stream->ratio; + } + if (done_frames < 0 || done_frames > needed_frames) { + // stream error + stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_ERROR); + } +} + +static void +cbjack_interleave_capture(cubeb_stream * stream, float **in, jack_nframes_t nframes, bool format_mismatch) +{ + float *in_buffer = stream->context->in_float_interleaved_buffer; + + for (unsigned int c = 0; c < stream->in_params.channels; c++) { + for (long f = 0; f < nframes; f++) { + in_buffer[(f * stream->in_params.channels) + c] = in[c][f] * stream->volume; + } + } + if (format_mismatch) { + float_to_s16ne(stream->context->in_resampled_interleaved_buffer_s16ne, in_buffer, nframes * stream->in_params.channels); + } else { + memset(stream->context->in_resampled_interleaved_buffer_float, 0, (FIFO_SIZE * MAX_CHANNELS * 3) * sizeof(float)); + memcpy(stream->context->in_resampled_interleaved_buffer_float, in_buffer, (FIFO_SIZE * MAX_CHANNELS * 2) * sizeof(float)); + } +} + +/*static*/ int +jack_init (cubeb ** context, char const * context_name) +{ + int r; + + *context = NULL; + + cubeb * ctx = (cubeb *)calloc(1, sizeof(*ctx)); + if (ctx == NULL) { + return CUBEB_ERROR; + } + + r = load_jack_lib(ctx); + if (r != 0) { + cbjack_destroy(ctx); + return CUBEB_ERROR; + } + + ctx->ops = &cbjack_ops; + + ctx->mutex = PTHREAD_MUTEX_INITIALIZER; + for (r = 0; r < MAX_STREAMS; r++) { + ctx->streams[r].mutex = PTHREAD_MUTEX_INITIALIZER; + } + + const char * jack_client_name = "cubeb"; + if (context_name) + jack_client_name = context_name; + + ctx->jack_client = api_jack_client_open(jack_client_name, + JackNoStartServer, + NULL); + + if (ctx->jack_client == NULL) { + cbjack_destroy(ctx); + return CUBEB_ERROR; + } + + ctx->jack_xruns = 0; + + api_jack_set_process_callback (ctx->jack_client, cbjack_process, ctx); + api_jack_set_xrun_callback (ctx->jack_client, cbjack_xrun_callback, ctx); + api_jack_set_graph_order_callback (ctx->jack_client, cbjack_graph_order_callback, ctx); + + if (api_jack_activate (ctx->jack_client)) { + cbjack_destroy(ctx); + return CUBEB_ERROR; + } + + ctx->jack_sample_rate = api_jack_get_sample_rate(ctx->jack_client); + ctx->jack_latency = 128 * 1000 / ctx->jack_sample_rate; + + ctx->active = true; + *context = ctx; + + return CUBEB_OK; +} + +static char const * +cbjack_get_backend_id(cubeb * context) +{ + return "jack"; +} + +static int +cbjack_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) +{ + *max_channels = MAX_CHANNELS; + return CUBEB_OK; +} + +static int +cbjack_get_latency(cubeb_stream * stm, unsigned int * latency_ms) +{ + *latency_ms = stm->context->jack_latency; + return CUBEB_OK; +} + +static int +cbjack_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms) +{ + *latency_ms = ctx->jack_latency; + return CUBEB_OK; +} + +static int +cbjack_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) +{ + if (!ctx->jack_client) { + jack_client_t * testclient = api_jack_client_open("test-samplerate", + JackNoStartServer, + NULL); + if (!testclient) { + return CUBEB_ERROR; + } + + *rate = api_jack_get_sample_rate(testclient); + api_jack_client_close(testclient); + + } else { + *rate = api_jack_get_sample_rate(ctx->jack_client); + } + return CUBEB_OK; +} + +static void +cbjack_destroy(cubeb * context) +{ + context->active = false; + + if (context->jack_client != NULL) + api_jack_client_close (context->jack_client); + + if (context->libjack) + dlclose(context->libjack); + + free(context); +} + +static cubeb_stream * +context_alloc_stream(cubeb * context, char const * stream_name) +{ + for (int i = 0; i < MAX_STREAMS; i++) { + if (!context->streams[i].in_use) { + cubeb_stream * stm = &context->streams[i]; + stm->in_use = true; + snprintf(stm->stream_name, 255, "%s_%u", stream_name, i); + return stm; + } + } + return NULL; +} + +static int +cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name, + cubeb_devid input_device, + cubeb_stream_params * input_stream_params, + cubeb_devid output_device, + cubeb_stream_params * output_stream_params, + unsigned int latency, + cubeb_data_callback data_callback, + cubeb_state_callback state_callback, + void * user_ptr) +{ + int stream_actual_rate = 0; + int jack_rate = api_jack_get_sample_rate(context->jack_client); + + if (output_stream_params + && (output_stream_params->format != CUBEB_SAMPLE_FLOAT32NE && + output_stream_params->format != CUBEB_SAMPLE_S16NE) + ) { + return CUBEB_ERROR_INVALID_FORMAT; + } + + if (input_stream_params + && (input_stream_params->format != CUBEB_SAMPLE_FLOAT32NE && + input_stream_params->format != CUBEB_SAMPLE_S16NE) + ) { + return CUBEB_ERROR_INVALID_FORMAT; + } + + *stream = NULL; + + // Find a free stream. + pthread_mutex_lock(&context->mutex); + cubeb_stream * stm = context_alloc_stream(context, stream_name); + + // No free stream? + if (stm == NULL) { + pthread_mutex_unlock(&context->mutex); + return CUBEB_ERROR; + } + + // unlock context mutex + pthread_mutex_unlock(&context->mutex); + + // Lock active stream + pthread_mutex_lock(&stm->mutex); + + stm->ports_ready = false; + stm->user_ptr = user_ptr; + stm->context = context; + stm->devs = NONE; + if (output_stream_params && !input_stream_params) { + stm->out_params = *output_stream_params; + stream_actual_rate = stm->out_params.rate; + stm->out_params.rate = jack_rate; + stm->devs = OUT_ONLY; + if (stm->out_params.format == CUBEB_SAMPLE_FLOAT32NE) { + context->output_bytes_per_frame = sizeof(float); + } else { + context->output_bytes_per_frame = sizeof(short); + } + } + if (input_stream_params && output_stream_params) { + stm->in_params = *input_stream_params; + stm->out_params = *output_stream_params; + stream_actual_rate = stm->out_params.rate; + stm->in_params.rate = jack_rate; + stm->out_params.rate = jack_rate; + stm->devs = DUPLEX; + if (stm->out_params.format == CUBEB_SAMPLE_FLOAT32NE) { + context->output_bytes_per_frame = sizeof(float); + stm->in_params.format = CUBEB_SAMPLE_FLOAT32NE; + } else { + context->output_bytes_per_frame = sizeof(short); + stm->in_params.format = CUBEB_SAMPLE_S16NE; + } + } else if (input_stream_params && !output_stream_params) { + stm->in_params = *input_stream_params; + stream_actual_rate = stm->in_params.rate; + stm->in_params.rate = jack_rate; + stm->devs = IN_ONLY; + if (stm->in_params.format == CUBEB_SAMPLE_FLOAT32NE) { + context->output_bytes_per_frame = sizeof(float); + } else { + context->output_bytes_per_frame = sizeof(short); + } + } + + stm->ratio = (float)stream_actual_rate / (float)jack_rate; + + stm->data_callback = data_callback; + stm->state_callback = state_callback; + stm->position = 0; + stm->volume = 1.0f; + context->jack_buffer_size = api_jack_get_buffer_size(context->jack_client); + context->fragment_size = context->jack_buffer_size; + + if (stm->devs == NONE) { + pthread_mutex_unlock(&stm->mutex); + return CUBEB_ERROR; + } + + stm->resampler = NULL; + + if (stm->devs == DUPLEX) { + stm->resampler = cubeb_resampler_create(stm, + &stm->in_params, + &stm->out_params, + stream_actual_rate, + stm->data_callback, + stm->user_ptr, + CUBEB_RESAMPLER_QUALITY_DESKTOP); + } else if (stm->devs == IN_ONLY) { + stm->resampler = cubeb_resampler_create(stm, + &stm->in_params, + nullptr, + stream_actual_rate, + stm->data_callback, + stm->user_ptr, + CUBEB_RESAMPLER_QUALITY_DESKTOP); + } else if (stm->devs == OUT_ONLY) { + stm->resampler = cubeb_resampler_create(stm, + nullptr, + &stm->out_params, + stream_actual_rate, + stm->data_callback, + stm->user_ptr, + CUBEB_RESAMPLER_QUALITY_DESKTOP); + } + + if (!stm->resampler) { + stm->in_use = false; + pthread_mutex_unlock(&stm->mutex); + return CUBEB_ERROR; + } + + if (stm->devs == DUPLEX || stm->devs == OUT_ONLY) { + for (unsigned int c = 0; c < stm->out_params.channels; c++) { + char portname[256]; + snprintf(portname, 255, "%s_out_%d", stm->stream_name, c); + stm->output_ports[c] = api_jack_port_register(stm->context->jack_client, + portname, + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, + 0); + } + } + + if (stm->devs == DUPLEX || stm->devs == IN_ONLY) { + for (unsigned int c = 0; c < stm->in_params.channels; c++) { + char portname[256]; + snprintf(portname, 255, "%s_in_%d", stm->stream_name, c); + stm->input_ports[c] = api_jack_port_register(stm->context->jack_client, + portname, + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsInput, + 0); + } + } + + cbjack_connect_ports(stm); + + *stream = stm; + + stm->ports_ready = true; + stm->pause = true; + pthread_mutex_unlock(&stm->mutex); + + return CUBEB_OK; +} + +static void +cbjack_stream_destroy(cubeb_stream * stream) +{ + pthread_mutex_lock(&stream->mutex); + stream->ports_ready = false; + + if (stream->devs == DUPLEX || stream->devs == OUT_ONLY) { + for (unsigned int c = 0; c < stream->out_params.channels; c++) { + if (stream->output_ports[c]) { + api_jack_port_unregister (stream->context->jack_client, stream->output_ports[c]); + stream->output_ports[c] = NULL; + } + } + } + + if (stream->devs == DUPLEX || stream->devs == IN_ONLY) { + for (unsigned int c = 0; c < stream->in_params.channels; c++) { + if (stream->input_ports[c]) { + api_jack_port_unregister (stream->context->jack_client, stream->input_ports[c]); + stream->input_ports[c] = NULL; + } + } + } + + if (stream->resampler) { + cubeb_resampler_destroy(stream->resampler); + stream->resampler = NULL; + } + stream->in_use = false; + pthread_mutex_unlock(&stream->mutex); +} + +static int +cbjack_stream_start(cubeb_stream * stream) +{ + stream->pause = false; + stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_STARTED); + return CUBEB_OK; +} + +static int +cbjack_stream_stop(cubeb_stream * stream) +{ + stream->pause = true; + stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_STOPPED); + return CUBEB_OK; +} + +static int +cbjack_stream_get_position(cubeb_stream * stream, uint64_t * position) +{ + *position = stream->position; + return CUBEB_OK; +} + +static int +cbjack_stream_set_volume(cubeb_stream * stm, float volume) +{ + stm->volume = volume; + return CUBEB_OK; +} + + +static int +cbjack_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const device) +{ + *device = (cubeb_device *)calloc(1, sizeof(cubeb_device)); + if (*device == NULL) + return CUBEB_ERROR; + + const char * j_in = "JACK capture"; + const char * j_out = "JACK playback"; + const char * empty = ""; + + if (stm->devs == DUPLEX) { + (*device)->input_name = strdup(j_in); + (*device)->output_name = strdup(j_out); + } else if (stm->devs == IN_ONLY) { + (*device)->input_name = strdup(j_in); + (*device)->output_name = strdup(empty); + } else if (stm->devs == OUT_ONLY) { + (*device)->input_name = strdup(empty); + (*device)->output_name = strdup(j_out); + } + + return CUBEB_OK; +} + +static int +cbjack_stream_device_destroy(cubeb_stream * stream, + cubeb_device * device) +{ + if (device->input_name) + free(device->input_name); + if (device->output_name) + free(device->output_name); + free(device); + return CUBEB_OK; +} + +static int +cbjack_enumerate_devices(cubeb * context, cubeb_device_type type, + cubeb_device_collection ** collection) +{ + if (!context) + return CUBEB_ERROR; + + uint32_t rate; + uint8_t i = 0; + uint8_t j; + cbjack_get_preferred_sample_rate(context, &rate); + const char * j_in = "JACK capture"; + const char * j_out = "JACK playback"; + + if (type & CUBEB_DEVICE_TYPE_OUTPUT) { + context->devinfo[i] = (cubeb_device_info *)malloc(sizeof(cubeb_device_info)); + context->devinfo[i]->device_id = strdup(j_out); + context->devinfo[i]->devid = context->devinfo[i]->device_id; + context->devinfo[i]->friendly_name = strdup(j_out); + context->devinfo[i]->group_id = strdup(j_out); + context->devinfo[i]->vendor_name = strdup(j_out); + context->devinfo[i]->type = CUBEB_DEVICE_TYPE_OUTPUT; + context->devinfo[i]->state = CUBEB_DEVICE_STATE_ENABLED; + context->devinfo[i]->preferred = CUBEB_DEVICE_PREF_ALL; + context->devinfo[i]->format = CUBEB_DEVICE_FMT_F32NE; + context->devinfo[i]->default_format = CUBEB_DEVICE_FMT_F32NE; + context->devinfo[i]->max_channels = MAX_CHANNELS; + 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; + i++; + } + + if (type & CUBEB_DEVICE_TYPE_INPUT) { + context->devinfo[i] = (cubeb_device_info *)malloc(sizeof(cubeb_device_info)); + context->devinfo[i]->device_id = strdup(j_in); + context->devinfo[i]->devid = context->devinfo[i]->device_id; + context->devinfo[i]->friendly_name = strdup(j_in); + context->devinfo[i]->group_id = strdup(j_in); + context->devinfo[i]->vendor_name = strdup(j_in); + context->devinfo[i]->type = CUBEB_DEVICE_TYPE_INPUT; + context->devinfo[i]->state = CUBEB_DEVICE_STATE_ENABLED; + context->devinfo[i]->preferred = CUBEB_DEVICE_PREF_ALL; + context->devinfo[i]->format = CUBEB_DEVICE_FMT_F32NE; + context->devinfo[i]->default_format = CUBEB_DEVICE_FMT_F32NE; + context->devinfo[i]->max_channels = MAX_CHANNELS; + 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; + i++; + } + + *collection = (cubeb_device_collection *) + malloc(sizeof(cubeb_device_collection) + + i * sizeof(cubeb_device_info *)); + + (*collection)->count = i; + + for (j = 0; j < i; j++) { + (*collection)->device[j] = context->devinfo[j]; + } + return CUBEB_OK; +} diff --git a/media/libcubeb/src/cubeb_resampler.cpp b/media/libcubeb/src/cubeb_resampler.cpp index 215749dd35ba..9bccc78db28b 100644 --- a/media/libcubeb/src/cubeb_resampler.cpp +++ b/media/libcubeb/src/cubeb_resampler.cpp @@ -37,12 +37,16 @@ to_speex_quality(cubeb_resampler_quality q) long noop_resampler::fill(void * input_buffer, long * input_frames_count, void * output_buffer, long output_frames) { + if (input_buffer) { + assert(input_frames_count); + } assert((input_buffer && output_buffer && *input_frames_count >= output_frames) || (!input_buffer && (!input_frames_count || *input_frames_count == 0)) || (!output_buffer && output_frames == 0)); if (output_buffer == nullptr) { + assert(input_buffer); output_frames = *input_frames_count; } @@ -131,6 +135,10 @@ cubeb_resampler_speex nullptr, out_unprocessed, output_frames_before_processing); + if (got < 0) { + return got; + } + output_processor->written(got); /* Process the output. If not enough frames have been returned from the @@ -156,8 +164,13 @@ cubeb_resampler_speex input_processor->input(input_buffer, *input_frames_count); resampled_input = input_processor->output(resampled_frame_count); - return data_callback(stream, user_ptr, - resampled_input, nullptr, resampled_frame_count); + long got = data_callback(stream, user_ptr, + resampled_input, nullptr, resampled_frame_count); + + /* Return the number of initial input frames or part of it. + * Since output_frames_needed == 0 in input scenario, the only + * available number outside resampler is the initial number of frames. */ + return (*input_frames_count) * (got / resampled_frame_count); } @@ -206,6 +219,10 @@ cubeb_resampler_speex resampled_input, out_unprocessed, output_frames_before_processing); + if (got < 0) { + return got; + } + output_processor->written(got); /* Process the output. If not enough frames have been returned from the diff --git a/media/libcubeb/src/cubeb_resampler_internal.h b/media/libcubeb/src/cubeb_resampler_internal.h index 8f8d6dcc7468..3a6aaca7572a 100644 --- a/media/libcubeb/src/cubeb_resampler_internal.h +++ b/media/libcubeb/src/cubeb_resampler_internal.h @@ -215,8 +215,8 @@ public: size_t output_for_input(uint32_t input_frames) { - return ceilf(input_frames * resampling_ratio) + 1 - - resampling_in_buffer.length() / channels; + return size_t(ceilf(input_frames / resampling_ratio) + - resampling_in_buffer.length() / channels); } /** Returns a buffer containing exactly `output_frame_count` resampled frames. @@ -263,8 +263,8 @@ public: * number of output frames will be exactly equal. */ uint32_t input_needed_for_output(uint32_t output_frame_count) { - return ceilf(output_frame_count * resampling_ratio) + 1 - - samples_to_frames(resampling_in_buffer.length()); + return uint32_t(ceilf(output_frame_count * resampling_ratio) + 1 + - samples_to_frames(resampling_in_buffer.length())); } /** Returns a pointer to the input buffer, that contains empty space for at diff --git a/media/libcubeb/src/cubeb_wasapi.cpp b/media/libcubeb/src/cubeb_wasapi.cpp index 5654131a7a0a..0ddf170f4418 100644 --- a/media/libcubeb/src/cubeb_wasapi.cpp +++ b/media/libcubeb/src/cubeb_wasapi.cpp @@ -122,7 +122,7 @@ public: owner = GetCurrentThreadId(); #endif } - + void leave() { #ifdef DEBUG @@ -729,7 +729,7 @@ refill_callback_duplex(cubeb_stream * stm) double output_duration = double(output_frames) / stm->output_mix_params.rate; double input_duration = double(input_frames) / stm->input_mix_params.rate; if (input_duration < output_duration) { - size_t padding = round((output_duration - input_duration) * stm->input_mix_params.rate); + size_t padding = size_t(round((output_duration - input_duration) * stm->input_mix_params.rate)); LOG("padding silence: out=%f in=%f pad=%u\n", output_duration, input_duration, padding); stm->linear_input_buffer.push_front_silence(padding * stm->input_stream_params.channels); } @@ -867,6 +867,7 @@ wasapi_stream_render_loop(LPVOID stream) continue; } case WAIT_OBJECT_0 + 1: { /* reconfigure */ + XASSERT(stm->output_client || stm->input_client); /* Close the stream */ if (stm->output_client) { stm->output_client->Stop(); @@ -888,6 +889,7 @@ wasapi_stream_render_loop(LPVOID stream) continue; } } + XASSERT(stm->output_client || stm->input_client); if (stm->output_client) { stm->output_client->Start(); } @@ -1529,7 +1531,7 @@ int setup_wasapi_stream(cubeb_stream * stm) return CUBEB_ERROR; } - XASSERT(!stm->output_client && "WASAPI stream already setup, close it first."); + XASSERT((!stm->output_client || !stm->input_client) && "WASAPI stream already setup, close it first."); if (has_input(stm)) { LOG("Setup capture: device=%x\n", (int)stm->input_device); @@ -1728,10 +1730,12 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream, void close_wasapi_stream(cubeb_stream * stm) { - XASSERT(stm); + XASSERT(stm && !stm->thread && !stm->shutdown_event); 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); @@ -1807,8 +1811,8 @@ int stream_start_one_side(cubeb_stream * stm, StreamDirection dir) return r; } - HRESULT hr = dir == OUTPUT ? stm->output_client->Start() : stm->input_client->Start(); - if (FAILED(hr)) { + HRESULT hr2 = dir == OUTPUT ? stm->output_client->Start() : stm->input_client->Start(); + if (FAILED(hr2)) { LOG("could not start the %s stream after reconfig: %x\n", dir == OUTPUT ? "output" : "input", hr); return CUBEB_ERROR; @@ -1824,20 +1828,20 @@ int stream_start_one_side(cubeb_stream * stm, StreamDirection dir) int wasapi_stream_start(cubeb_stream * stm) { - int rv; + XASSERT(stm && !stm->thread && !stm->shutdown_event); + XASSERT(stm->output_client || stm->input_client); + auto_lock lock(stm->stream_reset_lock); - XASSERT(stm && !stm->thread && !stm->shutdown_event); - if (stm->output_client) { - rv = stream_start_one_side(stm, OUTPUT); + int rv = stream_start_one_side(stm, OUTPUT); if (rv != CUBEB_OK) { return rv; } } if (stm->input_client) { - rv = stream_start_one_side(stm, INPUT); + int rv = stream_start_one_side(stm, INPUT); if (rv != CUBEB_OK) { return rv; } @@ -1849,7 +1853,7 @@ int wasapi_stream_start(cubeb_stream * stm) return CUBEB_ERROR; } - stm->thread = (HANDLE) _beginthreadex(NULL, 256 * 1024, wasapi_stream_render_loop, stm, STACK_SIZE_PARAM_IS_A_RESERVATION, NULL); + stm->thread = (HANDLE) _beginthreadex(NULL, 512 * 1024, wasapi_stream_render_loop, stm, STACK_SIZE_PARAM_IS_A_RESERVATION, NULL); if (stm->thread == NULL) { LOG("could not create WASAPI render thread.\n"); return CUBEB_ERROR; @@ -1865,6 +1869,8 @@ int wasapi_stream_stop(cubeb_stream * stm) XASSERT(stm); HRESULT hr; + XASSERT(stm->output_client || stm->input_client); + { auto_lock lock(stm->stream_reset_lock); @@ -2198,6 +2204,9 @@ wasapi_enumerate_devices(cubeb * context, cubeb_device_type type, } *out = (cubeb_device_collection *) malloc(sizeof(cubeb_device_collection) + sizeof(cubeb_device_info*) * (cc > 0 ? cc - 1 : 0)); + if (!*out) { + return CUBEB_ERROR; + } (*out)->count = 0; for (i = 0; i < cc; i++) { hr = collection->Item(i, &dev); diff --git a/media/libcubeb/src/cubeb_winmm.c b/media/libcubeb/src/cubeb_winmm.c index 35866e29d7a0..746909ba8fa8 100644 --- a/media/libcubeb/src/cubeb_winmm.c +++ b/media/libcubeb/src/cubeb_winmm.c @@ -207,7 +207,7 @@ winmm_refill_stream(cubeb_stream * stm) short * b = (short *) hdr->lpData; uint32_t i; for (i = 0; i < got * stm->params.channels; i++) { - b[i] *= stm->soft_volume; + b[i] = (short) (b[i] * stm->soft_volume); } } } @@ -701,7 +701,8 @@ winmm_stream_get_latency(cubeb_stream * stm, uint32_t * latency) return CUBEB_ERROR; } - *latency = written - time.u.sample; + XASSERT(written - time.u.sample <= UINT32_MAX); + *latency = (uint32_t) (written - time.u.sample); return CUBEB_OK; } @@ -787,7 +788,10 @@ static char * guid_to_cstr(LPGUID guid) { char * ret = malloc(sizeof(char) * 40); - _snprintf(ret, sizeof(char) * 40, + if (!ret) { + return NULL; + } + _snprintf_s(ret, sizeof(char) * 40, _TRUNCATE, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", guid->Data1, guid->Data2, guid->Data3, guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3], @@ -818,7 +822,10 @@ static char * device_id_idx(UINT devid) { char * ret = (char *)malloc(sizeof(char)*16); - _snprintf(ret, 16, "%u", devid); + if (!ret) { + return NULL; + } + _snprintf_s(ret, 16, _TRUNCATE, "%u", devid); return ret; } @@ -828,6 +835,9 @@ winmm_create_device_from_outcaps2(LPWAVEOUTCAPS2A caps, UINT devid) cubeb_device_info * ret; ret = calloc(1, sizeof(cubeb_device_info)); + if (!ret) { + return NULL; + } ret->devid = (cubeb_devid)(size_t)devid; ret->device_id = device_id_idx(devid); ret->friendly_name = _strdup(caps->szPname); @@ -856,6 +866,9 @@ winmm_create_device_from_outcaps(LPWAVEOUTCAPSA caps, UINT devid) cubeb_device_info * ret; ret = calloc(1, sizeof(cubeb_device_info)); + if (!ret) { + return NULL; + } ret->devid = (cubeb_devid)(size_t)devid; ret->device_id = device_id_idx(devid); ret->friendly_name = _strdup(caps->szPname); @@ -903,6 +916,9 @@ winmm_create_device_from_incaps2(LPWAVEINCAPS2A caps, UINT devid) cubeb_device_info * ret; ret = calloc(1, sizeof(cubeb_device_info)); + if (!ret) { + return NULL; + } ret->devid = (cubeb_devid)(size_t)devid; ret->device_id = device_id_idx(devid); ret->friendly_name = _strdup(caps->szPname); @@ -931,6 +947,9 @@ winmm_create_device_from_incaps(LPWAVEINCAPSA caps, UINT devid) cubeb_device_info * ret; ret = calloc(1, sizeof(cubeb_device_info)); + if (!ret) { + return NULL; + } ret->devid = (cubeb_devid)(size_t)devid; ret->device_id = device_id_idx(devid); ret->friendly_name = _strdup(caps->szPname); diff --git a/media/libcubeb/tests/test_latency.cpp b/media/libcubeb/tests/test_latency.cpp index 5b4da8e7da2a..5e58f87292b7 100644 --- a/media/libcubeb/tests/test_latency.cpp +++ b/media/libcubeb/tests/test_latency.cpp @@ -2,7 +2,7 @@ #undef NDEBUG #endif #include -#include +#include "cubeb/cubeb.h" #include #include #ifdef CUBEB_GECKO_BUILD diff --git a/media/libcubeb/tests/test_sanity.cpp b/media/libcubeb/tests/test_sanity.cpp index b09a4f955685..c6bd9de79589 100644 --- a/media/libcubeb/tests/test_sanity.cpp +++ b/media/libcubeb/tests/test_sanity.cpp @@ -43,7 +43,6 @@ static long test_data_callback(cubeb_stream * stm, void * user_ptr, const void * inputbuffer, void * outputbuffer, long nframes) { assert(stm && user_ptr == &dummy && outputbuffer && nframes > 0); - memset(outputbuffer, 0, nframes * sizeof(short)); #if (defined(_WIN32) || defined(__WIN32__)) memset(outputbuffer, 0, nframes * sizeof(float)); #else @@ -125,6 +124,9 @@ test_context_variables(void) params.channels = STREAM_CHANNELS; params.format = STREAM_FORMAT; params.rate = STREAM_RATE; +#if defined(__ANDROID__) + params.stream_type = CUBEB_STREAM_TYPE_MUSIC; +#endif r = cubeb_get_min_latency(ctx, params, &value); assert(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED); if (r == CUBEB_OK) { @@ -158,6 +160,9 @@ test_init_destroy_stream(void) params.format = STREAM_FORMAT; params.rate = STREAM_RATE; params.channels = STREAM_CHANNELS; +#if defined(__ANDROID__) + params.stream_type = CUBEB_STREAM_TYPE_MUSIC; +#endif r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY, test_data_callback, test_state_callback, &dummy); @@ -186,6 +191,9 @@ test_init_destroy_multiple_streams(void) params.format = STREAM_FORMAT; params.rate = STREAM_RATE; params.channels = STREAM_CHANNELS; +#if defined(__ANDROID__) + params.stream_type = CUBEB_STREAM_TYPE_MUSIC; +#endif for (i = 0; i < ARRAY_LENGTH(stream); ++i) { r = cubeb_stream_init(ctx, &stream[i], "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY, @@ -219,6 +227,9 @@ test_configure_stream(void) params.format = STREAM_FORMAT; params.rate = STREAM_RATE; params.channels = 2; // panning +#if defined(__ANDROID__) + params.stream_type = CUBEB_STREAM_TYPE_MUSIC; +#endif r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY, test_data_callback, test_state_callback, &dummy); @@ -252,6 +263,9 @@ test_init_start_stop_destroy_multiple_streams(int early, int delay_ms) params.format = STREAM_FORMAT; params.rate = STREAM_RATE; params.channels = STREAM_CHANNELS; +#if defined(__ANDROID__) + params.stream_type = CUBEB_STREAM_TYPE_MUSIC; +#endif for (i = 0; i < ARRAY_LENGTH(stream); ++i) { r = cubeb_stream_init(ctx, &stream[i], "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY, @@ -312,6 +326,9 @@ test_init_destroy_multiple_contexts_and_streams(void) params.format = STREAM_FORMAT; params.rate = STREAM_RATE; params.channels = STREAM_CHANNELS; +#if defined(__ANDROID__) + params.stream_type = CUBEB_STREAM_TYPE_MUSIC; +#endif for (i = 0; i < ARRAY_LENGTH(ctx); ++i) { r = cubeb_init(&ctx[i], "test_sanity"); @@ -352,6 +369,9 @@ test_basic_stream_operations(void) params.format = STREAM_FORMAT; params.rate = STREAM_RATE; params.channels = STREAM_CHANNELS; +#if defined(__ANDROID__) + params.stream_type = CUBEB_STREAM_TYPE_MUSIC; +#endif r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY, test_data_callback, test_state_callback, &dummy); @@ -401,6 +421,9 @@ test_stream_position(void) params.format = STREAM_FORMAT; params.rate = STREAM_RATE; params.channels = STREAM_CHANNELS; +#if defined(__ANDROID__) + params.stream_type = CUBEB_STREAM_TYPE_MUSIC; +#endif r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY, test_data_callback, test_state_callback, &dummy); @@ -484,7 +507,11 @@ test_drain_data_callback(cubeb_stream * stm, void * user_ptr, const void * input } /* once drain has started, callback must never be called again */ assert(do_drain != 2); +#if (defined(_WIN32) || defined(__WIN32__)) + memset(outputbuffer, 0, nframes * sizeof(float)); +#else memset(outputbuffer, 0, nframes * sizeof(short)); +#endif total_frames_written += nframes; return nframes; } @@ -517,6 +544,9 @@ test_drain(void) params.format = STREAM_FORMAT; params.rate = STREAM_RATE; params.channels = STREAM_CHANNELS; +#if defined(__ANDROID__) + params.stream_type = CUBEB_STREAM_TYPE_MUSIC; +#endif r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY, test_drain_data_callback, test_drain_state_callback, &dummy);