-
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
Fix input_needed_for_output to not return one less than needed, causing draining down the line #811
base: master
Are you sure you want to change the base?
Conversation
…ng draining down the line The calculation was incorrect. This meant that in some circumstances, e.g. playing a 44.1Hz stream on a 96kHz interface, with a block of 441 frames on Windows, rounding was eventually returning 440 instead of 441 in the callback, leading to the stream entering the draining phase.
…ck sizes and sample-rates This might be a bit much, runtime is 25s debug ASAN, 6s opt, but should be thorough.
This fixes BMO#1940319. |
#include "cubeb/cubeb.h" | ||
#include "cubeb_log.h" | ||
#include "cubeb_resampler.h" |
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.
These should be outside #ifdef NOMINMAX
.
I'd put them beside the cubeb_resampler_internal.h include.
|
||
cubeb_resampler * resampler = cubeb_resampler_create( | ||
nullptr, nullptr, &out_params, source_rate, data_cb, nullptr, | ||
CUBEB_RESAMPLER_QUALITY_DEFAULT, CUBEB_RESAMPLER_RECLOCK_NONE); |
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.
CUBEB_RESAMPLER_QUALITY_VOIP should make the test run a bit faster.
I don't expect the resampler latency to affect any of the input output block size arithmetic after the prefill at intialization.
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.
I see now that github interprets review of a commit as review of the entire pull request.
Thank you for testing thoroughly.
I expect that this function should be able to be simple without special cases. The special cases make me suspect that something else may not be quite right.
int32_t resampled_frames_left = | ||
samples_to_frames(resampling_out_buffer.length()); |
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 explain in the commit message that resampling_out_buffer.length()
is removed from the equation because resampled frames are not carried over from one output()
operation to the next.
@@ -288,15 +288,18 @@ template <typename T> class cubeb_resampler_speex_one_way : public processor { | |||
assert(output_frame_count >= 0); // Check overflow |
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.
- exactly
output_frame_count
resampled frames. This can return a number- slightly bigger than what is strictly necessary, but it guaranteed that the
- number of output frames will be exactly equal. */
The description is not really describing what the function is doing.
Unless this function knows the internal state of the speex resampler, it cannot know how to produce exactly output_frame_count
resampled frames from the speex resampler.
- Consider resampling at a ratio of 2 input frames to 3 output frames. Sometimes an input of 1 frame will produce 1 frame of output, but sometimes 2 frames of output.
The cubeb_resampler_speex_one_way::output()
should return exactly output_frame_count
frames, but that is because output_frame_count
is passed to output()
.
The function can calculate a number of input frames that is known sufficient to produce (at least) output_frame_count
resampled frames.
if (input_frames_needed_frac < 0) { | ||
return 0; |
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.
resampling_ratio
is always non-negative, as is length()
, so float arithmetic will always produce a non-negative input_frames_needed_frac
, and so this path is never taken.
I'm uncomfortable with using float
arithmetic because I don't know that we can prove that n / m * (i * m) >= n * i
over the ranges of rates that we care about. Replacing resampling_ratio
with a fraction, with integer arithmetic, would provide assurance of this. We'd then want some checks on the ranges for the integer arithmetic.
if (static_cast<float>(unresampled_frames_left) > resampling_ratio && | ||
resampling_ratio > 1) { | ||
input_frame_needed -= unresampled_frames_left; |
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.
I can't think why unresampled_frames_left
should be ignored when less than resampling_ratio
nor when resampling_ratio <= 1
.
Can unresampled_frames_left
always be subtracted from input_frame_needed
?
The main defect that I see in the old code is that unresampled_frames_left
was subtracted from output_frame_count
rather than from an input frame count.
Did you observe a problem with that defect resolved?
If so, then what were the specific parameters?
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 was an attempt with always subtracting unresampled_frames_left
, which fails with 16000 -> 44100 wanting 96 output samples. I'm still looking into this:
https://pernos.co/debug/KvmgNpBzyzyI7mFKNzXHhw/index.html
The calculation was incorrect. This meant that in some circumstances,
e.g. playing a 44.1Hz stream on a 96kHz interface, with a block of 441
frames on Windows, rounding was eventually returning 440 instead of 441
in the callback, leading to the stream entering the draining phase.