nom_midi/types.rs
1mod note;
2pub use note::Note;
3
4#[derive(Debug, PartialEq, Clone)]
5pub struct SimpleMidiFile<'src> {
6 pub header: MidiHeader,
7 pub tracks: Vec<Track<'src>>,
8}
9
10// header
11// ======
12
13/// A data structure for the Midi file header chunk
14#[derive(Debug, PartialEq, Copy, Clone)]
15pub struct MidiHeader {
16 /// The format of the file
17 pub format: MidiFormat,
18 /// The way time is divided
19 pub division: Division,
20}
21
22/// Represents the different formats of a midi file
23///
24/// The parameter is the number of tracks
25#[derive(Debug, PartialEq, Copy, Clone)]
26pub enum MidiFormat {
27 /// There is only 1 track
28 SingleTrack,
29 /// There are multiple tracks to be played concurrently
30 MultipleTrack(u16),
31 /// There are multiple songs that can be played in any order/combination
32 /// (e.g. a set of sequences for a drum machine)
33 MultipleSong(u16),
34}
35
36impl MidiFormat {
37 /// How many tracks are there in the format
38 pub fn count(&self) -> u16 {
39 match *self {
40 MidiFormat::SingleTrack => 1,
41 MidiFormat::MultipleTrack(ref n) => *n,
42 MidiFormat::MultipleSong(ref n) => *n,
43 }
44 }
45}
46
47/// The way time is divided in the midi track
48#[derive(Debug, PartialEq, Copy, Clone)]
49pub enum Division {
50 /// This means the time is indicated by the number of sub-divisons of a quarter-note
51 /// (a.k.a. crotchet). For example, 4 would mean the maximum resolution is semi-quavers.
52 ///
53 /// A common choice is 96 = 2^5 * 3 allowing dotted notes down to hemidemisemiquavers
54 Metrical(u16),
55 /// The time is absolute, and expresses as a combination of frames per second, and the
56 /// sub-frame resolution
57 Timecode {
58 /// The number of frames per second
59 fps: Fps,
60 /// The sub-frame resolution
61 ///
62 /// Typical values are 4 (corresponding to MIDI Time Code), 8, 10, 80 (corresponding to
63 /// SMPTE bit resolution), or 100.
64 res: u8,
65 },
66}
67
68/// There are only 4 valid fps, below
69#[derive(Debug, PartialEq, Copy, Clone)]
70#[repr(u8)]
71pub enum Fps {
72 /// 24 fps
73 TwentyFour = 24,
74 /// 25 fps
75 TwentyFive = 25,
76 /// 29 fps
77 TwentyNine = 29,
78 /// 30 fps
79 Thirty = 30,
80}
81
82impl Into<u8> for Fps {
83 fn into(self) -> u8 {
84 self as u8
85 }
86}
87
88// Track
89// =====
90
91/// A track chunk (a collection of events)
92#[derive(Debug, PartialEq, Clone)]
93pub struct Track<'src> {
94 pub events: Vec<Event<'src>>,
95}
96
97// Events
98// ======
99
100/// An event present in a track chunk
101#[derive(Debug, PartialEq, Clone)]
102pub struct Event<'src> {
103 pub delta_time: u32,
104 pub event: EventType<'src>,
105}
106
107/// The type of an event in a track chunk, along with event-specific data
108#[derive(Debug, PartialEq, Clone)]
109pub enum EventType<'src> {
110 Midi(MidiEvent),
111 SystemExclusive(SystemExclusiveEvent<'src>),
112 EscapeSequence(EscapeSequence<'src>),
113 Meta(MetaEvent<'src>),
114}
115
116// Midi Events
117// ===========
118
119/// The midi event, along with the channel it applies to
120#[derive(Debug, PartialEq, Copy, Clone)]
121pub struct MidiEvent {
122 /// The channel the midi event applies to
123 pub channel: u8,
124 /// The event
125 pub event: MidiEventType,
126}
127
128/// A midi event
129///
130/// Normally, the majority of messages will be of this type. They are the key messages for
131/// starting and stopping sound, along with changing pitch.
132///
133/// Note that for all values, the top bit is not used, so the numbers will be interpreted the same
134/// for either u8 or i8. I use u8 here.
135#[derive(Debug, PartialEq, Copy, Clone)]
136pub enum MidiEventType {
137 /// Stop sounding the given note
138 ///
139 /// The second param is the release velocity
140 NoteOff(Note, u8),
141 /// Start sounding the given note
142 ///
143 /// The second param is the attack velocity
144 NoteOn(Note, u8),
145 /// Apply aftertouch pressure to the given note
146 ///
147 /// The second param is the amount of aftertouch
148 PolyphonicPressure(Note, u8),
149 /// Set a controller to a value
150 ///
151 /// The first param is the controller to set, and the second param is the value to set it to
152 Controller(u8, u8),
153 /// Select the specified program
154 ///
155 /// The second param is the program to set.
156 ProgramChange(u8),
157 /// Allows all notes to have a specific aftertouch used as default, similar to
158 /// `PolyphonicPressure`
159 ChannelPressure(u8),
160 /// Apply pitch bend to all notes
161 ///
162 /// First param is less significant byte, and second is most significant byte. The value of
163 /// `0x00 0x40` means 'no bend', less means bend down and more means bend up.
164 PitchBend(u8, u8),
165}
166
167// SysEx Events
168// ============
169
170/// A system exclusive message
171#[derive(Debug, PartialEq, Clone)]
172pub struct SystemExclusiveEvent<'src>(pub &'src [u8]);
173
174/// An escape sequence (something not possible to include elsewhere)
175#[derive(Debug, PartialEq, Clone)]
176pub struct EscapeSequence<'src>(pub &'src [u8]);
177
178// Meta Events
179// ===========
180
181/// A special non-MIDI event
182#[derive(Debug, PartialEq, Clone)]
183pub enum MetaEvent<'src> {
184 /// The sequence number (as would be used in a MIDI Cue message)
185 SequenceNumber(u16),
186 /// Free text, can include comments and other useful information, if that information
187 /// doesn't naturally fit in another text-based field
188 Text(&'src [u8]),
189 /// A copyright notice
190 Copyright(&'src [u8]),
191 /// The name of the current sequence or track (depending on context)
192 SequenceOrTrackName(&'src [u8]),
193 /// The name of the current track
194 //TrackName(String),
195 /// The name of the instrument for this track (e.g. "Flute", "Piano", "Tenor", etc.)
196 InstrumentName(&'src [u8]),
197 /// A syllable or set of syllables to be sung as part of a vocal track.
198 Lyric(&'src [u8]),
199 /// A useful position-dependent note in the music (e.g. rehersal mark "A", loop point,
200 /// section name)
201 Marker(&'src [u8]),
202 /// A marker to indicate this event should be synchronized with some non-midi event, e.g. "car
203 /// crash on screen", "actors leave stage", etc.
204 CuePoint(&'src [u8]),
205 /// Indicates what patch or program name should be used by the immediately subsequent Bank
206 /// Select and Program Change messages.
207 ProgramName(&'src [u8]),
208 /// The name of the hardware device used to produce sounds for this track. Might be inserted
209 /// for example if using a branded synth or keyboard to generate midi events.
210 DeviceName(&'src [u8]),
211 /// Indicate which channel subsequent SysEx and Meta events apply to. Lasts until the next
212 /// event of this type, or a normal MIDI event
213 MidiChannelPrefix(u8), // actually u4
214 /// Specify which port future MIDI event apply to. This exists to increase the 4-bit channel
215 /// limit, and so it's functionality overlaps with channels
216 MidiPort(u8), // actually u7
217 /// This event must be at the end of each track, and must not be anywhere else
218 EndOfTrack,
219 /// Specifies the number of microseconds per quarter note for future MIDI events.
220 Tempo(u32), // actually u24
221 /// This is complicated and I don't understand it.
222 SMPTEOffset(SMPTEOffset),
223 /// Set the time signature. If no time signature event occurs before a MIDI event the default
224 /// is `(4, 4)`
225 TimeSignature(TimeSignature),
226 /// Set the key signature. The default is C major.
227 KeySignature(KeySignature),
228 /// Vendor specific events. I don't try to parse them - just return the data
229 SequencerSpecificEvent(&'src [u8]),
230 /// An unrecognised event. To be future-compatible, just ignore these
231 Unknown(u8, &'src [u8]),
232}
233
234/// I don't understand this, but I should be decoding it correctly for those that do
235#[derive(Debug, PartialEq, Clone)]
236pub struct SMPTEOffset {
237 pub fps: Fps,
238 pub hour: u8, // 0 - 23
239 pub minute: u8, // 0 - 59
240 pub second: u8, // 0 - 59
241 pub no_frames: u8, // 0-23/24/28/29, depending on fps
242 pub no_fractional_frames: u8,
243}
244
245/// A time signature
246///
247/// # Examples
248/// Assuming `no_32nd_in_quarter` is 8
249///
250/// - A time signature of 4/4, with a metronome click every 1/4 note, would be encoded
251/// `FF 58 04 04 02 18 08`. There are 24 MIDI Clocks per quarter-note, hence cc=24 (0x18).
252///
253/// - A time signature of 6/8, with a metronome click every 3rd 1/8 note, would be encoded
254/// `FF 58 04 06 03 24 08` Remember, a 1/4 note is 24 MIDI Clocks, therefore a bar of 6/8 is
255/// 72 MIDI Clocks. Hence 3 1/8 notes is 36 (=0x24) MIDI Clocks.
256///
257/// (from http://www.somascape.org/midi/tech/mfile.html)
258#[derive(Debug, PartialEq, Copy, Clone)]
259pub struct TimeSignature {
260 /// The number of beats per bar
261 pub top: u8,
262 /// The size of those beats (1 = semibreve, 2 = minim, 3 = crotchet etc.)
263 pub bottom: u8,
264 /// Clock ticks between metronome clicks
265 pub ticks_per_metronome_click: u8,
266 /// The number of notated 32nd-notes in a MIDI quarter note - for a 1-1 corresponence this
267 /// should be 8.
268 pub number_32nd_in_quarter: u8,
269}
270
271/// All possible Key Signatures
272#[derive(Debug, PartialEq, Copy, Clone)]
273pub enum KeySignature {
274 CMajor,
275 // sharps
276 GMajor,
277 DMajor,
278 AMajor,
279 EMajor,
280 BMajor,
281 FSharpMajor,
282 CSharpMajor,
283 // flats
284 FMajor,
285 BFlatMajor,
286 EFlatMajor,
287 AFlatMajor,
288 DFlatMajor,
289 GFlatMajor,
290 CFlatMajor,
291
292 // minor
293 AMinor,
294 // sharps
295 EMinor,
296 BMinor,
297 FSharpMinor,
298 CSharpMinor,
299 GSharpMinor,
300 DSharpMinor,
301 ASharpMinor,
302 // flats
303 DMinor,
304 GMinor,
305 CMinor,
306 FMinor,
307 BFlatMinor,
308 EFlatMinor,
309 AFlatMinor,
310}
311
312impl KeySignature {
313 /// Count the number of sharps or flats
314 pub fn count(&self) -> u8 {
315 use self::KeySignature::*;
316 match *self {
317 CMajor | AMinor => 0,
318 GMajor | FMajor | EMinor | DMinor => 1,
319 DMajor | BFlatMajor | BMinor | GMinor => 2,
320 AMajor | EFlatMajor | FSharpMinor | CMinor => 3,
321 EMajor | AFlatMajor | CSharpMinor | FMinor => 4,
322 BMajor | DFlatMajor | GSharpMinor | BFlatMinor => 5,
323 FSharpMajor | GFlatMajor | DSharpMinor | EFlatMinor => 6,
324 CSharpMajor | CFlatMajor | ASharpMinor | AFlatMinor => 7,
325 }
326 }
327
328 /// Helper fn for whether there are sharps or flats, that doesn't panic
329 fn is_sharps_unchecked(&self) -> bool {
330 use self::KeySignature::*;
331 match *self {
332 GMajor | DMajor | AMajor | EMajor | BMajor | FSharpMajor | CSharpMajor | EMinor
333 | BMinor | FSharpMinor | CSharpMinor | GSharpMinor | DSharpMinor | ASharpMinor => true,
334 _ => false,
335 }
336 }
337
338 /// Whether there are sharps or flats
339 ///
340 /// # Panics
341 /// Panics if there are no sharps or flats. Use `count` to check this first to avoid
342 pub fn is_sharps(&self) -> bool {
343 use self::KeySignature::*;
344 match *self {
345 CMajor | AMinor => panic!("No sharps or flats"),
346 _ => self.is_sharps_unchecked(),
347 }
348 }
349
350 /// Get a tuple of the number of sharps/flats, and a bool that is true for sharps, false for
351 /// flats.
352 ///
353 /// The second value is not specified (could be anything) when the first is 0.
354 pub fn for_display(&self) -> (u8, bool) {
355 (self.count(), self.is_sharps_unchecked())
356 }
357}