use crate::core::*; /// Define a MIDI phrase. #[macro_export] macro_rules! phrase { ($($t:expr => $msg:expr),* $(,)?) => {{ #[allow(unused_mut)] let mut phrase = BTreeMap::new(); $(phrase.insert($t, vec![]);)* $(phrase.get_mut(&$t).unwrap().push($msg);)* phrase }} } pub type PhraseData = Vec>; #[derive(Debug)] /// A MIDI sequence. pub struct Phrase { pub name: String, pub length: usize, pub notes: PhraseData, pub looped: Option<(usize, usize)>, /// Immediate note-offs in view pub percussive: bool } impl Default for Phrase { fn default () -> Self { Self::new("", 0, None) } } impl Phrase { pub fn new (name: &str, length: usize, notes: Option) -> Self { Self { name: name.to_string(), length, notes: notes.unwrap_or(vec![Vec::with_capacity(16);length]), looped: Some((0, length)), percussive: true, } } pub fn record_event (&mut self, pulse: usize, message: MidiMessage) { if pulse >= self.length { panic!("extend phrase first") } self.notes[pulse].push(message); } /// Check if a range `start..end` contains MIDI Note On `k` pub fn contains_note_on (&self, k: u7, start: usize, end: usize) -> bool { //panic!("{:?} {start} {end}", &self); for events in self.notes[start.max(0)..end.min(self.notes.len())].iter() { for event in events.iter() { match event { MidiMessage::NoteOn {key,..} => { if *key == k { return true } } _ => {} } } } return false } /// Write a chunk of MIDI events to an output port. pub fn process_out ( &self, output: &mut MIDIChunk, notes_on: &mut [bool;128], timebase: &Arc, (frame0, frames, _): (usize, usize, f64), ) { let mut buf = Vec::with_capacity(8); for (time, tick) in Ticks(timebase.pulse_per_frame()).between_frames( frame0, frame0 + frames ) { let tick = tick % self.length; for message in self.notes[tick].iter() { buf.clear(); let channel = 0.into(); let message = *message; LiveEvent::Midi { channel, message }.write(&mut buf).unwrap(); output[time as usize].push(buf.clone()); match message { MidiMessage::NoteOn { key, .. } => notes_on[key.as_int() as usize] = true, MidiMessage::NoteOff { key, .. } => notes_on[key.as_int() as usize] = false, _ => {} } } } } }