1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
use super::wrappers::{AddSound, ChannelCountConverter, ClearSounds, SampleRateConverter};
use crate::sound::NextSample;
use crate::Sound;
type MixedSound = SampleRateConverter<ChannelCountConverter<Box<dyn Sound>>>;
/// Mix multiple sounds together to be played simultaneously.
///
/// The [Manager][crate::manager::Manager] contains a SoundMixer so you might
/// not need to crate one yourself but instead add multiple sounds on the
/// Manager.
///
/// If a Sound returns an Error from next_sample, the error is logged and the
/// Sound is dropped but other sounds keep playing.
pub struct SoundMixer {
sounds: Vec<MixedSound>,
paused_sounds: Vec<MixedSound>,
output_channel_count: u16,
output_sample_rate: u32,
metadata_changed: bool,
next_output_channel_idx: u16,
}
impl SoundMixer {
/// Create a new empty sound mixer with an output channel count and sample
/// rate that all added sounds will be converted to.
pub fn new(output_channel_count: u16, output_sample_rate: u32) -> Self {
SoundMixer {
sounds: Vec::new(),
paused_sounds: Vec::new(),
output_channel_count,
output_sample_rate,
metadata_changed: false,
next_output_channel_idx: 0,
}
}
/// Set the output channel count and sample rate.
/// Added sounds will be converted to the output values. Must only be called
/// when the next sample is for the first channel in the frame.
pub fn set_output_channel_count_and_sample_rate(
&mut self,
output_channel_count: u16,
output_sample_rate: u32,
) {
self.metadata_changed = true;
self.output_channel_count = output_channel_count;
self.output_sample_rate = output_sample_rate;
// Now re-wrap all the sounds with the new values.
// Move all sounds to a single vec for simplicity
self.sounds.append(&mut self.paused_sounds);
let mut old = Vec::new();
std::mem::swap(&mut self.sounds, &mut old);
for mixed_sound in old {
let inner = mixed_sound.into_inner().into_inner();
// add will rewrap the sound
self.add(inner);
}
}
}
impl Sound for SoundMixer {
fn channel_count(&self) -> u16 {
self.output_channel_count
}
fn sample_rate(&self) -> u32 {
self.output_sample_rate
}
fn on_start_of_batch(&mut self) {
// Attempt to grab from paused sounds again
self.sounds.append(&mut self.paused_sounds);
for sound in &mut self.sounds {
sound.on_start_of_batch();
}
}
/// Guaranteed to not return an Error.
fn next_sample(&mut self) -> Result<crate::sound::NextSample, crate::Error> {
if self.metadata_changed {
assert!(self.next_output_channel_idx == 0);
self.metadata_changed = false;
return Ok(NextSample::MetadataChanged);
}
let mut output: i16 = 0;
let mut to_remove = Vec::new();
for (idx, sound) in self.sounds.iter_mut().enumerate() {
loop {
match sound.next_sample() {
Ok(NextSample::Sample(s)) => {
output = output.saturating_add(s);
break;
}
Ok(NextSample::MetadataChanged) => {
// We know that the channel_count and sample_rate haven't changed because
// we have wrapped the sound in converters. It is pausable that the
// MetadataChanged implies we need to start over at the first channel.
// Normally however Metadata only change on the first sample of a frame
// so handle that by looping around and calling next_sample again
// immediately
if self.next_output_channel_idx != 0 {
// In the rare case we see MetadataChange not on
// the first channel, lets pause the sound until the
// next batch to avoid de-syncing the channels.
to_remove.push((idx, true));
break;
}
}
Ok(NextSample::Paused) => {
to_remove.push((idx, true));
break;
}
Ok(NextSample::Finished) => {
to_remove.push((idx, false));
break;
}
Err(e) => {
// TODO probably want to let applications subscribe to be notified of these
// errors
log::error!("dropping sound in SoundMixer which returned error: {}", e);
to_remove.push((idx, false));
break;
}
}
}
}
for (idx, paused) in to_remove.into_iter().rev() {
let sound = self.sounds.swap_remove(idx);
if paused {
self.paused_sounds.push(sound);
}
// otherwise drop finished sound
}
self.next_output_channel_idx += 1;
if self.next_output_channel_idx == self.output_channel_count {
self.next_output_channel_idx = 0;
}
match (self.sounds.is_empty(), self.paused_sounds.is_empty()) {
// We assume that we are finished since this sound has been handed
// off to the Manager so new sounds can't be added without a
// Controllable. If this is wrapped in a Controllable, the Finished
// is changed to a Paused by the wrapper.
(true, true) => {
self.next_output_channel_idx = 0;
Ok(NextSample::Finished)
}
(true, false) => {
self.next_output_channel_idx = 0;
Ok(NextSample::Finished)
}
(false, _) => Ok(NextSample::Sample(output)),
}
}
}
impl AddSound for SoundMixer {
fn add(&mut self, sound: Box<dyn Sound>) {
self.sounds.push(SampleRateConverter::new(
ChannelCountConverter::new(sound, self.output_channel_count),
self.output_sample_rate,
));
}
}
impl ClearSounds for SoundMixer {
/// Remove all audio sounds.
fn clear(&mut self) {
self.sounds.clear();
self.paused_sounds.clear();
}
}
#[cfg(test)]
#[path = "./tests/sound_mixer.rs"]
mod tests;