mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
start with 4 tracks; remove ArrangerSceneApi
This commit is contained in:
parent
72dd3756db
commit
0a59594730
5 changed files with 70 additions and 91 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)]
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue