midi_types/
message.rs

1//! This module contains data types to represent the different messages that can be sent over MIDI.
2
3use crate::Note;
4
5/// An enum with variants for all possible Midi messages.
6#[derive(Debug, PartialEq, Clone, Copy, Eq)]
7#[cfg_attr(feature = "defmt", derive(defmt::Format))]
8pub enum MidiMessage {
9    // Channel voice messages
10    /// Note Off message
11    NoteOff(Channel, Note, Value7),
12
13    /// Note on message
14    NoteOn(Channel, Note, Value7),
15
16    /// Key pressure message for polyphonic aftertouch
17    KeyPressure(Channel, Note, Value7),
18
19    /// Control change message
20    ControlChange(Channel, Control, Value7),
21
22    /// Program change message
23    ProgramChange(Channel, Program),
24
25    /// Channel pressure message for channel aftertouch
26    ChannelPressure(Channel, Value7),
27
28    /// Pitch bend message
29    PitchBendChange(Channel, Value14),
30
31    // System common messages
32    // /// System exclusive message starts
33    // SystemExclusive {
34    //     /// The system exclusive manufacturer id, this is either a 1 byte or 3 byte number
35    //     manufacturer_id: u32,
36    // },
37
38    // /// System exclusive data is received
39    // SystemExclusiveData (Value7),
40
41    // /// Signals the end of the system exclusive block
42    // EndOfExclusive,
43    /// Midi time code quarter frame
44    QuarterFrame(QuarterFrame),
45
46    /// Set the song position pointer
47    SongPositionPointer(Value14),
48
49    /// Specifies which sequence or song is to be played
50    SongSelect(Value7),
51
52    /// Tune all oscillators
53    TuneRequest,
54
55    // System real time messages
56    /// Timing tick message
57    TimingClock,
58
59    /// Start message
60    Start,
61
62    /// Continue message
63    Continue,
64
65    /// Stop message
66    Stop,
67
68    /// Active sensing message
69    ActiveSensing,
70
71    /// Reset message
72    Reset,
73}
74
75impl MidiMessage {
76    /// The length of the rendered data, including the status
77    #[allow(clippy::len_without_is_empty)]
78    #[must_use]
79    pub const fn len(&self) -> usize {
80        match self {
81            Self::NoteOff(..)
82            | Self::NoteOn(..)
83            | Self::KeyPressure(..)
84            | Self::ControlChange(..)
85            | Self::PitchBendChange(..)
86            | Self::SongPositionPointer(..) => 3,
87            Self::ProgramChange(..)
88            | Self::ChannelPressure(..)
89            | Self::QuarterFrame(..)
90            | Self::SongSelect(..) => 2,
91            Self::TuneRequest
92            | Self::TimingClock
93            | Self::Start
94            | Self::Continue
95            | Self::Stop
96            | Self::ActiveSensing
97            | Self::Reset => 1,
98        }
99    }
100}
101
102#[allow(missing_docs)]
103/// Status byte constants
104pub mod status {
105    pub const NOTE_OFF: u8 = 0x80;
106    pub const NOTE_ON: u8 = 0x90;
107    pub const KEY_PRESSURE: u8 = 0xA0;
108    pub const CONTROL_CHANGE: u8 = 0xB0;
109    pub const PITCH_BEND_CHANGE: u8 = 0xE0;
110    pub const SONG_POSITION_POINTER: u8 = 0xF2;
111    pub const PROGRAM_CHANGE: u8 = 0xC0;
112    pub const CHANNEL_PRESSURE: u8 = 0xD0;
113    pub const QUARTER_FRAME: u8 = 0xF1;
114    pub const SONG_SELECT: u8 = 0xF3;
115    pub const TUNE_REQUEST: u8 = 0xF6;
116    pub const TIMING_CLOCK: u8 = 0xF8;
117    pub const START: u8 = 0xFA;
118    pub const CONTINUE: u8 = 0xFB;
119    pub const STOP: u8 = 0xFC;
120    pub const ACTIVE_SENSING: u8 = 0xFE;
121    pub const RESET: u8 = 0xFF;
122
123    pub const SYSEX_START: u8 = 0xF0;
124    pub const SYSEX_END: u8 = 0xF7;
125}
126
127/// Represents a Midi channel, Midi channels can range from 0 to 15, but are represented as 1 based
128/// values Channel 1 to 16
129#[derive(Debug, PartialEq, Eq, Copy, Clone)]
130#[cfg_attr(feature = "defmt", derive(defmt::Format))]
131pub struct Channel(u8);
132
133impl Channel {
134    /// Create a new `Channel`
135    ///
136    /// # Arguments
137    /// * `channel` - the 0 based channel value
138    ///
139    /// # Note
140    /// * The `channel` will be clamped so it is in the 0..15 valid range.
141    #[must_use]
142    pub const fn new(channel: u8) -> Self {
143        debug_assert!(channel <= 15, "Channel exceeds valid range");
144        Self(if channel > 15 { 15 } else { channel })
145    }
146
147    /// MIDI channel 1
148    pub const C1: Self = Self::new(0);
149    /// MIDI channel 2
150    pub const C2: Self = Self::new(1);
151    /// MIDI channel 3
152    pub const C3: Self = Self::new(2);
153    /// MIDI channel 4
154    pub const C4: Self = Self::new(3);
155    /// MIDI channel 5
156    pub const C5: Self = Self::new(4);
157    /// MIDI channel 6
158    pub const C6: Self = Self::new(5);
159    /// MIDI channel 7
160    pub const C7: Self = Self::new(6);
161    /// MIDI channel 8
162    pub const C8: Self = Self::new(7);
163    /// MIDI channel 9
164    pub const C9: Self = Self::new(8);
165    /// MIDI channel 10
166    pub const C10: Self = Self::new(9);
167    /// MIDI channel 11
168    pub const C11: Self = Self::new(10);
169    /// MIDI channel 12
170    pub const C12: Self = Self::new(11);
171    /// MIDI channel 13
172    pub const C13: Self = Self::new(12);
173    /// MIDI channel 14
174    pub const C14: Self = Self::new(13);
175    /// MIDI channel 15
176    pub const C15: Self = Self::new(14);
177    /// MIDI channel 16
178    pub const C16: Self = Self::new(15);
179
180    /// The minimum MIDI channel
181    pub const MIN: Self = Self::C1;
182    /// The maximum MIDI channel
183    pub const MAX: Self = Self::C16;
184}
185
186impl From<u8> for Channel {
187    fn from(channel: u8) -> Self {
188        Self::new(channel)
189    }
190}
191
192impl From<Channel> for u8 {
193    fn from(channel: Channel) -> Self {
194        channel.0
195    }
196}
197
198/// A Midi controller number
199#[derive(Debug, PartialEq, Eq, Copy, Clone)]
200#[cfg_attr(feature = "defmt", derive(defmt::Format))]
201pub struct Control(u8);
202
203impl Control {
204    /// Create a new `Control`
205    ///
206    /// # Arguments
207    /// * `control` - the control number value
208    ///
209    /// # Note
210    /// * The `control` number will be clamped so it is in the 0..127 valid range
211    #[must_use]
212    pub const fn new(control: u8) -> Self {
213        debug_assert!(control < 127, "Control exceeds valid range");
214        Self(if control > 127 { 127 } else { control })
215    }
216}
217
218impl From<u8> for Control {
219    fn from(control: u8) -> Self {
220        Self::new(control)
221    }
222}
223
224impl From<Control> for u8 {
225    fn from(control: Control) -> Self {
226        control.0
227    }
228}
229
230/// A Midi program number, these usually correspond to presets on Midi devices
231#[derive(Debug, PartialEq, Eq, Copy, Clone)]
232#[cfg_attr(feature = "defmt", derive(defmt::Format))]
233pub struct Program(u8);
234
235impl Program {
236    /// Create a new `Program`
237    ///
238    /// # Arguments
239    /// * `program` - the program number value
240    ///
241    /// # Note
242    /// * The `program` will be clamped so it is in the 0..127 valid range
243    #[must_use]
244    pub const fn new(program: u8) -> Self {
245        debug_assert!(program < 127, "Program exceeds valid range");
246        Self(if program > 127 { 127 } else { program })
247    }
248}
249
250impl From<u8> for Program {
251    fn from(program: u8) -> Self {
252        Self::new(program)
253    }
254}
255
256impl From<Program> for u8 {
257    fn from(program: Program) -> Self {
258        program.0
259    }
260}
261
262/// A 7 bit Midi data value stored in an unsigned 8 bit integer, the msb is always 0
263#[derive(Debug, PartialEq, Eq, Copy, Clone)]
264#[cfg_attr(feature = "defmt", derive(defmt::Format))]
265pub struct Value7(u8);
266
267impl Value7 {
268    /// Create a new `Value7`
269    ///
270    /// # Arguments
271    /// * `value` - the value
272    ///
273    /// # Note
274    /// * The `value` will be clamped so it is in the 0..127 valid range
275    #[must_use]
276    pub const fn new(value: u8) -> Self {
277        debug_assert!(value <= 127, "Value7 exceeds valid range");
278        Self(if value > 127 { 127 } else { value })
279    }
280}
281
282impl From<u8> for Value7 {
283    fn from(value: u8) -> Self {
284        Self::new(value)
285    }
286}
287
288impl From<Value7> for u8 {
289    fn from(value: Value7) -> Self {
290        value.0
291    }
292}
293
294/// A 14 bit Midi value stored as two 7 bit Midi data values, where the msb is always 0 to signify
295/// that this is a data value.
296#[derive(Debug, PartialEq, Eq, Copy, Clone)]
297#[cfg_attr(feature = "defmt", derive(defmt::Format))]
298pub struct Value14(u8, u8);
299
300impl Value14 {
301    /// Create a new `Value14`
302    ///
303    /// # Arguments
304    /// * `val` - the value
305    ///
306    /// # Note
307    /// * The `val` will be clamped so it is in the 0..127 valid range
308    #[must_use]
309    pub const fn new(msb: u8, lsb: u8) -> Self {
310        debug_assert!(msb <= 127, "Value14 msb exceeds valid range");
311        debug_assert!(lsb <= 127, "Value14 lsb exceeds valid range");
312        Self(
313            if msb >= 127 { 127 } else { msb },
314            if lsb >= 127 { 127 } else { lsb },
315        )
316    }
317}
318
319impl From<(u8, u8)> for Value14 {
320    fn from(value: (u8, u8)) -> Self {
321        Self::new(value.0, value.1)
322    }
323}
324
325impl From<Value14> for (u8, u8) {
326    fn from(value: Value14) -> (u8, u8) {
327        (value.0, value.1)
328    }
329}
330
331impl From<u16> for Value14 {
332    fn from(value: u16) -> Self {
333        debug_assert!(value <= 16383, "Value14 exceeds valid range");
334        let value = if value > 16383 { 16383 } else { value };
335        Self(((value & 0x3f80) >> 7) as u8, (value & 0x007f) as u8)
336    }
337}
338
339impl From<Value14> for u16 {
340    fn from(value: Value14) -> Self {
341        (Self::from(value.0) << 7) + Self::from(value.1)
342    }
343}
344
345///Convert from -8192i16..8191i16
346impl From<i16> for Value14 {
347    #[allow(clippy::cast_sign_loss)]
348    fn from(value: i16) -> Self {
349        debug_assert!(value >= -8192, "Value14 exceeds valid range");
350        debug_assert!(value <= 8191, "Value14 exceeds valid range");
351        let value = value.clamp(-8192, 8191) + 8192;
352        Self::new(((value & 0x3f80) >> 7) as u8, (value & 0x007f) as u8)
353    }
354}
355
356///Convert into -8192i16..8191i16
357impl From<Value14> for i16 {
358    #[allow(clippy::cast_possible_wrap)]
359    fn from(value: Value14) -> Self {
360        let v: u16 = value.into();
361        (v as Self) - 8192
362    }
363}
364
365///Convert from -1.0..1.0
366impl From<f32> for Value14 {
367    #[allow(clippy::cast_possible_truncation)]
368    fn from(value: f32) -> Self {
369        Self::from((value * if value > 0.0 { 8191.0 } else { 8192.0 }) as i16)
370    }
371}
372
373///Convert into -1.0..1.0
374impl From<Value14> for f32 {
375    fn from(value: Value14) -> Self {
376        let v: i16 = value.into();
377        let v = Self::from(v) / if v > 0 { 8191.0 } else { 8192.0 };
378        v.clamp(-1.0, 1.0)
379    }
380}
381
382/*
383/// The SMPTE type used. This indicates the number of frames per second
384#[derive(Debug, PartialEq, Copy, Clone)]
385#[cfg_attr(feature = "defmt", derive(defmt::Format))]
386pub enum SmpteType {
387    /// 24 frames per second
388    Frames24,
389
390    /// 25 frames per second
391    Frames25,
392
393    /// 29.97 frames per second
394    DropFrame30,
395
396    /// 30 frames per second
397    Frames30,
398}
399
400/// The value of the quarter frame message, this message contains a message type and a value. Each
401/// of these eight messages encodes a 4 bit part of the midi time code. As one of these is sent
402/// every quarter frames, the complete midi time code is sent every two frames.
403#[derive(Debug, PartialEq, Copy, Clone)]
404#[cfg_attr(feature = "defmt", derive(defmt::Format))]
405pub enum QuarterFrameType {
406    /// Frame number low nibble
407    FramesLS,
408
409    /// Frame count high nibble
410    FramesMS,
411
412    /// Seconds low nibble
413    SecondsLS,
414
415    /// Seconds high nibble
416    SecondsMS,
417
418    /// Minutes low nibble
419    MinutesLS,
420
421    /// Minutes high nibble
422    MinutesMS,
423
424    /// Hours low nibble
425    HoursLS,
426
427    /// Combined hours high nibble and smpte type (frames per second)
428    HoursMS,
429}
430*/
431
432/// A MIDI Quarter Frame value, used for sync.
433#[derive(Debug, PartialEq, Eq, Copy, Clone)]
434#[cfg_attr(feature = "defmt", derive(defmt::Format))]
435pub struct QuarterFrame(u8);
436
437impl QuarterFrame {
438    /// Create a new `QuarterFrame`
439    ///
440    /// # Arguments
441    /// * `frame` - the value
442    ///
443    /// # Note
444    /// * The `frame` will be clamped so it is in the 0..127 valid range
445    #[must_use]
446    pub const fn new(frame: u8) -> Self {
447        debug_assert!(frame <= 127, "QuarterFrame exceeds valid range");
448        Self(if frame > 127 { 127 } else { frame })
449    }
450}
451
452/*
453impl QuarterFrame {
454    pub fn frame_type(&self) -> QuarterFrameType {
455        unimplemented!()
456    }
457
458    pub fn value(&self) -> u8 {
459        unimplemented!()
460    }
461
462    pub fn smpte_type(&self) -> SmpteType {
463        unimplemented!()
464    }
465}
466*/
467
468impl From<u8> for QuarterFrame {
469    fn from(frame: u8) -> Self {
470        Self::new(frame)
471    }
472}
473
474impl From<QuarterFrame> for u8 {
475    fn from(value: QuarterFrame) -> Self {
476        value.0
477    }
478}
479
480#[cfg(test)]
481mod test {
482    use super::*;
483
484    #[test]
485    fn should_combine_7_bit_vals_into_14() {
486        let val = Value14::new(0b01010101u8, 0b01010111u8);
487        assert_eq!(0b0010101011010111u16, val.into());
488        assert_eq!((0b01010101u8, 0b01010111u8), val.into())
489    }
490
491    #[test]
492    fn conversion_u16_14() {
493        let val: Value14 = Value14::from(16383u16);
494        assert_eq!((127, 127), val.into());
495        assert_eq!(16383u16, val.into());
496
497        let val: Value14 = Value14::from(16256u16);
498        assert_eq!((127, 0), val.into());
499        assert_eq!(16256u16, val.into());
500
501        let val: Value14 = Value14::from(127u16);
502        assert_eq!((0, 127), val.into());
503        assert_eq!(127u16, val.into());
504
505        let val: Value14 = Value14::from(0u16);
506        assert_eq!((0, 0), val.into());
507        assert_eq!(0u16, val.into());
508    }
509
510    #[test]
511    fn conversion_i16_14() {
512        let val: Value14 = Value14::from(8191i16);
513        assert_eq!((127, 127), val.into());
514        assert_eq!(8191i16, val.into());
515        assert_eq!(val, Value14::new(127, 127));
516
517        let val: Value14 = Value14::from(8190i16);
518        assert_eq!((127, 126), val.into());
519        assert_eq!(8190i16, val.into());
520
521        let val: Value14 = Value14::from(-8192i16);
522        assert_eq!((0, 0), val.into());
523        assert_eq!(-8192i16, val.into());
524
525        let val: Value14 = Value14::from(0i16);
526        assert_eq!((64, 0), val.into());
527        assert_eq!(0i16, val.into());
528
529        let val: Value14 = Value14::from(1i16);
530        assert_eq!((64, 1), val.into());
531        assert_eq!(1i16, val.into());
532    }
533
534    #[test]
535    fn conversion_f32_14() {
536        let val: Value14 = Value14::from(0.0f32);
537        assert_eq!((64, 0), val.into());
538        assert_eq!(0.0f32, val.into());
539
540        let val: Value14 = Value14::from(1.0f32);
541        assert_eq!((127, 127), val.into());
542        assert_eq!(1.0f32, val.into());
543
544        let val: Value14 = Value14::from(-1.0f32);
545        assert_eq!((0, 0), val.into());
546        assert_eq!(-1.0f32, val.into());
547    }
548}