First 512 samples of a source added to an empty Sink are always mono
To reproduce this issue, try following the music_wav example with a 2-channel WAV file with audio within the first ~11ms. Record the audio output and compare with the input. Instead of being the same, the first 11ms of the recorded output should be about double-length and mono (due to the stereo file being read as mono and converted to stereo.)
This seems to be related to the interaction between the mixer's UniformSourceIterator and SourcesQueueOutput, which plays "silence" at 1 channel when there are no sources. In particular, changing the "silence" played by SourcesQueueOutput from 1 channel to 2 channels (by editing my local copy of queue.rs) causes the sound to play normally.
I'm suspicious of the conditional logic on current_span_len -- I wonder if it's transitioning from silence to an actual sound improperly, and returning a span length of THRESHOLD on that borderline. But I can't investigate it right now, so I'll leave this issue here until I get the time to look at it further.
Looking through the code carefully, it does seem like SourcesQueueOutput has a fundamental problem with returning the previous sample_rate and channel count on source boundaries, as documented on the failing test. This fundamental issue relates to the general problems with span, documented in e.g. #694 , but I can't find another issue for this specific behavior.
It's actually pretty hard to find a completely satisfying solution to this that doesn't completely overhaul the Span system. Some ideas that occurred to me
- Peekable source iterator, so we know if we're on a source boundary or not
- ... but implementing Source on something peekable is hard, because you have to keep track of the previous metadata to return accurate results while peeking on a span boundary
- Use current_span_len() == 0 as a hint as to whether we're on a source boundary
- This isn't perfect, because, according to the current definition of Source, a Source can return current_span_len() as None to indicate a span that "stops when the Iterator ends", but there's actually no way we can possibly properly satisfy the span interface for sources like that, so I think this might be a reasonable tradeoff
I've got the second option working locally with that broken test passing, so I'll try submitting a PR