1use crate::{parse_midi_event, serialize_message, ParserState};
24use std::borrow::Borrow;
25
26#[derive(Eq, Ord, PartialEq, PartialOrd, Debug, Clone)]
28pub struct MIDIMessageNote {
29 pub channel: u8,
30 pub note: u8,
31 pub velocity: u8,
32}
33
34#[derive(Eq, Ord, PartialEq, PartialOrd, Debug, Clone)]
36pub enum MIDIMessage<Buffer: Borrow<[u8]>> {
37 NoteOn(MIDIMessageNote),
38 NoteOff(MIDIMessageNote),
39 PolyphonicKeyPressure {
40 channel: u8,
41 note: u8,
42 pressure: u8,
43 },
44 ControlChange {
45 channel: u8,
46 controller_number: u8,
47 value: u8,
48 },
49 ProgramChange {
50 channel: u8,
51 program_number: u8,
52 },
53 ChannelPressure {
54 channel: u8,
55 pressure: u8,
56 },
57 PitchWheelChange {
58 channel: u8,
59 value: u16,
60 },
61 SysExMessage(MIDISysExEvent<Buffer>),
62 SongPositionPointer {
63 beats: u16,
64 },
65 SongSelect {
66 song: u8,
67 },
68 TuneRequest,
69 TimingClock,
70 Start,
71 Continue,
72 Stop,
73 ActiveSensing,
74 Reset,
75 Other {
76 status: u8,
77 },
78}
79
80impl<Buffer: Borrow<[u8]>> MIDIMessage<Buffer> {
81 pub fn note_on(channel: u8, note: u8, velocity: u8) -> Self {
83 MIDIMessage::NoteOn(MIDIMessageNote {
84 channel,
85 note,
86 velocity,
87 })
88 }
89
90 pub fn note_off(channel: u8, note: u8, velocity: u8) -> Self {
92 MIDIMessage::NoteOff(MIDIMessageNote {
93 channel,
94 note,
95 velocity,
96 })
97 }
98
99 pub fn control_change(channel: u8, controller_number: u8, value: u8) -> Self {
100 MIDIMessage::ControlChange {
101 channel,
102 controller_number,
103 value,
104 }
105 }
106
107 pub fn size_hint(&self) -> usize {
109 match self {
110 MIDIMessage::NoteOn(_) => 3,
111 MIDIMessage::NoteOff(_) => 3,
112 MIDIMessage::PolyphonicKeyPressure { .. } => 3,
113 MIDIMessage::ControlChange { .. } => 3,
114 MIDIMessage::ProgramChange { .. } => 2,
115 MIDIMessage::ChannelPressure { .. } => 2,
116 MIDIMessage::PitchWheelChange { .. } => 3,
117 MIDIMessage::SysExMessage(inner) => 2 + inner.message.borrow().len(),
118 MIDIMessage::SongPositionPointer { .. } => 3,
119 MIDIMessage::SongSelect { .. } => 2,
120 MIDIMessage::TuneRequest => 1,
121 MIDIMessage::TimingClock => 1,
122 MIDIMessage::Start => 1,
123 MIDIMessage::Continue => 1,
124 MIDIMessage::Stop => 1,
125 MIDIMessage::ActiveSensing => 1,
126 MIDIMessage::Reset => 1,
127 MIDIMessage::Other { .. } => 1,
128 }
129 }
130}
131
132impl<'a> TryFrom<&'a [u8]> for MIDIMessage<&'a [u8]> {
133 type Error = nom::Err<nom::error::Error<&'a [u8]>>;
134
135 fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
136 let mut state = ParserState::default();
137 let (_, message) = parse_midi_event(value, &mut state)?;
138 Ok(message)
139 }
140}
141
142impl<B: Borrow<[u8]>> From<MIDIMessage<B>> for Vec<u8> {
143 fn from(msg: MIDIMessage<B>) -> Vec<u8> {
144 let mut output = vec![];
145 let _ = serialize_message(msg, &mut output);
146 output
147 }
148}
149
150pub type Input<'a> = &'a [u8];
151
152#[derive(Debug, PartialEq, Eq)]
154pub enum MIDIFileFormat {
155 Single,
159 Simultaneous,
164 Sequential,
168 Unknown,
171}
172
173#[derive(Debug, PartialEq, Eq)]
174pub enum MIDIFileDivision {
175 TicksPerQuarterNote { ticks_per_quarter_note: u16 },
180 SMPTE { format: u8, ticks_per_frame: u8 },
184}
185
186#[derive(Debug, PartialEq, Eq)]
188pub struct MIDIFileHeader {
189 pub format: MIDIFileFormat,
191 pub num_tracks: u16,
193 pub division: MIDIFileDivision,
195}
196
197#[derive(Debug)]
198pub enum MIDIFileChunk<StringRepr: Borrow<str>, Buffer: Borrow<[u8]>> {
199 Header(MIDIFileHeader),
200 Track { events: Vec<MIDITrackEvent<Buffer>> },
201 Unknown { name: StringRepr, body: Buffer },
202}
203
204#[derive(Eq, Ord, PartialEq, PartialOrd, Debug, Clone)]
205pub enum MIDITrackInner<Buffer: Borrow<[u8]>> {
206 Message(MIDIMessage<Buffer>),
207 Meta(MIDIMetaEvent<Buffer>),
208}
209
210#[derive(Eq, Ord, PartialEq, PartialOrd, Debug, Clone)]
211pub struct MIDITrackEvent<Buffer: Borrow<[u8]>> {
212 pub delta_time: u32,
213 pub inner: MIDITrackInner<Buffer>,
214}
215
216impl<Buffer: Borrow<[u8]>> MIDITrackEvent<Buffer> {
217 pub fn new(delta_time: u32, event: MIDITrackInner<Buffer>) -> Self {
218 MIDITrackEvent {
219 delta_time,
220 inner: event,
221 }
222 }
223
224 pub fn delta_time(&self) -> u32 {
225 self.delta_time
226 }
227
228 pub fn message(&self) -> Option<&MIDIMessage<Buffer>> {
229 match &self.inner {
230 MIDITrackInner::Message(message) => Some(message),
231 _ => None,
232 }
233 }
234}
235
236#[derive(Eq, Ord, PartialEq, PartialOrd, Debug, Clone)]
237pub struct MIDIMetaEvent<Buffer: Borrow<[u8]>> {
238 pub meta_type: u8,
239 pub length: u32,
240 pub bytes: Buffer,
241}
242
243impl<Buffer: Borrow<[u8]>> MIDIMetaEvent<Buffer> {
244 pub fn new(meta_type: u8, length: u32, bytes: Buffer) -> Self {
245 MIDIMetaEvent {
246 meta_type,
247 length,
248 bytes,
249 }
250 }
251}
252
253#[derive(Eq, Ord, PartialEq, PartialOrd, Debug, Clone)]
254pub struct MIDISysExEvent<Buffer: Borrow<[u8]>> {
255 pub message: Buffer,
256}
257
258impl<Buffer: Borrow<[u8]>> MIDISysExEvent<Buffer> {
259 pub fn new(message: Buffer) -> Self {
260 MIDISysExEvent { message }
261 }
262}
263
264#[derive(Debug)]
265pub struct MIDIFile<StringRepr: Borrow<str>, Buffer: Borrow<[u8]>> {
266 pub chunks: Vec<MIDIFileChunk<StringRepr, Buffer>>,
267}
268
269impl<StringRepr: Borrow<str>, Buffer: Borrow<[u8]>> MIDIFile<StringRepr, Buffer> {
270 pub fn new(chunks: Vec<MIDIFileChunk<StringRepr, Buffer>>) -> Self {
271 Self { chunks }
272 }
273
274 pub fn chunks(&self) -> &Vec<MIDIFileChunk<StringRepr, Buffer>> {
275 &self.chunks
276 }
277
278 pub fn header(&self) -> Option<&MIDIFileHeader> {
279 self.chunks.iter().find_map(|chunk| match chunk {
280 MIDIFileChunk::Header(header) => Some(header),
281 _ => None,
282 })
283 }
284
285 pub fn track_chunks(&self) -> impl Iterator<Item = &MIDITrackEvent<Buffer>> {
286 self.chunks
287 .iter()
288 .filter_map(|chunk| match chunk {
289 MIDIFileChunk::Track { events } => Some(events),
290 _ => None,
291 })
292 .flatten()
293 }
294
295 pub fn ticks_per_quarter_note(&self) -> u16 {
296 if let Some(MIDIFileHeader {
297 division:
298 MIDIFileDivision::TicksPerQuarterNote {
299 ticks_per_quarter_note,
300 },
301 ..
302 }) = self.header()
303 {
304 *ticks_per_quarter_note
305 } else {
306 0
307 }
308 }
309}