pyxel/
music.rs

1use std::cmp::min;
2
3use crate::audio::Audio;
4use crate::blip_buf::BlipBuf;
5use crate::pyxel::{CHANNELS, SOUNDS};
6use crate::settings::{CLOCK_RATE, SAMPLE_RATE, TICKS_PER_SECOND};
7
8pub type SharedSeq = shared_type!(Vec<u32>);
9
10#[derive(Clone)]
11pub struct Music {
12    pub seqs: Vec<SharedSeq>,
13}
14
15pub type SharedMusic = shared_type!(Music);
16
17impl Music {
18    pub fn new() -> SharedMusic {
19        new_shared_type!(Self { seqs: Vec::new() })
20    }
21
22    pub fn set(&mut self, seqs: &[Vec<u32>]) {
23        self.seqs = seqs
24            .iter()
25            .map(|seq| new_shared_type!(seq.clone()))
26            .collect();
27
28        let num_channels = CHANNELS.lock().len();
29        while self.seqs.len() < num_channels {
30            self.seqs.push(new_shared_type!(Vec::new()));
31        }
32    }
33
34    pub fn save(&self, filename: &str, count: u32, ffmpeg: Option<bool>) {
35        assert!(count > 0);
36
37        let seqs: Vec<_> = (0..self.seqs.len())
38            .map(|i| {
39                let pyxel_sounds = SOUNDS.lock();
40                self.seqs[i]
41                    .lock()
42                    .iter()
43                    .map(|&sound_index| pyxel_sounds[sound_index as usize].clone())
44                    .collect::<Vec<_>>()
45            })
46            .collect();
47
48        let ticks_per_music = seqs
49            .iter()
50            .map(|sounds| {
51                sounds
52                    .iter()
53                    .map(|sound| {
54                        let sound = sound.lock();
55                        sound.speed * sound.notes.len() as u32
56                    })
57                    .sum::<u32>()
58            })
59            .max()
60            .unwrap_or(0);
61
62        let samples_per_music = ticks_per_music * SAMPLE_RATE / TICKS_PER_SECOND;
63        let num_samples = samples_per_music * count;
64
65        if num_samples == 0 {
66            return;
67        }
68
69        let mut samples = vec![0; num_samples as usize];
70        let mut blip_buf = BlipBuf::new(num_samples as usize);
71        blip_buf.set_rates(CLOCK_RATE as f64, SAMPLE_RATE as f64);
72
73        let channels = CHANNELS.lock();
74        channels.iter().for_each(|channel| channel.lock().stop());
75
76        {
77            let mut channels: Vec<_> = channels.iter().map(|channel| channel.lock()).collect();
78            for i in 0..min(channels.len(), seqs.len()) {
79                channels[i].play(seqs[i].clone(), None, true, false);
80            }
81        }
82
83        Audio::render_samples(&channels, &mut blip_buf, &mut samples);
84        Audio::save_samples(filename, &samples, ffmpeg.unwrap_or(false));
85        channels.iter().for_each(|channel| channel.lock().stop());
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    #[test]
94    fn test_music_new() {
95        let music = Music::new();
96        assert_eq!(music.lock().seqs.len(), 0);
97    }
98
99    #[test]
100    fn test_music_set() {
101        let music = Music::new();
102        music
103            .lock()
104            .set(&[vec![0, 1, 2], vec![1, 2, 3], vec![2, 3, 4]]);
105
106        for i in 0..3 {
107            assert_eq!(
108                &*music.lock().seqs[i as usize].lock(),
109                &vec![i, i + 1, i + 2]
110            );
111        }
112    }
113}