MIDI 2.0 and UMP entry point
examples/tutorials/src/bin/midi2.rs · 97 source lines
Read the chapters in source order. Select a numbered source line to expand its explanation; the line numbers and code match this snapshot exactly.
cargo run -p ravet-examples --bin midi2 --locked -- --offlineChapters
Import framing and processing types
Midi2Message/Midi2Kind preserve full-resolution channel-voice fields; UmpDecoder handles complete packet framing. Foundation audio and block types validate sample-timed event storage, independently of synthesis or device delivery.
1//! Preserve full-resolution MIDI 2.0 fields in framed, sample-timed UMP events.
Source note: Preserve full-resolution MIDI 2.0 fields in framed, sample-timed UMP events.
Link to line 12use ravet_foundation::{
Bring these module/type names into scope for “Import framing and processing types”; the surrounding chapter explains their responsibilities.
Link to line 23 AudioBuffer, AudioLayout, Event, EventData, Midi2Kind, Midi2Message, ProcessBlock,
Supply these names as the remaining imports, arguments or fields of the surrounding declaration.
Link to line 34 ProcessConfig, Transport, UmpDecoder,
Supply these names as the remaining imports, arguments or fields of the surrounding declaration.
Link to line 45};
Close the surrounding scope, value or argument list in “Import framing and processing types”.
Link to line 56
Blank line separating the surrounding statements.
Link to line 6Select the UI inspector or offline trace
No arguments and --ui-test select the UI inspector. Only --offline selects this codec trace; other combinations return a usage error. Neither this trace nor the inspector is a MIDI 2.0 synthesizer.
7fn main() -> Result<(), Box<dyn std::error::Error>> {
Define main. No arguments and --ui-test select the UI inspector. Only --offline selects this codec trace; other combinations return a usage error. Neither this trace nor the inspector is a MIDI 2.0 synthesizer.
Link to line 78 if std::env::args_os().len() == 1
Evaluate this condition or visit this range in source order. The statements in the following scope implement “Select the UI inspector or offline trace”.
Link to line 89 || std::env::args_os().nth(1).is_some_and(|a| a == "--ui-test")
Accept this alternative condition in the surrounding check; evaluation stops once the combined condition is true.
Link to line 910 {
Close the surrounding scope, value or argument list in “Select the UI inspector or offline trace”.
Link to line 1011 return ravet_examples::launch("midi2");
Return this result to the caller; the remaining statements in the function are skipped.
Link to line 1112 }
Close the surrounding scope, value or argument list in “Select the UI inspector or offline trace”.
Link to line 1213 if std::env::args_os().len() != 2 || std::env::args_os().nth(1).is_none_or(|a| a != "--offline")
Evaluate this condition or visit this range in source order. The statements in the following scope implement “Select the UI inspector or offline trace”.
Link to line 1314 {
Close the surrounding scope, value or argument list in “Select the UI inspector or offline trace”.
Link to line 1415 return Err("usage: midi2 [--offline | --ui-test directory]".into());
Exit this path with an explicit error/fault; the caller must not treat this branch as successfully applied state.
Link to line 1516 }
Close the surrounding scope, value or argument list in “Select the UI inspector or offline trace”.
Link to line 16Define full-resolution messages
Use explicit zero-based group 1, channel 2 and key 60. Construct note-on/off with 16-bit velocities and per-note bend/poly pressure with 32-bit expression. Two events share frame 17 so the fixture can check stable ordering.
17 // Group 1, channel 2, key 60: explicit zero-based addressing.
Source note: Group 1, channel 2, key 60: explicit zero-based addressing.
Link to line 1718 let messages = [
Bind messages to the expression on this and any following continuation lines. The chapter above explains how this intermediate value is used.
Link to line 1819 (
Continue the surrounding value or expression with this exact operand. Read it together with the preceding statement and the “Define full-resolution messages” explanation above.
Link to line 1920 0,
Supply these names as the remaining imports, arguments or fields of the surrounding declaration.
Link to line 2021 Midi2Kind::NoteOn {
Define or initialize Midi2Kind with the displayed type/value. Its ownership and use are described in “Define full-resolution messages”.
Link to line 2122 key: 60,
Define or initialize key with the displayed type/value. Its ownership and use are described in “Define full-resolution messages”.
Link to line 2223 velocity: 0xabcd,
Use a 16-bit note-on velocity value that cannot be represented losslessly as a 7-bit MIDI 1.0 velocity.
Link to line 2324 attribute_type: 0,
Define or initialize attribute_type with the displayed type/value. Its ownership and use are described in “Define full-resolution messages”.
Link to line 2425 attribute: 0,
Define or initialize attribute with the displayed type/value. Its ownership and use are described in “Define full-resolution messages”.
Link to line 2526 },
Close the surrounding scope, value or argument list in “Define full-resolution messages”.
Link to line 2627 ),
Close the surrounding scope, value or argument list in “Define full-resolution messages”.
Link to line 2728 (
Continue the surrounding value or expression with this exact operand. Read it together with the preceding statement and the “Define full-resolution messages” explanation above.
Link to line 2829 17,
Supply these names as the remaining imports, arguments or fields of the surrounding declaration.
Link to line 2930 Midi2Kind::PerNotePitchBend {
Define or initialize Midi2Kind with the displayed type/value. Its ownership and use are described in “Define full-resolution messages”.
Link to line 3031 key: 60,
Define or initialize key with the displayed type/value. Its ownership and use are described in “Define full-resolution messages”.
Link to line 3132 value: 0x9123_4567,
Use an exact 32-bit per-note pitch-bend value to verify that the UMP round trip preserves expression precision.
Link to line 3233 },
Close the surrounding scope, value or argument list in “Define full-resolution messages”.
Link to line 3334 ),
Close the surrounding scope, value or argument list in “Define full-resolution messages”.
Link to line 3435 (
Continue the surrounding value or expression with this exact operand. Read it together with the preceding statement and the “Define full-resolution messages” explanation above.
Link to line 3536 17,
Supply these names as the remaining imports, arguments or fields of the surrounding declaration.
Link to line 3637 Midi2Kind::PolyPressure {
Define or initialize Midi2Kind with the displayed type/value. Its ownership and use are described in “Define full-resolution messages”.
Link to line 3738 key: 60,
Define or initialize key with the displayed type/value. Its ownership and use are described in “Define full-resolution messages”.
Link to line 3839 value: 0xdead_beef,
Use an exact 32-bit poly-pressure test value, retaining same-frame ordering with the preceding bend message.
Link to line 3940 },
Close the surrounding scope, value or argument list in “Define full-resolution messages”.
Link to line 4041 ),
Close the surrounding scope, value or argument list in “Define full-resolution messages”.
Link to line 4142 (
Continue the surrounding value or expression with this exact operand. Read it together with the preceding statement and the “Define full-resolution messages” explanation above.
Link to line 4243 48,
Supply these names as the remaining imports, arguments or fields of the surrounding declaration.
Link to line 4344 Midi2Kind::NoteOff {
Define or initialize Midi2Kind with the displayed type/value. Its ownership and use are described in “Define full-resolution messages”.
Link to line 4445 key: 60,
Define or initialize key with the displayed type/value. Its ownership and use are described in “Define full-resolution messages”.
Link to line 4546 velocity: 0x1234,
Define or initialize velocity with the displayed type/value. Its ownership and use are described in “Define full-resolution messages”.
Link to line 4647 attribute_type: 0,
Define or initialize attribute_type with the displayed type/value. Its ownership and use are described in “Define full-resolution messages”.
Link to line 4748 attribute: 0,
Define or initialize attribute with the displayed type/value. Its ownership and use are described in “Define full-resolution messages”.
Link to line 4849 },
Close the surrounding scope, value or argument list in “Define full-resolution messages”.
Link to line 4950 ),
Close the surrounding scope, value or argument list in “Define full-resolution messages”.
Link to line 5051 ];
Close the surrounding scope, value or argument list in “Define full-resolution messages”.
Link to line 51Frame and verify packets
Encode each typed message, push its words individually into UmpDecoder, and emit only a complete packet. finish rejects an incomplete trailing packet. Decode the framed packet and assert exact typed equality before retaining it as a timed EventData::Ump.
52 let mut events = Vec::new();
Bind mutable events to the expression on this and any following continuation lines. The chapter above explains how this intermediate value is used.
Link to line 5253 let mut decoder = UmpDecoder::new();
Bind mutable decoder to the expression on this and any following continuation lines. The chapter above explains how this intermediate value is used.
Link to line 5354 for (frame_offset, kind) in messages {
Evaluate this condition or visit this range in source order. The statements in the following scope implement “Frame and verify packets”.
Link to line 5455 let original = Midi2Message::new(1, 2, kind).expect("fixed valid MIDI 2.0 fields");
Bind original to the expression on this and any following continuation lines. The chapter above explains how this intermediate value is used.
Link to line 5556 let packet = original.packet();
Bind packet to the expression on this and any following continuation lines. The chapter above explains how this intermediate value is used.
Link to line 5657 // A word stream may arrive in fragments; emit only a complete packet.
Source note: A word stream may arrive in fragments; emit only a complete packet.
Link to line 5758 let mut framed = None;
Bind mutable framed to the expression on this and any following continuation lines. The chapter above explains how this intermediate value is used.
Link to line 5859 for word in packet.as_words() {
Evaluate this condition or visit this range in source order. The statements in the following scope implement “Frame and verify packets”.
Link to line 5960 framed = decoder.push(*word);
Feed one UMP word at a time. The decoder returns a packet only when the required number of words has arrived.
Link to line 6061 }
Close the surrounding scope, value or argument list in “Frame and verify packets”.
Link to line 6162 decoder.finish().expect("no partial packet remains");
Check that no partial UMP packet remains after feeding the message’s words.
Link to line 6263 let framed = framed.expect("complete two-word MIDI 2.0 packet");
Bind framed to the expression on this and any following continuation lines. The chapter above explains how this intermediate value is used.
Link to line 6364 assert_eq!(
Verify the fixture’s expected result. A mismatch fails the test rather than becoming a successful support claim.
Link to line 6465 Midi2Message::from_packet(framed).expect("encoded MIDI 2.0 packet"),
Require the invariant “encoded MIDI 2.0 packet”. expect panics if it is broken; external inputs must be validated before relying on it.
Link to line 6566 original
Continue the surrounding value or expression with this exact operand. Read it together with the preceding statement and the “Frame and verify packets” explanation above.
Link to line 6667 );
Close the current expression or scope; no additional processing operation occurs on this line.
Link to line 6768 events.push(Event {
Append the supplied item to this owned collection. UI/fixture collections grow outside the audio callback.
Link to line 6869 frame_offset,
Supply these names as the remaining imports, arguments or fields of the surrounding declaration.
Link to line 6970 data: EventData::Ump(framed),
Typed event payload; timing is kept separately in frame_offset.
Link to line 7071 });
Close the current expression or scope; no additional processing operation occurs on this line.
Link to line 7172 }
Close the surrounding scope, value or argument list in “Frame and verify packets”.
Link to line 72Validate a 64-frame instrument block
Prepare a stereo instrument configuration with no input, 64 maximum frames and capacity for four events. Borrow two zero-filled output arrays with AudioBuffer::separate. validate_block checks timing and storage but does not synthesize anything.
73 // Foundation validates timing/storage, without imposing MIDI 2.0 synthesis policy.
Source note: Foundation validates timing/storage, without imposing MIDI 2.0 synthesis policy.
Link to line 7374 let config = ProcessConfig::new(48_000., 64, 4, AudioLayout::STEREO_INSTRUMENT)?;
Bind config to the expression on this and any following continuation lines. The chapter above explains how this intermediate value is used.
Link to line 7475 let mut left = [0.; 64];
Bind mutable left to the expression on this and any following continuation lines. The chapter above explains how this intermediate value is used.
Link to line 7576 let mut right = [0.; 64];
Bind mutable right to the expression on this and any following continuation lines. The chapter above explains how this intermediate value is used.
Link to line 7677 let mut channels = [&mut left[..], &mut right[..]];
Bind mutable channels to the expression on this and any following continuation lines. The chapter above explains how this intermediate value is used.
Link to line 7778 let audio = AudioBuffer::separate(&[], &mut channels, 64)?;
Borrow distinct input/output slices. An instrument supplies an empty input slice; output storage already belongs to the caller.
Link to line 7879 let block = ProcessBlock::new(audio, &events, Transport::default());
Combine borrowed audio, the active input-event slice and explicit transport into one processing view.
Link to line 7980 config.validate_block(&block)?;
Check the complete block against prepared limits before the callback mutates DSP history.
Link to line 80Print the lossless result
Read the validated events in order and decode each UMP again for display. Print offset, addressing, exact hexadecimal words and typed payload. The final messages state the retained precision and the lack of a synth/device/export integration.
81 for event in block.events {
Visit the supplied event slice in order. No temporary event collection is created.
Link to line 8182 if let EventData::Ump(packet) = event.data {
Evaluate this condition or visit this range in source order. The statements in the following scope implement “Print the lossless result”.
Link to line 8283 let message = Midi2Message::from_packet(packet).expect("encoded MIDI 2.0 packet");
Bind message to the expression on this and any following continuation lines. The chapter above explains how this intermediate value is used.
Link to line 8384 println!(
Report this explicit fixture result to stdout; printing belongs to the offline/control path, not the audio callback.
Link to line 8485 "frame={} group={} channel={} words={:08x?} {:?}",
The ? operator returns an error immediately on failure and unwraps the success value for the next step.
Link to line 8586 event.frame_offset,
Continue the surrounding value or expression with this exact operand. Read it together with the preceding statement and the “Print the lossless result” explanation above.
Link to line 8687 message.group(),
Continue the surrounding value or expression with this exact operand. Read it together with the preceding statement and the “Print the lossless result” explanation above.
Link to line 8788 message.channel(),
Continue the surrounding value or expression with this exact operand. Read it together with the preceding statement and the “Print the lossless result” explanation above.
Link to line 8889 packet.as_words(),
Continue the surrounding value or expression with this exact operand. Read it together with the preceding statement and the “Print the lossless result” explanation above.
Link to line 8990 message.kind()
Call the displayed operation with these arguments as the next step in “Print the lossless result”. The chapter describes its role and validation.
Link to line 9091 );
Close the current expression or scope; no additional processing operation occurs on this line.
Link to line 9192 }
Close the surrounding scope, value or argument list in “Print the lossless result”.
Link to line 9293 }
Close the surrounding scope, value or argument list in “Print the lossless result”.
Link to line 9394 println!("16-bit velocity and 32-bit expression preserved; same-frame event order retained.");
Report this explicit fixture result to stdout; printing belongs to the offline/control path, not the audio callback.
Link to line 9495 println!("Codec/event walkthrough only: no MIDI 2.0 synth, device or plug-in export.");
Report this explicit fixture result to stdout; printing belongs to the offline/control path, not the audio callback.
Link to line 9596 Ok(())
Report successful completion without returning a payload.
Link to line 9697}
Close the surrounding scope, value or argument list in “Print the lossless result”.
Link to line 97