### Create System Real-Time Start Message Source: https://context7.com/alexcharlton/midi-msg/llms.txt Example of creating and serializing a `Start` system real-time message. ```rust use midi_msg::* let start = MidiMsg::SystemRealTime { msg: SystemRealTimeMsg::Start }.to_midi(); assert_eq!(start, vec![0xFA]); ``` -------------------------------- ### Run Example to Interpret MIDI Messages Source: https://github.com/alexcharlton/midi-msg/blob/master/readme.md Execute the `test_read_input` example to observe how midi-msg interprets received MIDI messages. This requires `midir` to be installed as a dev-dependency. ```bash cargo run --example test_read_input ``` -------------------------------- ### Create Program Change Message Source: https://context7.com/alexcharlton/midi-msg/llms.txt Example of creating a `ProgramChange` message to select a General MIDI instrument. ```rust use midi_msg::* // Program change — select General MIDI "Acoustic Grand Piano" let pc = MidiMsg::ChannelVoice { channel: Channel::Ch1, msg: ChannelVoiceMsg::ProgramChange { program: 0 }, }.to_midi(); assert_eq!(pc, vec![0xC0, 0]); ``` -------------------------------- ### Create System Common Song Select Message Source: https://context7.com/alexcharlton/midi-msg/llms.txt Example of creating a `SongSelect` message with a 7-bit value. ```rust use midi_msg::* let song_sel = MidiMsg::SystemCommon { msg: SystemCommonMsg::SongSelect(5), }.to_midi(); assert_eq!(song_sel, vec![0xF3, 5]); ``` -------------------------------- ### Create RPN Parameter Control Change Message Source: https://context7.com/alexcharlton/midi-msg/llms.txt Example of creating a `ControlChange` message for a `Parameter` using `Parameter::FineTuningEntry`. ```rust use midi_msg::* // RPN: fine tuning parameter let rpn = MidiMsg::ChannelVoice { channel: Channel::Ch2, msg: ChannelVoiceMsg::ControlChange { control: ControlChange::Parameter(Parameter::FineTuningEntry(-30)), }, }.to_midi(); assert!(!rpn.is_empty()); ``` -------------------------------- ### Example MIDI Message Interpretation Output Source: https://github.com/alexcharlton/midi-msg/blob/master/readme.md This output demonstrates the format of interpreted MIDI messages, showing timestamps and the message type with its parameters. ```text 1816489080: ChannelVoice { channel: Ch1, msg: NoteOn { note: 62, velocity: 59 } } 1816643991: ChannelVoice { channel: Ch1, msg: NoteOff { note: 62, velocity: 53 } } ``` -------------------------------- ### Create Polyphonic Mode Channel Mode Message Source: https://context7.com/alexcharlton/midi-msg/llms.txt Example of switching to monophonic mode with a specified number of voices using `PolyMode::Mono`. ```rust use midi_msg::* // Switch to monophonic mode with 4 voices let mono = MidiMsg::ChannelMode { channel: Channel::Ch1, msg: ChannelModeMsg::PolyMode(PolyMode::Mono(4)), }.to_midi(); assert_eq!(mono, vec![0xB0, 126, 4]); ``` -------------------------------- ### SystemRealTimeMsg Source: https://context7.com/alexcharlton/midi-msg/llms.txt Single-byte timing and transport messages like TimingClock, Start, Continue, Stop, ActiveSensing, and SystemReset. ```APIDOC ## SystemRealTimeMsg Single-byte timing and transport messages: `TimingClock`, `Start`, `Continue`, `Stop`, `ActiveSensing`, `SystemReset`. ### Example: Timing Clock ```rust use midi_msg::* let clock = MidiMsg::SystemRealTime { msg: SystemRealTimeMsg::TimingClock }.to_midi(); assert_eq!(clock, vec![0xF8]); ``` ### Example: Start ```rust use midi_msg::* let start = MidiMsg::SystemRealTime { msg: SystemRealTimeMsg::Start }.to_midi(); assert_eq!(start, vec![0xFA]); ``` ### Example: Stop ```rust use midi_msg::* let stop = MidiMsg::SystemRealTime { msg: SystemRealTimeMsg::Stop }.to_midi(); assert_eq!(stop, vec![0xFC]); ``` ### Example: Round-trip deserialization ```rust use midi_msg::* let mut ctx = ReceiverContext::new(); let (msg, len) = MidiMsg::from_midi_with_context(&[0xF8], &mut ctx).unwrap(); assert_eq!(len, 1); assert!(msg.is_system_real_time()); ``` ``` -------------------------------- ### Find Next MIDI Message Offset Source: https://context7.com/alexcharlton/midi-msg/llms.txt Scans a byte slice to find the starting byte offset of the next MIDI status byte, skipping running-status bytes. Useful for error recovery in raw MIDI data. ```rust use midi_msg::next_message; let raw = vec![ 0x90, 0x3C, 0x7F, // NoteOn 0x80, 0x3C, 0x00, // NoteOff ]; // Index of the second message's status byte assert_eq!(next_message(&raw), Some(3)); // Called mid-message (offset by 1 byte into the first message) assert_eq!(next_message(&raw[1..]), Some(2)); // 3 - 1 = 2 // Empty or single-message slice returns None assert_eq!(next_message(&raw[3..]), None); ``` -------------------------------- ### Build and Serialize a Standard MIDI File Source: https://context7.com/alexcharlton/midi-msg/llms.txt Demonstrates programmatically building a Standard MIDI File (SMF) using `MidiFile`, `extend_track`, and serializing it to bytes. Requires the `file` feature. ```rust #[cfg(feature = "file")] fn build_midi_file() { use midi_msg::* let mut file = MidiFile::default(); file.add_track(Track::default()); // NoteOn at beat 0 file.extend_track(0, MidiMsg::ChannelVoice { channel: Channel::Ch1, msg: ChannelVoiceMsg::NoteOn { note: 60, velocity: 100 }, }, 0.0); // NoteOff at beat 1 file.extend_track(0, MidiMsg::ChannelVoice { channel: Channel::Ch1, msg: ChannelVoiceMsg::NoteOff { note: 60, velocity: 0 }, }, 1.0); // Required end-of-track meta event at beat 4 file.extend_track(0, MidiMsg::Meta { msg: Meta::EndOfTrack }, 4.0); // Serialize to bytes let bytes: Vec = file.to_midi(); // Round-trip: parse bytes back into a MidiFile let file2 = MidiFile::from_midi(&bytes).expect("valid SMF"); assert_eq!(file, file2); // Parse from an .mid file on disk (std environment) let raw = std::fs::read("tests/echoes.mid").unwrap(); let parsed = MidiFile::from_midi(&raw).expect("valid .mid file"); println!("Tracks: {}", parsed.tracks.len()); } ``` -------------------------------- ### Create Universal Real-Time System Exclusive Messages Source: https://context7.com/alexcharlton/midi-msg/llms.txt Shows how to construct Universal Real-Time System Exclusive messages, such as master volume, for a specified device. Requires the `sysex` feature. ```rust use midi_msg::* // Universal Real-Time: master volume (0–16383) to device 3 let vol = MidiMsg::SystemExclusive { msg: SystemExclusiveMsg::UniversalRealTime { device: DeviceID::Device(3), msg: UniversalRealTimeMsg::MasterVolume(1000), }, }.to_midi(); assert_eq!(vol, vec![0xF0, 0x7F, 0x03, 0x04, 0x01, 0x68, 0x07, 0xF7]); ``` -------------------------------- ### Create System Real-Time Timing Clock Message Source: https://context7.com/alexcharlton/midi-msg/llms.txt Shows how to create and serialize a `TimingClock` system real-time message. ```rust use midi_msg::* let clock = MidiMsg::SystemRealTime { msg: SystemRealTimeMsg::TimingClock }.to_midi(); assert_eq!(clock, vec![0xF8]); ``` -------------------------------- ### Create System Real-Time Stop Message Source: https://context7.com/alexcharlton/midi-msg/llms.txt Demonstrates creating and serializing a `Stop` system real-time message. ```rust use midi_msg::* let stop = MidiMsg::SystemRealTime { msg: SystemRealTimeMsg::Stop }.to_midi(); assert_eq!(stop, vec![0xFC]); ``` -------------------------------- ### Frequency / MIDI Note Conversion Utilities Source: https://context7.com/alexcharlton/midi-msg/llms.txt Provides four free functions for converting between frequencies (Hz) and MIDI note numbers, supporting fractional-cent precision. These utilities are `no_std` compatible. ```APIDOC ## Frequency / MIDI note conversion utilities Four free functions convert between Hz and MIDI note numbers (with fractional-cent precision). These use `micromath` approximations and are `no_std` compatible. ```rust use midi_msg::* // Frequency → floating-point MIDI note (1.0 = 100 cents) let note = freq_to_midi_note_float(440.0); assert!((note - 69.0).abs() < 0.01); // A4 = MIDI note 69 // Floating-point MIDI note → Hz let freq = midi_note_float_to_freq(69.0); assert!((freq - 440.0).abs() < 0.1); // MIDI note number + cents offset → Hz let freq_g4 = midi_note_cents_to_freq(67, 0.0); // G4 ≈ 392 Hz assert!((freq_g4 - 392.0).abs() < 0.1); // Hz → (note_number, cents_above_semitone) let (note_num, cents) = freq_to_midi_note_cents(261.6256); // C4 assert_eq!(note_num, 60); // middle C assert!(cents.abs() < 1.0); // very close to the semitone ``` ``` -------------------------------- ### Create Reset All Controllers Channel Mode Message Source: https://context7.com/alexcharlton/midi-msg/llms.txt Demonstrates sending a `ResetAllControllers` message on a MIDI channel. ```rust use midi_msg::* // Reset all controllers let reset = MidiMsg::ChannelMode { channel: Channel::Ch1, msg: ChannelModeMsg::ResetAllControllers, }.to_midi(); assert_eq!(reset, vec![0xB0, 121, 0]); ``` -------------------------------- ### Stateful MIDI Deserialization with ReceiverContext Source: https://context7.com/alexcharlton/midi-msg/llms.txt Use `from_midi_with_context` for parsing MIDI data while maintaining state like running status and 14-bit CC extensions. Enable `.complex_cc()` on the context for semantic CC interpretation; otherwise, raw CC values are returned. ```rust use midi_msg::* let mut ctx = ReceiverContext::new(); // simple CC mode (default) // A full NoteOn followed by a running-status NoteOn (no status byte) let raw: Vec = vec![ 0x93, 0x66, 0x70, // status + data 0x55, 0x60, // running status: same channel/type, different note ]; let (_msg1, len1) = MidiMsg::from_midi_with_context(&raw, &mut ctx).unwrap(); let (msg2, len2) = MidiMsg::from_midi_with_context(&raw[len1..], &mut ctx).unwrap(); assert_eq!(len2, 2); // only data bytes, no status byte assert_eq!(msg2, MidiMsg::ChannelVoice { channel: Channel::Ch4, msg: ChannelVoiceMsg::NoteOn { note: 0x55, velocity: 0x60 }, }); // Complex CC: enable semantic interpretation let mut ctx = ReceiverContext::new().complex_cc(); let volume_bytes = MidiMsg::ChannelVoice { channel: Channel::Ch1, msg: ChannelVoiceMsg::ControlChange { control: ControlChange::Volume(1000) }, }.to_midi(); // MSB arrives first let (msg1, l1) = MidiMsg::from_midi_with_context(&volume_bytes, &mut ctx).unwrap(); // LSB is automatically consumed and merged into the previous message // (depending on stream ordering both may be consumed in one call) println!("{:?}", msg1); // ChannelVoice { channel: Ch1, msg: ControlChange { control: Volume(1000) } } ``` -------------------------------- ### Create System Common Tune Request Message Source: https://context7.com/alexcharlton/midi-msg/llms.txt Demonstrates creating and serializing a `TuneRequest` system common message. ```rust use midi_msg::* let tune = MidiMsg::SystemCommon { msg: SystemCommonMsg::TuneRequest }.to_midi(); assert_eq!(tune, vec![0xF6]); ``` -------------------------------- ### Create System Common Song Position Message Source: https://context7.com/alexcharlton/midi-msg/llms.txt Shows how to create a `SongPosition` message with a 16-bit value. ```rust use midi_msg::* let song_pos = MidiMsg::SystemCommon { msg: SystemCommonMsg::SongPosition(1000), }.to_midi(); assert_eq!(song_pos, vec![0xF2, 0x68, 0x07]); ``` -------------------------------- ### Create Polyphonic Aftertouch Message Source: https://context7.com/alexcharlton/midi-msg/llms.txt Demonstrates creating a `PolyPressure` message for channel 3 and serializing it to MIDI bytes. ```rust use midi_msg::* // Polyphonic aftertouch let poly = MidiMsg::ChannelVoice { channel: Channel::Ch3, msg: ChannelVoiceMsg::PolyPressure { note: 64, pressure: 80 }, }.to_midi(); assert_eq!(poly, vec![0xA2, 64, 80]); ``` -------------------------------- ### Create All Sound Off Channel Mode Message Source: https://context7.com/alexcharlton/midi-msg/llms.txt Shows how to send an `AllSoundOff` message on a specific MIDI channel. ```rust use midi_msg::* // All sound off on channel 3 let aso = MidiMsg::ChannelMode { channel: Channel::Ch3, msg: ChannelModeMsg::AllSoundOff, }.to_midi(); assert_eq!(aso, vec![0xB2, 120, 0]); ``` -------------------------------- ### Create High-Resolution Note On Message Source: https://context7.com/alexcharlton/midi-msg/llms.txt Shows how to create a `HighResNoteOn` message with a 14-bit velocity and notes its serialization format. ```rust use midi_msg::* // High-resolution note on (CA-031): 14-bit velocity let hi_res = MidiMsg::ChannelVoice { channel: Channel::Ch3, msg: ChannelVoiceMsg::HighResNoteOn { note: 77, velocity: 1000 }, }.to_midi(); // Serializes as: NoteOn status, note, MSB velocity, then HighResVelocity CC (0xB0 | ch, 0x58, LSB) assert!(hi_res.len() > 3); ``` -------------------------------- ### Create Named 14-bit Pan Control Change Message Source: https://context7.com/alexcharlton/midi-msg/llms.txt Demonstrates creating a `ControlChange` message for `Pan` with a 14-bit value, resulting in a 5-byte MIDI message. ```rust use midi_msg::* // Named 14-bit pan let pan = MidiMsg::ChannelVoice { channel: Channel::Ch1, msg: ChannelVoiceMsg::ControlChange { control: ControlChange::Pan(8192) }, // centre }.to_midi(); assert_eq!(pan.len(), 5); // MSB + LSB pair ``` -------------------------------- ### Convert Control Change: Complex to Simple Source: https://context7.com/alexcharlton/midi-msg/llms.txt Demonstrates converting a `ControlChange::Breath` message back to its simple `ControlChange::CC` form. ```rust use midi_msg::* // Convert complex → simple let simple = ControlChange::Breath(40 << 7).to_simple(); assert_eq!(simple, ControlChange::CC { control: 2, value: 40 }); ``` -------------------------------- ### Add midi-msg to Cargo.toml Source: https://github.com/alexcharlton/midi-msg/blob/master/readme.md Include this line in your Cargo.toml to add the midi-msg crate to your project. ```toml midi-msg = "0.9" ``` -------------------------------- ### Deserialize System Real-Time Message with Context Source: https://context7.com/alexcharlton/midi-msg/llms.txt Illustrates deserializing a `TimingClock` message from MIDI bytes using a `ReceiverContext` and verifying its type. ```rust use midi_msg::* // Round-trip deserialization let mut ctx = ReceiverContext::new(); let (msg, len) = MidiMsg::from_midi_with_context(&[0xF8], &mut ctx).unwrap(); assert_eq!(len, 1); assert!(msg.is_system_real_time()); ``` -------------------------------- ### Add midi-msg to Cargo.toml (no_std) Source: https://github.com/alexcharlton/midi-msg/blob/master/readme.md Use this configuration for midi-msg in a no_std environment, disabling default features and enabling specific ones like sysex or file. ```toml midi-msg = { version = "0.9", default-features = false, features=["sysex"/"file"] } ``` -------------------------------- ### Convert Control Change: Simple to Complex Source: https://context7.com/alexcharlton/midi-msg/llms.txt Illustrates converting a simple `ControlChange::CC` message to its more complex `ControlChange::Breath` representation. ```rust use midi_msg::* // Convert simple → complex let complex = ControlChange::CC { control: 2, value: 40 }.to_complex(); assert_eq!(complex, ControlChange::Breath(40 << 7)); ``` -------------------------------- ### Create Commercial System Exclusive Messages Source: https://context7.com/alexcharlton/midi-msg/llms.txt Demonstrates creating commercial System Exclusive messages with one-byte and three-byte manufacturer IDs. Ensure the `sysex` feature is enabled. ```rust use midi_msg::* // Commercial SysEx (one-byte manufacturer ID 0x01) let commercial = MidiMsg::SystemExclusive { msg: SystemExclusiveMsg::Commercial { id: ManufacturerID(0x01, None), data: vec![0x10, 0x20, 0x00], }, }.to_midi(); assert_eq!(commercial, vec![0xF0, 0x01, 0x10, 0x20, 0x00, 0xF7]); // Three-byte manufacturer ID let three_byte = MidiMsg::SystemExclusive { msg: SystemExclusiveMsg::Commercial { id: ManufacturerID(0x01, Some(0x03)), data: vec![0x7F, 0x00], }, }.to_midi(); assert_eq!(three_byte[..4], [0xF0, 0x00, 0x01, 0x03]); ``` -------------------------------- ### MidiMsg::from_midi_with_context Source: https://context7.com/alexcharlton/midi-msg/llms.txt Parses MIDI messages with a mutable ReceiverContext that tracks running status and 14-bit CC extension pairing. It can automatically merge consecutive MSB + LSB CC pairs into a single high-resolution ControlChange variant. ```APIDOC ## MidiMsg::from_midi_with_context - Stateful deserialization with ReceiverContext Parses MIDI with a mutable `ReceiverContext` that tracks running status and 14-bit CC extension pairing. Consecutive MSB + LSB CC pairs are automatically merged into a single high-resolution `ControlChange` variant (e.g., `ControlChange::Volume(u16)`). Pass `.complex_cc()` on the context to enable semantic CC interpretation; omit it to receive raw `ControlChange::CC { control, value }` values. ### Method `MidiMsg::from_midi_with_context(data: &[u8], context: &mut ReceiverContext) -> Option<(MidiMsg, usize)>` ### Parameters #### Path Parameters - `data` (*&[u8]*) - Required - A byte slice containing the MIDI data to parse. - `context` (*&mut ReceiverContext*) - Required - A mutable reference to the `ReceiverContext` for stateful parsing. ### Response #### Success Response - `(MidiMsg, usize)` - A tuple containing the parsed `MidiMsg` and the number of bytes consumed from the input data. ### Request Example ```rust use midi_msg::*; let mut ctx = ReceiverContext::new(); // simple CC mode (default) // A full NoteOn followed by a running-status NoteOn (no status byte) let raw: Vec = vec![ 0x93, 0x66, 0x70, // status + data 0x55, 0x60, // running status: same channel/type, different note ]; let (_msg1, len1) = MidiMsg::from_midi_with_context(&raw, &mut ctx).unwrap(); let (msg2, len2) = MidiMsg::from_midi_with_context(&raw[len1..], &mut ctx).unwrap(); assert_eq!(len2, 2); // only data bytes, no status byte assert_eq!(msg2, MidiMsg::ChannelVoice { channel: Channel::Ch4, msg: ChannelVoiceMsg::NoteOn { note: 0x55, velocity: 0x60 } }); // Complex CC: enable semantic interpretation let mut ctx = ReceiverContext::new().complex_cc(); let volume_bytes = MidiMsg::ChannelVoice { channel: Channel::Ch1, msg: ChannelVoiceMsg::ControlChange { control: ControlChange::Volume(1000) }, }.to_midi(); // MSB arrives first let (msg1, l1) = MidiMsg::from_midi_with_context(&volume_bytes, &mut ctx).unwrap(); // LSB is automatically consumed and merged into the previous message // (depending on stream ordering both may be consumed in one call) println!("{:?}", msg1); // ChannelVoice { channel: Ch1, msg: ControlChange { control: Volume(1000) } } ``` ``` -------------------------------- ### MidiMsg::from_midi_with_context_no_extensions Source: https://context7.com/alexcharlton/midi-msg/llms.txt Similar to `from_midi_with_context`, but suppresses the automatic merging of consecutive MSB/LSB CC pairs and HighResVelocity prefix messages. Each raw CC byte pair is returned as a separate `MidiMsg`. ```APIDOC ## MidiMsg::from_midi_with_context_no_extensions - Stateful deserialization without CC merging Like `from_midi_with_context` but suppresses the automatic merging of consecutive MSB/LSB CC pairs and HighResVelocity prefix messages. Each raw CC byte pair is returned as a separate `MidiMsg`. ### Method `MidiMsg::from_midi_with_context_no_extensions(data: &[u8], context: &mut ReceiverContext) -> Option<(MidiMsg, usize)>` ### Parameters #### Path Parameters - `data` (*&[u8]*) - Required - A byte slice containing the MIDI data to parse. - `context` (*&mut ReceiverContext*) - Required - A mutable reference to the `ReceiverContext` for stateful parsing. ### Response #### Success Response - `(MidiMsg, usize)` - A tuple containing the parsed `MidiMsg` and the number of bytes consumed from the input data. ### Request Example ```rust use midi_msg::*; let mut ctx = ReceiverContext::new().complex_cc(); let volume_bytes = MidiMsg::ChannelVoice { channel: Channel::Ch1, msg: ChannelVoiceMsg::ControlChange { control: ControlChange::Volume(1000) }, }.to_midi(); // emits CC 7 (MSB) + CC 39 (LSB) let (msb_msg, l1) = MidiMsg::from_midi_with_context_no_extensions(&volume_bytes, &mut ctx).unwrap(); let (lsb_msg, _l2) = MidiMsg::from_midi_with_context_no_extensions(&volume_bytes[l1..], &mut ctx).unwrap(); // Each CC is returned individually matches!(msb_msg, MidiMsg::ChannelVoice { msg: ChannelVoiceMsg::ControlChange { .. }, .. }); matches!(lsb_msg, MidiMsg::ChannelVoice { msg: ChannelVoiceMsg::ControlChange { .. }, .. }); ``` ``` -------------------------------- ### Create Universal Non-Real-Time System Exclusive Messages Source: https://context7.com/alexcharlton/midi-msg/llms.txt Illustrates creating Universal Non-Real-Time System Exclusive messages, like the EOF handshake, for any device. The `sysex` feature must be enabled. ```rust use midi_msg::* // Non-Real-Time: EOF handshake let eof = MidiMsg::SystemExclusive { msg: SystemExclusiveMsg::UniversalNonRealTime { device: DeviceID::AllCall, msg: UniversalNonRealTimeMsg::EOF, }, }.to_midi(); assert_eq!(eof, vec![0xF0, 0x7E, 0x7F, 0x7B, 0x00, 0xF7]); ``` -------------------------------- ### MidiFile Source: https://context7.com/alexcharlton/midi-msg/llms.txt Represents and manipulates Standard MIDI Files (SMF). Supports serialization to and deserialization from byte arrays, as well as programmatic construction and modification of MIDI files. ```APIDOC ## `MidiFile` — Standard MIDI File support (feature: `file`) `MidiFile` represents an SMF with a `Header` and a `Vec`. It can be serialized with `to_midi()` and deserialized with `MidiFile::from_midi(&bytes)`. Convenience method `extend_track(track_idx, msg, beat)` accepts absolute beat timestamps (as `f64`) and automatically computes delta times. The `add_track` / `remove_track` / `extend_track` API makes it easy to build files programmatically. ```rust #[cfg(feature = "file")] fn build_midi_file() { use midi_msg::*; let mut file = MidiFile::default(); file.add_track(Track::default()); // NoteOn at beat 0 file.extend_track(0, MidiMsg::ChannelVoice { channel: Channel::Ch1, msg: ChannelVoiceMsg::NoteOn { note: 60, velocity: 100 }, }, 0.0); // NoteOff at beat 1 file.extend_track(0, MidiMsg::ChannelVoice { channel: Channel::Ch1, msg: ChannelVoiceMsg::NoteOff { note: 60, velocity: 0 }, }, 1.0); // Required end-of-track meta event at beat 4 file.extend_track(0, MidiMsg::Meta { msg: Meta::EndOfTrack }, 4.0); // Serialize to bytes let bytes: Vec = file.to_midi(); // Round-trip: parse bytes back into a MidiFile let file2 = MidiFile::from_midi(&bytes).expect("valid SMF"); assert_eq!(file, file2); // Parse from an .mid file on disk (std environment) let raw = std::fs::read("tests/echoes.mid").unwrap(); let parsed = MidiFile::from_midi(&raw).expect("valid .mid file"); println!("Tracks: {}", parsed.tracks.len()); } ``` ``` -------------------------------- ### ControlChange Source: https://context7.com/alexcharlton/midi-msg/llms.txt Handles standard CC numbers as named variants (e.g., Volume, Pan), generic CC, high-resolution CC, and Parameter for RPN/NRPN. ```APIDOC ## ControlChange Covers all standard CC numbers as named semantic variants (e.g., `Volume(u16)`, `Pan(u16)`, `Hold(u8)`), plus generic `CC { control, value }` and `CCHighRes { control1, control2, value }` variants, and `Parameter(Parameter)` for RPN/NRPN. The `to_complex()` / `to_simple()` / `to_simple_high_res()` methods convert between representations. ### Example: Named 14-bit pan ```rust use midi_msg::* let pan = MidiMsg::ChannelVoice { channel: Channel::Ch1, msg: ChannelVoiceMsg::ControlChange { control: ControlChange::Pan(8192) }, // centre }.to_midi(); assert_eq!(pan.len(), 5); // MSB + LSB pair ``` ### Example: Generic CC (simple mode) ```rust use midi_msg::* let cc = ControlChange::CC { control: 85, value: 77 }; assert_eq!(cc.control(), 85); assert_eq!(cc.value(), 77); ``` ### Example: Convert simple → complex ```rust use midi_msg::* let complex = ControlChange::CC { control: 2, value: 40 }.to_complex(); assert_eq!(complex, ControlChange::Breath(40 << 7)); ``` ### Example: Convert complex → simple ```rust use midi_msg::* let simple = ControlChange::Breath(40 << 7).to_simple(); assert_eq!(simple, ControlChange::CC { control: 2, value: 40 }); ``` ### Example: RPN: fine tuning parameter ```rust use midi_msg::* let rpn = MidiMsg::ChannelVoice { channel: Channel::Ch2, msg: ChannelVoiceMsg::ControlChange { control: ControlChange::Parameter(Parameter::FineTuningEntry(-30)), }, }.to_midi(); assert!(!rpn.is_empty()); ``` ``` -------------------------------- ### Batch Serialize Multiple MIDI Messages Source: https://context7.com/alexcharlton/midi-msg/llms.txt Use `messages_to_midi` to serialize a slice of `MidiMsg` into a single `Vec`, which is more efficient than concatenating individual `to_midi()` calls. ```rust use midi_msg::* let msgs = vec![ MidiMsg::ChannelVoice { channel: Channel::Ch1, msg: ChannelVoiceMsg::NoteOn { note: 60, velocity: 100 }, }, MidiMsg::ChannelVoice { channel: Channel::Ch1, msg: ChannelVoiceMsg::NoteOff { note: 60, velocity: 0 }, }, MidiMsg::SystemRealTime { msg: SystemRealTimeMsg::TimingClock }, ]; let bytes = MidiMsg::messages_to_midi(&msgs); // bytes contains all three messages packed back-to-back assert_eq!(bytes, vec![0x90, 0x3C, 0x64, 0x80, 0x3C, 0x00, 0xF8]); ``` -------------------------------- ### Convert Frequency to MIDI Note Number and Cents Source: https://context7.com/alexcharlton/midi-msg/llms.txt Converts a frequency in Hz to a MIDI note number and the cents offset from that semitone. Useful for analyzing pitch. Requires `freq_to_midi_note_cents`. ```rust use midi_msg:: freq_to_midi_note_float, midi_note_float_to_freq, midi_note_cents_to_freq, freq_to_midi_note_cents, ; // Hz → (note_number, cents_above_semitone) let (note_num, cents) = freq_to_midi_note_cents(261.6256); // C4 assert_eq!(note_num, 60); // middle C assert!(cents.abs() < 1.0); // very close to the semitone ``` -------------------------------- ### Convert MIDI Note with Cents to Frequency Source: https://context7.com/alexcharlton/midi-msg/llms.txt Calculates the frequency in Hz for a given MIDI note number and an additional cents offset. Requires `midi_note_cents_to_freq`. ```rust use midi_msg:: freq_to_midi_note_float, midi_note_float_to_freq, midi_note_cents_to_freq, freq_to_midi_note_cents, ; // MIDI note number + cents offset → Hz let freq_g4 = midi_note_cents_to_freq(67, 0.0); // G4 ≈ 392 Hz assert!((freq_g4 - 392.0).abs() < 0.1); ``` -------------------------------- ### Stateless Deserialization of MIDI Messages Source: https://context7.com/alexcharlton/midi-msg/llms.txt Parses the first MIDI message from a byte slice, returning the `MidiMsg` and the number of bytes consumed. This method does not support running-status messages; use `from_midi_with_context` for stateful deserialization. ```rust use midi_msg::* let raw: Vec = vec![ 0x93, 0x66, 0x70, // NoteOn ch4, note 0x66, vel 0x70 0x93, 0x55, 0x60, // second message (ignored here) ]; let (msg, len) = MidiMsg::from_midi(&raw).expect("valid MIDI"); assert_eq!(len, 3); assert_eq!(msg, MidiMsg::ChannelVoice { channel: Channel::Ch4, msg: ChannelVoiceMsg::NoteOn { note: 0x66, velocity: 0x70 }, }); ``` ```rust // Error handling example match MidiMsg::from_midi(&[]) { Err(ParseError::UnexpectedEnd) => println!("empty slice"), _ => {} } ``` -------------------------------- ### Convert Frequency to MIDI Note (Float) Source: https://context7.com/alexcharlton/midi-msg/llms.txt Converts a frequency in Hz to a floating-point MIDI note number. Useful for precise pitch representation. Requires `freq_to_midi_note_float`. ```rust use midi_msg:: freq_to_midi_note_float, midi_note_float_to_freq, midi_note_cents_to_freq, freq_to_midi_note_cents, ; // Frequency → floating-point MIDI note (1.0 = 100 cents) let note = freq_to_midi_note_float(440.0); assert!((note - 69.0).abs() < 0.01); // A4 = MIDI note 69 ``` -------------------------------- ### SystemExclusiveMsg Source: https://context7.com/alexcharlton/midi-msg/llms.txt Handles various types of System Exclusive (SysEx) MIDI messages, including commercial and universal real-time/non-real-time variants. Manufacturer IDs can be one or three bytes. ```APIDOC ## `SystemExclusiveMsg` — System exclusive messages (feature: `sysex`) Four variants: `Commercial { id: ManufacturerID, data }`, `NonCommercial { data }`, `UniversalRealTime { device, msg }`, and `UniversalNonRealTime { device, msg }`. Manufacturer IDs can be one or three bytes. `UniversalRealTime` covers master volume/balance/tuning, MIDI Machine Control, show control, time code, and more. ```rust use midi_msg::* // Commercial SysEx (one-byte manufacturer ID 0x01) let commercial = MidiMsg::SystemExclusive { msg: SystemExclusiveMsg::Commercial { id: ManufacturerID(0x01, None), data: vec![0x10, 0x20, 0x00], }, }.to_midi(); assert_eq!(commercial, vec![0xF0, 0x01, 0x10, 0x20, 0x00, 0xF7]); // Three-byte manufacturer ID let three_byte = MidiMsg::SystemExclusive { msg: SystemExclusiveMsg::Commercial { id: ManufacturerID(0x01, Some(0x03)), data: vec![0x7F, 0x00], }, }.to_midi(); assert_eq!(three_byte[..4], [0xF0, 0x00, 0x01, 0x03]); // Universal Real-Time: master volume (0–16383) to device 3 let vol = MidiMsg::SystemExclusive { msg: SystemExclusiveMsg::UniversalRealTime { device: DeviceID::Device(3), msg: UniversalRealTimeMsg::MasterVolume(1000), }, }.to_midi(); assert_eq!(vol, vec![0xF0, 0x7F, 0x03, 0x04, 0x01, 0x68, 0x07, 0xF7]); // Non-Real-Time: EOF handshake let eof = MidiMsg::SystemExclusive { msg: SystemExclusiveMsg::UniversalNonRealTime { device: DeviceID::AllCall, msg: UniversalNonRealTimeMsg::EOF, }, }.to_midi(); assert_eq!(eof, vec![0xF0, 0x7E, 0x7F, 0x7B, 0x00, 0xF7]); ``` ``` -------------------------------- ### SystemCommonMsg Source: https://context7.com/alexcharlton/midi-msg/llms.txt System common messages including TimeCodeQuarterFrame, SongPosition, SongSelect, and TuneRequest. ```APIDOC ## SystemCommonMsg Covers `TimeCodeQuarterFrame1`–`8` (MTC), `SongPosition(u16)`, `SongSelect(u8)`, and `TuneRequest`. ### Example: Song Position ```rust use midi_msg::* let song_pos = MidiMsg::SystemCommon { msg: SystemCommonMsg::SongPosition(1000), }.to_midi(); assert_eq!(song_pos, vec![0xF2, 0x68, 0x07]); ``` ### Example: Song Select ```rust use midi_msg::* let song_sel = MidiMsg::SystemCommon { msg: SystemCommonMsg::SongSelect(5), }.to_midi(); assert_eq!(song_sel, vec![0xF3, 5]); ``` ### Example: Tune Request ```rust use midi_msg::* let tune = MidiMsg::SystemCommon { msg: SystemCommonMsg::TuneRequest }.to_midi(); assert_eq!(tune, vec![0xF6]); ``` ``` -------------------------------- ### Serialize MidiMsg to Bytes Source: https://context7.com/alexcharlton/midi-msg/llms.txt Converts any `MidiMsg` into a `Vec` representing a valid MIDI byte sequence. Numeric values that exceed their permitted range are clamped to the maximum or minimum allowed value. ```rust use midi_msg::* // Note On, channel 1, middle-C (60), velocity 127 let bytes = MidiMsg::ChannelVoice { channel: Channel::Ch1, msg: ChannelVoiceMsg::NoteOn { note: 60, velocity: 127 }, }.to_midi(); assert_eq!(bytes, vec![0x90, 0x3C, 0x7F]); ``` ```rust // Note Off let bytes = MidiMsg::ChannelVoice { channel: Channel::Ch1, msg: ChannelVoiceMsg::NoteOff { note: 60, velocity: 0 }, }.to_midi(); assert_eq!(bytes, vec![0x80, 0x3C, 0x00]); ``` ```rust // Pitch bend (centre = 8192) let bytes = MidiMsg::ChannelVoice { channel: Channel::Ch10, msg: ChannelVoiceMsg::PitchBend { bend: 1000 }, }.to_midi(); assert_eq!(bytes, vec![0xE9, 0x68, 0x07]); ``` ```rust // High-resolution 14-bit volume CC on channel 2 let bytes = MidiMsg::ChannelVoice { channel: Channel::Ch2, msg: ChannelVoiceMsg::ControlChange { control: ControlChange::Volume(1000), }, }.to_midi(); // Emits MSB (CC 7) followed by LSB (CC 39) assert_eq!(bytes, vec![0xB1, 7, 0x07, 39, 0x68]); ``` -------------------------------- ### Convert MIDI Note (Float) to Frequency Source: https://context7.com/alexcharlton/midi-msg/llms.txt Converts a floating-point MIDI note number back to its corresponding frequency in Hz. Requires `midi_note_float_to_freq`. ```rust use midi_msg:: freq_to_midi_note_float, midi_note_float_to_freq, midi_note_cents_to_freq, freq_to_midi_note_cents, ; // Floating-point MIDI note → Hz let freq = midi_note_float_to_freq(69.0); assert!((freq - 440.0).abs() < 0.1); ``` -------------------------------- ### Stateful MIDI Deserialization Without CC Merging Source: https://context7.com/alexcharlton/midi-msg/llms.txt Use `from_midi_with_context_no_extensions` to disable automatic merging of consecutive MSB/LSB CC pairs and HighResVelocity messages. Each raw CC byte pair is returned as a separate `MidiMsg`. ```rust use midi_msg::* let mut ctx = ReceiverContext::new().complex_cc(); let volume_bytes = MidiMsg::ChannelVoice { channel: Channel::Ch1, msg: ChannelVoiceMsg::ControlChange { control: ControlChange::Volume(1000) }, }.to_midi(); // emits CC 7 (MSB) + CC 39 (LSB) let (msb_msg, l1) = MidiMsg::from_midi_with_context_no_extensions(&volume_bytes, &mut ctx).unwrap(); let (lsb_msg, _l2) = MidiMsg::from_midi_with_context_no_extensions(&volume_bytes[l1..], &mut ctx).unwrap(); // Each CC is returned individually matches!(msb_msg, MidiMsg::ChannelVoice { msg: ChannelVoiceMsg::ControlChange { .. }, .. }); matches!(lsb_msg, MidiMsg::ChannelVoice { msg: ChannelVoiceMsg::ControlChange { .. }, .. }); ``` -------------------------------- ### Append MIDI Message to Buffer Source: https://context7.com/alexcharlton/midi-msg/llms.txt Use `extend_midi` to append the serialized form of a `MidiMsg` to a provided `Vec`, avoiding extra allocations per message. ```rust use midi_msg::* let mut buf: Vec = Vec::new(); MidiMsg::ChannelVoice { channel: Channel::Ch1, msg: ChannelVoiceMsg::ProgramChange { program: 40 }, // Violin in GM1 }.extend_midi(&mut buf); MidiMsg::SystemRealTime { msg: SystemRealTimeMsg::Start }.extend_midi(&mut buf); assert_eq!(buf, vec![0xC0, 40, 0xFA]); ``` -------------------------------- ### Generic Control Change Message Properties Source: https://context7.com/alexcharlton/midi-msg/llms.txt Shows how to access the `control` and `value` fields of a generic `ControlChange::CC` variant. ```rust use midi_msg::* // Generic CC (simple mode) let cc = ControlChange::CC { control: 85, value: 77 }; assert_eq!(cc.control(), 85); assert_eq!(cc.value(), 77); ``` -------------------------------- ### MidiMsg::to_midi Source: https://context7.com/alexcharlton/midi-msg/llms.txt Converts any MidiMsg into a Vec containing a valid MIDI byte sequence. Overflowing numeric values are silently clamped to their permitted range. ```APIDOC ## MidiMsg::to_midi — Serialize a message to bytes ### Description Converts any `MidiMsg` into a `Vec` containing a valid MIDI byte sequence. Overflowing numeric values are silently clamped to their permitted range (e.g., a note value of 200 is clamped to 127). ### Method Rust function call ### Endpoint N/A (Rust library function) ### Parameters N/A (Method operates on the `MidiMsg` instance) ### Request Example ```rust use midi_msg::* // Note On, channel 1, middle-C (60), velocity 127 let bytes = MidiMsg::ChannelVoice { channel: Channel::Ch1, msg: ChannelVoiceMsg::NoteOn { note: 60, velocity: 127 }, }.to_midi(); assert_eq!(bytes, vec![0x90, 0x3C, 0x7F]); // Note Off let bytes = MidiMsg::ChannelVoice { channel: Channel::Ch1, msg: ChannelVoiceMsg::NoteOff { note: 60, velocity: 0 }, }.to_midi(); assert_eq!(bytes, vec![0x80, 0x3C, 0x00]); // Pitch bend (centre = 8192) let bytes = MidiMsg::ChannelVoice { channel: Channel::Ch10, msg: ChannelVoiceMsg::PitchBend { bend: 1000 }, }.to_midi(); assert_eq!(bytes, vec![0xE9, 0x68, 0x07]); // High-resolution 14-bit volume CC on channel 2 let bytes = MidiMsg::ChannelVoice { channel: Channel::Ch2, msg: ChannelVoiceMsg::ControlChange { control: ControlChange::Volume(1000), }, }.to_midi(); // Emits MSB (CC 7) followed by LSB (CC 39) assert_eq!(bytes, vec![0xB1, 7, 0x07, 39, 0x68]); ``` ### Response #### Success Response - `Vec`: A vector of bytes representing the MIDI message. ``` -------------------------------- ### MidiMsg::from_midi Source: https://context7.com/alexcharlton/midi-msg/llms.txt Stateless deserialization of the first MIDI message from a byte slice. Returns the parsed MidiMsg and the number of bytes consumed. ```APIDOC ## MidiMsg::from_midi — Stateless deserialization ### Description Parses the first MIDI message from a byte slice and returns `(MidiMsg, bytes_consumed)`. Does not support running-status messages (a second message without a status byte will error); use `from_midi_with_context` for those. ### Method Rust function call ### Endpoint N/A (Rust library function) ### Parameters - `bytes` (*slice of u8*): The byte slice to parse. ### Request Example ```rust use midi_msg::*; let raw: Vec = vec![ 0x93, 0x66, 0x70, // NoteOn ch4, note 0x66, vel 0x70 0x93, 0x55, 0x60, // second message (ignored here) ]; let (msg, len) = MidiMsg::from_midi(&raw).expect("valid MIDI"); assert_eq!(len, 3); assert_eq!(msg, MidiMsg::ChannelVoice { channel: Channel::Ch4, msg: ChannelVoiceMsg::NoteOn { note: 0x66, velocity: 0x70 }, }); // Error handling example match MidiMsg::from_midi(&[]) { Err(ParseError::UnexpectedEnd) => println!("empty slice"), _ => {} } ``` ### Response #### Success Response - `(MidiMsg, usize)`: A tuple containing the parsed `MidiMsg` and the number of bytes consumed from the input slice. #### Error Response - `ParseError`: If the byte slice is empty or does not represent a valid MIDI message. ``` -------------------------------- ### next_message Source: https://context7.com/alexcharlton/midi-msg/llms.txt Scans a raw byte slice to find the byte offset of the next MIDI status byte, skipping running-status data bytes. This function is useful for error recovery during message deserialization. ```APIDOC ## `next_message` — Find the byte offset of the next MIDI message Scans a raw byte slice and returns the index of the first byte that starts the *next* MIDI status byte after the current position, skipping running-status data bytes. Useful for error recovery when a message cannot be deserialized. ```rust use midi_msg::next_message; let raw = vec![ 0x90, 0x3C, 0x7F, // NoteOn 0x80, 0x3C, 0x00, // NoteOff ]; // Index of the second message's status byte assert_eq!(next_message(&raw), Some(3)); // Called mid-message (offset by 1 byte into the first message) assert_eq!(next_message(&raw[1..]), Some(2)); // 3 - 1 = 2 // Empty or single-message slice returns None assert_eq!(next_message(&raw[3..]), None); ``` ```