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}