rustysynth_ext/
midifile_sequencer.rs1use std::cmp;
2use std::sync::Arc;
3
4use crate::midifile::MidiFile;
5use crate::midifile::MidiMessage;
6use crate::synthesizer::Synthesizer;
7
8#[derive(Debug)]
10#[non_exhaustive]
11pub struct MidiFileSequencer {
12 synthesizer: Synthesizer,
13
14 speed: f64,
15
16 midi_file: Option<Arc<MidiFile>>,
17 play_loop: bool,
18
19 block_wrote: usize,
20
21 current_time: f64,
22 msg_index: usize,
23 loop_index: usize,
24}
25
26impl MidiFileSequencer {
27 pub fn new(synthesizer: Synthesizer) -> Self {
33 Self {
34 synthesizer,
35 speed: 1.0,
36 midi_file: None,
37 play_loop: false,
38 block_wrote: 0,
39 current_time: 0.0,
40 msg_index: 0,
41 loop_index: 0,
42 }
43 }
44
45 pub fn play(&mut self, midi_file: &Arc<MidiFile>, play_loop: bool) {
52 self.midi_file = Some(Arc::clone(midi_file));
53 self.play_loop = play_loop;
54
55 self.block_wrote = self.synthesizer.block_size;
56
57 self.current_time = 0.0;
58 self.msg_index = 0;
59 self.loop_index = 0;
60
61 self.synthesizer.reset()
62 }
63
64 pub fn stop(&mut self) {
66 self.midi_file = None;
67 self.synthesizer.reset();
68 }
69
70 pub fn render(&mut self, left: &mut [f32], right: &mut [f32]) {
81 if left.len() != right.len() {
82 panic!("The output buffers for the left and right must be the same length.");
83 }
84
85 let left_length = left.len();
86 let mut wrote: usize = 0;
87 while wrote < left_length {
88 if self.block_wrote == self.synthesizer.block_size {
89 self.process_events();
90 self.block_wrote = 0;
91 self.current_time += self.speed * self.synthesizer.block_size as f64
92 / self.synthesizer.sample_rate as f64;
93 }
94
95 let src_rem = self.synthesizer.block_size - self.block_wrote;
96 let dst_rem = left_length - wrote;
97 let rem = cmp::min(src_rem, dst_rem);
98
99 self.synthesizer.render(
100 &mut left[wrote..wrote + rem],
101 &mut right[wrote..wrote + rem],
102 );
103
104 self.block_wrote += rem;
105 wrote += rem;
106 }
107 }
108
109 fn process_events(&mut self) {
110 let midi_file = match self.midi_file.as_ref() {
111 Some(value) => value,
112 None => return,
113 };
114
115 while self.msg_index < midi_file.messages.len() {
116 let time = midi_file.times[self.msg_index];
117 let msg = midi_file.messages[self.msg_index];
118
119 if time <= self.current_time {
120 match msg {
121 MidiMessage::Normal {
122 status,
123 data1,
124 data2,
125 } => {
126 let channel = status & 0x0F;
127 let command = status & 0xF0;
128 self.synthesizer.process_midi_message(
129 channel as i32,
130 command as i32,
131 data1 as i32,
132 data2 as i32,
133 );
134 }
135 MidiMessage::LoopStart if self.play_loop => self.loop_index = self.msg_index,
136 MidiMessage::LoopEnd
141 if self.play_loop && time > midi_file.times[self.loop_index] =>
142 {
143 self.current_time = midi_file.times[self.loop_index];
144 self.msg_index = self.loop_index;
145 self.synthesizer.note_off_all(false);
146 }
147 _ => (),
148 }
149 self.msg_index += 1;
150 } else {
151 break;
152 }
153 }
154
155 if self.msg_index == midi_file.messages.len()
156 && self.play_loop
157 && self.loop_index < self.msg_index
158 && midi_file.times[self.loop_index] < midi_file.times[self.msg_index - 1]
159 {
160 self.current_time = midi_file.times[self.loop_index];
161 self.msg_index = self.loop_index;
162 self.synthesizer.note_off_all(false);
163 }
164 }
165
166 pub fn get_synthesizer(&self) -> &Synthesizer {
168 &self.synthesizer
169 }
170
171 pub fn get_midi_file(&self) -> Option<&MidiFile> {
173 match &self.midi_file {
174 None => None,
175 Some(value) => Some(value),
176 }
177 }
178
179 pub fn get_position(&self) -> f64 {
181 self.current_time
182 }
183
184 pub fn end_of_sequence(&self) -> bool {
191 match &self.midi_file {
192 None => true,
193 Some(value) => self.msg_index == value.messages.len(),
194 }
195 }
196
197 pub fn get_speed(&self) -> f64 {
204 self.speed
205 }
206
207 pub fn set_speed(&mut self, value: f64) {
213 if value < 0.0 {
214 panic!("The playback speed must be a non-negative value.");
215 }
216
217 self.speed = value;
218 }
219}
220
221#[cfg(test)]
222mod tests {
223 use super::*;
224
225 #[cfg(not(target_family = "wasm"))]
226 use std::sync::mpsc;
227 #[cfg(not(target_family = "wasm"))]
228 use std::thread;
229 #[cfg(not(target_family = "wasm"))]
230 use std::time::Duration;
231
232 use crate::test_font::*;
233 use crate::{SoundFont, SynthesizerSettings};
234
235 fn synthesizer() -> Synthesizer {
236 let mut reader = std::io::Cursor::new(build_sf2(&sample_spec(), 1));
237 let sound_font = Arc::new(SoundFont::new(&mut reader).unwrap());
238 let settings = SynthesizerSettings::new(44100);
239 Synthesizer::new(&sound_font, &settings).unwrap()
240 }
241
242 #[test]
245 fn test_a_zero_length_loop_does_not_hang_the_sequencer() {
246 let midi_file = Arc::new(
247 MidiFile::new_with_events([
248 (0.0, MidiMessage::LoopStart),
249 (0.0, MidiMessage::LoopEnd),
250 (0.0, MidiMessage::EndOfTrack),
251 ])
252 .unwrap(),
253 );
254
255 let render = move |midi_file: Arc<MidiFile>| {
256 let mut sequencer = MidiFileSequencer::new(synthesizer());
257 sequencer.play(&midi_file, true);
258
259 let mut left = vec![0_f32; 512];
260 let mut right = vec![0_f32; 512];
261 sequencer.render(&mut left, &mut right);
262
263 sequencer.end_of_sequence()
264 };
265
266 #[cfg(not(target_family = "wasm"))]
270 {
271 let (sender, receiver) = mpsc::channel();
272 thread::spawn(move || {
273 sender.send(render(midi_file)).unwrap();
274 });
275
276 let end_of_sequence = receiver
277 .recv_timeout(Duration::from_secs(10))
278 .expect("the sequencer did not finish rendering");
279 assert!(end_of_sequence);
280 }
281
282 #[cfg(target_family = "wasm")]
283 assert!(render(midi_file));
284 }
285
286 #[test]
289 fn test_a_normal_loop_still_repeats() {
290 let midi_file = Arc::new(
291 MidiFile::new_with_events([
292 (0.0, MidiMessage::LoopStart),
293 (
294 0.0,
295 MidiMessage::Normal {
296 status: 0x90,
297 data1: 60,
298 data2: 100,
299 },
300 ),
301 (1.0, MidiMessage::LoopEnd),
302 (2.0, MidiMessage::EndOfTrack),
303 ])
304 .unwrap(),
305 );
306
307 let mut sequencer = MidiFileSequencer::new(synthesizer());
308 sequencer.play(&midi_file, true);
309
310 let mut left = vec![0_f32; 44100];
311 let mut right = vec![0_f32; 44100];
312 sequencer.render(&mut left, &mut right);
313
314 assert!(!sequencer.end_of_sequence());
315 assert!(sequencer.get_position() < 2.0);
316 }
317}