diff --git a/src/cubeb_audiounit.cpp b/src/cubeb_audiounit.cpp index 4b506d0d2..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,10 +2972,13 @@ static int audiounit_stream_get_position(cubeb_stream * stm, uint64_t * position) { assert(stm); - if (stm->current_latency_frames > stm->frames_played) { + 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; } else { - *position = stm->frames_played - stm->current_latency_frames; + *position = stm->frames_played - latency_frames; } return CUBEB_OK; } @@ -2984,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; + 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 } 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; }