Fix sources sometimes playing in the wrong channels

For example, if a stereo source starts playing on an odd-numbered call
to DynamicMixer::next() then the output stream will play its first
sample in the right channel, its second in the left, etc. This is wrong.
The first sample should be played in the left channel.
This commit is contained in:
Chris Patuzzo 2021-07-22 20:21:03 +01:00 committed by est31
parent 2e08a7efa6
commit 437bcf4ce7

View file

@ -30,6 +30,8 @@ where
let output = DynamicMixer { let output = DynamicMixer {
current_sources: Vec::with_capacity(16), current_sources: Vec::with_capacity(16),
input: input.clone(), input: input.clone(),
sample_count: 0,
still_pending: vec![],
}; };
(input, output) (input, output)
@ -69,6 +71,12 @@ pub struct DynamicMixer<S> {
// The pending sounds. // The pending sounds.
input: Arc<DynamicMixerController<S>>, input: Arc<DynamicMixerController<S>>,
// The number of samples produced so far.
sample_count: usize,
// A temporary vec used in start_pending_sources.
still_pending: Vec<Box<dyn Source<Item = S> + Send>>,
} }
impl<S> Source for DynamicMixer<S> impl<S> Source for DynamicMixer<S>
@ -105,12 +113,11 @@ where
#[inline] #[inline]
fn next(&mut self) -> Option<S> { fn next(&mut self) -> Option<S> {
if self.input.has_pending.load(Ordering::SeqCst) { if self.input.has_pending.load(Ordering::SeqCst) {
// TODO: relax ordering? self.start_pending_sources();
let mut pending = self.input.pending_sources.lock().unwrap();
self.current_sources.extend(pending.drain(..));
self.input.has_pending.store(false, Ordering::SeqCst); // TODO: relax ordering?
} }
self.sample_count += 1;
if self.current_sources.is_empty() { if self.current_sources.is_empty() {
return None; return None;
} }
@ -143,6 +150,33 @@ where
} }
} }
impl<S> DynamicMixer<S>
where
S: Sample + Send + 'static,
{
// Samples from the #next() function are interlaced for each of the channels.
// We need to ensure we start playing sources so that their samples are
// in-step with the modulo of the samples produced so far. Otherwise, the
// sound will play on the wrong channels, e.g. left / right will be reversed.
fn start_pending_sources(&mut self) {
let mut pending = self.input.pending_sources.lock().unwrap(); // TODO: relax ordering?
for source in pending.drain(..) {
let in_step = self.sample_count % source.channels() as usize == 0;
if in_step {
self.current_sources.push(source);
} else {
self.still_pending.push(source);
}
}
std::mem::swap(&mut self.still_pending, &mut pending);
let has_pending = !pending.is_empty();
self.input.has_pending.store(has_pending, Ordering::SeqCst); // TODO: relax ordering?
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::buffer::SamplesBuffer; use crate::buffer::SamplesBuffer;