1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
use crate::types::{Division, Fps, MidiFormat, MidiHeader};
use nom::{
error::{make_error, ErrorKind},
Err, IResult,
};
pub fn parse_format(i: &[u8]) -> IResult<&[u8], MidiFormat> {
use nom::number::streaming::be_u16;
let (i, format) = be_u16(i)?;
match format {
0 => {
let (i, num_tracks) = be_u16(i)?;
if num_tracks != 1 {
Err(Err::Error(make_error(i, ErrorKind::Digit)))
} else {
Ok((i, MidiFormat::SingleTrack))
}
}
1 => {
let (i, num_tracks) = be_u16(i)?;
Ok((i, MidiFormat::MultipleTrack(num_tracks)))
}
2 => {
let (i, num_tracks) = be_u16(i)?;
Ok((i, MidiFormat::MultipleSong(num_tracks)))
}
_ => Err(Err::Error(make_error(i, ErrorKind::Digit))),
}
}
pub fn parse_division(i: &[u8]) -> IResult<&[u8], Division> {
use nom::{bytes::streaming::take, number::streaming::be_u16};
let (i, bytes) = take(2usize)(i)?;
let division = if bytes[0] & 0x80 == 0x80 {
let fps = match bytes[1] {
0xE8 => Fps::TwentyFour,
0xE7 => Fps::TwentyFive,
0xE3 => Fps::TwentyNine,
0xE2 => Fps::Thirty,
_ => return Err(Err::Error(make_error(i, ErrorKind::Digit))),
};
let res = bytes[0] & 0x7F;
Division::Timecode { fps, res }
} else {
let (_, note_div) = be_u16(bytes)?;
Division::Metrical(note_div & 0x7FFF)
};
Ok((i, division))
}
pub fn parse_header_chunk(i: &[u8]) -> IResult<&[u8], MidiHeader> {
use nom::bytes::streaming::tag;
use nom::number::streaming::be_u32;
let (i, _) = tag("MThd")(i)?;
let (i, hdr_len) = be_u32(i)?;
if hdr_len != 6 {
return Err(Err::Error(make_error(i, ErrorKind::Digit)));
}
let (i, format) = parse_format(i)?;
let (i, division) = parse_division(i)?;
Ok((i, MidiHeader { format, division }))
}
#[test]
fn test_header_chunk() {
let midi_file = [77u8, 84, 104, 100, 0, 0, 0, 6, 0, 1, 0, 5, 1, 0];
assert_eq!(
parse_header_chunk(&midi_file[..]),
Ok((
&b""[..],
MidiHeader {
format: MidiFormat::MultipleTrack(5),
division: Division::Metrical(256),
}
))
);
}