mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
93 lines
2.9 KiB
Rust
93 lines
2.9 KiB
Rust
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<Vec<MidiMessage>>;
|
|
|
|
#[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<PhraseData>) -> 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<Timebase>,
|
|
(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,
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|