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}