Skip to main content

rustysynth_ext/
midifile_sequencer.rs

1use std::cmp;
2use std::sync::Arc;
3
4use crate::midifile::Message;
5use crate::midifile::MidiFile;
6use crate::synthesizer::Synthesizer;
7
8/// An instance of the MIDI file sequencer.
9#[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    /// Initializes a new instance of the sequencer.
28    ///
29    /// # Arguments
30    ///
31    /// * `synthesizer` - The synthesizer to be handled by the sequencer.
32    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    /// Plays the MIDI file.
46    ///
47    /// # Arguments
48    ///
49    /// * `midi_file` - The MIDI file to be played.
50    /// * `play_loop` - If `true`, the MIDI file loops after reaching the end.
51    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    /// Stops playing.
65    pub fn stop(&mut self) {
66        self.midi_file = None;
67        self.synthesizer.reset();
68    }
69
70    /// Renders the waveform.
71    ///
72    /// # Arguments
73    ///
74    /// * `left` - The buffer of the left channel to store the rendered waveform.
75    /// * `right` - The buffer of the right channel to store the rendered waveform.
76    ///
77    /// # Remarks
78    ///
79    /// The output buffers for the left and right must be the same length.
80    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                    Message::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                    Message::LoopStart if self.play_loop => self.loop_index = self.msg_index,
136                    Message::LoopEnd if self.play_loop => {
137                        self.current_time = midi_file.times[self.loop_index];
138                        self.msg_index = self.loop_index;
139                        self.synthesizer.note_off_all(false);
140                    }
141                    _ => (),
142                }
143                self.msg_index += 1;
144            } else {
145                break;
146            }
147        }
148
149        if self.msg_index == midi_file.messages.len() && self.play_loop {
150            self.current_time = midi_file.times[self.loop_index];
151            self.msg_index = self.loop_index;
152            self.synthesizer.note_off_all(false);
153        }
154    }
155
156    /// Gets the synthesizer handled by the sequencer.
157    pub fn get_synthesizer(&self) -> &Synthesizer {
158        &self.synthesizer
159    }
160
161    /// Gets the currently playing MIDI file.
162    pub fn get_midi_file(&self) -> Option<&MidiFile> {
163        match &self.midi_file {
164            None => None,
165            Some(value) => Some(value),
166        }
167    }
168
169    /// Gets the current playback position in seconds.
170    pub fn get_position(&self) -> f64 {
171        self.current_time
172    }
173
174    /// Gets a value that indicates whether the current playback position is at the end of the sequence.
175    ///
176    /// # Remarks
177    ///
178    /// If the `play` method has not yet been called, this value will be `true`.
179    /// This value will never be `true` if loop playback is enabled.
180    pub fn end_of_sequence(&self) -> bool {
181        match &self.midi_file {
182            None => true,
183            Some(value) => self.msg_index == value.messages.len(),
184        }
185    }
186
187    /// Gets the current playback speed.
188    ///
189    /// # Remarks
190    ///
191    /// The default value is 1.
192    /// The tempo will be multiplied by this value during playback.
193    pub fn get_speed(&self) -> f64 {
194        self.speed
195    }
196
197    /// Sets the playback speed.
198    ///
199    /// # Remarks
200    ///
201    /// The value must be non-negative.
202    pub fn set_speed(&mut self, value: f64) {
203        if value < 0.0 {
204            panic!("The playback speed must be a non-negative value.");
205        }
206
207        self.speed = value;
208    }
209}