audio_engine/
mixer.rs

1use crate::{converter, SampleRate, SoundId, SoundSource};
2use std::{
3    collections::HashMap,
4    hash::Hash,
5    sync::atomic::{AtomicU64, Ordering},
6};
7
8fn next_id() -> SoundId {
9    static GLOBAL_COUNT: AtomicU64 = AtomicU64::new(0);
10    GLOBAL_COUNT.fetch_add(1, Ordering::Relaxed)
11}
12
13struct SoundInner<G = ()> {
14    id: SoundId,
15    data: Box<dyn SoundSource + Send>,
16    volume: f32,
17    group: G,
18    looping: bool,
19    drop: bool,
20}
21impl<G> SoundInner<G> {
22    fn new(group: G, data: Box<dyn SoundSource + Send>) -> Self {
23        Self {
24            id: next_id(),
25            data,
26            volume: 1.0,
27            group,
28            looping: false,
29            drop: true,
30        }
31    }
32}
33
34/// Keep track of each Sound, and mix they output together.
35pub struct Mixer<G: Eq + Hash + Send + 'static = ()> {
36    sounds: Vec<SoundInner<G>>,
37    playing: usize,
38    channels: u16,
39    sample_rate: SampleRate,
40    group_volumes: HashMap<G, f32>,
41}
42
43impl<G: Eq + Hash + Send + 'static> Mixer<G> {
44    /// Create a new Mixer.
45    ///
46    /// The created Mixer output samples with given sample rate and number of channels. This
47    /// configuration can be changed by calling [`set_config`](Self::set_config).
48    pub fn new(channels: u16, sample_rate: SampleRate) -> Self {
49        Self {
50            sounds: vec![],
51            playing: 0,
52            channels,
53            sample_rate,
54            group_volumes: HashMap::new(),
55        }
56    }
57
58    /// Change the number of channels and the sample rate.
59    ///
60    /// This keep also keep all currently playing sounds, and convert them to the new config, if
61    /// necessary.
62    pub fn set_config(&mut self, channels: u16, sample_rate: SampleRate) {
63        struct Nop;
64        #[rustfmt::skip]
65        impl SoundSource for Nop {
66            fn channels(&self) -> u16 { 0 }
67            fn sample_rate(&self) -> u32 { 0 }
68            fn reset(&mut self) { }
69            fn write_samples(&mut self, _: &mut [i16]) -> usize { 0 }
70        }
71
72        let not_chaged = self.channels == channels && self.sample_rate == sample_rate;
73        if not_chaged {
74            return;
75        }
76        if !self.sounds.is_empty() {
77            for sound in self.sounds.iter_mut() {
78                // FIXME: if the config change multiple times, this will nest multiple converts,
79                // increasing processing and loosing quality.
80                // Maybe I should create something like a tree of converters, and always keep the
81                // convertes Concrete.
82                if sound.data.channels() != channels {
83                    let inner = std::mem::replace(&mut sound.data, Box::new(Nop));
84                    sound.data = Box::new(converter::ChannelConverter::new(inner, channels));
85                }
86                if sound.data.sample_rate() != sample_rate.0 {
87                    let inner = std::mem::replace(&mut sound.data, Box::new(Nop));
88                    sound.data =
89                        Box::new(converter::SampleRateConverter::new(inner, sample_rate.0));
90                }
91            }
92        }
93        self.channels = channels;
94        self.sample_rate = sample_rate;
95    }
96
97    /// Add new sound to the Mixer.
98    ///
99    /// Return the SoundId associated with that sound. This Id is globally unique.
100    ///
101    /// The added sound is started in stopped state, and [`play`](Self::play) must be called to start playing
102    /// it. [`mark_to_remove`](Self::mark_to_remove) is true by default.
103    pub fn add_sound(&mut self, group: G, sound: Box<dyn SoundSource + Send>) -> SoundId {
104        let sound_inner = SoundInner::new(group, sound);
105        let id = sound_inner.id;
106        self.sounds.push(sound_inner);
107        id
108    }
109
110    /// Start playing the sound associated with the given id.
111    ///
112    /// If the sound was paused or stop, it will start playing again.
113    /// Otherwise, does nothing.
114    pub fn play(&mut self, id: SoundId) {
115        for i in (self.playing..self.sounds.len()).rev() {
116            if self.sounds[i].id == id {
117                self.sounds.swap(self.playing, i);
118                self.playing += 1;
119                break;
120            }
121        }
122    }
123
124    /// Pause the sound associated with the given id.
125    ///
126    /// If the sound is playing, it will pause. If play is called,
127    /// this sound will continue from where it was when paused.
128    /// If the sound is not playing, does nothing.
129    pub fn pause(&mut self, id: SoundId) {
130        for i in (0..self.playing).rev() {
131            if self.sounds[i].id == id {
132                self.playing -= 1;
133                self.sounds.swap(self.playing, i);
134                break;
135            }
136        }
137    }
138
139    /// Stop the sound associated with the given id.
140    ///
141    /// If the sound is playing, it will pause and reset the song. When play is called,
142    /// this sound will start from the begging.
143    ///
144    /// Even if the sound is not playing, it will reset the sound to the start. If the sound is
145    /// [marked to be removed](Self::mark_to_remove), this sound will be removed from the Mixer.
146    pub fn stop(&mut self, id: SoundId) {
147        for i in (0..self.sounds.len()).rev() {
148            if self.sounds[i].id == id {
149                if self.sounds[i].drop {
150                    self.sounds.swap_remove(i);
151                } else {
152                    self.sounds[i].data.reset();
153                }
154                if i < self.playing {
155                    self.playing -= 1;
156                    self.sounds.swap(self.playing, i);
157                }
158                break;
159            }
160        }
161    }
162
163    /// Reset the sound associated with the given id.
164    ///
165    /// This reset the sound to the start, the sound being playing or not.
166    pub fn reset(&mut self, id: SoundId) {
167        for i in (0..self.sounds.len()).rev() {
168            if self.sounds[i].id == id {
169                self.sounds[i].data.reset();
170                break;
171            }
172        }
173    }
174
175    /// Set if the sound associated with the given id will loop.
176    ///
177    /// If true, ever time the sound reachs its end, it will reset, and continue to play in a loop.
178    ///
179    /// This also set [`mark_to_remove`](Self::mark_to_remove) to false.
180    pub fn set_loop(&mut self, id: SoundId, looping: bool) {
181        for i in (0..self.sounds.len()).rev() {
182            if self.sounds[i].id == id {
183                self.sounds[i].looping = looping;
184                break;
185            }
186        }
187    }
188
189    /// Set the volume of the sound associated with the given id.
190    ///
191    /// The output samples of the SoundSource assicociated with the given id will be multiplied by
192    /// this volume.
193    pub fn set_volume(&mut self, id: SoundId, volume: f32) {
194        for i in (0..self.sounds.len()).rev() {
195            if self.sounds[i].id == id {
196                self.sounds[i].volume = volume;
197                break;
198            }
199        }
200    }
201
202    /// Set the volume of the given group.
203    ///
204    /// The volume of all sounds associated with this group is multiplied by this volume.
205    pub fn set_group_volume(&mut self, group: G, volume: f32) {
206        self.group_volumes.insert(group, volume);
207    }
208
209    /// Mark if the sound will be removed after it reachs its end.
210    ///
211    /// If false, it will be possible to reset the sound and play it again after it has already
212    /// reached its end. Otherwise, the sound will be removed when it reachs its end, even if it is
213    /// marked to loop.
214    pub fn mark_to_remove(&mut self, id: SoundId, drop: bool) {
215        for i in (0..self.sounds.len()).rev() {
216            if self.sounds[i].id == id {
217                self.sounds[i].drop = drop;
218                break;
219            }
220        }
221    }
222
223    /// The number of sounds in the mixer.
224    ///
225    /// This include the sounds that are currently stopped.
226    pub fn sound_count(&self) -> usize {
227        self.sounds.len()
228    }
229
230    /// The number of sounds being played currently.
231    ///
232    /// Does not include the sounds that are currently stopped.
233    pub fn playing_count(&self) -> usize {
234        self.playing
235    }
236}
237
238impl<G: Eq + Hash + Send + 'static> SoundSource for Mixer<G> {
239    fn channels(&self) -> u16 {
240        self.channels
241    }
242
243    fn sample_rate(&self) -> u32 {
244        self.sample_rate.0
245    }
246
247    fn reset(&mut self) {}
248
249    fn write_samples(&mut self, buffer: &mut [i16]) -> usize {
250        if self.playing == 0 {
251            for b in buffer.iter_mut() {
252                *b = 0;
253            }
254            return buffer.len();
255        }
256
257        let mut buf = vec![0; buffer.len()];
258        let mut s = 0;
259        while s < self.playing {
260            let mut len = 0;
261            loop {
262                len += self.sounds[s].data.write_samples(&mut buf[len..]);
263                if len < buffer.len() {
264                    self.sounds[s].data.reset();
265                    if self.sounds[s].looping {
266                        continue;
267                    }
268                }
269                break;
270            }
271
272            let group_volume = *self
273                .group_volumes
274                .get(&self.sounds[s].group)
275                .unwrap_or(&1.0);
276            let volume = self.sounds[s].volume * group_volume;
277
278            if (volume - 1.0).abs() < 1.0 / i16::max_value() as f32 {
279                for i in 0..len {
280                    buffer[i] = buffer[i].saturating_add(buf[i]);
281                }
282            } else {
283                for i in 0..len {
284                    buffer[i] = buffer[i].saturating_add((buf[i] as f32 * volume) as i16);
285                }
286            }
287
288            if len < buffer.len() {
289                if self.sounds[s].drop {
290                    let _ = self.sounds.swap_remove(s);
291                }
292                self.playing -= 1;
293                if self.playing > 0 && self.playing < self.sounds.len() {
294                    self.sounds.swap(s, self.playing);
295                }
296            } else {
297                s += 1;
298            }
299        }
300
301        buffer.len()
302    }
303}
304
305#[cfg(test)]
306mod test {
307    use crate::SoundSource;
308
309    use super::Mixer;
310
311    struct DebugSource {
312        i: usize,
313        v: i16,
314        len: usize,
315    }
316    impl DebugSource {
317        fn new(v: i16, len: usize) -> Self {
318            Self { i: 0, v, len }
319        }
320    }
321    impl SoundSource for DebugSource {
322        fn channels(&self) -> u16 {
323            1
324        }
325
326        fn sample_rate(&self) -> u32 {
327            1
328        }
329
330        fn reset(&mut self) {
331            self.i = 0;
332        }
333
334        fn write_samples(&mut self, buffer: &mut [i16]) -> usize {
335            for (i, o) in buffer.iter_mut().enumerate() {
336                *o = self.v;
337                self.i += 1;
338                if self.i > self.len {
339                    return i;
340                }
341            }
342            buffer.len()
343        }
344    }
345
346    #[test]
347    fn start_stopped() {
348        let mut mixer = Mixer::new(1, crate::SampleRate(1));
349
350        // create new sound
351        let id = mixer.add_sound((), Box::new(DebugSource::new(2, 5)));
352
353        // sound start in stopped state
354        let mut buffer = [0; 10];
355        assert_eq!(mixer.write_samples(&mut buffer), 10);
356        assert_eq!(buffer, [0; 10]);
357
358        // play the sound
359        mixer.play(id);
360        assert_eq!(mixer.write_samples(&mut buffer), 10);
361        assert_eq!(buffer, [2, 2, 2, 2, 2, 0, 0, 0, 0, 0]);
362
363        // mark to remove is true, sound was removed
364        mixer.reset(id);
365        mixer.play(id);
366        assert_eq!(mixer.write_samples(&mut buffer), 10);
367        assert_eq!(buffer, [0; 10]);
368
369        // create new sound
370        let id = mixer.add_sound((), Box::new(DebugSource::new(2, 5)));
371
372        mixer.stop(id);
373    }
374
375    #[test]
376    fn mark_to_remove_true() {
377        let mut mixer = Mixer::new(1, crate::SampleRate(1));
378
379        // create new sound
380        let id = mixer.add_sound((), Box::new(DebugSource::new(2, 5)));
381
382        // stop the sound will remove the sound if marked to remove is true
383        mixer.stop(id);
384
385        // mark to remove is true, sound was removed
386        mixer.reset(id);
387        mixer.play(id);
388        let mut buffer = [0; 10];
389        assert_eq!(mixer.write_samples(&mut buffer), 10);
390        assert_eq!(buffer, [0; 10]);
391    }
392
393    #[test]
394    fn mark_to_remove_false() {
395        let mut mixer = Mixer::new(1, crate::SampleRate(1));
396
397        let id = mixer.add_sound((), Box::new(DebugSource::new(2, 5)));
398        mixer.mark_to_remove(id, false);
399
400        // stop the sound will not remove the sound if marked to remove is false
401        mixer.stop(id);
402
403        // sound start in stopped state
404        let mut buffer = [0; 10];
405        assert_eq!(mixer.write_samples(&mut buffer), 10);
406        assert_eq!(buffer, [0; 10]);
407
408        // play the sound
409        mixer.play(id);
410        assert_eq!(mixer.playing_count(), 1);
411        assert_eq!(mixer.write_samples(&mut buffer), 10);
412        assert_eq!(buffer, [2, 2, 2, 2, 2, 0, 0, 0, 0, 0]);
413
414        // mark to remove is false
415        mixer.play(id);
416        buffer = [0; 10];
417        assert_eq!(mixer.playing_count(), 1);
418        assert_eq!(mixer.write_samples(&mut buffer), 10);
419        assert_eq!(buffer, [2, 2, 2, 2, 2, 0, 0, 0, 0, 0]);
420    }
421
422    #[test]
423    fn volume() {
424        let mut mixer = Mixer::new(1, crate::SampleRate(1));
425
426        let id0 = mixer.add_sound((), Box::new(DebugSource::new(10, 2)));
427        let id1 = mixer.add_sound((), Box::new(DebugSource::new(10, 4)));
428        let id2 = mixer.add_sound((), Box::new(DebugSource::new(10, 6)));
429
430        mixer.set_volume(id0, 0.2);
431        mixer.set_volume(id1, 0.4);
432        mixer.set_volume(id2, 0.8);
433
434        mixer.play(id0);
435        mixer.play(id1);
436        mixer.play(id2);
437
438        let mut buffer = [0; 10];
439        assert_eq!(mixer.write_samples(&mut buffer), 10);
440        assert_eq!(buffer, [14, 14, 12, 12, 8, 8, 0, 0, 0, 0]);
441    }
442
443    #[test]
444    fn group_volume() {
445        #[derive(Eq, Hash, PartialEq)]
446        enum Group {
447            A,
448            B,
449        }
450
451        let mut mixer = Mixer::new(1, crate::SampleRate(1));
452
453        let a0 = mixer.add_sound(Group::A, Box::new(DebugSource::new(10, 2)));
454        let a1 = mixer.add_sound(Group::A, Box::new(DebugSource::new(10, 4)));
455        let a2 = mixer.add_sound(Group::A, Box::new(DebugSource::new(10, 6)));
456
457        let b0 = mixer.add_sound(Group::B, Box::new(DebugSource::new(10, 8)));
458        let b1 = mixer.add_sound(Group::B, Box::new(DebugSource::new(10, 10)));
459        let b2 = mixer.add_sound(Group::B, Box::new(DebugSource::new(10, 12)));
460
461        mixer.set_volume(a0, 0.2);
462        mixer.set_volume(a1, 0.4);
463        mixer.set_volume(a2, 0.8);
464
465        mixer.set_volume(b0, 0.2);
466        mixer.set_volume(b1, 0.4);
467        mixer.set_volume(b2, 0.8);
468
469        mixer.set_group_volume(Group::A, 2.0);
470        mixer.set_group_volume(Group::B, 4.0);
471
472        assert_eq!(mixer.sound_count(), 6);
473        assert_eq!(mixer.playing_count(), 0);
474
475        mixer.play(a0);
476        mixer.play(a1);
477        mixer.play(a2);
478
479        mixer.play(b0);
480        mixer.play(b1);
481        mixer.play(b2);
482
483        assert_eq!(mixer.sound_count(), 6);
484        assert_eq!(mixer.playing_count(), 6);
485
486        let mut buffer = [0; 10];
487        assert_eq!(mixer.write_samples(&mut buffer), 10);
488        // 28 + 56
489        assert_eq!(buffer, [84, 84, 80, 80, 72, 72, 56, 56, 48, 48]);
490        buffer = [0; 10];
491        assert_eq!(mixer.write_samples(&mut buffer), 10);
492        // 28 + 56
493        assert_eq!(buffer, [32, 32, 0, 0, 0, 0, 0, 0, 0, 0]);
494
495        assert_eq!(mixer.sound_count(), 0);
496        assert_eq!(mixer.playing_count(), 0);
497    }
498}