use alloc::vec::Vec;
use core::{error, fmt};
const DEFAULT_TEMPO: u64 = 500_000;
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Event {
Msg {
time: u32,
msg: u32,
},
Sysex {
time: u32,
data: Vec<u8>,
},
}
impl Event {
pub fn time(&self) -> u32 {
match self {
Event::Msg { time, .. } => *time,
Event::Sysex { time, .. } => *time,
}
}
}
#[non_exhaustive]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ParseError {
BadHeader,
UnsupportedFormat(u16),
UnsupportedDivision,
BadTrack,
UnexpectedEof,
}
impl error::Error for ParseError {}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ParseError::BadHeader => f.write_str("invalid SMF header"),
ParseError::UnsupportedFormat(n) => {
write!(f, "unsupported SMF format {n}")
}
ParseError::UnsupportedDivision => {
f.write_str("SMPTE time division not supported")
}
ParseError::BadTrack => f.write_str("invalid track chunk"),
ParseError::UnexpectedEof => f.write_str("unexpected end of data"),
}
}
}
struct Reader<'a> {
data: &'a [u8],
pos: usize,
}
impl<'a> Reader<'a> {
fn new(data: &'a [u8]) -> Self {
Reader { data, pos: 0 }
}
fn remaining(&self) -> usize {
self.data.len() - self.pos
}
fn read_u8(&mut self) -> Result<u8, ParseError> {
if self.pos >= self.data.len() {
return Err(ParseError::UnexpectedEof);
}
let b = self.data[self.pos];
self.pos += 1;
Ok(b)
}
fn read_u16(&mut self) -> Result<u16, ParseError> {
if self.remaining() < 2 {
return Err(ParseError::UnexpectedEof);
}
let hi = self.data[self.pos] as u16;
let lo = self.data[self.pos + 1] as u16;
self.pos += 2;
Ok((hi << 8) | lo)
}
fn read_u32(&mut self) -> Result<u32, ParseError> {
if self.remaining() < 4 {
return Err(ParseError::UnexpectedEof);
}
let a = self.data[self.pos] as u32;
let b = self.data[self.pos + 1] as u32;
let c = self.data[self.pos + 2] as u32;
let d = self.data[self.pos + 3] as u32;
self.pos += 4;
Ok((a << 24) | (b << 16) | (c << 8) | d)
}
fn read_bytes(&mut self, n: usize) -> Result<&'a [u8], ParseError> {
if self.remaining() < n {
return Err(ParseError::UnexpectedEof);
}
let slice = &self.data[self.pos..self.pos + n];
self.pos += n;
Ok(slice)
}
fn read_vlq(&mut self) -> Result<u32, ParseError> {
let mut val: u32 = 0;
for _ in 0..4 {
let b = self.read_u8()?;
val = (val << 7) | (b & 0x7F) as u32;
if b & 0x80 == 0 {
return Ok(val);
}
}
Err(ParseError::UnexpectedEof)
}
}
struct RawEvent {
tick: u64,
kind: RawEventKind,
}
enum RawEventKind {
Msg(u32),
Sysex(Vec<u8>),
Tempo(u64),
}
fn msg_len(status: u8) -> usize {
match status & 0xF0 {
0x80 | 0x90 | 0xA0 | 0xB0 | 0xE0 => 2,
0xC0 | 0xD0 => 1,
_ => 0,
}
}
fn parse_track(data: &[u8]) -> Result<Vec<RawEvent>, ParseError> {
let mut r = Reader::new(data);
let mut events = Vec::new();
let mut tick: u64 = 0;
let mut running_status: u8 = 0;
while r.remaining() > 0 {
let delta = r.read_vlq()? as u64;
tick += delta;
let peek = r.read_u8()?;
if peek == 0xFF {
let meta_type = r.read_u8()?;
let len = r.read_vlq()? as usize;
let body = r.read_bytes(len)?;
if meta_type == 0x51 && len == 3 {
let tempo = (body[0] as u64) << 16
| (body[1] as u64) << 8
| body[2] as u64;
events.push(RawEvent {
tick,
kind: RawEventKind::Tempo(tempo),
});
}
} else if peek == 0xF0 || peek == 0xF7 {
let len = r.read_vlq()? as usize;
let body = r.read_bytes(len)?;
let mut sysex = Vec::with_capacity(1 + body.len());
sysex.push(0xF0);
sysex.extend_from_slice(body);
events.push(RawEvent {
tick,
kind: RawEventKind::Sysex(sysex),
});
} else {
let status;
let first_data;
if peek & 0x80 != 0 {
status = peek;
running_status = status;
first_data = r.read_u8()?;
} else {
status = running_status;
first_data = peek;
}
let n = msg_len(status);
if n == 0 {
return Err(ParseError::BadTrack);
}
let mut msg = status as u32 | (first_data as u32) << 8;
if n == 2 {
let second = r.read_u8()?;
msg |= (second as u32) << 16;
}
events.push(RawEvent {
tick,
kind: RawEventKind::Msg(msg),
});
}
}
Ok(events)
}
pub fn parse(data: &[u8]) -> Result<Vec<Event>, ParseError> {
let mut r = Reader::new(data);
let magic = r.read_u32()?;
if magic != 0x4D546864 {
return Err(ParseError::BadHeader);
}
let header_len = r.read_u32()?;
if header_len < 6 {
return Err(ParseError::BadHeader);
}
let format = r.read_u16()?;
let ntracks = r.read_u16()?;
let division = r.read_u16()?;
if format > 1 {
return Err(ParseError::UnsupportedFormat(format));
}
if division & 0x8000 != 0 {
return Err(ParseError::UnsupportedDivision);
}
let tpqn = division as u64;
if header_len > 6 {
r.read_bytes((header_len - 6) as usize)?;
}
let mut all_events: Vec<RawEvent> = Vec::new();
for _ in 0..ntracks {
let chunk_magic = r.read_u32()?;
let chunk_len = r.read_u32()? as usize;
if chunk_magic != 0x4D54726B {
r.read_bytes(chunk_len)?;
continue;
}
let track_data = r.read_bytes(chunk_len)?;
let mut track_events = parse_track(track_data)?;
all_events.append(&mut track_events);
}
all_events.sort_by_key(|e| e.tick);
let mut tempo = DEFAULT_TEMPO;
let mut last_tick: u64 = 0;
let mut last_sample: u64 = 0;
let mut events = Vec::new();
for raw in &all_events {
let dt = raw.tick - last_tick;
let samples = dt * tempo * 32 / (tpqn * 1000);
last_sample += samples as u64;
last_tick = raw.tick;
let time = last_sample.min(u32::MAX as u64) as u32;
match &raw.kind {
RawEventKind::Tempo(t) => {
tempo = *t;
}
RawEventKind::Msg(msg) => {
events.push(Event::Msg { time, msg: *msg });
}
RawEventKind::Sysex(data) => {
events.push(Event::Sysex {
time,
data: data.clone(),
});
}
}
}
Ok(events)
}
#[cfg(test)]
mod tests {
use super::*;
fn make_header(format: u16, ntracks: u16, division: u16) -> Vec<u8> {
let mut h = Vec::new();
h.extend_from_slice(b"MThd");
h.extend_from_slice(&6u32.to_be_bytes());
h.extend_from_slice(&format.to_be_bytes());
h.extend_from_slice(&ntracks.to_be_bytes());
h.extend_from_slice(&division.to_be_bytes());
h
}
fn make_track(events: &[u8]) -> Vec<u8> {
let mut t = Vec::new();
t.extend_from_slice(b"MTrk");
t.extend_from_slice(&(events.len() as u32).to_be_bytes());
t.extend_from_slice(events);
t
}
#[test]
fn test_single_note() {
let mut data = make_header(0, 1, 480);
let track = [
0x00, 0x90, 0x3C, 0x64, 0x83, 0x60, 0x80, 0x3C, 0x00, ];
data.extend_from_slice(&make_track(&track));
let events = parse(&data).unwrap();
assert_eq!(events.len(), 2);
assert!(
matches!(&events[0], Event::Msg { time: 0, msg } if *msg == 0x643C90)
);
assert!(matches!(&events[1], Event::Msg { time: 16000, .. }));
}
#[test]
fn test_running_status() {
let mut data = make_header(0, 1, 96);
let track = [
0x00, 0x90, 0x3C, 0x64, 0x60, 0x3E, 0x64, ];
data.extend_from_slice(&make_track(&track));
let events = parse(&data).unwrap();
assert_eq!(events.len(), 2);
assert!(
matches!(&events[1], Event::Msg { msg, .. } if *msg == 0x643E90)
);
}
#[test]
fn test_tempo_change() {
let mut data = make_header(0, 1, 480);
let track = [
0x00, 0xFF, 0x51, 0x03, 0x0F, 0x42, 0x40, 0x83, 0x60, 0x90, 0x3C, 0x64, ];
data.extend_from_slice(&make_track(&track));
let events = parse(&data).unwrap();
assert_eq!(events.len(), 1);
assert!(matches!(&events[0], Event::Msg { time: 32000, .. }));
}
#[test]
fn test_sysex() {
let mut data = make_header(0, 1, 480);
let track = [0x00, 0xF0, 0x05, 0x41, 0x10, 0x16, 0x12, 0xF7];
data.extend_from_slice(&make_track(&track));
let events = parse(&data).unwrap();
assert_eq!(events.len(), 1);
match &events[0] {
Event::Sysex { data, .. } => {
assert_eq!(data[0], 0xF0);
assert_eq!(data[1], 0x41);
}
_ => panic!("expected sysex"),
}
}
#[test]
fn test_format2_rejected() {
let data = make_header(2, 1, 480);
assert!(parse(&data).is_err());
}
}