-
Notifications
You must be signed in to change notification settings - Fork 128
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Compensate for resampler latency where it is utilized #531
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like the OpenSL backend needs updating too, can you check please?
The Rust version of the CoreAudio backend will need to be adjusted too: https://github.com/ChunMinChang/cubeb-coreaudio-rs, cc @ChunMinChang
src/cubeb_wasapi.cpp
Outdated
@@ -2399,28 +2399,30 @@ int wasapi_stream_get_position(cubeb_stream * stm, uint64_t * position) | |||
return CUBEB_OK; | |||
} | |||
|
|||
int wasapi_stream_get_latency(cubeb_stream * stm, uint32_t * latency) | |||
int wasapi_stream_get_latency(cubeb_stream *stm, uint32_t *latency) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please retain the original formatting for all of this code.
src/cubeb_wasapi.cpp
Outdated
REFERENCE_TIME latency_hns; | ||
HRESULT hr = stm->output_client->GetStreamLatency(&latency_hns); | ||
if (FAILED(hr)) { | ||
if (!has_output(stm)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this check needed in addition to stm->output_client?
We probably also want to adjust cubeb_stream_get_position to account for resampler latency. Otherwise, cubeb_stream_get_latency will report a different latency value to the value we're using to adjust the position reported via cubeb_stream_get_position. |
@kinetiknz One thing for the opensl backend. There is no get_latency implementation at all currently. That's why i didn't do any changes there. |
I can add it, we have the right number. |
Alright, so other than the changes for OpenSL, and any rust backends that should be everything. |
|
Thank you for doing that. Have you tested it? Let me know if I can help with that? Also, did you check the numbers returned by |
Also, the title of the patch is not very descriptive and indicates a windows 10 fix which is not the case. |
src/cubeb_audiounit.cpp
Outdated
@@ -2972,7 +2972,7 @@ audiounit_stream_get_position(cubeb_stream * stm, uint64_t * position) | |||
if (stm->current_latency_frames > stm->frames_played) { | |||
*position = 0; | |||
} else { | |||
*position = stm->frames_played - stm->current_latency_frames; | |||
*position = stm->frames_played - (stm->current_latency_frames + cubeb_resampler_latency(stm->resampler.get())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's possible frames_played
is less than the latency subtracted here, so this could wrap around. We'll need to clamp to 0 here (and in the other backends the same way).
src/cubeb_wasapi.cpp
Outdated
if (!stm->output_client) { | ||
/* Verify that AudioClient has been initialized. */ | ||
if (!stm->output_client) | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move the { up to the previous line here and for the has_output check above.
src/cubeb_wasapi.cpp
Outdated
} | ||
*latency = hns_to_frames(stm, latency_hns); | ||
double delay = current_stream_delay(stm); | ||
*latency = std::ceil(delay * stm->output_mix_params.rate) + cubeb_resampler_latency(stm->resampler.get()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should just include the resampler delay inside the current_stream_delay implementation?
I'll work on these changes when i get time. Which will probably be in a few weeks. |
Just an update, haven't forgotten but I've started a new job and have been in the process of moving things are starting to settle down so hopefully soon 🤞 |
@mdsitton, any update? |
I'll try to get back on this soon, maybe this weekend. |
Looks like the bug with wasapi i was also fixing in this pr has already been fixed on master so i've pulled that one out of this. I've updated everything else mostly. One thing i need to do still is convert the audiounit code to convert the re-sampler frame latency to the stream sample freqency instead of the hw output frequency. I think there may be some issues here on the audiounit code though i need to dig through the code some more to verify. |
Tomorrow i'll pull out my macbook and work on the audiounit backend a bit closer. I haven't really tested the backends too much yet. Mostly just updating my previous code and reworking some parts. |
Fix issue with jack, it doesn't use smart pointers Fix jack compiler error
…ream 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.
@kinetiknz This should be ready to review again whenever you get the chance. #559 - AAudio PR will need to be worked on for this whenever this is merged as well as the rust version of audiounit. |
@kinetiknz any update on when this will be reviewed? |
@padenot Perhaps you could take a look at this then? been waiting on someone to review for 3 months now! I'd certainly like this to get merged if possible. Or to get further feedback on the change. Thanks! |
@@ -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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will underflow on stream start, you want to branch here and return 0 if this would be negative: if the stream has just started, it's still filling the buffers in the underlying layer, and nothing is being output. Because stream_get_position
is the time of what is being heard, then 0 is the correct answer in this situation.
/* Calculate how far behind the current stream head the playback cursor is. */ | ||
uint64_t stream_delay = static_cast<uint64_t>(current_stream_delay(stm) * stm->output_stream_params.rate); | ||
uint64_t resampler_delay = ceil(cubeb_resampler_latency(stm->resampler.get()) * resample_ratio); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is tricky. cubeb_resampler_latency
returns the output latency, i.e., the number of frames of latency, expressed in output rate, so we don't need this, because stream_get_position
returns its value in frames at the output (= stream) rate.
|
||
|
||
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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same logic applies here.
@@ -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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is input latency, and the input mix rate is frequently different from the output rate (for example because the microphone and the output device are two separate devices, that have been configured with different rates: this is sub-optimal, but happens).
*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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It appears that you can't write this function with the current API: cubeb_resampler
is a bidirectional resampler, that can simultaneously resample an input stream at rate A (microphone) and an output stream at rate B (speakers), both to a rate C (that can be equal to either A or B). There is nothing in the API that surfaces the input latency, I'll add it.
In general there is some work needed on the resampler, that became very clear when working on https://github.com/kinetiknz/cubeb/pull/598
@@ -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<uint32_t>(ceil(cubeb_resampler_latency(stm->resampler.get()) * stm->resample_ratio)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is already in logical stream rate, no need to adjust.
Work on #527