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