Skip to content
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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

mdsitton
Copy link
Contributor

Work on #527

@mdsitton mdsitton changed the title *stream_get_latency changes, adds resampler latency, and fixes wasapi latency windows 10 *_stream_get_latency changes, adds resampler latency, and fixes wasapi latency windows 10 Aug 17, 2019
Copy link
Collaborator

@kinetiknz kinetiknz left a 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

@@ -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)
Copy link
Collaborator

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.

REFERENCE_TIME latency_hns;
HRESULT hr = stm->output_client->GetStreamLatency(&latency_hns);
if (FAILED(hr)) {
if (!has_output(stm))
Copy link
Collaborator

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?

@kinetiknz
Copy link
Collaborator

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.

@mdsitton
Copy link
Contributor Author

@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.

@padenot
Copy link
Collaborator

padenot commented Aug 29, 2019

I can add it, we have the right number.

@mdsitton
Copy link
Contributor Author

Alright, so other than the changes for OpenSL, and any rust backends that should be everything.

@padenot
Copy link
Collaborator

padenot commented Aug 29, 2019

I can add it, we have the right number.

#535

@achronop
Copy link
Contributor

achronop commented Sep 3, 2019

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 cubeb_resampler_latency method? are they sensible? Just curiosity since this is the first time that this method will be used.

@achronop
Copy link
Contributor

achronop commented Sep 3, 2019

Also, the title of the patch is not very descriptive and indicates a windows 10 fix which is not the case.

@@ -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()));
Copy link
Collaborator

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).

if (!stm->output_client) {
/* Verify that AudioClient has been initialized. */
if (!stm->output_client)
{
Copy link
Collaborator

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.

}
*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());
Copy link
Collaborator

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?

@mdsitton
Copy link
Contributor Author

I'll work on these changes when i get time. Which will probably be in a few weeks.

@mdsitton
Copy link
Contributor Author

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 🤞

@ChunMinChang
Copy link
Member

@mdsitton, any update?

@mdsitton
Copy link
Contributor Author

mdsitton commented Jun 2, 2020

I'll try to get back on this soon, maybe this weekend.

@mdsitton mdsitton changed the title *_stream_get_latency changes, adds resampler latency, and fixes wasapi latency windows 10 Compensate for resampler latency where it is utilized Jun 7, 2020
@mdsitton
Copy link
Contributor Author

mdsitton commented Jun 7, 2020

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.

@mdsitton
Copy link
Contributor Author

mdsitton commented Jun 7, 2020

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.
@mdsitton
Copy link
Contributor Author

@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 kinetiknz self-requested a review June 29, 2020 03:00
@mdsitton
Copy link
Contributor Author

@kinetiknz any update on when this will be reviewed?

@mdsitton
Copy link
Contributor Author

@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!

@padenot padenot self-requested a review September 15, 2020 08:41
@@ -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);
Copy link
Collaborator

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);
Copy link
Collaborator

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);
Copy link
Collaborator

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);
Copy link
Collaborator

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);
Copy link
Collaborator

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));
Copy link
Collaborator

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants