clone EdnItem

This commit is contained in:
🪞👃🪞 2025-01-05 22:52:35 +01:00
parent 400fd9b6e9
commit 3dd8a7bc0d
8 changed files with 190 additions and 169 deletions

View file

@ -1,3 +1,8 @@
mod groovebox_audio; pub use self::groovebox_audio::*;
mod groovebox_command; pub use self::groovebox_command::*;
mod groovebox_tui; pub use self::groovebox_tui::*;
mod groovebox_edn; pub use self::groovebox_edn::*;
use crate::*;
use super::*;
use KeyCode::{Char, Delete, Tab, Up, Down, Left, Right};
@ -6,10 +11,6 @@ use GrooveboxCommand as Cmd;
use MidiEditCommand::*;
use PhrasePoolCommand::*;
mod groovebox_audio; pub use self::groovebox_audio::*;
mod groovebox_command; pub use self::groovebox_command::*;
mod groovebox_tui; pub use self::groovebox_tui::*;
pub struct Groovebox {
_jack: Arc<RwLock<JackConnection>>,
pub player: MidiPlayer,
@ -24,7 +25,7 @@ pub struct Groovebox {
pub midi_buf: Vec<Vec<Vec<u8>>>,
pub perf: PerfModel,
}
has_clock!(|self: Groovebox|self.player.clock());
impl Groovebox {
pub fn new (
jack: &Arc<RwLock<JackConnection>>,
@ -59,91 +60,3 @@ impl Groovebox {
}
}
has_clock!(|self: Groovebox|self.player.clock());
impl EdnViewData<TuiOut> for &Groovebox {
fn get_bool (&self, item: EdnItem<&str>) -> bool { todo!() }
fn get_unit (&self, item: EdnItem<&str>) -> u16 {
use EdnItem::*;
match item.to_str() {
":sample-h" => if self.compact { 0 } else { 5 },
":samples-w" => if self.compact { 4 } else { 11 },
":samples-y" => if self.compact { 1 } else { 0 },
":pool-w" => if self.compact { 5 } else {
let w = self.size.w();
if w > 60 { 20 } else if w > 40 { 15 } else { 10 }
},
_ => 0
}
}
fn get_content <'a> (&'a self, item: EdnItem<&str>) -> RenderBox<'a, TuiOut> {
use EdnItem::*;
match item.to_str() {
":input-meter-l" => Box::new(Meter("L/", self.sampler.input_meter[0])),
":input-meter-r" => Box::new(Meter("R/", self.sampler.input_meter[1])),
":transport" => Box::new(TransportView::new(true, &self.player.clock)),
":clip-play" => Box::new(ClipSelected::play_phrase(&self.player)),
":clip-next" => Box::new(ClipSelected::next_phrase(&self.player)),
":clip-edit" => Box::new(MidiEditClip(&self.editor)),
":edit-stat" => Box::new(MidiEditStatus(&self.editor)),
":pool-view" => Box::new(PoolView(self.compact, &self.pool)),
":midi-view" => Box::new(&self.editor),
":sample-view" => Box::new(SampleViewer::from_sampler(&self.sampler, self.editor.note_point())),
":sample-stat" => Box::new(SamplerStatus(&self.sampler, self.editor.note_point())),
":samples-view" => Box::new(SampleList::new(self.compact, &self.sampler, &self.editor)),
_ => panic!("{item:?}")
}
}
}
/// Status bar for sequencer app
#[derive(Clone)]
pub struct GrooveboxStatus {
pub(crate) width: usize,
pub(crate) cpu: Option<String>,
pub(crate) size: String,
pub(crate) playing: bool,
}
from!(|state: &Groovebox|GrooveboxStatus = {
let samples = state.clock().chunk.load(Relaxed);
let rate = state.clock().timebase.sr.get();
let buffer = samples as f64 / rate;
let width = state.size.w();
Self {
width,
playing: state.clock().is_rolling(),
cpu: state.perf.percentage().map(|cpu|format!("{cpu:.01}%")),
size: format!("{}x{}│", width, state.size.h()),
}
});
render!(TuiOut: (self: GrooveboxStatus) => Fixed::y(2, lay!(
Self::help(),
Fill::xy(Align::se(Tui::fg_bg(TuiTheme::orange(), TuiTheme::g(25), self.stats()))),
)));
impl GrooveboxStatus {
fn help () -> impl Content<TuiOut> {
let single = |binding, command|row!(" ", col!(
Tui::fg(TuiTheme::yellow(), binding),
command
));
let double = |(b1, c1), (b2, c2)|col!(
row!(" ", Tui::fg(TuiTheme::yellow(), b1), " ", c1,),
row!(" ", Tui::fg(TuiTheme::yellow(), b2), " ", c2,),
);
Tui::fg_bg(TuiTheme::g(255), TuiTheme::g(50), row!(
single("SPACE", "play/pause"),
double(("▲▼▶◀", "cursor"), ("Ctrl", "scroll"), ),
double(("a", "append"), ("s", "set note"),),
double((",.", "length"), ("<>", "triplet"), ),
double(("[]", "phrase"), ("{}", "order"), ),
double(("q", "enqueue"), ("e", "edit"), ),
double(("c", "color"), ("", ""),),
))
}
fn stats (&self) -> impl Content<TuiOut> + use<'_> {
row!(&self.cpu, &self.size)
}
}

View file

@ -0,0 +1,44 @@
use crate::*;
impl EdnViewData<TuiOut> for &Groovebox {
fn get_unit (&self, item: EdnItem<&str>) -> u16 {
use EdnItem::*;
match item.to_str() {
":sample-h" => if self.compact { 0 } else { 5 },
":samples-w" => if self.compact { 4 } else { 11 },
":samples-y" => if self.compact { 1 } else { 0 },
":pool-w" => if self.compact { 5 } else {
let w = self.size.w();
if w > 60 { 20 } else if w > 40 { 15 } else { 10 }
},
_ => 0
}
}
fn get_content <'a> (&'a self, item: EdnItem<&str>) -> RenderBox<'a, TuiOut> {
use EdnItem::*;
match item {
Nil => Box::new(()),
Sym(bol) => match bol {
":input-meter-l" => Meter("L/", self.sampler.input_meter[0]).boxed(),
":input-meter-r" => Box::new(Meter("R/", self.sampler.input_meter[1])),
":transport" => Box::new(TransportView::new(true, &self.player.clock)),
":clip-play" => Box::new(ClipSelected::play_phrase(&self.player)),
":clip-next" => Box::new(ClipSelected::next_phrase(&self.player)),
":clip-edit" => Box::new(MidiEditClip(&self.editor)),
":edit-stat" => Box::new(MidiEditStatus(&self.editor)),
":pool-view" => Box::new(PoolView(self.compact, &self.pool)),
":midi-view" => Box::new(&self.editor),
":sample-view" => Box::new(SampleViewer::from_sampler(&self.sampler, self.editor.note_point())),
":sample-stat" => Box::new(SamplerStatus(&self.sampler, self.editor.note_point())),
":samples-view" => Box::new(SampleList::new(self.compact, &self.sampler, &self.editor)),
_ => panic!("unknown sym {bol:?}")
},
Exp(items) => Box::new(EdnView::from_items(*self, items.as_slice())),
_ => panic!("no content for {item:?}")
}
}
}

View file

@ -8,10 +8,11 @@ const EDN: &'static str = include_str!("groovebox.edn");
impl Content<TuiOut> for Groovebox {
fn content (&self) -> impl Render<TuiOut> {
self.size.of(EdnView::new(self, EDN))
self.size.of(EdnView::from_source(self, EDN))
}
}
// this works:
//render!(TuiOut: (self: Groovebox) => self.size.of(
//Bsp::s(self.toolbar_view(),
//Bsp::n(self.selector_view(),
@ -59,6 +60,54 @@ impl Groovebox {
}
}
///// Status bar for sequencer app
//#[derive(Clone)]
//pub struct GrooveboxStatus {
//pub(crate) width: usize,
//pub(crate) cpu: Option<String>,
//pub(crate) size: String,
//pub(crate) playing: bool,
//}
//from!(|state: &Groovebox|GrooveboxStatus = {
//let samples = state.clock().chunk.load(Relaxed);
//let rate = state.clock().timebase.sr.get();
//let buffer = samples as f64 / rate;
//let width = state.size.w();
//Self {
//width,
//playing: state.clock().is_rolling(),
//cpu: state.perf.percentage().map(|cpu|format!("│{cpu:.01}%")),
//size: format!("{}x{}│", width, state.size.h()),
//}
//});
//render!(TuiOut: (self: GrooveboxStatus) => Fixed::y(2, lay!(
//Self::help(),
//Fill::xy(Align::se(Tui::fg_bg(TuiTheme::orange(), TuiTheme::g(25), self.stats()))),
//)));
//impl GrooveboxStatus {
//fn help () -> impl Content<TuiOut> {
//let single = |binding, command|row!(" ", col!(
//Tui::fg(TuiTheme::yellow(), binding),
//command
//));
//let double = |(b1, c1), (b2, c2)|col!(
//row!(" ", Tui::fg(TuiTheme::yellow(), b1), " ", c1,),
//row!(" ", Tui::fg(TuiTheme::yellow(), b2), " ", c2,),
//);
//Tui::fg_bg(TuiTheme::g(255), TuiTheme::g(50), row!(
//single("SPACE", "play/pause"),
//double(("▲▼▶◀", "cursor"), ("Ctrl", "scroll"), ),
//double(("a", "append"), ("s", "set note"),),
//double((",.", "length"), ("<>", "triplet"), ),
//double(("[]", "phrase"), ("{}", "order"), ),
//double(("q", "enqueue"), ("e", "edit"), ),
//double(("c", "color"), ("", ""),),
//))
//}
//fn stats (&self) -> impl Content<TuiOut> + use<'_> {
//row!(&self.cpu, &self.size)
//}
//}
//render!(TuiOut: (self: Groovebox) => self.size.of(
//Bsp::s(self.toolbar_view(),
//Bsp::n(self.selector_view(),