From 97bf554023c9e15357e7f1b891e10d987f7dadfd Mon Sep 17 00:00:00 2001 From: Matthew Sitton Date: Sun, 7 Jun 2020 12:53:28 -0500 Subject: [PATCH 1/2] Add resampler latency to latency and position calculations Fix issue with jack, it doesn't use smart pointers Fix jack compiler error --- src/cubeb_audiounit.cpp | 8 +++++--- src/cubeb_jack.cpp | 6 +++--- src/cubeb_opensl.c | 6 +++--- src/cubeb_wasapi.cpp | 24 ++++++++++++++++++------ 4 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/cubeb_audiounit.cpp b/src/cubeb_audiounit.cpp index 4b506d0d2..31bef2a55 100644 --- a/src/cubeb_audiounit.cpp +++ b/src/cubeb_audiounit.cpp @@ -2969,10 +2969,12 @@ static int audiounit_stream_get_position(cubeb_stream * stm, uint64_t * position) { assert(stm); - if (stm->current_latency_frames > stm->frames_played) { + uint32_t latency_frames = stm->total_output_latency_frames + cubeb_resampler_latency(stm->resampler.get()); + + if (latency_frames > stm->frames_played) { *position = 0; } else { - *position = stm->frames_played - stm->current_latency_frames; + *position = stm->frames_played - latency_frames; } return CUBEB_OK; } @@ -2984,7 +2986,7 @@ audiounit_stream_get_latency(cubeb_stream * stm, uint32_t * latency) //TODO return CUBEB_ERROR_NOT_SUPPORTED; #else - *latency = stm->total_output_latency_frames; + *latency = stm->total_output_latency_frames + cubeb_resampler_latency(stm->resampler.get()); return CUBEB_OK; #endif } diff --git a/src/cubeb_jack.cpp b/src/cubeb_jack.cpp index 2320b7391..2ec245b4d 100644 --- a/src/cubeb_jack.cpp +++ b/src/cubeb_jack.cpp @@ -658,9 +658,9 @@ cbjack_get_max_channel_count(cubeb * /*ctx*/, uint32_t * max_channels) } static int -cbjack_get_latency(cubeb_stream * stm, unsigned int * latency_ms) +cbjack_get_latency(cubeb_stream * stm, unsigned int * latency) { - *latency_ms = stm->context->jack_latency; + *latency = stm->context->jack_latency + ceil(cubeb_resampler_latency(stm->resampler) * stm->ratio); return CUBEB_OK; } @@ -955,7 +955,7 @@ cbjack_stream_stop(cubeb_stream * stream) static int cbjack_stream_get_position(cubeb_stream * stream, uint64_t * position) { - *position = stream->position; + *position = stream->position - ceil(cubeb_resampler_latency(stream->resampler) * stream->ratio); return CUBEB_OK; } diff --git a/src/cubeb_opensl.c b/src/cubeb_opensl.c index 65520cf3f..ff913c8a2 100644 --- a/src/cubeb_opensl.c +++ b/src/cubeb_opensl.c @@ -1669,7 +1669,7 @@ opensl_stream_get_position(cubeb_stream * stm, uint64_t * position) } uint64_t samplerate = stm->user_output_rate; - uint32_t output_latency = stm->output_latency_ms; + uint32_t output_latency = stm->output_latency_ms + ((double)cubeb_resampler_latency(stm->resampler) / stm->output_configured_rate); pthread_mutex_lock(&stm->mutex); int64_t maximum_position = stm->written * (int64_t)stm->user_output_rate / stm->output_configured_rate; @@ -1703,8 +1703,8 @@ opensl_stream_get_latency(cubeb_stream * stm, uint32_t * latency) uint32_t stream_latency_frames = stm->user_output_rate * (stm->output_latency_ms / 1000); - - return stream_latency_frames + cubeb_resampler_latency(stm->resampler); + uint32_t resampler_latency_frames = (uint32_t)ceil(cubeb_resampler_latency(stm->resampler) * ((double)stm->user_output_rate) / stm->output_configured_rate); + return stream_latency_frames + resampler_latency_frames; } int diff --git a/src/cubeb_wasapi.cpp b/src/cubeb_wasapi.cpp index 3cde0cc23..626e9b461 100644 --- a/src/cubeb_wasapi.cpp +++ b/src/cubeb_wasapi.cpp @@ -2596,12 +2596,15 @@ int wasapi_stream_get_position(cubeb_stream * stm, uint64_t * position) return CUBEB_ERROR; } + double resample_ratio = stream_to_mix_samplerate_ratio(stm->output_stream_params, stm->output_mix_params); + /* Calculate how far behind the current stream head the playback cursor is. */ - uint64_t stream_delay = static_cast(current_stream_delay(stm) * stm->output_stream_params.rate); + uint64_t resampler_delay = ceil(cubeb_resampler_latency(stm->resampler.get()) * resample_ratio); + uint64_t stream_delay = static_cast((current_stream_delay(stm) * stm->output_stream_params.rate)) + resampler_delay; /* Calculate the logical stream head in frames at the stream sample rate. */ uint64_t max_pos = stm->total_frames_written + - static_cast(round(stm->frames_written * stream_to_mix_samplerate_ratio(stm->output_stream_params, stm->output_mix_params))); + static_cast(round(stm->frames_written * resample_ratio)); *position = max_pos; if (stream_delay <= *position) { @@ -2637,13 +2640,18 @@ int wasapi_stream_get_latency(cubeb_stream * stm, uint32_t * latency) if (FAILED(hr)) { return CUBEB_ERROR; } + + + double resample_ratio = stream_to_mix_samplerate_ratio(stm->output_stream_params, stm->output_mix_params); + uint32_t resample_latency = ceil(cubeb_resampler_latency(stm->resampler.get()) * resample_ratio); + // This happens on windows 10: no error, but always 0 for latency. if (latency_hns == 0) { double delay_s = current_stream_delay(stm); - // convert to sample-frames - *latency = delay_s * stm->output_stream_params.rate; + // convert to output(mix) rate sample-frames + *latency = (delay_s * stm->output_mix_params.rate) + resample_latency; } else { - *latency = hns_to_frames(stm, latency_hns); + *latency = hns_to_frames(stm, latency_hns) + resample_latency; } return CUBEB_OK; @@ -2664,7 +2672,11 @@ int wasapi_stream_get_input_latency(cubeb_stream * stm, uint32_t * latency) return CUBEB_ERROR; } - *latency = hns_to_frames(stm, stm->input_latency_hns); + + double resample_ratio = stream_to_mix_samplerate_ratio(stm->output_stream_params, stm->output_mix_params); + uint32_t resample_latency = ceil(cubeb_resampler_latency(stm->resampler.get()) * resample_ratio); + + *latency = hns_to_frames(stm, stm->input_latency_hns) + resample_latency; return CUBEB_OK; } From b758e0d92bf0f804cc551471243b1a6016b5c743 Mon Sep 17 00:00:00 2001 From: Matthew Sitton Date: Wed, 10 Jun 2020 22:01:09 -0500 Subject: [PATCH 2/2] Make sure we scale the returned number of samples to what the user stream rate actually is. Main difference with audiounit is that the backend doesn't use the resampler all of the time. It is only used when the input and output sample rates differ. --- src/cubeb_audiounit.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/cubeb_audiounit.cpp b/src/cubeb_audiounit.cpp index 31bef2a55..86c0202e5 100644 --- a/src/cubeb_audiounit.cpp +++ b/src/cubeb_audiounit.cpp @@ -242,6 +242,7 @@ struct cubeb_stream { atomic current_latency_frames{ 0 }; atomic total_output_latency_frames { 0 }; unique_ptr resampler; + float resample_ratio; /* This is true if a device change callback is currently running. */ atomic switching_device{ false }; atomic buffer_size_change_state{ false }; @@ -2681,6 +2682,8 @@ audiounit_setup_stream(cubeb_stream * stm) input_unconverted_params.rate = stm->input_hw_rate; } + stm->resample_ratio = static_cast(stm->output_stream_params.rate) / target_sample_rate; + /* Create resampler. Output params are unchanged * because we do not need conversion on the output. */ stm->resampler.reset(cubeb_resampler_create(stm, @@ -2969,7 +2972,8 @@ static int audiounit_stream_get_position(cubeb_stream * stm, uint64_t * position) { assert(stm); - uint32_t latency_frames = stm->total_output_latency_frames + cubeb_resampler_latency(stm->resampler.get()); + uint32_t resample_latency = static_cast(ceil(cubeb_resampler_latency(stm->resampler.get()) * stm->resample_ratio)); + uint32_t latency_frames = stm->total_output_latency_frames + resample_latency; if (latency_frames > stm->frames_played) { *position = 0; @@ -2986,7 +2990,9 @@ audiounit_stream_get_latency(cubeb_stream * stm, uint32_t * latency) //TODO return CUBEB_ERROR_NOT_SUPPORTED; #else - *latency = stm->total_output_latency_frames + cubeb_resampler_latency(stm->resampler.get()); + uint32_t resample_latency = static_cast(ceil(cubeb_resampler_latency(stm->resampler.get()) * stm->resample_ratio)); + + *latency = stm->total_output_latency_frames + resample_latency; return CUBEB_OK; #endif }