mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 04:06:45 +01:00
wip: unify apps
This commit is contained in:
parent
a9cad18891
commit
cff87657b9
12 changed files with 291 additions and 247 deletions
157
tek/src/app.rs
Normal file
157
tek/src/app.rs
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
use crate::*;
|
||||
#[derive(Default)]
|
||||
pub struct App {
|
||||
//jack: Arc<RwLock<JackConnection>>,
|
||||
//view: EdnView<TuiOut, &'a Self>,
|
||||
//pool: Option<PoolModel>,
|
||||
//editor: Option<MidiEditor>,
|
||||
//player: Option<MidiPlayer>,
|
||||
//compact: AtomicBool,
|
||||
//size: Measure<TuiOut>,
|
||||
//perf: PerfModel,
|
||||
//note_buf: Vec<u8>,
|
||||
//midi_buf: Vec<Vec<Vec<u8>>>
|
||||
|
||||
pub jack: Arc<RwLock<JackConnection>>,
|
||||
pub edn: String,
|
||||
pub clock: Clock,
|
||||
pub color: ItemPalette,
|
||||
pub editing: AtomicBool,
|
||||
pub pool: Option<PoolModel>,
|
||||
pub editor: Option<MidiEditor>,
|
||||
pub player: Option<MidiPlayer>,
|
||||
pub sampler: Option<Sampler>,
|
||||
pub midi_buf: Vec<Vec<Vec<u8>>>,
|
||||
pub midi_ins: Vec<JackPort<MidiIn>>,
|
||||
pub midi_outs: Vec<JackPort<MidiOut>>,
|
||||
pub audio_ins: Vec<JackPort<AudioIn>>,
|
||||
pub audio_outs: Vec<JackPort<AudioOut>>,
|
||||
pub note_buf: Vec<u8>,
|
||||
pub tracks: Vec<ArrangerTrack>,
|
||||
pub scenes: Vec<ArrangerScene>,
|
||||
pub selected: ArrangerSelection,
|
||||
pub splits: Vec<u16>,
|
||||
pub size: Measure<TuiOut>,
|
||||
pub perf: PerfModel,
|
||||
}
|
||||
impl EdnViewData<TuiOut> for &App {}
|
||||
impl App {
|
||||
pub fn sequencer (
|
||||
jack: &Arc<RwLock<JackConnection>>,
|
||||
pool: PoolModel,
|
||||
editor: MidiEditor,
|
||||
player: Option<MidiPlayer>,
|
||||
midi_froms: &[PortConnection],
|
||||
midi_tos: &[PortConnection],
|
||||
) -> Self {
|
||||
Self {
|
||||
edn: include_str!("sequencer.edn").to_string(),
|
||||
jack: jack.clone(),
|
||||
pool: Some(pool),
|
||||
editor: Some(editor),
|
||||
player: player,
|
||||
editing: false.into(),
|
||||
midi_buf: vec![vec![];65536],
|
||||
color: ItemPalette::random(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
pub fn groovebox (
|
||||
jack: &Arc<RwLock<JackConnection>>,
|
||||
pool: PoolModel,
|
||||
editor: MidiEditor,
|
||||
player: Option<MidiPlayer>,
|
||||
midi_froms: &[PortConnection],
|
||||
midi_tos: &[PortConnection],
|
||||
sampler: Sampler,
|
||||
audio_froms: &[&[PortConnection]],
|
||||
audio_tos: &[&[PortConnection]],
|
||||
) -> Self {
|
||||
Self {
|
||||
edn: include_str!("groovebox.edn").to_string(),
|
||||
sampler: Some(sampler),
|
||||
..Self::sequencer(
|
||||
jack, pool, editor,
|
||||
player, midi_froms, midi_tos
|
||||
)
|
||||
}
|
||||
}
|
||||
pub fn arranger (
|
||||
jack: &Arc<RwLock<JackConnection>>,
|
||||
pool: PoolModel,
|
||||
editor: MidiEditor,
|
||||
midi_froms: &[PortConnection],
|
||||
midi_tos: &[PortConnection],
|
||||
sampler: Sampler,
|
||||
audio_froms: &[&[PortConnection]],
|
||||
audio_tos: &[&[PortConnection]],
|
||||
scenes: usize,
|
||||
tracks: usize,
|
||||
track_width: usize,
|
||||
) -> Self {
|
||||
Self {
|
||||
edn: include_str!("arranger.edn").to_string(),
|
||||
..Self::groovebox(
|
||||
jack, pool, editor,
|
||||
None, midi_froms, midi_tos,
|
||||
sampler, audio_froms, audio_tos
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
render!(TuiOut: (self: App) => self.size.of(EdnView::from_source(self, self.edn.as_ref())));
|
||||
|
||||
/// Root view for standalone `tek_sequencer`.
|
||||
pub struct Sequencer {
|
||||
pub _jack: Arc<RwLock<JackConnection>>,
|
||||
|
||||
pub pool: PoolModel,
|
||||
pub editor: MidiEditor,
|
||||
pub player: MidiPlayer,
|
||||
|
||||
pub transport: bool,
|
||||
pub selectors: bool,
|
||||
pub compact: bool,
|
||||
|
||||
pub size: Measure<TuiOut>,
|
||||
pub status: bool,
|
||||
pub note_buf: Vec<u8>,
|
||||
pub midi_buf: Vec<Vec<Vec<u8>>>,
|
||||
pub perf: PerfModel,
|
||||
}
|
||||
|
||||
pub struct Groovebox {
|
||||
pub _jack: Arc<RwLock<JackConnection>>,
|
||||
pub player: MidiPlayer,
|
||||
pub pool: PoolModel,
|
||||
pub editor: MidiEditor,
|
||||
pub sampler: Sampler,
|
||||
|
||||
pub compact: bool,
|
||||
pub size: Measure<TuiOut>,
|
||||
pub status: bool,
|
||||
pub note_buf: Vec<u8>,
|
||||
pub midi_buf: Vec<Vec<Vec<u8>>>,
|
||||
pub perf: PerfModel,
|
||||
}
|
||||
|
||||
/// Root view for standalone `tek_arranger`
|
||||
pub struct Arranger {
|
||||
pub jack: Arc<RwLock<JackConnection>>,
|
||||
pub midi_ins: Vec<JackPort<MidiIn>>,
|
||||
pub midi_outs: Vec<JackPort<MidiOut>>,
|
||||
pub clock: Clock,
|
||||
pub pool: PoolModel,
|
||||
pub tracks: Vec<ArrangerTrack>,
|
||||
pub scenes: Vec<ArrangerScene>,
|
||||
pub splits: [u16;2],
|
||||
pub selected: ArrangerSelection,
|
||||
pub color: ItemPalette,
|
||||
pub size: Measure<TuiOut>,
|
||||
pub note_buf: Vec<u8>,
|
||||
pub midi_buf: Vec<Vec<Vec<u8>>>,
|
||||
pub editor: MidiEditor,
|
||||
pub editing: AtomicBool,
|
||||
pub perf: PerfModel,
|
||||
pub compact: bool,
|
||||
}
|
||||
|
|
@ -5,26 +5,6 @@ mod arranger_track; pub use self::arranger_track::*;
|
|||
mod arranger_h;
|
||||
use ClockCommand::{Play, Pause};
|
||||
use self::ArrangerCommand as Cmd;
|
||||
/// Root view for standalone `tek_arranger`
|
||||
pub struct Arranger {
|
||||
pub jack: Arc<RwLock<JackConnection>>,
|
||||
pub midi_ins: Vec<JackPort<MidiIn>>,
|
||||
pub midi_outs: Vec<JackPort<MidiOut>>,
|
||||
pub clock: Clock,
|
||||
pub pool: PoolModel,
|
||||
pub tracks: Vec<ArrangerTrack>,
|
||||
pub scenes: Vec<ArrangerScene>,
|
||||
pub splits: [u16;2],
|
||||
pub selected: ArrangerSelection,
|
||||
pub color: ItemPalette,
|
||||
pub size: Measure<TuiOut>,
|
||||
pub note_buf: Vec<u8>,
|
||||
pub midi_buf: Vec<Vec<Vec<u8>>>,
|
||||
pub editor: MidiEditor,
|
||||
pub editing: AtomicBool,
|
||||
pub perf: PerfModel,
|
||||
pub compact: bool,
|
||||
}
|
||||
render!(TuiOut: (self: Arranger) => self.size.of(EdnView::from_source(self, Self::EDN)));
|
||||
impl EdnViewData<TuiOut> for &Arranger {
|
||||
fn get_content <'a> (&'a self, item: EdnItem<&'a str>) -> RenderBox<'a, TuiOut> {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use crate::*;
|
||||
#[derive(PartialEq, Clone, Copy, Debug)]
|
||||
#[derive(PartialEq, Clone, Copy, Debug, Default)]
|
||||
/// Represents the current user selection in the arranger
|
||||
pub enum ArrangerSelection {
|
||||
/// The whole mix is selected
|
||||
Mix,
|
||||
#[default] Mix,
|
||||
/// A track is selected.
|
||||
Track(usize),
|
||||
/// A scene is selected.
|
||||
|
|
|
|||
|
|
@ -7,28 +7,7 @@ use MidiEditCommand::*;
|
|||
use MidiPoolCommand::*;
|
||||
use KeyCode::{Char, Delete, Tab, Up, Down, Left, Right};
|
||||
use std::marker::ConstParamTy;
|
||||
pub struct Groovebox {
|
||||
pub _jack: Arc<RwLock<JackConnection>>,
|
||||
pub player: MidiPlayer,
|
||||
pub pool: PoolModel,
|
||||
pub editor: MidiEditor,
|
||||
pub sampler: Sampler,
|
||||
|
||||
pub compact: bool,
|
||||
pub size: Measure<TuiOut>,
|
||||
pub status: bool,
|
||||
pub note_buf: Vec<u8>,
|
||||
pub midi_buf: Vec<Vec<Vec<u8>>>,
|
||||
pub perf: PerfModel,
|
||||
}
|
||||
render!(TuiOut: (self: Groovebox) => self.size.of(EdnView::from_source(self, Self::EDN)));
|
||||
// this works:
|
||||
//render!(TuiOut: (self: Groovebox) => self.size.of(
|
||||
//Bsp::s(self.toolbar_view(),
|
||||
//Bsp::n(self.selector_view(),
|
||||
//Bsp::n(self.sample_view(),
|
||||
//Bsp::n(self.status_view(),
|
||||
//Bsp::w(self.pool_view(), Fill::xy(Bsp::e(self.sampler_view(), &self.editor)))))))));
|
||||
impl EdnViewData<TuiOut> for &Groovebox {
|
||||
fn get_content <'a> (&'a self, item: EdnItem<&'a str>) -> RenderBox<'a, TuiOut> {
|
||||
use EdnItem::*;
|
||||
|
|
@ -36,11 +15,11 @@ impl EdnViewData<TuiOut> for &Groovebox {
|
|||
Nil => Box::new(()),
|
||||
Exp(items) => Box::new(EdnView::from_items(*self, items.as_slice())),
|
||||
Sym(":editor") => (&self.editor).boxed(),
|
||||
Sym(":pool") => self.pool_view().boxed(),
|
||||
Sym(":status") => self.status_view().boxed(),
|
||||
Sym(":toolbar") => self.toolbar_view().boxed(),
|
||||
Sym(":sampler") => self.sampler_view().boxed(),
|
||||
Sym(":sample") => self.sample_view().boxed(),
|
||||
Sym(":pool") => self.pool().boxed(),
|
||||
Sym(":status") => self.status().boxed(),
|
||||
Sym(":toolbar") => self.toolbar().boxed(),
|
||||
Sym(":sampler") => self.sampler().boxed(),
|
||||
Sym(":sample") => self.sample().boxed(),
|
||||
_ => panic!("no content for {item:?}")
|
||||
}
|
||||
}
|
||||
|
|
@ -60,14 +39,14 @@ impl EdnViewData<TuiOut> for &Groovebox {
|
|||
}
|
||||
impl Groovebox {
|
||||
const EDN: &'static str = include_str!("groovebox.edn");
|
||||
fn toolbar_view (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
fn toolbar (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
Fill::x(Fixed::y(2, lay!(
|
||||
Fill::x(Align::w(Meter("L/", self.sampler.input_meter[0]))),
|
||||
Fill::x(Align::e(Meter("R/", self.sampler.input_meter[1]))),
|
||||
Align::x(TransportView::new(true, &self.player.clock)),
|
||||
)))
|
||||
}
|
||||
fn status_view (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
fn status (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
row!(
|
||||
self.player.play_status(),
|
||||
self.player.next_status(),
|
||||
|
|
@ -75,7 +54,7 @@ impl Groovebox {
|
|||
self.editor.edit_status(),
|
||||
)
|
||||
}
|
||||
fn sample_view (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
fn sample (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
let note_pt = self.editor.note_point();
|
||||
let sample_h = if self.compact { 0 } else { 5 };
|
||||
Max::y(sample_h, Fill::xy(
|
||||
|
|
@ -83,21 +62,19 @@ impl Groovebox {
|
|||
Fill::x(Align::w(Fixed::y(1, self.sampler.status(note_pt)))),
|
||||
self.sampler.viewer(note_pt))))
|
||||
}
|
||||
fn pool_view (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
fn pool (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
let w = self.size.w();
|
||||
let pool_w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 };
|
||||
Fixed::x(if self.compact { 5 } else { pool_w },
|
||||
PoolView(self.compact, &self.pool))
|
||||
}
|
||||
fn sampler_view (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
fn sampler (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
let note_pt = self.editor.note_point();
|
||||
let sampler_w = if self.compact { 4 } else { 40 };
|
||||
let sampler_y = if self.compact { 1 } else { 0 };
|
||||
Fixed::x(sampler_w, Push::y(sampler_y, Fill::y(
|
||||
SampleList::new(self.compact, &self.sampler, &self.editor))))
|
||||
Fixed::x(sampler_w, Push::y(sampler_y, Fill::y(self.sampler.list(self.compact, &self.editor))))
|
||||
}
|
||||
}
|
||||
|
||||
audio!(|self: Groovebox, client, scope|{
|
||||
let t0 = self.perf.get_t0();
|
||||
if Control::Quit == ClockAudio(&mut self.player).process(client, scope) {
|
||||
|
|
@ -130,7 +107,6 @@ audio!(|self: Groovebox, client, scope|{
|
|||
self.perf.update(t0, scope);
|
||||
Control::Continue
|
||||
});
|
||||
|
||||
has_clock!(|self: Groovebox|self.player.clock());
|
||||
pub enum GrooveboxCommand {
|
||||
Compact(bool),
|
||||
|
|
@ -141,7 +117,6 @@ pub enum GrooveboxCommand {
|
|||
Enqueue(Option<Arc<RwLock<MidiClip>>>),
|
||||
Sampler(SamplerCommand),
|
||||
}
|
||||
|
||||
command!(|self: GrooveboxCommand, state: Groovebox|match self {
|
||||
Self::Enqueue(clip) => {
|
||||
state.player.enqueue_next(clip.as_ref());
|
||||
|
|
@ -173,10 +148,7 @@ command!(|self: GrooveboxCommand, state: Groovebox|match self {
|
|||
None
|
||||
},
|
||||
});
|
||||
|
||||
handle!(TuiIn: |self: Groovebox, input|
|
||||
GrooveboxCommand::execute_with_state(self, input.event()));
|
||||
|
||||
handle!(TuiIn: |self: Groovebox, input|GrooveboxCommand::execute_with_state(self, input.event()));
|
||||
keymap!(<'a> KEYS_GROOVEBOX = |state: Groovebox, input: Event| GrooveboxCommand {
|
||||
// Tab: Toggle compact mode
|
||||
key(Tab) => Cmd::Compact(!state.compact),
|
||||
|
|
@ -268,21 +240,6 @@ keymap!(<'a> KEYS_GROOVEBOX = |state: Groovebox, input: Event| GrooveboxCommand
|
|||
//row!(&self.cpu, &self.size)
|
||||
//}
|
||||
//}
|
||||
//render!(TuiOut: (self: Groovebox) => self.size.of(
|
||||
//Bsp::s(self.toolbar_view(),
|
||||
//Bsp::n(self.selector_view(),
|
||||
//Bsp::n(self.sample_view(),
|
||||
//Bsp::n(self.status_view(),
|
||||
//Bsp::w(self.pool_view(), Fill::xy(Bsp::e(self.sampler_view(), &self.editor)))))))));
|
||||
|
||||
//const GROOVEBOX_EDN: &'static str = include_str!("groovebox.edn");
|
||||
|
||||
//impl Content<TuiOut> for Groovebox {
|
||||
//fn content (&self) -> impl Content<TuiOut> {
|
||||
//EdnView::parse(self.edn.as_slice())
|
||||
//}
|
||||
//}
|
||||
|
||||
//macro_rules! edn_context {
|
||||
//($Struct:ident |$l:lifetime, $state:ident| {
|
||||
//$($key:literal = $field:ident: $Type:ty => $expr:expr,)*
|
||||
|
|
@ -328,17 +285,17 @@ keymap!(<'a> KEYS_GROOVEBOX = |state: Groovebox, input: Event| GrooveboxCommand
|
|||
//}
|
||||
|
||||
////impl Groovebox {
|
||||
////fn status_view (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
////fn status (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
////let note_pt = self.editor.note_point();
|
||||
////Align::w(Fixed::y(1, ))
|
||||
////}
|
||||
////fn pool_view (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
////fn pool (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
////let w = self.size.w();
|
||||
////let pool_w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 };
|
||||
////Fixed::x(if self.compact { 5 } else { pool_w },
|
||||
////)
|
||||
////}
|
||||
////fn sampler_view (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
////fn sampler (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
////let sampler_w = if self.compact { 4 } else { 11 };
|
||||
////let sampler_y = if self.compact { 1 } else { 0 };
|
||||
////Fixed::x(sampler_w, Push::y(sampler_y, Fill::y(
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ pub type Usually<T> = std::result::Result<T, Box<dyn Error>>;
|
|||
/// Standard optional result type.
|
||||
pub type Perhaps<T> = std::result::Result<Option<T>, Box<dyn Error>>;
|
||||
|
||||
pub mod app; pub use self::app::*;
|
||||
pub mod arranger; pub use self::arranger::*;
|
||||
pub mod groovebox; pub use self::groovebox::*;
|
||||
pub mod mixer; pub use self::mixer::*;
|
||||
|
|
|
|||
|
|
@ -4,24 +4,6 @@ use KeyCode::{Tab, Char};
|
|||
use SequencerCommand as Cmd;
|
||||
use MidiEditCommand::*;
|
||||
use MidiPoolCommand::*;
|
||||
/// Root view for standalone `tek_sequencer`.
|
||||
pub struct Sequencer {
|
||||
pub _jack: Arc<RwLock<JackConnection>>,
|
||||
|
||||
pub pool: PoolModel,
|
||||
pub editor: MidiEditor,
|
||||
pub player: MidiPlayer,
|
||||
|
||||
pub transport: bool,
|
||||
pub selectors: bool,
|
||||
pub compact: bool,
|
||||
|
||||
pub size: Measure<TuiOut>,
|
||||
pub status: bool,
|
||||
pub note_buf: Vec<u8>,
|
||||
pub midi_buf: Vec<Vec<Vec<u8>>>,
|
||||
pub perf: PerfModel,
|
||||
}
|
||||
render!(TuiOut: (self: Sequencer) => self.size.of(EdnView::from_source(self, Self::EDN)));
|
||||
impl EdnViewData<TuiOut> for &Sequencer {
|
||||
fn get_content <'a> (&'a self, item: EdnItem<&'a str>) -> RenderBox<'a, TuiOut> {
|
||||
|
|
@ -179,34 +161,3 @@ command!(|self: SequencerCommand, state: Sequencer|match self {
|
|||
None
|
||||
},
|
||||
});
|
||||
|
||||
/// Status bar for sequencer app
|
||||
#[derive(Clone)]
|
||||
pub struct SequencerStatus {
|
||||
pub(crate) width: usize,
|
||||
pub(crate) cpu: Option<Arc<str>>,
|
||||
pub(crate) size: Arc<str>,
|
||||
pub(crate) playing: bool,
|
||||
}
|
||||
from!(|state:&Sequencer|SequencerStatus = {
|
||||
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}%").into()),
|
||||
size: format!("{}x{}│", width, state.size.h()).into(),
|
||||
}
|
||||
});
|
||||
render!(TuiOut: (self: SequencerStatus) => Fixed::y(2, lay!(
|
||||
Sequencer::help(),
|
||||
Fill::xy(Align::se(Tui::fg_bg(TuiTheme::orange(), TuiTheme::g(25), self.stats()))),
|
||||
)));
|
||||
impl SequencerStatus {
|
||||
fn stats (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
row!(&self.cpu, &self.size)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue