midi_msg/lib.rs
1//! This is a library for serializing and deserializing MIDI (byte) streams.
2//!
3//! ## Creating MIDI byte sequences
4//! [`MidiMsg`] is the starting point for all MIDI messages. All `MidiMsg`s
5//! can be serialized into a valid MIDI sequence. You can create a `MidiMsg`
6//! and turn it into a `Vec<u8>` like so:
7//!
8//! ```
9//! use midi_msg::*;
10//!
11//! MidiMsg::ChannelVoice {
12//! channel: Channel::Ch1,
13//! msg: ChannelVoiceMsg::NoteOn {
14//! note: 60,
15//! velocity: 127
16//! }
17//! }
18//! .to_midi();
19//! ```
20//!
21//! ## Deserializing MIDI byte sequences
22//! Likewise, byte sequences can be deserialized into `MidiMsg`s with [`MidiMsg::from_midi`]:
23//!
24//! ```
25//! use midi_msg::*;
26//!
27//! // These are two MIDI 'note on' messages:
28//! let midi_bytes: Vec<u8> = vec![
29//! 0x93, 0x66, 0x70, // First msg
30//! 0x93, 0x55, 0x60, // Second msg
31//!];
32//!
33//! let (msg, len) = MidiMsg::from_midi(&midi_bytes).expect("Not an error");
34//!
35//! assert_eq!(len, 3);
36//! assert_eq!(msg, MidiMsg::ChannelVoice {
37//! channel: Channel::Ch4,
38//! msg: ChannelVoiceMsg::NoteOn {
39//! note: 0x66,
40//! velocity: 0x70
41//! }
42//! });
43//! ```
44//!
45//! Where then `len` returned is the number of bytes used when a `MidiMsg` has been deserialized.
46//!
47//! Similarly, [`MidiMsg::from_midi_with_context`] can be used to track the state associated
48//! with a MIDI stream, which is necessary to deserialize certain messages:
49//!
50//! ```
51//! use midi_msg::*;
52//!
53//! let mut ctx = ReceiverContext::new();
54//!
55//! // This is a three-byte MIDI 'note on' message followed by a two-byte "running status"
56//! // 'note on' message, which inherits its first ("status") byte from the last `ChannelModeMsg`:
57//! let midi_bytes: Vec<u8> = vec![
58//! 0x93, 0x66, 0x70, // First msg
59//! 0x55, 0x60, // Running status msg
60//!];
61//!
62//! let (_msg1, len1) =
63//! MidiMsg::from_midi_with_context(&midi_bytes, &mut ctx).expect("Not an error");
64//! let (msg2, len2) =
65//! MidiMsg::from_midi_with_context(&midi_bytes[len1..], &mut ctx).expect("Not an error");
66//!
67//! assert_eq!(len2, 2);
68//! assert_eq!(msg2, MidiMsg::ChannelVoice {
69//! channel: Channel::Ch4,
70//! msg: ChannelVoiceMsg::NoteOn {
71//! note: 0x55,
72//! velocity: 0x60
73//! }
74//! });
75//! ```
76//!
77//! The previous message would not have been deserializable without the context:
78//!
79//! ```should_panic
80//! use midi_msg::*;
81//!
82//! let midi_bytes: Vec<u8> = vec![
83//! 0x93, 0x66, 0x70, // First msg
84//! 0x55, 0x60, // Running status msg
85//!];
86//!
87//! let (_msg1, len1) = MidiMsg::from_midi(&midi_bytes).expect("Not an error");
88//! MidiMsg::from_midi(&midi_bytes[len1..]).unwrap();
89//!
90//! ```
91//!
92//! ## Midi files
93//! We can work with Standard Midi Files (SMF) in much the same way. The [`MidiFile`] struct represents this data type and it can be serialized into a `Vec<u8>` and deserialized from a `Vec<u8>`. It holds a header and a list of tracks. Regular [`Track::Midi`] tracks contains a list of [`MidiMsg`] events along with the "delta time" that separates subsequent ones. The definition of the delta time is given by the `division` field in the [`Header`].
94//!
95//! Convenience functions are provided for constructing a `MidiFile` based on a series of events and absolute beat or frame timings. For example, the following creates a `MidiFile` with a single track containing a single note.
96//!
97//! ```
98//! # #[cfg(feature = "file")]
99//! # fn main() {
100//! use midi_msg::*;
101//!
102//! let mut file = MidiFile::default();
103//! // Add a track, updating the header with the number of tracks:
104//! file.add_track(Track::default());
105//! // Add a note on message at beat 0:
106//! file.extend_track(0, MidiMsg::ChannelVoice {
107//! channel: Channel::Ch1,
108//! msg: ChannelVoiceMsg::NoteOn {
109//! note: 60,
110//! velocity: 127
111//! }
112//! }, 0.0);
113//! // Add a note off message at beat 1:
114//! file.extend_track(0, MidiMsg::ChannelVoice {
115//! channel: Channel::Ch1,
116//! msg: ChannelVoiceMsg::NoteOff {
117//! note: 60,
118//! velocity: 0
119//! }
120//! }, 1.0);
121//! // Add an end of track message at beat 4,
122//! // which is the only required (by the spec) message in a track:
123//! file.extend_track(0, MidiMsg::Meta { msg: Meta::EndOfTrack }, 4.0);
124//!
125//! // Now we can serialize the track to a Vec<u8>:
126//! let midi_bytes = file.to_midi();
127//! // And we can deserialize it back to a MidiFile:
128//! let file2 = MidiFile::from_midi(&midi_bytes).unwrap();
129//! assert_eq!(file, file2);
130//! # }
131//!
132//! # #[cfg(not(feature = "file"))]
133//! # fn main() {}
134//! ```
135//!
136//! ## Notes
137//!
138//! See the [readme](https://github.com/AlexCharlton/midi-msg/blob/master/readme.md) for a
139//! list of the MIDI Manufacturer Association documents that are referenced throughout these docs.
140//!
141//! Deserialization of most of `UniversalRealTimeMsg` and `UniversalNonRealTimeMsg` has not
142//! yet been implemented.
143
144#![cfg_attr(not(feature = "std"), no_std)]
145
146extern crate alloc;
147
148mod util;
149pub use util::{
150 freq_to_midi_note_cents, freq_to_midi_note_float, midi_note_cents_to_freq,
151 midi_note_float_to_freq,
152};
153
154mod parse_error;
155pub use parse_error::*;
156mod context;
157pub use context::*;
158mod time_code;
159pub use time_code::*;
160
161mod channel_voice;
162pub use channel_voice::*;
163mod channel_mode;
164pub use channel_mode::*;
165mod general_midi;
166pub use general_midi::*;
167mod system_common;
168pub use system_common::*;
169mod system_real_time;
170pub use system_real_time::*;
171#[cfg(feature = "sysex")]
172mod system_exclusive;
173#[cfg(feature = "sysex")]
174pub use system_exclusive::*;
175#[cfg(feature = "file")]
176mod file;
177#[cfg(feature = "file")]
178pub use file::*;
179
180mod message;
181pub use message::*;
182
183// A helper used in tests
184#[cfg(test)]
185pub fn test_serialization(msg: MidiMsg, ctx: &mut ReceiverContext) {
186 #[cfg(not(feature = "std"))]
187 use crate::alloc::format;
188
189 let midi = msg.to_midi();
190 let (msg2, len) = MidiMsg::from_midi_with_context(&midi, ctx).unwrap_or_else(|_| panic!("The input message should be serialized into a deserializable stream\nInput: {:?}\nGot: {:#?}",
191 &midi,
192 &msg));
193 assert_eq!(
194 midi.len(),
195 len,
196 "Expected deserializing of {:?} to be of length {} but got {:?} which has length {}",
197 &msg,
198 midi.len(),
199 &msg2,
200 len
201 );
202 assert_eq!(msg, msg2);
203}