From 437bcf4ce79dace5e112d1a7441c66a7e76c18f6 Mon Sep 17 00:00:00 2001 From: Chris Patuzzo Date: Thu, 22 Jul 2021 20:21:03 +0100 Subject: [PATCH] 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. --- src/dynamic_mixer.rs | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/src/dynamic_mixer.rs b/src/dynamic_mixer.rs index cc676d2..fc6c058 100644 --- a/src/dynamic_mixer.rs +++ b/src/dynamic_mixer.rs @@ -30,6 +30,8 @@ where let output = DynamicMixer { current_sources: Vec::with_capacity(16), input: input.clone(), + sample_count: 0, + still_pending: vec![], }; (input, output) @@ -69,6 +71,12 @@ pub struct DynamicMixer { // The pending sounds. input: Arc>, + + // The number of samples produced so far. + sample_count: usize, + + // A temporary vec used in start_pending_sources. + still_pending: Vec + Send>>, } impl Source for DynamicMixer @@ -105,12 +113,11 @@ where #[inline] fn next(&mut self) -> Option { if self.input.has_pending.load(Ordering::SeqCst) { - // TODO: relax ordering? - 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.start_pending_sources(); } + self.sample_count += 1; + if self.current_sources.is_empty() { return None; } @@ -143,6 +150,33 @@ where } } +impl DynamicMixer +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)] mod tests { use crate::buffer::SamplesBuffer;