mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
356 lines
14 KiB
Rust
356 lines
14 KiB
Rust
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 }
|
||
//}
|