-
Notifications
You must be signed in to change notification settings - Fork 286
SourcesQueueOutput can peek metadata from next source #812
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
Conversation
channels and sample_rate currently return the old metadata on source boundaries, which is problematic for UniformSourceIterator, which checks metadata on span boundaries. This commit takes advantage of current_span_len's behavior to use it as a cheap "peek" to hopefully find out if we're done with the current source or not.
do_skip_duration's math was prone to rounding errors -- this commit shuffles the math around a bit so that the sample skip and the duration subtraction are each holding onto as much precision as possible.
The queue will now change samples and sample rate immediately on source boundaries that correctly report their current_span_len, so this test passes
unrelated to the rest of the PR, but since I'm fixing current_span_len, might as well do this too thanks @max-m
roderickvd
left a comment
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.
Thanks a lot for tackling this one! Sorry for the late response. Here are a few points and questions for further improvement.
| #[inline] | ||
| fn current_span_len(&self) -> Option<usize> { | ||
| None | ||
| Some(self.data.len() - self.pos) |
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.
The contract is that current_span_len returns the length of the span / buffer regardless of its current position. That's for size_hint to do.
| #[inline] | ||
| fn size_hint(&self) -> (usize, Option<usize>) { | ||
| (self.data.len(), Some(self.data.len())) | ||
| (self.data.len() - self.pos, Some(self.data.len() - self.pos)) |
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.
SamplesBuffer could therefore also implement ExactSizeIterator. I know that's not directly related to this change, but see it also doesn't implement it currently.
| fn channels(&self) -> ChannelCount { | ||
| self.current.channels() | ||
| // current_span_len() should never return 0 unless the source is empty, so this is a cheeky hint | ||
| if self.current.current_span_len() != Some(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.
I like the cheekiness 😄
How much sense do you think it would make to add an is_empty helper method to Source?
Fixes incorrect sample_rate() and channels() metadata returned by SourcesQueueOutput when transitioning between sources with different formats or from an empty queue. This caused audio distortion at boundaries where metadata changes occurred. Changes: - Clarified Source::current_span_len() contract: returns total span size, Some(0) when exhausted - Fixed SamplesBuffer to return Some(total_size) or Some(0) when exhausted (was None) - Fixed SamplesBuffer::size_hint() to return remaining samples - Updated SourcesQueueOutput to peek next source metadata when current is exhausted - Added Source::is_exhausted() helper method for cleaner exhaustion checks throughout codebase - Implemented ExactSizeIterator for SamplesBuffer - Improved SkipDuration precision to avoid rounding errors This supersedes PR #812 with a cleaner implementation following the proper current_span_len() contract. Enabled previously ignored queue::tests::basic test to prevent regressions. Fixes #811 Co-authored-by: 0----0 <9070490+0----0@users.noreply.github.com>
Fixes #811
SourcesQueueOutput currently violates the span interface by returning incorrect values for sample_rate and channels while the iterator is positioned on a source boundary (i.e. at the end of one source, before the next source has started.).
This PR adjusts its sample_rate and channels implementations so that they use current_span_len == 0 as an admissible heuristic to check if the current source is empty. This means that it can obey the span interface contract as long as the sources are returning Some current_span_len values. (If they aren't, there's probably no way we can possibly obey that contract.)
In order to actually uncomment the
basictest for the queue, I also had to adjust theSamplesBufferimplementation to return a Some current_span_len value. This showed some unrelated rounding errors inSkipDuration, so I made unrelated adjustments to theSkipDurationmath to reduce rounding errors, just to keep tests passing.