1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct MidiEvent {
8 pub timestamp: u64,
10 pub channel: u8,
12 pub kind: MidiEventKind,
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
18pub enum MidiEventKind {
19 NoteOn { note: u8, velocity: u8 },
21 NoteOff { note: u8, velocity: u8 },
23 PitchBend { value: i16 },
25 ChannelPressure { pressure: u8 },
27 PolyPressure { note: u8, pressure: u8 },
29 ControlChange { cc: u8, value: u8 },
31 ProgramChange { program: u8 },
33 AllNotesOff,
35 AllSoundOff,
37}
38
39impl MidiEvent {
40 pub fn from_bytes(bytes: &[u8], timestamp: u64) -> Option<Self> {
43 if bytes.is_empty() { return None; }
44 let status = bytes[0];
45 let channel = status & 0x0F;
46 let kind_byte = status >> 4;
47
48 let kind = match kind_byte {
49 0x8 => {
50 if bytes.len() < 3 { return None; }
52 MidiEventKind::NoteOff { note: bytes[1] & 0x7F, velocity: bytes[2] & 0x7F }
53 }
54 0x9 => {
55 if bytes.len() < 3 { return None; }
56 let vel = bytes[2] & 0x7F;
57 if vel == 0 {
58 MidiEventKind::NoteOff { note: bytes[1] & 0x7F, velocity: 0 }
60 } else {
61 MidiEventKind::NoteOn { note: bytes[1] & 0x7F, velocity: vel }
62 }
63 }
64 0xA => {
65 if bytes.len() < 3 { return None; }
66 MidiEventKind::PolyPressure { note: bytes[1] & 0x7F, pressure: bytes[2] & 0x7F }
67 }
68 0xB => {
69 if bytes.len() < 3 { return None; }
70 let cc = bytes[1] & 0x7F;
71 let val = bytes[2] & 0x7F;
72 match cc {
73 120 => MidiEventKind::AllSoundOff,
74 123 => MidiEventKind::AllNotesOff,
75 _ => MidiEventKind::ControlChange { cc, value: val },
76 }
77 }
78 0xC => {
79 if bytes.len() < 2 { return None; }
80 MidiEventKind::ProgramChange { program: bytes[1] & 0x7F }
81 }
82 0xD => {
83 if bytes.len() < 2 { return None; }
84 MidiEventKind::ChannelPressure { pressure: bytes[1] & 0x7F }
85 }
86 0xE => {
87 if bytes.len() < 3 { return None; }
88 let lsb = bytes[1] as i16;
89 let msb = bytes[2] as i16;
90 let value = ((msb << 7) | lsb) - 8192;
91 MidiEventKind::PitchBend { value }
92 }
93 _ => return None,
94 };
95
96 Some(MidiEvent { timestamp, channel, kind })
97 }
98
99 pub fn is_note_on(&self) -> bool {
101 matches!(self.kind, MidiEventKind::NoteOn { .. })
102 }
103
104 pub fn is_note_off(&self) -> bool {
106 matches!(self.kind, MidiEventKind::NoteOff { .. })
107 }
108
109 pub fn note(&self) -> Option<u8> {
111 match self.kind {
112 MidiEventKind::NoteOn { note, .. } | MidiEventKind::NoteOff { note, .. } => Some(note),
113 _ => None,
114 }
115 }
116
117 pub fn velocity(&self) -> Option<u8> {
119 match self.kind {
120 MidiEventKind::NoteOn { velocity, .. } | MidiEventKind::NoteOff { velocity, .. } => Some(velocity),
121 _ => None,
122 }
123 }
124
125 pub fn velocity_linear(&self) -> f32 {
127 self.velocity().map(|v| v as f32 / 127.0).unwrap_or(0.0)
128 }
129}
130
131pub mod cc {
133 pub const MOD_WHEEL: u8 = 1;
134 pub const BREATH: u8 = 2;
135 pub const FOOT_PEDAL: u8 = 4;
136 pub const PORTAMENTO_TIME: u8 = 5;
137 pub const VOLUME: u8 = 7;
138 pub const BALANCE: u8 = 8;
139 pub const PAN: u8 = 10;
140 pub const EXPRESSION: u8 = 11;
141 pub const SUSTAIN_PEDAL: u8 = 64;
142 pub const PORTAMENTO: u8 = 65;
143 pub const SOSTENUTO: u8 = 66;
144 pub const SOFT_PEDAL: u8 = 67;
145 pub const LEGATO: u8 = 68;
146 pub const REVERB_SEND: u8 = 91;
147 pub const CHORUS_SEND: u8 = 93;
148}