tek/tek/src/arranger.rs

356 lines
14 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use crate::*;
use EdnItem::*;
use ClockCommand::{Play, Pause};
use self::ArrangerCommand as Cmd;
pub const TRACK_MIN_WIDTH: usize = 9;
command!(|self: ClipCommand, state: App|match self { _ => todo!("clip command") });
command!(|self: SceneCommand, state: App|match self { _ => todo!("scene command") });
command!(|self: TrackCommand, state: App|match self { _ => todo!("track command") });
#[derive(Clone, Debug)] pub enum ClipCommand {
Get(usize, usize),
Put(usize, usize, Option<Arc<RwLock<MidiClip>>>),
Enqueue(usize, usize),
Edit(Option<Arc<RwLock<MidiClip>>>),
SetLoop(usize, usize, bool),
SetColor(usize, usize, ItemPalette),
}
impl EdnCommand<App> for ClipCommand {
fn from_edn <'a> (state: &App, head: &EdnItem<&str>, tail: &'a [EdnItem<String>]) -> Self {
match (head, tail) {
(Sym("get"), [a, b ]) => Self::Get(0, 0),
(Sym("put"), [a, b, c ]) => Self::Put(0, 0, None),
(Sym("enqueue"), [a, b ]) => Self::Enqueue(0, 0),
(Sym("edit"), [a ]) => Self::Edit(None),
(Sym("loop"), [a, b, c ]) => Self::SetLoop(0, 0, true),
(Sym("color"), [a, b, c ]) => Self::SetColor(0, 0, ItemPalette::random()),
_ => panic!(),
}
}
}
#[derive(Clone, Debug)] pub enum SceneCommand {
Add,
Del(usize),
Swap(usize, usize),
SetSize(usize),
SetZoom(usize),
SetColor(usize, ItemPalette),
Enqueue(usize),
}
impl EdnCommand<App> for SceneCommand {
fn from_edn <'a> (state: &App, head: &EdnItem<&str>, tail: &'a [EdnItem<String>]) -> Self {
match (head, tail) {
(Sym("add"), [ ]) => Self::Add,
(Sym("del"), [a ]) => Self::Del(0),
(Sym("swap"), [a, b ]) => Self::Swap(0, 0),
(Sym("size"), [a ]) => Self::SetSize(0),
(Sym("zoom"), [a, ]) => Self::SetZoom(0),
(Sym("color"), [a, b, ]) => Self::SetColor(0, ItemPalette::random()),
(Sym("enqueue"), [a, ]) => Self::Enqueue(0),
_ => panic!(),
}
}
}
#[derive(Clone, Debug)] pub enum TrackCommand {
Add,
Del(usize),
Stop(usize),
Swap(usize, usize),
SetSize(usize),
SetZoom(usize),
SetColor(usize, ItemPalette),
}
impl EdnCommand<App> for TrackCommand {
fn from_edn <'a> (state: &App, head: &EdnItem<&str>, tail: &'a [EdnItem<String>]) -> Self {
match (head, tail) {
(Sym("add"), [ ]) => Self::Add,
(Sym("del"), [a ]) => Self::Del(0),
(Sym("stop"), [a ]) => Self::Stop(0),
(Sym("swap"), [a, b ]) => Self::Swap(0, 0),
(Sym("size"), [a ]) => Self::SetSize(0),
(Sym("zoom"), [a, ]) => Self::SetZoom(0),
(Sym("color"), [a, b, ]) => Self::SetColor(0, ItemPalette::random()),
_ => panic!(),
}
}
}
pub trait Arrangement: HasClock + HasJack {
fn tracks (&self) -> &Vec<ArrangerTrack>;
fn tracks_mut (&mut self) -> &mut Vec<ArrangerTrack>;
fn scenes (&self) -> &Vec<ArrangerScene>;
fn scenes_mut (&mut self) -> &mut Vec<ArrangerScene>;
fn selected (&self) -> &ArrangerSelection;
fn selected_mut (&mut self) -> &mut ArrangerSelection;
fn track_next_name (&self) -> Arc<str> {
format!("Trk{:02}", self.tracks().len() + 1).into()
}
fn track (&self) -> Option<&ArrangerTrack> {
self.selected().track().and_then(|s|self.tracks().get(s))
}
fn track_mut (&mut self) -> Option<&mut ArrangerTrack> {
self.selected().track().and_then(|s|self.tracks_mut().get_mut(s))
}
fn track_del (&mut self, index: usize) {
self.tracks_mut().remove(index);
for scene in self.scenes_mut().iter_mut() {
scene.clips.remove(index);
}
}
fn track_add (&mut self, name: Option<&str>, color: Option<ItemPalette>)
-> Usually<&mut ArrangerTrack>
{
let name = name.map_or_else(||self.track_next_name(), |x|x.to_string().into());
let track = ArrangerTrack {
width: (name.len() + 2).max(9),
color: color.unwrap_or_else(ItemPalette::random),
player: MidiPlayer::from(self.clock()),
name,
};
self.tracks_mut().push(track);
let len = self.tracks().len();
let index = len - 1;
for scene in self.scenes_mut().iter_mut() {
while scene.clips.len() < len {
scene.clips.push(None);
}
}
Ok(&mut self.tracks_mut()[index])
}
fn tracks_add (
&mut self,
count: usize,
width: usize,
midi_from: &[PortConnection],
midi_to: &[PortConnection],
) -> Usually<()> {
let jack = self.jack().clone();
let track_color_1 = ItemColor::random();
let track_color_2 = ItemColor::random();
for i in 0..count {
let color = track_color_1.mix(track_color_2, i as f32 / count as f32).into();
let mut track = self.track_add(None, Some(color))?;
track.width = width;
let port = JackPort::<MidiIn>::new(&jack, &format!("{}I", &track.name), midi_from)?;
track.player.midi_ins.push(port);
let port = JackPort::<MidiOut>::new(&jack, &format!("{}O", &track.name), midi_to)?;
track.player.midi_outs.push(port);
}
Ok(())
}
fn scene_default_name (&self) -> Arc<str> {
format!("Sc{:3>}", self.scenes().len() + 1).into()
}
fn scene (&self) -> Option<&ArrangerScene> {
self.selected().scene().and_then(|s|self.scenes().get(s))
}
fn scene_mut (&mut self) -> Option<&mut ArrangerScene> {
self.selected().scene().and_then(|s|self.scenes_mut().get_mut(s))
}
fn scene_del (&mut self, index: usize) {
todo!("delete scene");
}
fn scene_add (&mut self, name: Option<&str>, color: Option<ItemPalette>)
-> Usually<&mut ArrangerScene>
{
let scene = ArrangerScene {
name: name.map_or_else(||self.scene_default_name(), |x|x.to_string().into()),
clips: vec![None;self.tracks().len()],
color: color.unwrap_or_else(ItemPalette::random),
};
self.scenes_mut().push(scene);
let index = self.scenes().len() - 1;
Ok(&mut self.scenes_mut()[index])
}
fn scenes_add (&mut self, n: usize) -> Usually<()> {
let scene_color_1 = ItemColor::random();
let scene_color_2 = ItemColor::random();
for i in 0..n {
let _scene = self.scene_add(None, Some(
scene_color_1.mix(scene_color_2, i as f32 / n as f32).into()
))?;
}
Ok(())
}
fn activate (&mut self) -> Usually<()> {
let selected = self.selected().clone();
match selected {
ArrangerSelection::Scene(s) => {
let mut clips = vec![];
for (t, _) in self.tracks().iter().enumerate() {
clips.push(self.scenes()[s].clips[t].clone());
}
for (t, track) in self.tracks_mut().iter_mut().enumerate() {
if track.player.play_clip.is_some() || clips[t].is_some() {
track.player.enqueue_next(clips[t].as_ref());
}
}
if self.clock().is_stopped() {
self.clock().play_from(Some(0))?;
}
},
ArrangerSelection::Clip(t, s) => {
let clip = self.scenes()[s].clips[t].clone();
self.tracks_mut()[t].player.enqueue_next(clip.as_ref());
},
_ => {}
}
Ok(())
}
fn clip (&self) -> Option<Arc<RwLock<MidiClip>>> {
self.scene()?.clips.get(self.selected().track()?)?.clone()
}
fn toggle_loop (&mut self) {
if let Some(clip) = self.clip() {
clip.write().unwrap().toggle_loop()
}
}
//fn randomize_color (&mut self) {
//match self.selected {
//ArrangerSelection::Mix => { self.color = ItemPalette::random() },
//ArrangerSelection::Track(t) => { self.tracks[t].color = ItemPalette::random() },
//ArrangerSelection::Scene(s) => { self.scenes[s].color = ItemPalette::random() },
//ArrangerSelection::Clip(t, s) => if let Some(clip) = &self.scenes[s].clips[t] {
//clip.write().unwrap().color = ItemPalette::random();
//}
//}
//}
}
#[derive(Default)] pub struct ArrangerScene {
/// Name of scene
pub(crate) name: Arc<str>,
/// Clips in scene, one per track
pub(crate) clips: Vec<Option<Arc<RwLock<MidiClip>>>>,
/// Identifying color of scene
pub(crate) color: ItemPalette,
}
impl ArrangerScene {
pub fn longest_name (scenes: &[Self]) -> usize {
scenes.iter().map(|s|s.name.len()).fold(0, usize::max)
}
/// Returns the pulse length of the longest clip in the scene
pub fn pulses (&self) -> usize {
self.clips.iter().fold(0, |a, p|{
a.max(p.as_ref().map(|q|q.read().unwrap().length).unwrap_or(0))
})
}
/// Returns true if all clips in the scene are
/// currently playing on the given collection of tracks.
pub fn is_playing (&self, tracks: &[ArrangerTrack]) -> bool {
self.clips.iter().any(|clip|clip.is_some()) && self.clips.iter().enumerate()
.all(|(track_index, clip)|match clip {
Some(c) => tracks
.get(track_index)
.map(|track|{
if let Some((_, Some(clip))) = track.player().play_clip() {
*clip.read().unwrap() == *c.read().unwrap()
} else {
false
}
})
.unwrap_or(false),
None => true
})
}
pub fn clip (&self, index: usize) -> Option<&Arc<RwLock<MidiClip>>> {
match self.clips.get(index) { Some(Some(clip)) => Some(clip), _ => None }
}
}
#[derive(Debug)] pub struct ArrangerTrack {
/// Name of track
pub name: Arc<str>,
/// Preferred width of track column
pub width: usize,
/// Identifying color of track
pub color: ItemPalette,
/// MIDI player state
pub player: MidiPlayer,
}
has_clock!(|self:ArrangerTrack|self.player.clock());
has_player!(|self:ArrangerTrack|self.player);
impl ArrangerTrack {
fn longest_name (tracks: &[Self]) -> usize {
tracks.iter().map(|s|s.name.len()).fold(0, usize::max)
}
fn width_inc (&mut self) {
self.width += 1;
}
fn width_dec (&mut self) {
if self.width > TRACK_MIN_WIDTH {
self.width -= 1;
}
}
}
#[derive(PartialEq, Clone, Copy, Debug, Default)]
/// Represents the current user selection in the arranger
pub enum ArrangerSelection {
/// The whole mix is selected
#[default] Mix,
/// A track is selected.
Track(usize),
/// A scene is selected.
Scene(usize),
/// A clip (track × scene) is selected.
Clip(usize, usize),
}
/// Focus identification methods
impl ArrangerSelection {
pub fn track (&self) -> Option<usize> {
use ArrangerSelection::*;
match self {
Clip(t, _) => Some(*t),
Track(t) => Some(*t),
_ => None
}
}
pub fn scene (&self) -> Option<usize> {
use ArrangerSelection::*;
match self {
Clip(_, s) => Some(*s),
Scene(s) => Some(*s),
_ => None
}
}
pub fn is_mix (&self) -> bool { matches!(self, Self::Mix) }
pub fn is_track (&self) -> bool { matches!(self, Self::Track(_)) }
pub fn is_scene (&self) -> bool { matches!(self, Self::Scene(_)) }
pub fn is_clip (&self) -> bool { matches!(self, Self::Clip(_, _)) }
pub fn description (
&self,
tracks: &[ArrangerTrack],
scenes: &[ArrangerScene],
) -> Arc<str> {
format!("Selected: {}", match self {
Self::Mix => "Everything".to_string(),
Self::Track(t) => tracks.get(*t).map(|track|format!("T{t}: {}", &track.name))
.unwrap_or_else(||"T??".into()),
Self::Scene(s) => scenes.get(*s).map(|scene|format!("S{s}: {}", &scene.name))
.unwrap_or_else(||"S??".into()),
Self::Clip(t, s) => match (tracks.get(*t), scenes.get(*s)) {
(Some(_), Some(scene)) => match scene.clip(*t) {
Some(clip) => format!("T{t} S{s} C{}", &clip.read().unwrap().name),
None => format!("T{t} S{s}: Empty")
},
_ => format!("T{t} S{s}: Empty"),
}
}).into()
}
}
impl Arrangement for App {
fn tracks (&self) -> &Vec<ArrangerTrack> { &self.tracks }
fn tracks_mut (&mut self) -> &mut Vec<ArrangerTrack> { &mut self.tracks }
fn scenes (&self) -> &Vec<ArrangerScene> { &self.scenes }
fn scenes_mut (&mut self) -> &mut Vec<ArrangerScene> { &mut self.scenes }
fn selected (&self) -> &ArrangerSelection { &self.selected }
fn selected_mut (&mut self) -> &mut ArrangerSelection { &mut self.selected }
}
//impl Arrangement for Arranger {
//fn tracks (&self) -> &Vec<ArrangerTrack> { &self.tracks }
//fn tracks_mut (&mut self) -> &mut Vec<ArrangerTrack> { &mut self.tracks }
//fn scenes (&self) -> &Vec<ArrangerScene> { &self.scenes }
//fn scenes_mut (&mut self) -> &mut Vec<ArrangerScene> { &mut self.scenes }
//fn selected (&self) -> &ArrangerSelection { &self.selected }
//fn selected_mut (&mut self) -> &mut ArrangerSelection { &mut self.selected }
//}