#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MidiEvent {
pub sample_offset: u32,
pub kind: MidiEventKind,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MidiEventKind {
NoteOn {
note: u8,
velocity: u8,
channel: u8,
},
NoteOff {
note: u8,
velocity: u8,
channel: u8,
},
ControlChange {
controller: u8,
value: u8,
channel: u8,
},
ProgramChange {
program: u8,
channel: u8,
},
PolyphonicAftertouch {
note: u8,
pressure: u8,
channel: u8,
},
ChannelAftertouch {
pressure: u8,
channel: u8,
},
PitchBend {
value: u16,
channel: u8,
},
TimingClock,
Start,
Continue,
Stop,
ActiveSensing,
SystemReset,
}
impl MidiEvent {
pub const PITCH_BEND_CENTER: u16 = 8192;
pub fn note_on(note: u8, velocity: u8, channel: u8, sample_offset: u32) -> Self {
Self {
sample_offset,
kind: MidiEventKind::NoteOn {
note: note.min(127),
velocity: velocity.min(127),
channel: channel.min(15),
},
}
}
pub fn note_off(note: u8, velocity: u8, channel: u8, sample_offset: u32) -> Self {
Self {
sample_offset,
kind: MidiEventKind::NoteOff {
note: note.min(127),
velocity: velocity.min(127),
channel: channel.min(15),
},
}
}
pub fn control_change(controller: u8, value: u8, channel: u8, sample_offset: u32) -> Self {
Self {
sample_offset,
kind: MidiEventKind::ControlChange {
controller: controller.min(127),
value: value.min(127),
channel: channel.min(15),
},
}
}
pub fn program_change(program: u8, channel: u8, sample_offset: u32) -> Self {
Self {
sample_offset,
kind: MidiEventKind::ProgramChange {
program: program.min(127),
channel: channel.min(15),
},
}
}
pub fn polyphonic_aftertouch(note: u8, pressure: u8, channel: u8, sample_offset: u32) -> Self {
Self {
sample_offset,
kind: MidiEventKind::PolyphonicAftertouch {
note: note.min(127),
pressure: pressure.min(127),
channel: channel.min(15),
},
}
}
pub fn channel_aftertouch(pressure: u8, channel: u8, sample_offset: u32) -> Self {
Self {
sample_offset,
kind: MidiEventKind::ChannelAftertouch {
pressure: pressure.min(127),
channel: channel.min(15),
},
}
}
pub fn pitch_bend(value: u16, channel: u8, sample_offset: u32) -> Self {
Self {
sample_offset,
kind: MidiEventKind::PitchBend {
value: value.min(16383),
channel: channel.min(15),
},
}
}
pub fn pitch_bend_center(channel: u8, sample_offset: u32) -> Self {
Self::pitch_bend(Self::PITCH_BEND_CENTER, channel, sample_offset)
}
pub fn timing_clock(sample_offset: u32) -> Self {
Self {
sample_offset,
kind: MidiEventKind::TimingClock,
}
}
pub fn start(sample_offset: u32) -> Self {
Self {
sample_offset,
kind: MidiEventKind::Start,
}
}
pub fn continue_playback(sample_offset: u32) -> Self {
Self {
sample_offset,
kind: MidiEventKind::Continue,
}
}
pub fn stop(sample_offset: u32) -> Self {
Self {
sample_offset,
kind: MidiEventKind::Stop,
}
}
pub fn active_sensing(sample_offset: u32) -> Self {
Self {
sample_offset,
kind: MidiEventKind::ActiveSensing,
}
}
pub fn system_reset(sample_offset: u32) -> Self {
Self {
sample_offset,
kind: MidiEventKind::SystemReset,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_note_on_creation() {
let event = MidiEvent::note_on(60, 100, 0, 0);
assert_eq!(event.sample_offset, 0);
match event.kind {
MidiEventKind::NoteOn { note, velocity, channel } => {
assert_eq!(note, 60);
assert_eq!(velocity, 100);
assert_eq!(channel, 0);
}
_ => panic!("Expected NoteOn event"),
}
}
#[test]
fn test_note_off_creation() {
let event = MidiEvent::note_off(60, 64, 0, 100);
assert_eq!(event.sample_offset, 100);
match event.kind {
MidiEventKind::NoteOff { note, velocity, channel } => {
assert_eq!(note, 60);
assert_eq!(velocity, 64);
assert_eq!(channel, 0);
}
_ => panic!("Expected NoteOff event"),
}
}
#[test]
fn test_control_change_creation() {
let event = MidiEvent::control_change(1, 64, 0, 0);
match event.kind {
MidiEventKind::ControlChange { controller, value, channel } => {
assert_eq!(controller, 1);
assert_eq!(value, 64);
assert_eq!(channel, 0);
}
_ => panic!("Expected ControlChange event"),
}
}
#[test]
fn test_program_change_creation() {
let event = MidiEvent::program_change(5, 0, 0);
match event.kind {
MidiEventKind::ProgramChange { program, channel } => {
assert_eq!(program, 5);
assert_eq!(channel, 0);
}
_ => panic!("Expected ProgramChange event"),
}
}
#[test]
fn test_value_clamping() {
let event = MidiEvent::note_on(200, 200, 20, 0);
match event.kind {
MidiEventKind::NoteOn { note, velocity, channel } => {
assert_eq!(note, 127); assert_eq!(velocity, 127); assert_eq!(channel, 15); }
_ => panic!("Expected NoteOn event"),
}
}
#[test]
fn test_pitch_bend_center() {
assert_eq!(MidiEvent::PITCH_BEND_CENTER, 8192);
let event = MidiEvent::pitch_bend_center(0, 0);
match event.kind {
MidiEventKind::PitchBend { value, channel } => {
assert_eq!(value, 8192);
assert_eq!(channel, 0);
}
_ => panic!("Expected PitchBend event"),
}
let manual = MidiEvent::pitch_bend(MidiEvent::PITCH_BEND_CENTER, 5, 100);
let helper = MidiEvent::pitch_bend_center(5, 100);
assert_eq!(manual.kind, helper.kind);
assert_eq!(manual.sample_offset, helper.sample_offset);
}
}