start with 4 tracks; remove ArrangerSceneApi

This commit is contained in:
🪞👃🪞 2024-12-18 19:57:26 +01:00
parent 72dd3756db
commit 0a59594730
5 changed files with 70 additions and 91 deletions

View file

@ -17,7 +17,7 @@ pub struct ArrangerCli {
transport: bool, transport: bool,
/// Number of tracks /// Number of tracks
#[arg(short = 'x', long, default_value_t = 8)] #[arg(short = 'x', long, default_value_t = 4)]
tracks: usize, tracks: usize,
/// Number of scenes /// Number of scenes

View file

@ -44,7 +44,7 @@ from_jack!(|jack| ArrangerTui {
render!(<Tui>|self: ArrangerTui|{ render!(<Tui>|self: ArrangerTui|{
let arranger = ||lay!(|add|{ let arranger = ||lay!(|add|{
let color = self.color; let color = self.color;
add(&Fill::wh(Lozenge(Style::default().bg(color.light.rgb).fg(color.darker.rgb))))?; //add(&Fill::wh(Lozenge(Style::default().bg(color.light.rgb).fg(color.darker.rgb))))?;
add(&self.size)?; add(&self.size)?;
match self.mode { match self.mode {
ArrangerMode::H => todo!("horizontal arranger"), ArrangerMode::H => todo!("horizontal arranger"),
@ -68,7 +68,16 @@ render!(<Tui>|self: ArrangerTui|{
let play = Fixed::wh(5, 2, PlayPause(self.clock.is_rolling())); let play = Fixed::wh(5, 2, PlayPause(self.clock.is_rolling()));
let transport = TransportView::from((self, None, true)); let transport = TransportView::from((self, None, true));
let with_transport = |x|col!([row!(![&play, &transport]), &x]); let with_transport = |x|col!([row!(![&play, &transport]), &x]);
with_transport(with_pool(col!([Fixed::h(self.splits[0], arranger()), &self.editor]))) let color = self.color;
let with_frame = |x|lay!([
Fill::wh(Tui::bg(color.darkest.rgb, " ")),
Fill::wh(Lozenge(Style::default().bg(color.light.rgb).fg(color.darker.rgb))),
x
]);
with_transport(with_pool(with_frame(row!([
Fixed::w(30, arranger()),
Fill::wh(&self.editor)
]))))
}); });
audio!(|self: ArrangerTui, client, scope|{ audio!(|self: ArrangerTui, client, scope|{
// Start profiling cycle // Start profiling cycle
@ -217,7 +226,15 @@ command!(|self:ArrangerCommand,state:ArrangerTui|{
_ => { todo!() } _ => { todo!() }
} }
}); });
command!(|self:ArrangerSceneCommand,_state:ArrangerTui|None); command!(|self:ArrangerSceneCommand,state:ArrangerTui|match self {
//Self::Delete(index) => { state.scene_del(index); },
Self::SetColor(index, color) => {
let old = state.scenes[index].color;
state.scenes[index].color = color;
Some(Self::SetColor(index, old))
},
_ => None
});
command!(|self:ArrangerTrackCommand,_state:ArrangerTui|None); command!(|self:ArrangerTrackCommand,_state:ArrangerTui|None);
command!(|self:ArrangerClipCommand, _state:ArrangerTui|None); command!(|self:ArrangerClipCommand, _state:ArrangerTui|None);
#[derive(Clone, Debug)] #[derive(Clone, Debug)]

View file

@ -1,50 +1,23 @@
use crate::*; use crate::*;
#[derive(Default, Debug, Clone)] pub struct ArrangerScene {
pub trait HasScenes<S: ArrangerSceneApi> { /// Name of scene
fn scenes (&self) -> &Vec<S>; pub(crate) name: Arc<RwLock<String>>,
fn scenes_mut (&mut self) -> &mut Vec<S>; /// Clips in scene, one per track
fn scene_add (&mut self, name: Option<&str>, color: Option<ItemPalette>) -> Usually<&mut S>; pub(crate) clips: Vec<Option<Arc<RwLock<Phrase>>>>,
fn scene_del (&mut self, index: usize) { /// Identifying color of scene
self.scenes_mut().remove(index); pub(crate) color: ItemPalette,
}
fn scene_default_name (&self) -> String {
format!("Scene {}", self.scenes().len() + 1)
}
fn selected_scene (&self) -> Option<&S> {
None
}
fn selected_scene_mut (&mut self) -> Option<&mut S> {
None
}
} }
impl ArrangerScene {
#[derive(Clone, Debug)] pub fn name (&self) -> &Arc<RwLock<String>> {
pub enum ArrangerSceneCommand { &self.name
Add, }
Delete(usize), pub fn clips (&self) -> &Vec<Option<Arc<RwLock<Phrase>>>> {
RandomColor, &self.clips
Play(usize), }
Swap(usize, usize), pub fn color (&self) -> ItemPalette {
SetSize(usize), self.color
SetZoom(usize), }
} pub fn ppqs (scenes: &[Self], factor: usize) -> Vec<(usize, usize)> {
//impl<T: ArrangerApi> Command<T> for ArrangerSceneCommand {
//fn execute (self, state: &mut T) -> Perhaps<Self> {
//match self {
//Self::Delete(index) => { state.scene_del(index); },
//_ => todo!()
//}
//Ok(None)
//}
//}
pub trait ArrangerSceneApi: Sized {
fn name (&self) -> &Arc<RwLock<String>>;
fn clips (&self) -> &Vec<Option<Arc<RwLock<Phrase>>>>;
fn color (&self) -> ItemPalette;
fn ppqs (scenes: &[Self], factor: usize) -> Vec<(usize, usize)> {
let mut total = 0; let mut total = 0;
if factor == 0 { if factor == 0 {
scenes.iter().map(|scene|{ scenes.iter().map(|scene|{
@ -58,21 +31,18 @@ pub trait ArrangerSceneApi: Sized {
}).collect() }).collect()
} }
} }
pub fn longest_name (scenes: &[Self]) -> usize {
fn longest_name (scenes: &[Self]) -> usize {
scenes.iter().map(|s|s.name().read().unwrap().len()).fold(0, usize::max) scenes.iter().map(|s|s.name().read().unwrap().len()).fold(0, usize::max)
} }
/// Returns the pulse length of the longest phrase in the scene /// Returns the pulse length of the longest phrase in the scene
fn pulses (&self) -> usize { pub fn pulses (&self) -> usize {
self.clips().iter().fold(0, |a, p|{ self.clips().iter().fold(0, |a, p|{
a.max(p.as_ref().map(|q|q.read().unwrap().length).unwrap_or(0)) a.max(p.as_ref().map(|q|q.read().unwrap().length).unwrap_or(0))
}) })
} }
/// Returns true if all phrases in the scene are /// Returns true if all phrases in the scene are
/// currently playing on the given collection of tracks. /// currently playing on the given collection of tracks.
fn is_playing <T: ArrangerTrackApi> (&self, tracks: &[T]) -> bool { pub fn is_playing <T: ArrangerTrackApi> (&self, tracks: &[T]) -> bool {
self.clips().iter().any(|clip|clip.is_some()) && self.clips().iter().enumerate() self.clips().iter().any(|clip|clip.is_some()) && self.clips().iter().enumerate()
.all(|(track_index, clip)|match clip { .all(|(track_index, clip)|match clip {
Some(clip) => tracks Some(clip) => tracks
@ -88,11 +58,20 @@ pub trait ArrangerSceneApi: Sized {
None => true None => true
}) })
} }
pub fn clip (&self, index: usize) -> Option<&Arc<RwLock<Phrase>>> {
fn clip (&self, index: usize) -> Option<&Arc<RwLock<Phrase>>> {
match self.clips().get(index) { Some(Some(clip)) => Some(clip), _ => None } match self.clips().get(index) { Some(Some(clip)) => Some(clip), _ => None }
} }
}
#[derive(Clone, Debug)]
pub enum ArrangerSceneCommand {
Add,
Delete(usize),
RandomColor,
Play(usize),
Swap(usize, usize),
SetSize(usize),
SetZoom(usize),
SetColor(usize, ItemPalette),
} }
pub fn to_arranger_scene_command (input: &TuiInput, s: usize) -> Option<ArrangerCommand> { pub fn to_arranger_scene_command (input: &TuiInput, s: usize) -> Option<ArrangerCommand> {
use KeyCode::{Char, Up, Down, Right, Enter, Delete}; use KeyCode::{Char, Up, Down, Right, Enter, Delete};
@ -108,20 +87,20 @@ pub fn to_arranger_scene_command (input: &TuiInput, s: usize) -> Option<Arranger
key_pat!(Char('<')) => Cmd::Scene(Scene::Swap(s, s - 1)), key_pat!(Char('<')) => Cmd::Scene(Scene::Swap(s, s - 1)),
key_pat!(Char('>')) => Cmd::Scene(Scene::Swap(s, s + 1)), key_pat!(Char('>')) => Cmd::Scene(Scene::Swap(s, s + 1)),
key_pat!(Char('q')) => Cmd::Scene(Scene::Play(s)), key_pat!(Char('q')) => Cmd::Scene(Scene::Play(s)),
key_pat!(Char('c')) => Cmd::Scene(Scene::SetColor(s, ItemPalette::random())),
key_pat!(Delete) => Cmd::Scene(Scene::Delete(s)), key_pat!(Delete) => Cmd::Scene(Scene::Delete(s)),
//key_pat!(Char('c')) => Cmd::Track(Scene::Color(s, ItemPalette::random())), //key_pat!(Char('c')) => Cmd::Track(Scene::Color(s, ItemPalette::random())),
_ => return None _ => return None
}) })
} }
impl ArrangerTui {
impl HasScenes<ArrangerScene> for ArrangerTui { pub fn scenes (&self) -> &Vec<ArrangerScene> {
fn scenes (&self) -> &Vec<ArrangerScene> {
&self.scenes &self.scenes
} }
fn scenes_mut (&mut self) -> &mut Vec<ArrangerScene> { pub fn scenes_mut (&mut self) -> &mut Vec<ArrangerScene> {
&mut self.scenes &mut self.scenes
} }
fn scene_add (&mut self, name: Option<&str>, color: Option<ItemPalette>) pub fn scene_add (&mut self, name: Option<&str>, color: Option<ItemPalette>)
-> Usually<&mut ArrangerScene> -> Usually<&mut ArrangerScene>
{ {
let name = name.map_or_else(||self.scene_default_name(), |x|x.to_string()); let name = name.map_or_else(||self.scene_default_name(), |x|x.to_string());
@ -134,29 +113,13 @@ impl HasScenes<ArrangerScene> for ArrangerTui {
let index = self.scenes().len() - 1; let index = self.scenes().len() - 1;
Ok(&mut self.scenes_mut()[index]) Ok(&mut self.scenes_mut()[index])
} }
fn selected_scene (&self) -> Option<&ArrangerScene> { fn scene_default_name (&self) -> String {
format!("S{:3>0}", self.scenes().len() + 1)
}
pub fn selected_scene (&self) -> Option<&ArrangerScene> {
self.selected.scene().map(|s|self.scenes().get(s)).flatten() self.selected.scene().map(|s|self.scenes().get(s)).flatten()
} }
fn selected_scene_mut (&mut self) -> Option<&mut ArrangerScene> { pub fn selected_scene_mut (&mut self) -> Option<&mut ArrangerScene> {
self.selected.scene().map(|s|self.scenes_mut().get_mut(s)).flatten() self.selected.scene().map(|s|self.scenes_mut().get_mut(s)).flatten()
} }
} }
#[derive(Default, Debug, Clone)] pub struct ArrangerScene {
/// Name of scene
pub(crate) name: Arc<RwLock<String>>,
/// Clips in scene, one per track
pub(crate) clips: Vec<Option<Arc<RwLock<Phrase>>>>,
/// Identifying color of scene
pub(crate) color: ItemPalette,
}
impl ArrangerSceneApi for ArrangerScene {
fn name (&self) -> &Arc<RwLock<String>> {
&self.name
}
fn clips (&self) -> &Vec<Option<Arc<RwLock<Phrase>>>> {
&self.clips
}
fn color (&self) -> ItemPalette {
self.color
}
}

View file

@ -19,7 +19,7 @@ pub trait ArrangerTracksApi<T: ArrangerTrackApi>: HasTracks<T> {
fn track_add (&mut self, name: Option<&str>, color: Option<ItemPalette>)-> Usually<&mut T>; fn track_add (&mut self, name: Option<&str>, color: Option<ItemPalette>)-> Usually<&mut T>;
fn track_del (&mut self, index: usize); fn track_del (&mut self, index: usize);
fn track_default_name (&self) -> String { fn track_default_name (&self) -> String {
format!("Track {}", self.tracks().len() + 1) format!("T{}", self.tracks().len() + 1)
} }
} }

View file

@ -60,19 +60,18 @@ impl PianoHorizontal {
} }
render!(<Tui>|self: PianoHorizontal|{ render!(<Tui>|self: PianoHorizontal|{
let color = self.color;
let keys = move||PianoHorizontalKeys(&self); let keys = move||PianoHorizontalKeys(&self);
let timeline = move||PianoHorizontalTimeline(&self); let timeline = move||PianoHorizontalTimeline(&self);
let notes = move||PianoHorizontalNotes(&self); let notes = move||PianoHorizontalNotes(&self);
let cursor = move||PianoHorizontalCursor(&self); let cursor = move||PianoHorizontalCursor(&self);
let keys_width = 5; let keys_width = 5;
Tui::bg(color.darker.rgb, Fill::wh(Bsp::s( Fill::wh(Bsp::s(
Fixed::h(1, Bsp::e(Fixed::w(keys_width, ""), Fill::w(timeline()),)), Fixed::h(1, Bsp::e(Fixed::w(keys_width, ""), Fill::w(timeline()),)),
Bsp::e( Bsp::e(
Fixed::w(keys_width, keys()), Fixed::w(keys_width, keys()),
Fill::wh(lay!([&self.size, Fill::wh(lay!([Fill::wh(notes()), Fill::wh(cursor()),]))])), Fill::wh(lay!([&self.size, Fill::wh(lay!([Fill::wh(notes()), Fill::wh(cursor()),]))])),
), ),
))) ))
}); });
pub struct PianoHorizontalTimeline<'a>(&'a PianoHorizontal); pub struct PianoHorizontalTimeline<'a>(&'a PianoHorizontal);