tek/crates/tek_sequencer/src/phrase.rs
2024-09-05 18:08:43 +03:00

200 lines
7.5 KiB
Rust

use crate::*;
#[derive(Debug)]
/// A MIDI sequence.
pub struct Phrase {
pub name: Arc<RwLock<String>>,
pub length: usize,
pub notes: PhraseData,
pub looped: Option<(usize, usize)>,
/// All notes are displayed with minimum length
pub percussive: bool
}
pub type PhraseData = Vec<Vec<MidiMessage>>;
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: Arc::new(RwLock::new(name.into())),
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,
_ => {}
}
}
}
}
fn from_edn <'e> (ppq: usize, args: &[Edn<'e>]) -> Usually<Self> {
let mut phrase = Self::default();
let mut name = String::new();
let mut beats = 0usize;
let mut steps = 0usize;
edn!(edn in args {
Edn::Map(map) => {
if let Some(Edn::Str(n)) = map.get(&Edn::Key(":name")) {
name = String::from(*n);
}
if let Some(Edn::Int(b)) = map.get(&Edn::Key(":beats")) {
beats = *b as usize;
phrase.length = ppq * beats;
for _ in phrase.notes.len()..phrase.length {
phrase.notes.push(Vec::with_capacity(16))
}
}
if let Some(Edn::Int(s)) = map.get(&Edn::Key(":steps")) {
steps = *s as usize;
}
},
Edn::List(args) => {
let time = (match args.get(0) {
Some(Edn::Key(text)) => text[1..].parse::<f64>()?,
Some(Edn::Int(i)) => *i as f64,
Some(Edn::Double(f)) => f64::from(*f),
_ => panic!("unexpected in phrase '{name}': {:?}", args.get(0)),
} * beats as f64 * ppq as f64 / steps as f64) as usize;
for edn in args[1..].iter() {
match edn {
Edn::List(args) => if let (
Some(Edn::Int(key)),
Some(Edn::Int(vel)),
) = (
args.get(0),
args.get(1),
) {
let (key, vel) = (
u7::from((*key as u8).min(127)),
u7::from((*vel as u8).min(127)),
);
phrase.notes[time].push(MidiMessage::NoteOn { key, vel })
} else {
panic!("unexpected list in phrase '{name}'")
},
_ => panic!("unexpected in phrase '{name}': {edn:?}")
}
}
},
_ => panic!("unexpected in phrase '{name}': {edn:?}"),
});
*phrase.name.write().unwrap() = name;
Ok(phrase)
}
}
/// 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
}}
}
impl<E: Engine> Arranger<E> {
pub fn phrase (&self) -> Option<&Arc<RwLock<Phrase>>> {
let track_id = self.selected.track()?;
self.tracks.get(track_id)?.phrases.get((*self.scene()?.clips.get(track_id)?)?)
}
pub fn phrase_next (&mut self) {
let track_index = self.selected.track();
let scene_index = self.selected.scene();
track_index
.and_then(|index|self.tracks.get_mut(index).map(|track|(index, track)))
.and_then(|(track_index, track)|{
let phrases = track.phrases.len();
scene_index
.and_then(|index|self.scenes.get_mut(index))
.and_then(|scene|{
if let Some(phrase_index) = scene.clips[track_index] {
if phrase_index >= phrases - 1 {
scene.clips[track_index] = None;
} else {
scene.clips[track_index] = Some(phrase_index + 1);
}
} else if phrases > 0 {
scene.clips[track_index] = Some(0);
}
Some(())
})
});
}
pub fn phrase_prev (&mut self) {
let track_index = self.selected.track();
let scene_index = self.selected.scene();
track_index
.and_then(|index|self.tracks.get_mut(index).map(|track|(index, track)))
.and_then(|(track_index, track)|{
let phrases = track.phrases.len();
scene_index
.and_then(|index|self.scenes.get_mut(index))
.and_then(|scene|{
if let Some(phrase_index) = scene.clips[track_index] {
scene.clips[track_index] = if phrase_index == 0 {
None
} else {
Some(phrase_index - 1)
};
} else if phrases > 0 {
scene.clips[track_index] = Some(phrases - 1);
}
Some(())
})
});
}
}