1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
//! MIDI event observer.
//!
//! IT modules can embed MIDI macros that, when triggered via `Zxx` /
//! `SFx`, emit MIDI data. xmrsplayer's internal interpreter handles
//! the `F0 F0 nn xx` "filter control" frames natively (they drive the
//! per-voice resonant filter) but for everything else — standard
//! channel messages (Note On/Off, CC, Program Change, Pitch Bend,
//! Aftertouch) and SysEx — the engine has no internal meaning to
//! attach, so the bytes are emitted as `MidiEvent`s to any subscribed
//! [`MidiObserver`].
//!
//! This lets downstream code bridge modules to real MIDI hardware or
//! a soft synth without the core replayer having to know anything
//! about MIDI I/O. The observer pattern mirrors the [`PlayerObserver`]
//! trait that already publishes row / tick / pattern-change events.
//!
//! # Scope
//!
//! * Events are emitted once per occurrence in a macro — a single
//! `Zxx` triggers the full macro, which may emit zero or more
//! events depending on the macro's byte sequence.
//! * The replayer itself does not enforce MIDI timing constraints
//! (running status, sysex aggregation across macros, etc.). Each
//! macro is parsed independently; whatever byte sequence the macro
//! encodes is what gets emitted.
//! * Tracker channel vs MIDI channel: `source_channel` in
//! [`MidiObserver::on_midi_event`] is the *pattern* channel the
//! macro was triggered on (0..num_channels). The `channel` field
//! inside [`MidiEvent`] variants is the *MIDI* channel encoded in
//! the status byte's low nibble (0..15). The two are independent:
//! a macro on tracker channel 3 can address MIDI channel 7.
//!
//! [`PlayerObserver`]: crate::observer::PlayerObserver
//! [`MidiObserver`]: crate::midi_observer::MidiObserver
//! [`MidiObserver::on_midi_event`]: crate::midi_observer::MidiObserver::on_midi_event
//! [`MidiEvent`]: crate::midi_observer::MidiEvent
use Vec;
/// A MIDI event emitted by the macro interpreter.
///
/// Channel messages carry the MIDI channel (0..15) and payload bytes
/// as documented in the MIDI 1.0 spec. SysEx data is passed through
/// verbatim including the leading `F0` but stopping BEFORE the
/// terminating `F7` — consumers wanting the raw stream can infer it.
/// `Raw` is a fallback for byte sequences that look like MIDI but
/// don't match a known status byte (or are too short to form a
/// complete message).
/// A consumer of MIDI events emitted by the macro interpreter.
///
/// Register with [`XmrsPlayer::add_midi_observer`](crate::xmrsplayer::XmrsPlayer::add_midi_observer).
///
/// The `source_channel` argument is the *tracker* channel (pattern
/// column, 0..num_channels) the macro fired on — distinct from the
/// MIDI channel carried inside the event's variant fields. See the
/// module-level note on the two channel concepts.