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