1#![allow(dead_code)]
2
3use std::io::Read;
4
5use crate::binary_reader::BinaryReader;
6use crate::four_cc::FourCC;
7use crate::read_counter::ReadCounter;
8use crate::MidiFileError;
9use crate::MidiFileLoopType;
10
11#[derive(Clone, Copy, Debug)]
12#[non_exhaustive]
13pub(crate) struct Message {
14 pub(crate) channel: u8,
15 pub(crate) command: u8,
16 pub(crate) data1: u8,
17 pub(crate) data2: u8,
18}
19
20impl Message {
21 pub(crate) const NORMAL: u8 = 0;
22 pub(crate) const TEMPO_CHANGE: u8 = 252;
23 pub(crate) const LOOP_START: u8 = 253;
24 pub(crate) const LOOP_END: u8 = 254;
25 pub(crate) const END_OF_TRACK: u8 = 255;
26
27 pub(crate) fn common1(status: u8, data1: u8) -> Self {
28 Self {
29 channel: status & 0x0F,
30 command: status & 0xF0,
31 data1,
32 data2: 0,
33 }
34 }
35
36 pub(crate) fn common2(status: u8, data1: u8, data2: u8, loop_type: MidiFileLoopType) -> Self {
37 let channel = status & 0x0F;
38 let command = status & 0xF0;
39
40 if command == 0xB0 {
41 match loop_type {
42 MidiFileLoopType::RpgMaker => {
43 if data1 == 111 {
44 return Message::loop_start();
45 }
46 }
47
48 MidiFileLoopType::IncredibleMachine => {
49 if data1 == 110 {
50 return Message::loop_start();
51 }
52 if data1 == 111 {
53 return Message::loop_end();
54 }
55 }
56
57 MidiFileLoopType::FinalFantasy => {
58 if data1 == 116 {
59 return Message::loop_start();
60 }
61 if data1 == 117 {
62 return Message::loop_end();
63 }
64 }
65
66 _ => (),
67 }
68 }
69
70 Self {
71 channel,
72 command,
73 data1,
74 data2,
75 }
76 }
77
78 pub(crate) fn tempo_change(tempo: i32) -> Self {
79 Self {
80 channel: Message::TEMPO_CHANGE,
81 command: (tempo >> 16) as u8,
82 data1: (tempo >> 8) as u8,
83 data2: tempo as u8,
84 }
85 }
86
87 pub(crate) fn loop_start() -> Self {
88 Self {
89 channel: Message::LOOP_START,
90 command: 0,
91 data1: 0,
92 data2: 0,
93 }
94 }
95
96 pub(crate) fn loop_end() -> Self {
97 Self {
98 channel: Message::LOOP_END,
99 command: 0,
100 data1: 0,
101 data2: 0,
102 }
103 }
104
105 pub(crate) fn end_of_track() -> Self {
106 Self {
107 channel: Message::END_OF_TRACK,
108 command: 0,
109 data1: 0,
110 data2: 0,
111 }
112 }
113
114 pub(crate) fn get_message_type(&self) -> u8 {
115 match self.channel {
116 Message::TEMPO_CHANGE => Message::TEMPO_CHANGE,
117 Message::LOOP_START => Message::LOOP_START,
118 Message::LOOP_END => Message::LOOP_END,
119 Message::END_OF_TRACK => Message::END_OF_TRACK,
120 _ => Message::NORMAL,
121 }
122 }
123
124 pub(crate) fn get_tempo(&self) -> f64 {
125 60000000.0
126 / (((self.command as i32) << 16) | ((self.data1 as i32) << 8) | (self.data2 as i32))
127 as f64
128 }
129}
130
131#[derive(Debug)]
133#[non_exhaustive]
134pub struct MidiFile {
135 pub(crate) messages: Vec<Message>,
136 pub(crate) times: Vec<f64>,
137}
138
139impl MidiFile {
140 pub fn new<R: Read>(reader: &mut R) -> Result<Self, MidiFileError> {
146 MidiFile::new_with_loop_type(reader, MidiFileLoopType::LoopPoint(0))
147 }
148
149 pub fn new_with_loop_type<R: Read>(
167 reader: &mut R,
168 loop_type: MidiFileLoopType,
169 ) -> Result<Self, MidiFileError> {
170 let chunk_type = BinaryReader::read_four_cc(reader)?;
171 if chunk_type != b"MThd" {
172 return Err(MidiFileError::InvalidChunkType {
173 expected: FourCC::from_bytes(*b"MThd"),
174 actual: chunk_type,
175 });
176 }
177
178 let size = BinaryReader::read_i32_big_endian(reader)?;
179 if size != 6 {
180 return Err(MidiFileError::InvalidChunkData(FourCC::from_bytes(
181 *b"MThd",
182 )));
183 }
184
185 let format = BinaryReader::read_i16_big_endian(reader)?;
186 if !(format == 0 || format == 1) {
187 return Err(MidiFileError::UnsupportedFormat(format));
188 }
189
190 let track_count = BinaryReader::read_i16_big_endian(reader)? as i32;
191 let resolution = BinaryReader::read_i16_big_endian(reader)? as i32;
192
193 let mut message_lists: Vec<Vec<Message>> = Vec::new();
194 let mut tick_lists: Vec<Vec<i32>> = Vec::new();
195
196 for _i in 0..track_count {
197 let (message_list, tick_list) = MidiFile::read_track(reader, loop_type)?;
198 message_lists.push(message_list);
199 tick_lists.push(tick_list);
200 }
201
202 match loop_type {
203 MidiFileLoopType::LoopPoint(loop_point) if loop_point != 0 => {
204 let loop_point = loop_point as i32;
205 let tick_list = &mut tick_lists[0];
206 let message_list = &mut message_lists[0];
207
208 if loop_point <= *tick_list.last().unwrap() {
209 for i in 0..tick_list.len() {
210 if tick_list[i] >= loop_point {
211 tick_list.insert(i, loop_point);
212 message_list.insert(i, Message::loop_start());
213 break;
214 }
215 }
216 } else {
217 tick_list.push(loop_point);
218 message_list.push(Message::loop_start());
219 }
220 }
221 _ => (),
222 }
223
224 let (messages, times) = MidiFile::merge_tracks(&message_lists, &tick_lists, resolution);
225
226 Ok(Self { messages, times })
227 }
228
229 fn discard_data<R: Read>(reader: &mut R) -> Result<(), MidiFileError> {
230 let size = BinaryReader::read_i32_variable_length(reader)? as usize;
231 BinaryReader::discard_data(reader, size)?;
232 Ok(())
233 }
234
235 fn read_tempo<R: Read>(reader: &mut R) -> Result<i32, MidiFileError> {
236 let size = BinaryReader::read_i32_variable_length(reader)?;
237 if size != 3 {
238 return Err(MidiFileError::InvalidTempoValue);
239 }
240
241 let b1 = BinaryReader::read_u8(reader)? as i32;
242 let b2 = BinaryReader::read_u8(reader)? as i32;
243 let b3 = BinaryReader::read_u8(reader)? as i32;
244
245 Ok((b1 << 16) | (b2 << 8) | b3)
246 }
247
248 fn read_track<R: Read>(
249 reader: &mut R,
250 loop_type: MidiFileLoopType,
251 ) -> Result<(Vec<Message>, Vec<i32>), MidiFileError> {
252 let chunk_type = BinaryReader::read_four_cc(reader)?;
253 if chunk_type != b"MTrk" {
254 return Err(MidiFileError::InvalidChunkType {
255 expected: FourCC::from_bytes(*b"MTrk"),
256 actual: chunk_type,
257 });
258 }
259
260 let size = BinaryReader::read_i32_big_endian(reader)? as usize;
261 let reader = &mut ReadCounter::new(reader);
262
263 let mut messages: Vec<Message> = Vec::new();
264 let mut ticks: Vec<i32> = Vec::new();
265
266 let mut tick: i32 = 0;
267 let mut last_status: u8 = 0;
268
269 loop {
270 let delta = BinaryReader::read_i32_variable_length(reader)?;
271 let first = BinaryReader::read_u8(reader)?;
272
273 tick += delta;
274
275 if (first & 128) == 0 {
276 let command = last_status & 0xF0;
277 if command == 0xC0 || command == 0xD0 {
278 messages.push(Message::common1(last_status, first));
279 ticks.push(tick);
280 } else {
281 let data2 = BinaryReader::read_u8(reader)?;
282 messages.push(Message::common2(last_status, first, data2, loop_type));
283 ticks.push(tick);
284 }
285
286 continue;
287 }
288
289 match first {
290 0xF0 => MidiFile::discard_data(reader)?,
291 0xF7 => MidiFile::discard_data(reader)?,
292 0xFF => match BinaryReader::read_u8(reader)? {
293 0x2F => {
294 BinaryReader::read_u8(reader)?;
295 messages.push(Message::end_of_track());
296 ticks.push(tick);
297
298 if reader.bytes_read() < size {
301 BinaryReader::discard_data(reader, size - reader.bytes_read())?;
302 }
303
304 return Ok((messages, ticks));
305 }
306 0x51 => {
307 messages.push(Message::tempo_change(MidiFile::read_tempo(reader)?));
308 ticks.push(tick);
309 }
310 _ => MidiFile::discard_data(reader)?,
311 },
312 _ => {
313 let command = first & 0xF0;
314 if command == 0xC0 || command == 0xD0 {
315 let data1 = BinaryReader::read_u8(reader)?;
316 messages.push(Message::common1(first, data1));
317 ticks.push(tick);
318 } else {
319 let data1 = BinaryReader::read_u8(reader)?;
320 let data2 = BinaryReader::read_u8(reader)?;
321 messages.push(Message::common2(first, data1, data2, loop_type));
322 ticks.push(tick);
323 }
324 }
325 }
326
327 last_status = first
328 }
329 }
330
331 fn merge_tracks(
332 message_lists: &[Vec<Message>],
333 tick_lists: &[Vec<i32>],
334 resolution: i32,
335 ) -> (Vec<Message>, Vec<f64>) {
336 let mut merged_messages: Vec<Message> = Vec::new();
337 let mut merged_times: Vec<f64> = Vec::new();
338
339 let mut indices: Vec<usize> = vec![0; message_lists.len()];
340
341 let mut current_tick: i32 = 0;
342 let mut current_time: f64 = 0.0;
343
344 let mut tempo: f64 = 120.0;
345
346 loop {
347 let mut min_tick = i32::MAX;
348 let mut min_index: i32 = -1;
349
350 for ch in 0..tick_lists.len() {
351 if indices[ch] < tick_lists[ch].len() {
352 let tick = tick_lists[ch][indices[ch]];
353 if tick < min_tick {
354 min_tick = tick;
355 min_index = ch as i32;
356 }
357 }
358 }
359
360 if min_index == -1 {
361 break;
362 }
363
364 let next_tick = tick_lists[min_index as usize][indices[min_index as usize]];
365 let delta_tick = next_tick - current_tick;
366 let delta_time = 60.0 / (resolution as f64 * tempo) * delta_tick as f64;
367
368 current_tick += delta_tick;
369 current_time += delta_time;
370
371 let message = message_lists[min_index as usize][indices[min_index as usize]];
372 if message.get_message_type() == Message::TEMPO_CHANGE {
373 tempo = message.get_tempo();
374 } else {
375 merged_messages.push(message);
376 merged_times.push(current_time);
377 }
378
379 indices[min_index as usize] += 1;
380 }
381
382 (merged_messages, merged_times)
383 }
384
385 pub fn get_length(&self) -> f64 {
387 *self.times.last().unwrap()
388 }
389}