AUDIO
Source snapshot · 12 October 2026

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 -- --offline
Chapters
  1. Import framing and processing types lines 1–6
  2. Select the UI inspector or offline trace lines 7–16
  3. Define full-resolution messages lines 17–51
  4. Frame and verify packets lines 52–72
  5. Validate a 64-frame instrument block lines 73–80
  6. Print the lossless result lines 81–97

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 1
2use ravet_foundation::{

Bring these module/type names into scope for “Import framing and processing types”; the surrounding chapter explains their responsibilities.

Link to line 2
3 AudioBuffer, AudioLayout, Event, EventData, Midi2Kind, Midi2Message, ProcessBlock,

Supply these names as the remaining imports, arguments or fields of the surrounding declaration.

Link to line 3
4 ProcessConfig, Transport, UmpDecoder,

Supply these names as the remaining imports, arguments or fields of the surrounding declaration.

Link to line 4
5};

Close the surrounding scope, value or argument list in “Import framing and processing types”.

Link to line 5
6

Blank line separating the surrounding statements.

Link to line 6

Select 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 7
8 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 8
9 || 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 9
10 {

Close the surrounding scope, value or argument list in “Select the UI inspector or offline trace”.

Link to line 10
11 return ravet_examples::launch("midi2");

Return this result to the caller; the remaining statements in the function are skipped.

Link to line 11
12 }

Close the surrounding scope, value or argument list in “Select the UI inspector or offline trace”.

Link to line 12
13 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 13
14 {

Close the surrounding scope, value or argument list in “Select the UI inspector or offline trace”.

Link to line 14
15 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 15
16 }

Close the surrounding scope, value or argument list in “Select the UI inspector or offline trace”.

Link to line 16

Define 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 17
18 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 18
19 (

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 19
20 0,

Supply these names as the remaining imports, arguments or fields of the surrounding declaration.

Link to line 20
21 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 21
22 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 22
23 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 23
24 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 24
25 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 25
26 },

Close the surrounding scope, value or argument list in “Define full-resolution messages”.

Link to line 26
27 ),

Close the surrounding scope, value or argument list in “Define full-resolution messages”.

Link to line 27
28 (

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 28
29 17,

Supply these names as the remaining imports, arguments or fields of the surrounding declaration.

Link to line 29
30 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 30
31 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 31
32 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 32
33 },

Close the surrounding scope, value or argument list in “Define full-resolution messages”.

Link to line 33
34 ),

Close the surrounding scope, value or argument list in “Define full-resolution messages”.

Link to line 34
35 (

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 35
36 17,

Supply these names as the remaining imports, arguments or fields of the surrounding declaration.

Link to line 36
37 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 37
38 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 38
39 value: 0xdead_beef,

Use an exact 32-bit poly-pressure test value, retaining same-frame ordering with the preceding bend message.

Link to line 39
40 },

Close the surrounding scope, value or argument list in “Define full-resolution messages”.

Link to line 40
41 ),

Close the surrounding scope, value or argument list in “Define full-resolution messages”.

Link to line 41
42 (

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 42
43 48,

Supply these names as the remaining imports, arguments or fields of the surrounding declaration.

Link to line 43
44 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 44
45 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 45
46 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 46
47 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 47
48 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 48
49 },

Close the surrounding scope, value or argument list in “Define full-resolution messages”.

Link to line 49
50 ),

Close the surrounding scope, value or argument list in “Define full-resolution messages”.

Link to line 50
51 ];

Close the surrounding scope, value or argument list in “Define full-resolution messages”.

Link to line 51

Frame 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 52
53 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 53
54 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 54
55 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 55
56 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 56
57 // 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 57
58 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 58
59 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 59
60 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 60
61 }

Close the surrounding scope, value or argument list in “Frame and verify packets”.

Link to line 61
62 decoder.finish().expect("no partial packet remains");

Check that no partial UMP packet remains after feeding the message’s words.

Link to line 62
63 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 63
64 assert_eq!(

Verify the fixture’s expected result. A mismatch fails the test rather than becoming a successful support claim.

Link to line 64
65 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 65
66 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 66
67 );

Close the current expression or scope; no additional processing operation occurs on this line.

Link to line 67
68 events.push(Event {

Append the supplied item to this owned collection. UI/fixture collections grow outside the audio callback.

Link to line 68
69 frame_offset,

Supply these names as the remaining imports, arguments or fields of the surrounding declaration.

Link to line 69
70 data: EventData::Ump(framed),

Typed event payload; timing is kept separately in frame_offset.

Link to line 70
71 });

Close the current expression or scope; no additional processing operation occurs on this line.

Link to line 71
72 }

Close the surrounding scope, value or argument list in “Frame and verify packets”.

Link to line 72

Validate 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 73
74 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 74
75 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 75
76 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 76
77 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 77
78 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 78
79 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 79
80 config.validate_block(&block)?;

Check the complete block against prepared limits before the callback mutates DSP history.

Link to line 80

Print 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 81
82 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 82
83 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 83
84 println!(

Report this explicit fixture result to stdout; printing belongs to the offline/control path, not the audio callback.

Link to line 84
85 "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 85
86 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 86
87 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 87
88 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 88
89 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 89
90 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 90
91 );

Close the current expression or scope; no additional processing operation occurs on this line.

Link to line 91
92 }

Close the surrounding scope, value or argument list in “Print the lossless result”.

Link to line 92
93 }

Close the surrounding scope, value or argument list in “Print the lossless result”.

Link to line 93
94 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 94
95 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 95
96 Ok(())

Report successful completion without returning a payload.

Link to line 96
97}

Close the surrounding scope, value or argument list in “Print the lossless result”.

Link to line 97

Source SHA-256: 31214f060b8615e98046b6fc1e63695e7f98da22c68922856ad3c34da459236c

API: ravet_examples · ravet_foundation

All tutorial walkthroughs