Last active
May 1, 2024 05:52
-
-
Save LucasAlfare/e7bb49f1d598f00e6b85a67c3a2d4ea7 to your computer and use it in GitHub Desktop.
draft: MIDI file reader in Rust (Format Spec. 1.1)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![allow(unused)] | |
use std::{ | |
fs::{self, File}, | |
io::Read, | |
}; | |
fn main() { | |
let bytes = read_file_bytes("demo.mid"); | |
let mut reader = MyReader::new(bytes); | |
println!("{:?}", reader); | |
let header_signature = reader.read_string(4); | |
println!("header signature={:?}", header_signature); | |
let header_length = reader.read_4_bytes(); | |
println!("header length={:?}", header_length); | |
let header_format = reader.read_2_bytes(); | |
println!("header format={:?}", header_format); | |
let header_num_tracks = reader.read_2_bytes(); | |
println!("header num tracks={:?}", header_num_tracks); | |
let header_time_division = reader.read_2_bytes(); | |
println!("header time division={:?}", header_time_division); | |
println!("---TRACKS---"); | |
for i in 0..header_num_tracks { | |
// for i in 0..1 { | |
let track_signature = reader.read_string(4); | |
println!("track {:?} signature={:?}", i, track_signature); | |
let track_length = reader.read_4_bytes(); | |
println!("\tlength={:?}", track_length); | |
let mut previous_status = 0; | |
loop { | |
let delta_time = reader.read_variable_length_value(); | |
let mut current_status = reader.read_1_byte(); | |
if current_status >> 7 == 0 { | |
current_status = previous_status; | |
reader.offset -= 1; | |
} | |
match current_status { | |
// Meta Events | |
0xFF => { | |
let kind = reader.read_1_byte(); | |
match kind { | |
// text event, copyright notice, track name, instrument name, lyric, | |
// marker, cue point | |
0x01 | 0x02 | 0x03 | 0x04 | 0x05 | 0x06 | 0x07 => { | |
let text_data_length = reader.read_variable_length_value(); | |
let text_data = reader.read_string(text_data_length); | |
println!( | |
"\tmeta event={:#04x}; delta time={:?}; data={:?}", | |
kind, delta_time, text_data | |
); | |
} | |
// time signature | |
0x58 => { | |
let num_data_items = reader.read_1_byte(); | |
let upper_signature_value = reader.read_1_byte(); | |
let power_of_two_to_lower_value = reader.read_1_byte(); | |
let num_midi_clocks_in_metronome_click = reader.read_1_byte(); | |
let num_of_32nd_notes_in_24_midi_clocks = reader.read_1_byte(); | |
println!( | |
"\tmeta event={:#04x}; delta time={:?}; data=[{:?}, {:?}, {:?}, {:?}]", | |
kind, | |
delta_time, | |
upper_signature_value, | |
power_of_two_to_lower_value, | |
num_midi_clocks_in_metronome_click, | |
num_of_32nd_notes_in_24_midi_clocks | |
); | |
} | |
// set tempo in microseconds per quarter note | |
0x51 => { | |
let num_data_items = reader.read_1_byte(); | |
let tempo_in_microseconds = reader.read_3_bytes(); | |
println!( | |
"\tmeta event={:#04x}; delta time={:?}; data=[{:?}]", | |
kind, delta_time, tempo_in_microseconds | |
); | |
} | |
// SMPTE Offset | |
0x54 => { | |
let data_length = reader.read_1_byte(); | |
println!( | |
"\tmeta event={:#04x}; delta time={:?}; data=[{:?}, {:?}, {:?}, {:?}, {:?}]", | |
kind, | |
delta_time, | |
reader.read_1_byte(), | |
reader.read_1_byte(), | |
reader.read_1_byte(), | |
reader.read_1_byte(), | |
reader.read_1_byte() | |
); | |
} | |
// key signature | |
0x59 => { | |
let data_length = reader.read_1_byte(); | |
println!( | |
"\tmeta event={:#04x}; delta time={:?}; data=[{:?}, {:?}]", | |
kind, | |
delta_time, | |
reader.read_1_byte(), | |
reader.read_1_byte() | |
); | |
} | |
// end of track | |
0x2F => { | |
let data_length = reader.read_1_byte(); | |
println!( | |
"\tmeta event={:#04x}; delta time={:?}; data=[no data]", | |
kind, delta_time | |
); | |
break; | |
} | |
// midi channel prefix | |
0x20 => { | |
let data_length = reader.read_1_byte(); | |
let current_effective_midi_channel = reader.read_1_byte(); | |
println!( | |
"\tmeta event={:#04x}; delta time={:?}; data=[{:?}]", | |
kind, delta_time, current_effective_midi_channel | |
); | |
} | |
// sequencer specific meta event | |
0x7f => { | |
let data_length = reader.read_variable_length_value(); | |
let mut aux_bytes = Vec::<u8>::new(); | |
for i in 0..data_length { | |
aux_bytes.push(reader.read_1_byte()); | |
} | |
println!( | |
"\tmeta event={:#04x}; delta time={:?};data =[{:?}]", | |
kind, delta_time, aux_bytes | |
); | |
} | |
_ => { | |
println!( | |
"Unhandled meta event kind: [{:#04x}]; delta time={:?}", | |
kind, delta_time | |
); | |
} | |
} | |
} | |
// System Exclusive Events | |
0xF0 | 0xF7 => { | |
// pass... | |
} | |
// Control Events | |
_ => { | |
previous_status = current_status; | |
let kind = current_status >> 4; | |
let target_channel = current_status & 0b1111; | |
match kind { | |
// select instrument | |
0b1100 => { | |
let target_instrument = reader.read_1_byte(); | |
println!( | |
"\tcontrol event={:#04b}; delta time={:?}; data =[{:?}]", | |
kind, delta_time, target_instrument | |
); | |
} | |
// note on, note off | |
0b1001 | 0b1000 => { | |
let note_number = reader.read_1_byte() & 0b01111111; | |
let note_velocity = reader.read_1_byte() & 0b01111111; | |
println!( | |
"\tcontrol event={:#04b}; delta time={:?}; data =[{:?}, {:?}]", | |
kind, delta_time, note_number, note_velocity | |
); | |
} | |
// channel mode | |
0b1011 => { | |
let channel_mode_type_code = reader.read_1_byte(); | |
let channel_mode_arg_1 = reader.read_1_byte(); | |
let channel_mode_arg_2 = reader.read_1_byte(); | |
println!( | |
"\tcontrol event={:#04b}; delta time={:?}; data =[{:?}, {:?}, {:?}]", | |
kind, delta_time, channel_mode_type_code, channel_mode_arg_1, channel_mode_arg_2 | |
); | |
} | |
// channel pressure | |
0b1101 => { | |
let channel_pressure = reader.read_1_byte(); | |
println!( | |
"\tcontrol event={:#04b}; delta time={:?}; data =[{:?}]", | |
kind, delta_time, channel_pressure | |
); | |
} | |
0b1110 => { | |
let pitch_bend = reader.read_1_byte(); | |
println!( | |
"\tcontrol event={:#04b}; delta time={:?}; data =[{:?}]", | |
kind, delta_time, pitch_bend | |
); | |
} | |
_ => { | |
println!( | |
"Unhandled control event kind: [{:#04b}]; delta time={:?}", | |
kind, delta_time | |
); | |
break; | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
fn read_file_bytes(pathname: &str) -> Vec<u8> { | |
let file_len = fs::metadata(pathname).unwrap().len(); | |
let mut buffer = vec![0; file_len as usize]; | |
let mut file = File::open(pathname).unwrap(); | |
let _ = file.read(&mut buffer); | |
buffer | |
} | |
fn debug_hex(n: u8, indents: u8) { | |
for i in 0..indents { | |
print!("\t"); | |
} | |
println!("{:#04x}", n); | |
} | |
#[derive(Debug)] | |
pub struct MyReader { | |
pub buffer: Vec<u8>, | |
pub offset: u32, | |
} | |
impl MyReader { | |
pub fn new(buffer: Vec<u8>) -> Self { | |
MyReader { buffer, offset: 0 } | |
} | |
pub fn read_1_byte(&mut self) -> u8 { | |
let result = self.buffer[self.offset as usize]; | |
self.offset += 1; | |
return result; | |
} | |
pub fn read_2_bytes(&mut self) -> u16 { | |
let a = self.buffer[(self.offset) as usize] as u16; | |
let b = self.buffer[(self.offset + 1) as usize] as u16; | |
let result = (a << 8) | b; | |
self.offset += 2; | |
result | |
} | |
pub fn read_3_bytes(&mut self) -> u32 { | |
let a = self.buffer[(self.offset + 0) as usize] as u32; | |
let b = self.buffer[(self.offset + 1) as usize] as u32; | |
let c = self.buffer[(self.offset + 2) as usize] as u32; | |
let result = (((a << 8) | b) << 16) | c; | |
self.offset += 3; | |
return result; | |
} | |
pub fn read_4_bytes(&mut self) -> u32 { | |
let a = self.buffer[(self.offset + 0) as usize] as u32; | |
let b = self.buffer[(self.offset + 1) as usize] as u32; | |
let c = self.buffer[(self.offset + 2) as usize] as u32; | |
let d = self.buffer[(self.offset + 3) as usize] as u32; | |
let result = (((((a << 24) | b) << 16) | c) << 8) | d; | |
self.offset += 4; | |
return result; | |
} | |
pub fn read_string(&mut self, length: u32) -> String { | |
let mut result = String::new(); | |
for i in 0..length { | |
let curr_byte = self.buffer[(self.offset + i) as usize]; | |
result.push(curr_byte as char); | |
} | |
self.offset += length; | |
return result; | |
} | |
pub fn read_variable_length_value(&mut self) -> u32 { | |
let mask: u8 = 0b0111_1111; | |
let mut result_number: u32 = 0; | |
let mut current_byte: u8; | |
loop { | |
current_byte = self.read_1_byte(); | |
result_number = (result_number << 7) | ((current_byte & mask) as u32); | |
if current_byte >> 7 == 0 { | |
return result_number; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment