quad_snd/
mixer.rs

1use crate::{AudioContext, PlaySoundParams};
2
3use std::cell::Cell;
4use std::collections::HashMap;
5use std::sync::Arc;
6use std::sync::mpsc;
7
8enum AudioMessage {
9    AddSound(u32, Vec<f32>),
10    Play(u32, u32, bool, f32),
11    Stop(u32),
12    StopAll(u32),
13    SetVolume(u32, f32),
14    SetVolumeAll(u32, f32),
15    Delete(u32),
16}
17
18#[derive(Debug)]
19pub struct SoundState {
20    sound_id: u32,
21    play_id: u32,
22    sample: usize,
23    data: Arc<[f32]>,
24    looped: bool,
25    volume: f32,
26}
27
28impl SoundState {
29    fn get_samples(&mut self, n: usize) -> &[f32] {
30        let data = &self.data[self.sample..];
31
32        self.sample += n;
33
34        match data.get(..n) {
35            Some(data) => data,
36            None => data,
37        }
38    }
39
40    fn rewind(&mut self) {
41        self.sample = 0;
42    }
43}
44
45pub struct Mixer {
46    rx: mpsc::Receiver<AudioMessage>,
47    sounds: HashMap<u32, Arc<[f32]>>,
48    mixer_state: Vec<SoundState>,
49}
50
51pub struct MixerBuilder {
52    rx: mpsc::Receiver<AudioMessage>,
53}
54
55pub struct MixerControl {
56    tx: mpsc::Sender<AudioMessage>,
57    sound_id: Cell<u32>,
58    play_id: Cell<u32>,
59}
60
61pub struct Playback {
62    play_id: u32,
63}
64
65impl Playback {
66    pub fn stop(self, ctx: &AudioContext) {
67        ctx.mixer_ctrl.send(AudioMessage::Stop(self.play_id));
68    }
69
70    pub fn set_volume(&self, ctx: &AudioContext, volume: f32) {
71        ctx.mixer_ctrl
72            .send(AudioMessage::SetVolume(self.play_id, volume));
73    }
74}
75
76impl MixerControl {
77    pub fn load(&self, data: &[u8]) -> u32 {
78        let sound_id = self.sound_id.get();
79
80        let samples = load_samples_from_file(data).unwrap();
81
82        self.tx
83            .send(crate::mixer::AudioMessage::AddSound(sound_id, samples))
84            .unwrap_or_else(|_| println!("Audio thread died"));
85        self.sound_id.set(sound_id + 1);
86
87        sound_id
88    }
89
90    pub fn play(&self, sound_id: u32, params: PlaySoundParams) -> Playback {
91        let play_id = self.play_id.get();
92
93        self.send(AudioMessage::Play(
94            sound_id,
95            play_id,
96            params.looped,
97            params.volume,
98        ));
99
100        self.play_id.set(play_id + 1);
101
102        Playback { play_id }
103    }
104
105    pub fn stop(&self, play_id: u32) {
106        self.send(AudioMessage::Stop(play_id));
107    }
108
109    pub fn stop_all(&self, sound_id: u32) {
110        self.send(AudioMessage::StopAll(sound_id));
111    }
112
113    pub fn set_volume_all(&self, sound_id: u32, volume: f32) {
114        self.send(AudioMessage::SetVolumeAll(sound_id, volume));
115    }
116
117    pub fn delete(&self, sound_id: u32) {
118        self.send(AudioMessage::Delete(sound_id));
119    }
120
121    fn send(&self, message: AudioMessage) {
122        self.tx
123            .send(message)
124            .unwrap_or_else(|_| println!("Audio thread died"))
125    }
126}
127
128impl MixerBuilder {
129    pub fn build(self) -> Mixer {
130        Mixer {
131            rx: self.rx,
132            sounds: HashMap::new(),
133            mixer_state: vec![],
134        }
135    }
136}
137
138impl Mixer {
139    pub fn new() -> (MixerBuilder, MixerControl) {
140        let (tx, rx) = mpsc::channel();
141
142        (
143            MixerBuilder { rx },
144            MixerControl {
145                tx,
146                sound_id: Cell::new(0),
147                play_id: Cell::new(0),
148            },
149        )
150    }
151
152    pub fn fill_audio_buffer(&mut self, buffer: &mut [f32], frames: usize) {
153        while let Ok(message) = self.rx.try_recv() {
154            match message {
155                AudioMessage::AddSound(id, data) => {
156                    self.sounds.insert(id, data.into());
157                }
158                AudioMessage::Play(sound_id, play_id, looped, volume) => {
159                    if let Some(data) = self.sounds.get(&sound_id) {
160                        self.mixer_state.push(SoundState {
161                            sound_id,
162                            play_id,
163                            sample: 0,
164                            data: data.clone(),
165                            looped,
166                            volume,
167                        });
168                    }
169                }
170                AudioMessage::Stop(play_id) => {
171                    if let Some(i) = self.mixer_state.iter().position(|s| s.play_id == play_id) {
172                        self.mixer_state.swap_remove(i);
173                    }
174                }
175                AudioMessage::StopAll(sound_id) => {
176                    for i in (0..self.mixer_state.len()).rev() {
177                        if self.mixer_state[i].sound_id == sound_id {
178                            self.mixer_state.swap_remove(i);
179                        }
180                    }
181                }
182                AudioMessage::SetVolume(play_id, volume) => {
183                    if let Some(sound) = self.mixer_state.iter_mut().find(|s| s.play_id == play_id)
184                    {
185                        sound.volume = volume;
186                    }
187                }
188                AudioMessage::SetVolumeAll(sound_id, volume) => {
189                    for sound in self
190                        .mixer_state
191                        .iter_mut()
192                        .filter(|s| s.sound_id == sound_id)
193                    {
194                        sound.volume = volume;
195                    }
196                }
197                AudioMessage::Delete(sound_id) => {
198                    for i in (0..self.mixer_state.len()).rev() {
199                        if self.mixer_state[i].sound_id == sound_id {
200                            self.mixer_state.swap_remove(i);
201                        }
202                    }
203                    self.sounds.remove(&sound_id);
204                }
205            }
206        }
207
208        // zeroize the buffer
209        buffer.fill(0.0);
210
211        // Note: Doing manual iteration so we can remove sounds that finished playing
212        let mut i = 0;
213
214        while let Some(sound) = self.mixer_state.get_mut(i) {
215            let volume = sound.volume;
216            let mut remainder = buffer.len();
217
218            loop {
219                let samples = sound.get_samples(remainder);
220
221                for (b, s) in buffer.iter_mut().zip(samples) {
222                    *b += s * volume;
223                }
224
225                remainder -= samples.len();
226
227                if remainder > 0 && sound.looped {
228                    sound.rewind();
229                    continue;
230                }
231
232                break;
233            }
234
235            if remainder > 0 {
236                self.mixer_state.swap_remove(i);
237            } else {
238                i += 1;
239            }
240        }
241    }
242}
243
244/// Parse ogg/wav/etc and get  resampled to 44100, 2 channel data
245pub fn load_samples_from_file(bytes: &[u8]) -> Result<Vec<f32>, ()> {
246    let mut audio_stream = {
247        let file = std::io::Cursor::new(bytes);
248        audrey::Reader::new(file).unwrap()
249    };
250
251    let description = audio_stream.description();
252    let channels_count = description.channel_count();
253    assert!(channels_count == 1 || channels_count == 2);
254
255    let mut frames: Vec<f32> = Vec::with_capacity(4096);
256    let mut samples_iterator = audio_stream
257        .samples::<f32>()
258        .map(std::result::Result::unwrap);
259
260    // audrey's frame docs: "TODO: Should consider changing this behaviour to check the audio file's actual number of channels and automatically convert to F's number of channels while reading".
261    // lets fix this TODO here
262    if channels_count == 1 {
263        frames.extend(samples_iterator.flat_map(|sample| [sample, sample]));
264    } else if channels_count == 2 {
265        frames.extend(samples_iterator);
266    }
267
268    let sample_rate = description.sample_rate();
269
270    // stupid nearest-neighbor resampler
271    if sample_rate != 44100 {
272        let mut new_length = ((44100 as f32 / sample_rate as f32) * frames.len() as f32) as usize;
273
274        // `new_length` must be an even number
275        new_length -= new_length % 2;
276
277        let mut resampled = vec![0.0; new_length];
278
279        for (n, sample) in resampled.chunks_exact_mut(2).enumerate() {
280            let ix = 2 * ((n as f32 / new_length as f32) * frames.len() as f32) as usize;
281            sample[0] = frames[ix];
282            sample[1] = frames[ix + 1];
283        }
284        return Ok(resampled);
285    }
286
287    Ok(frames)
288}