diff options
Diffstat (limited to 'media/libcubeb/src/cubeb_jack.cpp')
-rw-r--r-- | media/libcubeb/src/cubeb_jack.cpp | 693 |
1 files changed, 401 insertions, 292 deletions
diff --git a/media/libcubeb/src/cubeb_jack.cpp b/media/libcubeb/src/cubeb_jack.cpp index 8f995da661..9dc5199e71 100644 --- a/media/libcubeb/src/cubeb_jack.cpp +++ b/media/libcubeb/src/cubeb_jack.cpp @@ -8,53 +8,53 @@ */ #define _DEFAULT_SOURCE #define _BSD_SOURCE +#ifndef __FreeBSD__ #define _POSIX_SOURCE -#include <algorithm> +#endif +#include "cubeb-internal.h" +#include "cubeb/cubeb.h" +#include "cubeb_resampler.h" +#include "cubeb_utils.h" #include <dlfcn.h> -#include <limits> -#include <stdio.h> -#include <sys/time.h> -#include <assert.h> -#include <string.h> #include <limits.h> -#include <poll.h> -#include <unistd.h> -#include <stdlib.h> -#include <pthread.h> #include <math.h> -#include "cubeb/cubeb.h" -#include "cubeb-internal.h" -#include "cubeb_resampler.h" +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> #include <jack/jack.h> #include <jack/statistics.h> -#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) \ - X(jack_set_error_function) \ +#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) \ + X(jack_set_error_function) \ X(jack_set_info_function) #define IMPORT_FUNC(x) static decltype(x) * api_##x; JACK_API_VISIT(IMPORT_FUNC); +#define JACK_DEFAULT_IN "JACK capture" +#define JACK_DEFAULT_OUT "JACK playback" + static const int MAX_STREAMS = 16; -static const int MAX_CHANNELS = 8; +static const int MAX_CHANNELS = 8; static const int FIFO_SIZE = 4096 * sizeof(float); enum devstream { @@ -64,6 +64,12 @@ enum devstream { DUPLEX, }; +enum cbjack_connect_ports_options { + CBJACK_CP_OPTIONS_NONE = 0x0, + CBJACK_CP_OPTIONS_SKIP_OUTPUT = 0x1, + CBJACK_CP_OPTIONS_SKIP_INPUT = 0x2, +}; + static void s16ne_to_float(float * dst, const int16_t * src, size_t n) { @@ -75,79 +81,110 @@ 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; + 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); +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_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); -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_frames, - 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 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_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); +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_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, + cubeb_devid output_device, + cubeb_stream_params * output_stream_params, + unsigned int latency_frames, + 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 -}; + .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, + .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_get_position = cbjack_stream_get_position, + .stream_get_latency = cbjack_get_latency, + .stream_get_input_latency = NULL, + .stream_set_volume = cbjack_stream_set_volume, + .stream_set_name = 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 { + /* Note: Must match cubeb_stream layout in cubeb.c. */ cubeb * context; + void * user_ptr; + /**/ /**< Mutex for each stream */ pthread_mutex_t mutex; - bool in_use; /**< Set to false iff the stream is free */ + 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; @@ -183,7 +220,6 @@ struct cubeb { cubeb_stream streams[MAX_STREAMS]; unsigned int active_streams; - cubeb_device_info * devinfo[2]; cubeb_device_collection_changed_callback collection_changed_callback; bool active; @@ -203,25 +239,28 @@ load_jack_lib(cubeb * context) 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 +#ifdef _WIN64 + context->libjack = LoadLibrary("libjack64.dll"); +#else + context->libjack = LoadLibrary("libjack.dll"); +#endif #else context->libjack = dlopen("libjack.so.0", RTLD_LAZY); + if (!context->libjack) { + context->libjack = dlopen("libjack.so", 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; \ - } \ +#define LOAD(x) \ + { \ + api_##x = (decltype(x) *)dlsym(context->libjack, #x); \ + if (!api_##x) { \ + dlclose(context->libjack); \ + return CUBEB_ERROR; \ + } \ } JACK_API_VISIT(LOAD); @@ -231,41 +270,72 @@ load_jack_lib(cubeb * context) } static void -cbjack_connect_ports (cubeb_stream * stream) +cbjack_connect_port_out(cubeb_stream * stream, const size_t out_port, + const char * const phys_in_port) +{ + const char * src_port = api_jack_port_name(stream->output_ports[out_port]); + + api_jack_connect(stream->context->jack_client, src_port, phys_in_port); +} + +static void +cbjack_connect_port_in(cubeb_stream * stream, const char * const phys_out_port, + size_t in_port) { - 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) { + const char * src_port = api_jack_port_name(stream->input_ports[in_port]); + + api_jack_connect(stream->context->jack_client, phys_out_port, src_port); +} + +static int +cbjack_connect_ports(cubeb_stream * stream, + enum cbjack_connect_ports_options options) +{ + int r = CUBEB_ERROR; + 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 || *phys_in_ports == NULL || + options & CBJACK_CP_OPTIONS_SKIP_OUTPUT) { 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]); + for (unsigned int c = 0; + c < stream->out_params.channels && phys_in_ports[c] != NULL; c++) { + cbjack_connect_port_out(stream, c, phys_in_ports[c]); + } - api_jack_connect (stream->context->jack_client, src_port, phys_in_ports[c]); + // Special case playing mono source in stereo + if (stream->out_params.channels == 1 && phys_in_ports[1] != NULL) { + cbjack_connect_port_out(stream, 0, phys_in_ports[1]); } + r = CUBEB_OK; + skipplayback: - if (*phys_out_ports == NULL) { + if (phys_out_ports == NULL || *phys_out_ports == NULL || + options & CBJACK_CP_OPTIONS_SKIP_INPUT) { 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); + for (unsigned int c = 0; + c < stream->in_params.channels && phys_out_ports[c] != NULL; c++) { + cbjack_connect_port_in(stream, phys_out_ports[c], c); } + r = CUBEB_OK; end: - api_jack_free(phys_out_ports); - api_jack_free(phys_in_ports); + if (phys_out_ports) { + api_jack_free(phys_out_ports); + } + if (phys_in_ports) { + api_jack_free(phys_in_ports); + } + return r; } static int @@ -274,9 +344,10 @@ 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; + float fragments = ceilf(((delay / 1000000.0) * ctx->jack_sample_rate) / + ctx->jack_buffer_size); + + ctx->jack_xruns += (unsigned int)fragments; return 0; } @@ -289,7 +360,7 @@ cbjack_graph_order_callback(void * arg) jack_nframes_t port_latency, max_latency = 0; for (int j = 0; j < MAX_STREAMS; j++) { - cubeb_stream *stm = &ctx->streams[j]; + cubeb_stream * stm = &ctx->streams[j]; if (!stm->in_use) continue; @@ -297,10 +368,11 @@ cbjack_graph_order_callback(void * arg) continue; for (i = 0; i < (int)stm->out_params.channels; ++i) { - api_jack_port_get_latency_range(stm->output_ports[i], JackPlaybackLatency, &latency_range); + 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; + max_latency = port_latency; } /* Cap minimum latency to 128 frames */ if (max_latency < 128) @@ -316,22 +388,21 @@ static int cbjack_process(jack_nframes_t nframes, void * arg) { cubeb * ctx = (cubeb *)arg; - int t_jack_xruns = ctx->jack_xruns; + unsigned int t_jack_xruns = ctx->jack_xruns; int i; + ctx->jack_xruns = 0; + 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]; + 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; + stm->position += t_jack_xruns * ctx->fragment_size * stm->ratio; if (!stm->ports_ready) continue; @@ -339,18 +410,20 @@ cbjack_process(jack_nframes_t nframes, void * arg) 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); + 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); + 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]; + float * buffer_out = bufs_out[c]; for (long f = 0; f < nframes; f++) { buffer_out[f] = 0.f; } @@ -359,7 +432,7 @@ cbjack_process(jack_nframes_t nframes, void * arg) 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]; + float * buffer_in = bufs_in[c]; for (long f = 0; f < nframes; f++) { buffer_in[f] = 0.f; } @@ -370,31 +443,38 @@ cbjack_process(jack_nframes_t nframes, void * arg) // 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; + 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); + 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); + 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); + 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); + 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); + 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); + cbjack_deinterleave_playback_refill_float(stm, nullptr, bufs_out, + nframes); } } // unlock stream mutex @@ -405,7 +485,7 @@ cbjack_process(jack_nframes_t nframes, void * arg) // output silence if (stm->devs & OUT_ONLY) { for (unsigned int c = 0; c < stm->out_params.channels; c++) { - float* buffer_out = bufs_out[c]; + float * buffer_out = bufs_out[c]; for (long f = 0; f < nframes; f++) { buffer_out[f] = 0.f; } @@ -414,7 +494,7 @@ cbjack_process(jack_nframes_t nframes, void * arg) if (stm->devs & IN_ONLY) { // capture silence for (unsigned int c = 0; c < stm->in_params.channels; c++) { - float* buffer_in = bufs_in[c]; + float * buffer_in = bufs_in[c]; for (long f = 0; f < nframes; f++) { buffer_in[f] = 0.f; } @@ -426,9 +506,10 @@ cbjack_process(jack_nframes_t nframes, void * arg) return 0; } - static void -cbjack_deinterleave_playback_refill_float(cubeb_stream * stream, float ** in, float ** bufs_out, jack_nframes_t nframes) +cbjack_deinterleave_playback_refill_float(cubeb_stream * stream, float ** in, + float ** bufs_out, + jack_nframes_t nframes) { float * out_interleaved_buffer = nullptr; @@ -439,21 +520,24 @@ cbjack_deinterleave_playback_refill_float(cubeb_stream * stream, float ** in, fl 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); - 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; + 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]; + 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; + buffer[f] = + out_interleaved_buffer[(f * stream->out_params.channels) + c] * + stream->volume; } if (done_frames < needed_frames) { // draining @@ -487,7 +571,9 @@ cbjack_deinterleave_playback_refill_float(cubeb_stream * stream, float ** in, fl } static void -cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream, short ** in, float ** bufs_out, jack_nframes_t nframes) +cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream, short ** in, + float ** bufs_out, + jack_nframes_t nframes) { float * out_interleaved_buffer = nullptr; @@ -498,22 +584,28 @@ cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream, short ** in, fl 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); + 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); + 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; + 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]; + 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; + buffer[f] = + out_interleaved_buffer[(f * stream->out_params.channels) + c] * + stream->volume; } if (done_frames < needed_frames) { // draining @@ -547,20 +639,25 @@ cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream, short ** in, fl } static void -cbjack_interleave_capture(cubeb_stream * stream, float **in, jack_nframes_t nframes, bool format_mismatch) +cbjack_interleave_capture(cubeb_stream * stream, float ** in, + jack_nframes_t nframes, bool format_mismatch) { - float *in_buffer = stream->context->in_float_interleaved_buffer; + 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; + 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); + 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)); + 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)); } } @@ -570,7 +667,7 @@ silent_jack_error_callback(char const * /*msg*/) } /*static*/ int -jack_init (cubeb ** context, char const * context_name) +jack_init(cubeb ** context, char const * context_name) { int r; @@ -601,9 +698,8 @@ jack_init (cubeb ** context, char const * context_name) if (context_name) jack_client_name = context_name; - ctx->jack_client = api_jack_client_open(jack_client_name, - JackNoStartServer, - NULL); + ctx->jack_client = + api_jack_client_open(jack_client_name, JackNoStartServer, NULL); if (ctx->jack_client == NULL) { cbjack_destroy(ctx); @@ -612,11 +708,12 @@ jack_init (cubeb ** context, char const * context_name) 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); + 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)) { + if (api_jack_activate(ctx->jack_client)) { cbjack_destroy(ctx); return CUBEB_ERROR; } @@ -651,7 +748,8 @@ 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_ms) +cbjack_get_min_latency(cubeb * ctx, cubeb_stream_params /*params*/, + uint32_t * latency_ms) { *latency_ms = ctx->jack_latency; return CUBEB_OK; @@ -661,9 +759,8 @@ 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); + jack_client_t * testclient = + api_jack_client_open("test-samplerate", JackNoStartServer, NULL); if (!testclient) { return CUBEB_ERROR; } @@ -683,7 +780,7 @@ cbjack_destroy(cubeb * context) context->active = false; if (context->jack_client != NULL) - api_jack_client_close (context->jack_client); + api_jack_client_close(context->jack_client); if (context->libjack) dlclose(context->libjack); @@ -706,35 +803,42 @@ context_alloc_stream(cubeb * context, char const * stream_name) } static int -cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name, - cubeb_devid input_device, +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_frames*/, cubeb_data_callback data_callback, - cubeb_state_callback state_callback, - void * user_ptr) + 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) - ) { + 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) - ) { + if (input_stream_params && + (input_stream_params->format != CUBEB_SAMPLE_FLOAT32NE && + input_stream_params->format != CUBEB_SAMPLE_S16NE)) { return CUBEB_ERROR_INVALID_FORMAT; } - if (input_device || output_device) + if ((input_device && input_device != JACK_DEFAULT_IN) || + (output_device && output_device != JACK_DEFAULT_OUT)) { return CUBEB_ERROR_NOT_SUPPORTED; + } + + // Loopback is unsupported + if ((input_stream_params && + (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK)) || + (output_stream_params && + (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK))) { + return CUBEB_ERROR_NOT_SUPPORTED; + } *stream = NULL; @@ -812,29 +916,17 @@ cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_ 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); + 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); + 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); + 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) { @@ -847,11 +939,18 @@ cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_ 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); + stm->output_ports[c] = + api_jack_port_register(stm->context->jack_client, portname, + JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); + if (!(output_stream_params->prefs & + CUBEB_STREAM_PREF_JACK_NO_AUTO_CONNECT)) { + if (cbjack_connect_ports(stm, CBJACK_CP_OPTIONS_SKIP_INPUT) != + CUBEB_OK) { + pthread_mutex_unlock(&stm->mutex); + cbjack_stream_destroy(stm); + return CUBEB_ERROR; + } + } } } @@ -859,16 +958,21 @@ cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_ 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); + stm->input_ports[c] = + api_jack_port_register(stm->context->jack_client, portname, + JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); + if (!(input_stream_params->prefs & + CUBEB_STREAM_PREF_JACK_NO_AUTO_CONNECT)) { + if (cbjack_connect_ports(stm, CBJACK_CP_OPTIONS_SKIP_OUTPUT) != + CUBEB_OK) { + pthread_mutex_unlock(&stm->mutex); + cbjack_stream_destroy(stm); + return CUBEB_ERROR; + } + } } } - cbjack_connect_ports(stm); - *stream = stm; stm->ports_ready = true; @@ -887,7 +991,8 @@ cbjack_stream_destroy(cubeb_stream * stream) 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]); + api_jack_port_unregister(stream->context->jack_client, + stream->output_ports[c]); stream->output_ports[c] = NULL; } } @@ -896,7 +1001,8 @@ cbjack_stream_destroy(cubeb_stream * stream) 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]); + api_jack_port_unregister(stream->context->jack_client, + stream->input_ports[c]); stream->input_ports[c] = NULL; } } @@ -940,16 +1046,16 @@ cbjack_stream_set_volume(cubeb_stream * stm, float volume) return CUBEB_OK; } - static int -cbjack_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const device) +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 * j_in = JACK_DEFAULT_IN; + const char * j_out = JACK_DEFAULT_OUT; const char * empty = ""; if (stm->devs == DUPLEX) { @@ -967,8 +1073,7 @@ cbjack_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const devic } static int -cbjack_stream_device_destroy(cubeb_stream * /*stream*/, - cubeb_device * device) +cbjack_stream_device_destroy(cubeb_stream * /*stream*/, cubeb_device * device) { if (device->input_name) free(device->input_name); @@ -980,68 +1085,72 @@ cbjack_stream_device_destroy(cubeb_stream * /*stream*/, static int cbjack_enumerate_devices(cubeb * context, cubeb_device_type type, - cubeb_device_collection ** collection) + 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"; + + cubeb_device_info * devices = new cubeb_device_info[2]; + if (!devices) + return CUBEB_ERROR; + PodZero(devices, 2); + collection->count = 0; 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 = 0; - context->devinfo[i]->latency_hi = 0; - i++; + cubeb_device_info * cur = &devices[collection->count]; + cur->device_id = JACK_DEFAULT_OUT; + cur->devid = (cubeb_devid)cur->device_id; + 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; + cur->format = CUBEB_DEVICE_FMT_F32NE; + cur->default_format = CUBEB_DEVICE_FMT_F32NE; + cur->max_channels = MAX_CHANNELS; + cur->min_rate = rate; + cur->max_rate = rate; + cur->default_rate = rate; + cur->latency_lo = 0; + cur->latency_hi = 0; + collection->count += 1; } 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 = 0; - context->devinfo[i]->latency_hi = 0; - i++; + cubeb_device_info * cur = &devices[collection->count]; + cur->device_id = JACK_DEFAULT_IN; + cur->devid = (cubeb_devid)cur->device_id; + 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; + cur->format = CUBEB_DEVICE_FMT_F32NE; + cur->default_format = CUBEB_DEVICE_FMT_F32NE; + cur->max_channels = MAX_CHANNELS; + cur->min_rate = rate; + cur->max_rate = rate; + cur->default_rate = rate; + cur->latency_lo = 0; + cur->latency_hi = 0; + collection->count += 1; } - *collection = (cubeb_device_collection *) - malloc(sizeof(cubeb_device_collection) + - i * sizeof(cubeb_device_info *)); + collection->device = devices; - (*collection)->count = i; + return CUBEB_OK; +} - for (j = 0; j < i; j++) { - (*collection)->device[j] = context->devinfo[j]; - } +static int +cbjack_device_collection_destroy(cubeb * /*ctx*/, + cubeb_device_collection * collection) +{ + XASSERT(collection); + delete[] collection->device; return CUBEB_OK; } |