From 025b515bfed3c0d8c742b8fa069183d93cc18fe4 Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Wed, 29 Jun 2016 14:27:01 +0200 Subject: [PATCH 01/15] cubeb.h - Use frames for the different latency in external interfaces. --- include/cubeb/cubeb.h | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/include/cubeb/cubeb.h b/include/cubeb/cubeb.h index 9bc06fb..95118dc 100644 --- a/include/cubeb/cubeb.h +++ b/include/cubeb/cubeb.h @@ -32,10 +32,10 @@ extern "C" { cubeb_init(&app_ctx, "Example Application"); int rv; int rate; - int latency_ms; + int latency_frames; uint64_t ts; - rv = cubeb_get_min_latency(app_ctx, output_params, &latency_ms); + rv = cubeb_get_min_latency(app_ctx, output_params, &latency_frames); if (rv != CUBEB_OK) { fprintf(stderr, "Could not get minimum latency"); return rv; @@ -61,7 +61,7 @@ extern "C" { rv = cubeb_stream_init(app_ctx, &stm, "Example Stream 1", NULL, input_params, NULL, output_params, - latency_ms, + latency_frames, data_cb, state_cb, NULL); if (rv != CUBEB_OK) { @@ -284,8 +284,8 @@ typedef struct { unsigned int max_rate; /**< Maximum sample rate supported. */ unsigned int min_rate; /**< Minimum sample rate supported. */ - unsigned int latency_lo_ms; /**< Lowest possible latency in milliseconds. */ - unsigned int latency_hi_ms; /**< Higest possible latency in milliseconds. */ + unsigned int latency_lo; /**< Lowest possible latency in frames. */ + unsigned int latency_hi; /**< Higest possible latency in frames. */ } cubeb_device_info; /** Device collection. */ @@ -363,19 +363,20 @@ char const * cubeb_get_backend_id(cubeb * context); @retval CUBEB_ERROR */ int cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels); -/** Get the minimal latency value, in milliseconds, that is guaranteed to work +/** Get the minimal latency value, in frames, that is guaranteed to work when creating a stream for the specified sample rate. This is platform, hardware and backend dependant. @param context A pointer to the cubeb context. @param params On some backends, the minimum achievable latency depends on the characteristics of the stream. - @param latency_ms The latency value, in ms, to pass to cubeb_stream_init. + @param latency_frames The latency value, in frames, to pass to + cubeb_stream_init. @retval CUBEB_OK @retval CUBEB_ERROR_INVALID_PARAMETER @retval CUBEB_ERROR_NOT_SUPPORTED */ int cubeb_get_min_latency(cubeb * context, cubeb_stream_params params, - uint32_t * latency_ms); + uint32_t * latency_frames); /** Get the preferred sample rate for this backend: this is hardware and platform dependant, and can avoid resampling, and/or trigger fastpaths. @@ -404,8 +405,8 @@ void cubeb_destroy(cubeb * context); default output device is used. @param output_stream_params Parameters for the output side of the stream, or NULL if this stream is input only. - @param latency Approximate stream latency in milliseconds. Valid range - is [1, 2000]. + @param latency Stream latency in frames. Valid range + is [1, 96000]. @param data_callback Will be called to preroll data before playback is started by cubeb_stream_start. @param state_callback A pointer to a state callback. @@ -422,7 +423,7 @@ int cubeb_stream_init(cubeb * context, cubeb_stream_params * input_stream_params, cubeb_devid output_device, cubeb_stream_params * output_stream_params, - unsigned int latency, + unsigned int latency_frames, cubeb_data_callback data_callback, cubeb_state_callback state_callback, void * user_ptr); From 01f43ff023c81f8464912db9b6074cd68f9128d0 Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Wed, 29 Jun 2016 15:36:54 +0200 Subject: [PATCH 02/15] Adjust the latency validation in cubeb.c to reflect the interface change. --- src/cubeb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cubeb.c b/src/cubeb.c index 2fc3860..f89a8f6 100644 --- a/src/cubeb.c +++ b/src/cubeb.c @@ -107,7 +107,7 @@ validate_stream_params(cubeb_stream_params * input_stream_params, int validate_latency(int latency) { - if (latency < 1 || latency > 2000) { + if (latency < 1 || latency > 96000) { return CUBEB_ERROR_INVALID_PARAMETER; } return CUBEB_OK; From 46584e72bd5df24d41634013c2fbe938195e690c Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Wed, 29 Jun 2016 14:48:59 +0200 Subject: [PATCH 03/15] Update alsa backend. --- src/cubeb_alsa.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/cubeb_alsa.c b/src/cubeb_alsa.c index d9794e0..26ae393 100644 --- a/src/cubeb_alsa.c +++ b/src/cubeb_alsa.c @@ -774,7 +774,7 @@ alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name, cubeb_stream_params * input_stream_params, cubeb_devid output_device, cubeb_stream_params * output_stream_params, - unsigned int latency, + unsigned int latency_frames, cubeb_data_callback data_callback, cubeb_state_callback state_callback, void * user_ptr) { @@ -782,6 +782,8 @@ alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name, int r; snd_pcm_format_t format; snd_pcm_uframes_t period_size; + int latency_us = 0; + assert(ctx && stream); @@ -845,16 +847,19 @@ alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name, r = snd_pcm_nonblock(stm->pcm, 1); assert(r == 0); + latency_us = latency_frames * 1e6 / stm->params.rate; + /* Ugly hack: the PA ALSA plugin allows buffer configurations that can't possibly work. See https://bugzilla.mozilla.org/show_bug.cgi?id=761274. Only resort to this hack if the handle_underrun workaround failed. */ if (!ctx->local_config && ctx->is_pa) { - latency = latency < 500 ? 500 : latency; + const int min_latency = 5e5; + latency_us = latency_us < min_latency ? min_latency: latency_us; } r = snd_pcm_set_params(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED, stm->params.channels, stm->params.rate, 1, - latency * 1000); + latency_us); if (r < 0) { alsa_stream_destroy(stm); return CUBEB_ERROR_INVALID_FORMAT; @@ -999,11 +1004,11 @@ alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) { } static int -alsa_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms) +alsa_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames) { - /* This is found to be an acceptable minimum, even on a super low-end + /* 40ms is found to be an acceptable minimum, even on a super low-end * machine. */ - *latency_ms = 40; + *latency_frames = 40 * params.rate / 1000; return CUBEB_OK; } From dd2a89d90d9ca0bceab07a9c541231d2851ab870 Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Wed, 29 Jun 2016 15:03:39 +0200 Subject: [PATCH 04/15] Don't fail the device enumeration test on backend where device enumeration is not supported. --- test/test_devices.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/test_devices.c b/test/test_devices.c index 9c502c0..7a7b84e 100644 --- a/test/test_devices.c +++ b/test/test_devices.c @@ -125,6 +125,12 @@ run_enumerate_devices(void) cubeb_get_backend_id(ctx)); r = cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_INPUT, &collection); + if (r == CUBEB_ERROR_NOT_SUPPORTED) { + fprintf(stderr, "Device enumeration not supported" + " for this backend, skipping this test.\n"); + r = CUBEB_OK; + goto cleanup; + } if (r != CUBEB_OK) { fprintf(stderr, "Error enumerating devices %d\n", r); goto cleanup; From 43bcb269993402c11a6247fdb1a5525f792d991d Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Wed, 29 Jun 2016 15:04:08 +0200 Subject: [PATCH 05/15] Update tests for the new latency interface. --- test/test_devices.c | 4 ++-- test/test_duplex.cpp | 6 +++--- test/test_sanity.cpp | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/test_devices.c b/test/test_devices.c index 7a7b84e..0a88bc4 100644 --- a/test/test_devices.c +++ b/test/test_devices.c @@ -89,14 +89,14 @@ print_device_info(cubeb_device_info * info, FILE * f) "\tCh: %u\n" "\tFormat: %s (0x%x) (default: %s)\n" "\tRate: %u - %u (default: %u)\n" - "\tLatency: lo %ums, hi %ums\n", + "\tLatency: lo %u frames, hi %u frames\n", info->device_id, info->preferred ? " (PREFERRED)" : "", info->friendly_name, info->group_id, info->vendor_name, devtype, devstate, info->max_channels, (devfmts[0] == ' ') ? &devfmts[1] : devfmts, (unsigned int)info->format, devdeffmt, info->min_rate, info->max_rate, info->default_rate, - info->latency_lo_ms, info->latency_hi_ms); + info->latency_lo, info->latency_hi); } static void diff --git a/test/test_duplex.cpp b/test/test_duplex.cpp index bc80366..85fbd3d 100644 --- a/test/test_duplex.cpp +++ b/test/test_duplex.cpp @@ -101,7 +101,7 @@ int main(int argc, char *argv[]) cubeb_stream_params output_params; int r; user_state stream_state = { false }; - uint32_t latency_ms = 0; + uint32_t latency_frames = 0; r = cubeb_init(&ctx, "Cubeb duplex example"); if (r != CUBEB_OK) { @@ -123,7 +123,7 @@ int main(int argc, char *argv[]) output_params.rate = 48000; output_params.channels = 2; - r = cubeb_get_min_latency(ctx, output_params, &latency_ms); + r = cubeb_get_min_latency(ctx, output_params, &latency_frames); if (r != CUBEB_OK) { fprintf(stderr, "Could not get minimal latency\n"); @@ -132,7 +132,7 @@ int main(int argc, char *argv[]) r = cubeb_stream_init(ctx, &stream, "Cubeb duplex", NULL, &input_params, NULL, &output_params, - latency_ms, data_cb, state_cb, &stream_state); + latency_frames, data_cb, state_cb, &stream_state); if (r != CUBEB_OK) { fprintf(stderr, "Error initializing cubeb stream\n"); return r; diff --git a/test/test_sanity.cpp b/test/test_sanity.cpp index c6bd9de..30c2d65 100644 --- a/test/test_sanity.cpp +++ b/test/test_sanity.cpp @@ -26,8 +26,8 @@ #define BEGIN_TEST fprintf(stderr, "START %s\n", __func__) #define END_TEST fprintf(stderr, "END %s\n", __func__) -#define STREAM_LATENCY 100 #define STREAM_RATE 44100 +#define STREAM_LATENCY 100 * STREAM_RATE / 1000 #define STREAM_CHANNELS 1 #if (defined(_WIN32) || defined(__WIN32__)) #define STREAM_FORMAT CUBEB_SAMPLE_FLOAT32LE From 645f58ed00945ec56711aa02d12f9b64bba381cf Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Wed, 29 Jun 2016 16:11:54 +0200 Subject: [PATCH 06/15] Update pulse backend. --- src/cubeb_pulse.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/cubeb_pulse.c b/src/cubeb_pulse.c index 6abf05c..a6658f3 100644 --- a/src/cubeb_pulse.c +++ b/src/cubeb_pulse.c @@ -582,10 +582,10 @@ pulse_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) } static int -pulse_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms) +pulse_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames) { // According to PulseAudio developers, this is a safe minimum. - *latency_ms = 25; + *latency_frames = 25 * params.rate / 1000; return CUBEB_OK; } @@ -670,12 +670,12 @@ create_pa_stream(cubeb_stream * stm, } static pa_buffer_attr -set_buffering_attribute(unsigned int latency, pa_sample_spec * sample_spec) +set_buffering_attribute(unsigned int latency_frames, pa_sample_spec * sample_spec) { pa_buffer_attr battr; battr.maxlength = -1; battr.prebuf = -1; - battr.tlength = WRAP(pa_usec_to_bytes)(latency * PA_USEC_PER_MSEC, sample_spec); + battr.tlength = latency_frames * WRAP(pa_frame_size)(sample_spec); battr.minreq = battr.tlength / 4; battr.fragsize = battr.minreq; @@ -693,7 +693,7 @@ pulse_stream_init(cubeb * context, cubeb_stream_params * input_stream_params, cubeb_devid output_device, cubeb_stream_params * output_stream_params, - unsigned int latency, + unsigned int latency_frames, cubeb_data_callback data_callback, cubeb_state_callback state_callback, void * user_ptr) @@ -734,7 +734,7 @@ pulse_stream_init(cubeb * context, WRAP(pa_stream_set_state_callback)(stm->output_stream, stream_state_callback, stm); WRAP(pa_stream_set_write_callback)(stm->output_stream, stream_write_callback, stm); - battr = set_buffering_attribute(latency, &stm->output_sample_spec); + battr = set_buffering_attribute(latency_frames, &stm->output_sample_spec); WRAP(pa_stream_connect_playback)(stm->output_stream, output_device, &battr, @@ -757,7 +757,7 @@ pulse_stream_init(cubeb * context, WRAP(pa_stream_set_state_callback)(stm->input_stream, stream_state_callback, stm); WRAP(pa_stream_set_read_callback)(stm->input_stream, stream_read_callback, stm); - battr = set_buffering_attribute(latency, &stm->input_sample_spec); + battr = set_buffering_attribute(latency_frames, &stm->input_sample_spec); WRAP(pa_stream_connect_record)(stm->input_stream, input_device, &battr, @@ -1056,8 +1056,8 @@ pulse_sink_info_cb(pa_context * context, const pa_sink_info * info, devinfo->max_rate = PA_RATE_MAX; devinfo->default_rate = info->sample_spec.rate; - devinfo->latency_lo_ms = 25; - devinfo->latency_hi_ms = 400; + devinfo->latency_lo = 0; + devinfo->latency_hi = 0; pulse_ensure_dev_list_data_list_size (list_data); list_data->devinfo[list_data->count++] = devinfo; @@ -1116,8 +1116,8 @@ pulse_source_info_cb(pa_context * context, const pa_source_info * info, devinfo->max_rate = PA_RATE_MAX; devinfo->default_rate = info->sample_spec.rate; - devinfo->latency_lo_ms = 1; - devinfo->latency_hi_ms = 10; + devinfo->latency_lo = 0; + devinfo->latency_hi = 0; pulse_ensure_dev_list_data_list_size (list_data); list_data->devinfo[list_data->count++] = devinfo; From e562eaa439cac97b1bbe159f8dbee483852a8a19 Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Wed, 29 Jun 2016 16:27:15 +0200 Subject: [PATCH 07/15] Update jack backend. --- src/cubeb_jack.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/cubeb_jack.cpp b/src/cubeb_jack.cpp index 265dc79..8f32f3d 100644 --- a/src/cubeb_jack.cpp +++ b/src/cubeb_jack.cpp @@ -85,8 +85,8 @@ extern "C" } static char const * cbjack_get_backend_id(cubeb * context); static int cbjack_get_max_channel_count(cubeb * ctx, uint32_t * max_channels); -static int cbjack_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms); -static int cbjack_get_latency(cubeb_stream * stm, unsigned int * latency_ms); +static int cbjack_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames); +static int cbjack_get_latency(cubeb_stream * stm, unsigned int * latency_frames); static int cbjack_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate); static void cbjack_destroy(cubeb * context); static void cbjack_interleave_capture(cubeb_stream * stream, float **in, jack_nframes_t nframes, bool format_mismatch); @@ -102,7 +102,7 @@ static int cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char cons cubeb_stream_params * input_stream_params, cubeb_devid output_device, cubeb_stream_params * output_stream_params, - unsigned int latency, + unsigned int latency_frames, cubeb_data_callback data_callback, cubeb_state_callback state_callback, void * user_ptr); @@ -307,10 +307,8 @@ cbjack_graph_order_callback(void * arg) max_latency = 128; } - if (cbjack_get_preferred_sample_rate(ctx, &rate) == CUBEB_ERROR) - ctx->jack_latency = (max_latency * 1000) / 48000; - else - ctx->jack_latency = (max_latency * 1000) / rate; + ctx->jack_latency = max_latency; + return 0; } @@ -705,7 +703,7 @@ cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_ cubeb_stream_params * input_stream_params, cubeb_devid output_device, cubeb_stream_params * output_stream_params, - unsigned int latency, + unsigned int latency_frames, cubeb_data_callback data_callback, cubeb_state_callback state_callback, void * user_ptr) @@ -999,8 +997,8 @@ cbjack_enumerate_devices(cubeb * context, cubeb_device_type type, context->devinfo[i]->min_rate = rate; context->devinfo[i]->max_rate = rate; context->devinfo[i]->default_rate = rate; - context->devinfo[i]->latency_lo_ms = 1; - context->devinfo[i]->latency_hi_ms = 10; + context->devinfo[i]->latency_lo = 0; + context->devinfo[i]->latency_hi = 0; i++; } @@ -1020,8 +1018,8 @@ cbjack_enumerate_devices(cubeb * context, cubeb_device_type type, context->devinfo[i]->min_rate = rate; context->devinfo[i]->max_rate = rate; context->devinfo[i]->default_rate = rate; - context->devinfo[i]->latency_lo_ms = 1; - context->devinfo[i]->latency_hi_ms = 10; + context->devinfo[i]->latency_lo = 0; + context->devinfo[i]->latency_hi = 0; i++; } From e292d8cfb1f14c895678cf168ac1e0a6467f5799 Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Fri, 1 Jul 2016 09:15:39 +0200 Subject: [PATCH 08/15] Update AudioUnit backend. --- src/cubeb_audiounit.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/cubeb_audiounit.cpp b/src/cubeb_audiounit.cpp index 41deebf..f0928ba 100644 --- a/src/cubeb_audiounit.cpp +++ b/src/cubeb_audiounit.cpp @@ -705,7 +705,7 @@ audiounit_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) } static int -audiounit_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms) +audiounit_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames) { #if TARGET_OS_IPHONE //TODO: [[AVAudioSession sharedInstance] inputLatency] @@ -716,7 +716,7 @@ audiounit_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * la return CUBEB_ERROR; } - *latency_ms = ceil((latency_range.mMinimum * 1000 ) / params.rate); + *latency_frames = latency_range.mMinimum; #endif return CUBEB_OK; @@ -906,7 +906,7 @@ audiounit_stream_init(cubeb * context, cubeb_stream_params * input_stream_params, cubeb_devid output_device, cubeb_stream_params * output_stream_params, - unsigned int latency, + unsigned int latency_frames, cubeb_data_callback data_callback, cubeb_state_callback state_callback, void * user_ptr) @@ -1127,7 +1127,7 @@ audiounit_stream_init(cubeb * context, // Setting the latency doesn't work well for USB headsets (eg. plantronics). // Keep the default latency for now. #if 0 - buffer_size = latency / 1000.0 * ss.mSampleRate; + buffer_size = latency; /* Get the range of latency this particular device can work with, and clamp * the requested latency to this acceptable range. */ @@ -1768,11 +1768,11 @@ audiounit_create_device_from_hwdev(AudioObjectID devid, cubeb_device_type type) adr.mSelector = kAudioDevicePropertyBufferFrameSizeRange; size = sizeof(AudioValueRange); if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &range) == noErr) { - ret->latency_lo_ms = ((latency + range.mMinimum) * 1000) / ret->default_rate; - ret->latency_hi_ms = ((latency + range.mMaximum) * 1000) / ret->default_rate; + ret->latency_lo = latency + range.mMinimum; + ret->latency_hi = latency + range.mMaximum; } else { - ret->latency_lo_ms = 10; /* Default to 10ms */ - ret->latency_hi_ms = 100; /* Default to 100ms */ + ret->latency_lo = 10 * ret->default_rate / 1000; /* Default to 10ms */ + ret->latency_hi = 100 * ret->default_rate / 1000; /* Default to 100ms */ } return ret; From b0b5c4939e82dbcd04cf528e8bb371cf77bd0da7 Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Mon, 4 Jul 2016 14:11:22 +0200 Subject: [PATCH 09/15] Rename latency_ms to latency_frames in test_latency.cpp. --- test/test_latency.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test_latency.cpp b/test/test_latency.cpp index 5e58f87..558ef95 100644 --- a/test/test_latency.cpp +++ b/test/test_latency.cpp @@ -21,7 +21,7 @@ int main(int argc, char * argv[]) int r; uint32_t max_channels; uint32_t preferred_rate; - uint32_t latency_ms; + uint32_t latency_frames; LOG("latency_test start"); r = cubeb_init(&ctx, "Cubeb audio test"); @@ -47,10 +47,10 @@ int main(int argc, char * argv[]) preferred_rate, max_channels }; - r = cubeb_get_min_latency(ctx, params, &latency_ms); + r = cubeb_get_min_latency(ctx, params, &latency_frames); assert(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED); if (r == CUBEB_OK) { - assert(latency_ms > 0 && "Invalid minimal latency."); + assert(latency_frames > 0 && "Invalid minimal latency."); LOG("cubeb_get_min_latency ok"); } From b8f755dc5b47353f7cf8e651d49d170d28a5e070 Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Mon, 4 Jul 2016 14:11:40 +0200 Subject: [PATCH 10/15] Update winmm backend. --- src/cubeb_winmm.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/cubeb_winmm.c b/src/cubeb_winmm.c index 746909b..65bb1ed 100644 --- a/src/cubeb_winmm.c +++ b/src/cubeb_winmm.c @@ -89,7 +89,7 @@ struct cubeb { PSLIST_HEADER work; CRITICAL_SECTION lock; unsigned int active_streams; - unsigned int minimum_latency; + unsigned int minimum_latency_ms; }; struct cubeb_stream { @@ -336,7 +336,7 @@ winmm_init(cubeb ** context, char const * context_name) InitializeCriticalSection(&ctx->lock); ctx->active_streams = 0; - ctx->minimum_latency = calculate_minimum_latency(); + ctx->minimum_latency_ms = calculate_minimum_latency(); *context = ctx; @@ -384,7 +384,7 @@ winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n cubeb_stream_params * input_stream_params, cubeb_devid output_device, cubeb_stream_params * output_stream_params, - unsigned int latency, + unsigned int latency_frames, cubeb_data_callback data_callback, cubeb_state_callback state_callback, void * user_ptr) @@ -467,11 +467,13 @@ winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n stm->user_ptr = user_ptr; stm->written = 0; - if (latency < context->minimum_latency) { - latency = context->minimum_latency; + uint32_t latency_ms = latency_frames * 1000 / output_stream_params->rate; + + if (latency_ms < context->minimum_latency_ms) { + latency_ms = context->minimum_latency_ms; } - bufsz = (size_t) (stm->params.rate / 1000.0 * latency * bytes_per_frame(stm->params) / NBUFS); + bufsz = (size_t) (stm->params.rate / 1000.0 * latency_ms * bytes_per_frame(stm->params) / NBUFS); if (bufsz % bytes_per_frame(stm->params) != 0) { bufsz += bytes_per_frame(stm->params) - (bufsz % bytes_per_frame(stm->params)); } @@ -600,7 +602,7 @@ static int winmm_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency) { // 100ms minimum, if we are not in a bizarre configuration. - *latency = ctx->minimum_latency; + *latency = ctx->minimum_latency_ms * params.rate / 1000; return CUBEB_OK; } @@ -854,8 +856,8 @@ winmm_create_device_from_outcaps2(LPWAVEOUTCAPS2A caps, UINT devid) &ret->format, &ret->default_format); /* Hardcoed latency estimates... */ - ret->latency_lo_ms = 100; - ret->latency_hi_ms = 200; + ret->latency_lo = 100 * ret->default_rate / 1000; + ret->latency_hi = 200 * ret->default_rate / 1000; return ret; } @@ -885,8 +887,8 @@ winmm_create_device_from_outcaps(LPWAVEOUTCAPSA caps, UINT devid) &ret->format, &ret->default_format); /* Hardcoed latency estimates... */ - ret->latency_lo_ms = 100; - ret->latency_hi_ms = 200; + ret->latency_lo = 100 * ret->default_rate / 1000; + ret->latency_hi = 200 * ret->default_rate / 1000; return ret; } @@ -935,8 +937,8 @@ winmm_create_device_from_incaps2(LPWAVEINCAPS2A caps, UINT devid) &ret->format, &ret->default_format); /* Hardcoed latency estimates... */ - ret->latency_lo_ms = 100; - ret->latency_hi_ms = 200; + ret->latency_lo = 100 * ret->default_rate / 1000; + ret->latency_hi = 200 * ret->default_rate / 1000; return ret; } @@ -966,8 +968,8 @@ winmm_create_device_from_incaps(LPWAVEINCAPSA caps, UINT devid) &ret->format, &ret->default_format); /* Hardcoed latency estimates... */ - ret->latency_lo_ms = 100; - ret->latency_hi_ms = 200; + ret->latency_lo = 100 * ret->default_rate / 1000; + ret->latency_hi = 200 * ret->default_rate / 1000; return ret; } From 9e60eb6bfd67fb7f211cc6b1ae62964f2bcdd1b1 Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Mon, 4 Jul 2016 14:11:57 +0200 Subject: [PATCH 11/15] Update wasapi backend. --- src/cubeb_wasapi.cpp | 88 +++++++++++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 30 deletions(-) diff --git a/src/cubeb_wasapi.cpp b/src/cubeb_wasapi.cpp index 044b5fe..dd5e4cd 100644 --- a/src/cubeb_wasapi.cpp +++ b/src/cubeb_wasapi.cpp @@ -62,23 +62,6 @@ DEFINE_PROPERTYKEY(PKEY_Device_InstanceId, 0x78c34fc8, 0x104a, 0x4aca, 0x9e (sizeof(array_) / sizeof(array_[0])) namespace { -uint32_t -ms_to_hns(uint32_t ms) -{ - return ms * 10000; -} - -uint32_t -hns_to_ms(REFERENCE_TIME hns) -{ - return static_cast(hns / 10000); -} - -double -hns_to_s(REFERENCE_TIME hns) -{ - return static_cast(hns) / 10000000; -} void SafeRelease(HANDLE handle) @@ -240,7 +223,7 @@ struct cubeb_stream /* The input and output device, or NULL for default. */ cubeb_devid input_device; cubeb_devid output_device; - /* The latency initially requested for this stream. */ + /* The latency initially requested for this stream, in frames. */ unsigned latency; cubeb_state_callback state_callback; cubeb_data_callback data_callback; @@ -438,6 +421,50 @@ double stream_to_mix_samplerate_ratio(cubeb_stream_params & stream, cubeb_stream return double(stream.rate) / mixer.rate; } + +uint32_t +get_rate(cubeb_stream * stm) +{ + return has_input(stm) ? stm->input_stream_params.rate + : stm->output_stream_params.rate; +} + +uint32_t +ms_to_hns(uint32_t ms) +{ + return ms * 10000; +} + +uint32_t +hns_to_ms(REFERENCE_TIME hns) +{ + return static_cast(hns / 10000); +} + +double +hns_to_s(REFERENCE_TIME hns) +{ + return static_cast(hns) / 10000000; +} + +uint32_t +hns_to_frames(cubeb_stream * stm, REFERENCE_TIME hns) +{ + return hns_to_ms(hns * get_rate(stm)) / 1000; +} + +uint32_t +hns_to_frames(uint32_t rate, REFERENCE_TIME hns) +{ + return hns_to_ms(hns * rate) / 1000; +} + +REFERENCE_TIME +frames_to_hns(cubeb_stream * stm, uint32_t frames) +{ + return frames * 1000 / get_rate(stm); +} + /* Upmix function, copies a mono channel into L and R */ template void @@ -1244,7 +1271,7 @@ wasapi_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) } int -wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms) +wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames) { HRESULT hr; IAudioClient * client; @@ -1287,7 +1314,8 @@ wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * laten /* According to the docs, the best latency we can achieve is by synchronizing the stream and the engine. http://msdn.microsoft.com/en-us/library/windows/desktop/dd370871%28v=vs.85%29.aspx */ - *latency_ms = hns_to_ms(default_period); + + *latency_frames = hns_to_frames(params.rate, default_period); SafeRelease(client); @@ -1476,7 +1504,7 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm, hr = (*audio_client)->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, - ms_to_hns(stm->latency), + frames_to_hns(stm, stm->latency), 0, mix_format, NULL); @@ -1642,7 +1670,7 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream, cubeb_stream_params * input_stream_params, cubeb_devid output_device, cubeb_stream_params * output_stream_params, - unsigned int latency, cubeb_data_callback data_callback, + unsigned int latency_frames, cubeb_data_callback data_callback, cubeb_state_callback state_callback, void * user_ptr) { HRESULT hr; @@ -1652,7 +1680,7 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream, return CUBEB_ERROR; } - XASSERT(context && stream); + XASSERT(context && stream && (input_stream_params || output_stream_params)); if (output_stream_params && output_stream_params->format != CUBEB_SAMPLE_FLOAT32NE || input_stream_params && input_stream_params->format != CUBEB_SAMPLE_FLOAT32NE) { @@ -1676,7 +1704,8 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream, stm->output_stream_params = *output_stream_params; stm->output_device = output_device; } - stm->latency = latency; + + stm->latency = latency_frames; stm->volume = 1.0; stm->stream_reset_lock = new owned_critical_section(); @@ -1947,8 +1976,7 @@ int wasapi_stream_get_latency(cubeb_stream * stm, uint32_t * latency) if (FAILED(hr)) { return CUBEB_ERROR; } - double latency_s = hns_to_s(latency_hns); - *latency = static_cast(latency_s * stm->output_stream_params.rate); + *latency = hns_to_frames(stm, latency_hns); return CUBEB_OK; } @@ -2141,11 +2169,11 @@ wasapi_create_device(IMMDeviceEnumerator * enumerator, IMMDevice * dev) if (SUCCEEDED(dev->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&client)) && SUCCEEDED(client->GetDevicePeriod(&def_period, &min_period))) { - ret->latency_lo_ms = hns_to_ms(min_period); - ret->latency_hi_ms = hns_to_ms(def_period); + ret->latency_lo = hns_to_frames(ret->default_rate, min_period); + ret->latency_hi = hns_to_frames(ret->default_rate, def_period); } else { - ret->latency_lo_ms = 0; - ret->latency_hi_ms = 0; + ret->latency_lo = 0; + ret->latency_hi = 0; } SafeRelease(client); From 050f849d8474c0f35543e1f0f1857e60eb6f20a4 Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Tue, 5 Jul 2016 16:19:10 +0200 Subject: [PATCH 12/15] Update AudioTrack backend. --- src/cubeb_audiotrack.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/cubeb_audiotrack.c b/src/cubeb_audiotrack.c index 59e3be2..fe26034 100644 --- a/src/cubeb_audiotrack.c +++ b/src/cubeb_audiotrack.c @@ -250,9 +250,6 @@ audiotrack_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * l return CUBEB_ERROR; } - /* Convert to milliseconds. */ - *latency_ms = *latency_ms * 1000 / params.rate; - return CUBEB_OK; } From ae44ce98f88ae0955ff7888d608f5ce25c4b5cd4 Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Tue, 5 Jul 2016 16:33:08 +0200 Subject: [PATCH 13/15] update opensl backend --- src/cubeb_opensl.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/cubeb_opensl.c b/src/cubeb_opensl.c index ec0639c..061536b 100644 --- a/src/cubeb_opensl.c +++ b/src/cubeb_opensl.c @@ -425,7 +425,7 @@ opensl_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) } static int -opensl_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms) +opensl_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames) { /* https://android.googlesource.com/platform/ndk.git/+/master/docs/opensles/index.html * We don't want to deal with JNI here (and we don't have Java on b2g anyways), @@ -475,7 +475,7 @@ opensl_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * laten /* To get a fast track in Android's mixer, we need to be at the native * samplerate, which is device dependant. Some devices might be able to * resample when playing a fast track, but it's pretty rare. */ - *latency_ms = NBUFS * primary_buffer_size / (primary_sampling_rate / 1000); + *latency_frames = NBUFS * primary_buffer_size; dlclose(libmedia); @@ -502,7 +502,7 @@ opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name cubeb_stream_params * input_stream_params, cubeb_devid output_device, cubeb_stream_params * output_stream_params, - unsigned int latency, + unsigned int latency_frames, cubeb_data_callback data_callback, cubeb_state_callback state_callback, void * user_ptr) { @@ -517,11 +517,6 @@ opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name *stream = NULL; - if (output_stream_params->channels < 1 || output_stream_params->channels > 32 || - latency < 1 || latency > 2000) { - return CUBEB_ERROR_INVALID_FORMAT; - } - SLDataFormat_PCM format; format.formatType = SL_DATAFORMAT_PCM; @@ -627,7 +622,7 @@ opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name stm->outputrate = preferred_sampling_rate; stm->bytespersec = stm->outputrate * stm->framesize; - stm->queuebuf_len = (stm->bytespersec * latency) / (1000 * NBUFS); + stm->queuebuf_len = stm->framesize * latency / NBUFS; // round up to the next multiple of stm->framesize, if needed. if (stm->queuebuf_len % stm->framesize) { stm->queuebuf_len += stm->framesize - (stm->queuebuf_len % stm->framesize); From 098b9f21bf99036362839d00fcd59990bcb67c01 Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Tue, 5 Jul 2016 16:35:14 +0200 Subject: [PATCH 14/15] Update sndio backend. --- src/cubeb_sndio.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cubeb_sndio.c b/src/cubeb_sndio.c index 2b3242b..f3defec 100644 --- a/src/cubeb_sndio.c +++ b/src/cubeb_sndio.c @@ -177,7 +177,7 @@ sndio_stream_init(cubeb * context, cubeb_stream_params * input_stream_params, cubeb_devid output_device, cubeb_stream_params * output_stream_params, - unsigned int latency, + unsigned int latency_frames, cubeb_data_callback data_callback, cubeb_state_callback state_callback, void *user_ptr) @@ -222,7 +222,7 @@ sndio_stream_init(cubeb * context, } wpar.rate = output_stream_params->rate; wpar.pchan = output_stream_params->channels; - wpar.appbufsz = latency * wpar.rate / 1000; + wpar.appbufsz = latency_frames; if (!sio_setpar(s->hdl, &wpar) || !sio_getpar(s->hdl, &rpar)) { sio_close(s->hdl); free(s); @@ -290,7 +290,7 @@ static int sndio_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms) { // XXX Not yet implemented. - *latency_ms = 40; + *latency = 2048; return CUBEB_OK; } From 9bb6846d0302cc5e3daab08c81a21fd1e543d72c Mon Sep 17 00:00:00 2001 From: Paul Adenot Date: Tue, 5 Jul 2016 16:35:58 +0200 Subject: [PATCH 15/15] Update kai backend. --- src/cubeb_kai.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cubeb_kai.c b/src/cubeb_kai.c index c3c468e..e0531b3 100644 --- a/src/cubeb_kai.c +++ b/src/cubeb_kai.c @@ -241,7 +241,7 @@ kai_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency) { /* We have at least two buffers. One is being played, the other one is being filled. So there is as much latency as one buffer. */ - *latency = FRAME_SIZE * 1000 / params.rate; + *latency = FRAME_SIZE; return CUBEB_OK; }