mirror of
https://codeberg.org/unspeaker/tek.git
synced 2026-09-18 12:56:42 +02:00
Compare commits
2 commits
4aefaf452c
...
ae496987c8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae496987c8 | ||
|
|
68236a7210 |
10 changed files with 610 additions and 297 deletions
10
Cargo.lock
generated
10
Cargo.lock
generated
|
|
@ -3145,6 +3145,7 @@ dependencies = [
|
||||||
"rand 0.8.7",
|
"rand 0.8.7",
|
||||||
"symphonia",
|
"symphonia",
|
||||||
"tek",
|
"tek",
|
||||||
|
"tek_proc",
|
||||||
"tengri",
|
"tengri",
|
||||||
"toml 0.9.12+spec-1.1.0",
|
"toml 0.9.12+spec-1.1.0",
|
||||||
"uuid",
|
"uuid",
|
||||||
|
|
@ -3153,6 +3154,15 @@ dependencies = [
|
||||||
"xdg",
|
"xdg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tek_proc"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.119",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.27.0"
|
version = "3.27.0"
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,8 @@ path = "src/tek.rs"
|
||||||
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
|
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tengri = { path = "./tengri", features = [ "term", "lang" ] }
|
tek_proc = { path = "./proc" }
|
||||||
|
tengri = { path = "./tengri", features = [ "term", "lang" ] }
|
||||||
|
|
||||||
ansi_term = { version = "0.12.1" }
|
ansi_term = { version = "0.12.1" }
|
||||||
atomic_float = { version = "1.0.0" }
|
atomic_float = { version = "1.0.0" }
|
||||||
|
|
|
||||||
11
proc/Cargo.toml
Normal file
11
proc/Cargo.toml
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
[package]
|
||||||
|
name = "tek_proc"
|
||||||
|
description = "Shorthands for implementing Tek commands."
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
||||||
|
[dependencies]
|
||||||
|
proc-macro2 = "1.0.106"
|
||||||
|
quote = "1.0.46"
|
||||||
|
syn = { version = "2.0.119", features = ["full", "extra-traits"] }
|
||||||
189
proc/src/lib.rs
Normal file
189
proc/src/lib.rs
Normal file
|
|
@ -0,0 +1,189 @@
|
||||||
|
use proc_macro::{TokenStream, Literal};
|
||||||
|
use proc_macro2::{TokenStream as TokenStream2, Span};
|
||||||
|
use quote::{quote, ToTokens, TokenStreamExt};
|
||||||
|
use std::collections::{HashMap, BTreeMap};
|
||||||
|
use syn::{
|
||||||
|
Path, ItemEnum, Ident, Type, Variant, MetaList, Expr, ExprPath, ExprBinary, Fields, BinOp,
|
||||||
|
parse::{ParseStream, Parse, Result}
|
||||||
|
};
|
||||||
|
|
||||||
|
macro_rules! attribute {
|
||||||
|
($name:ident { $($body:tt)* }) => {
|
||||||
|
#[proc_macro_attribute] pub fn $name (meta: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
|
write(self::$name::Def(
|
||||||
|
syn::parse_macro_input!(meta as self::$name::Meta),
|
||||||
|
syn::parse_macro_input!(item as self::$name::Item),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
mod $name {
|
||||||
|
use crate::*;
|
||||||
|
$($body)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attribute!(command {
|
||||||
|
#[derive(Debug, Clone)] pub struct Def(
|
||||||
|
pub Meta, pub Item
|
||||||
|
);
|
||||||
|
#[derive(Debug, Clone)] pub struct Meta(
|
||||||
|
pub Path
|
||||||
|
);
|
||||||
|
#[derive(Debug, Clone)] pub struct Item(
|
||||||
|
pub ItemEnum, pub HashMap<Ident, (Fields, Expr)>
|
||||||
|
);
|
||||||
|
impl Parse for Meta {
|
||||||
|
fn parse (input: ParseStream) -> Result<Self> {
|
||||||
|
Ok(Self(input.parse()?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Parse for Item {
|
||||||
|
fn parse (input: ParseStream) -> Result<Self> {
|
||||||
|
let mut item: ItemEnum = input.parse()?;
|
||||||
|
let mut branches: HashMap<Ident, (Fields, Expr)> = Default::default();
|
||||||
|
let mut variants_filtered = item.variants.clone();
|
||||||
|
variants_filtered.clear();
|
||||||
|
for variant in item.variants.iter_mut() {
|
||||||
|
let Variant { attrs, ident, fields, discriminant } = variant;
|
||||||
|
let mut attrs_filtered = attrs.clone();
|
||||||
|
attrs_filtered.clear();
|
||||||
|
for attr in attrs.iter() {
|
||||||
|
if let syn::Meta::List(MetaList { ref path, ref tokens, .. }) = attr.meta
|
||||||
|
&& path == &Path::from(Ident::new("command", Span::call_site()))
|
||||||
|
&& let Ok(handler) = syn::parse2::<Expr>(tokens.clone())
|
||||||
|
{
|
||||||
|
branches.insert(ident.clone(), (fields.clone(), handler));
|
||||||
|
} else {
|
||||||
|
attrs_filtered.push(attr.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*attrs = attrs_filtered;
|
||||||
|
variants_filtered.push(variant.clone())
|
||||||
|
}
|
||||||
|
item.variants = variants_filtered;
|
||||||
|
Ok(Self(item, branches))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ToTokens for Def {
|
||||||
|
fn to_tokens (&self, out: &mut TokenStream2) {
|
||||||
|
let Self(Meta(state), Item(item, branches)) = self;
|
||||||
|
let ident = &item.ident;
|
||||||
|
let mut body = quote! {};
|
||||||
|
for (variant, (fields, handler)) in branches.iter() {
|
||||||
|
let (handler, wrapper) = if let Expr::Path(ExprPath { path, .. }) = handler {
|
||||||
|
(path, None)
|
||||||
|
} else if let Expr::Binary(ExprBinary {
|
||||||
|
op: BinOp::BitOr(_), left, right, ..
|
||||||
|
}) = handler && let Expr::Path(ExprPath {
|
||||||
|
path: handler, ..
|
||||||
|
}) = &**left && let Expr::Path(ExprPath {
|
||||||
|
path: wrapper, ..
|
||||||
|
}) = &**right {
|
||||||
|
(handler, Some(wrapper))
|
||||||
|
} else {
|
||||||
|
panic!()
|
||||||
|
};
|
||||||
|
match fields {
|
||||||
|
Fields::Named(fields) => todo!("named command fields"),
|
||||||
|
Fields::Unnamed(fields) => {
|
||||||
|
let mut params = quote! {};
|
||||||
|
let mut values = quote! {};
|
||||||
|
for (index, _field) in fields.unnamed.iter().enumerate() {
|
||||||
|
let name = Ident::new(&format!("arg{index}"), Span::call_site());
|
||||||
|
append(&mut params, quote! { #name, });
|
||||||
|
append(&mut values, quote! { #name, });
|
||||||
|
}
|
||||||
|
let invocation = if let Some(wrapper) = wrapper {
|
||||||
|
quote! { Ok(Some(#wrapper(#handler(state, #values)))) }
|
||||||
|
} else {
|
||||||
|
quote! { #handler(state, #values) }
|
||||||
|
};
|
||||||
|
append(&mut body, quote! {
|
||||||
|
#ident::#variant (#params) => #invocation,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
append(&mut body, quote! {
|
||||||
|
#ident::#variant => #handler(state),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
append(out, quote! {
|
||||||
|
#item
|
||||||
|
|
||||||
|
impl dizzle::Act<#state> for #ident {
|
||||||
|
fn act (&self, state: &mut #state) -> Perhaps<Self> {
|
||||||
|
match self {
|
||||||
|
#body
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
attribute!(keyword {
|
||||||
|
#[derive(Debug, Clone)] pub struct Def(
|
||||||
|
pub Meta, pub Item
|
||||||
|
);
|
||||||
|
#[derive(Debug, Clone)] pub struct Meta(
|
||||||
|
pub Path
|
||||||
|
);
|
||||||
|
#[derive(Debug, Clone)] pub struct Item(
|
||||||
|
pub ItemEnum, pub BTreeMap<Ident, Vec<(Literal, BTreeMap<Ident, Type>)>>
|
||||||
|
);
|
||||||
|
impl Parse for Meta {
|
||||||
|
fn parse (input: ParseStream) -> Result<Self> {
|
||||||
|
Ok(Self(input.parse()?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Parse for Item {
|
||||||
|
fn parse (input: ParseStream) -> Result<Self> {
|
||||||
|
let mut item: ItemEnum = input.parse()?;
|
||||||
|
for Variant { attrs, ident, fields, discriminant } in item.variants.iter_mut() {
|
||||||
|
attrs.retain(|attr|if let syn::Meta::List(MetaList {
|
||||||
|
ref path, ref tokens, ..
|
||||||
|
}) = attr.meta && path == &Path::from(Ident::new(
|
||||||
|
"keyword", Span::call_site()
|
||||||
|
)) {
|
||||||
|
// TODO
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(Self(item, Default::default()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ToTokens for Def {
|
||||||
|
fn to_tokens (&self, out: &mut TokenStream2) {
|
||||||
|
let Self(Meta(state), Item(item, variants)) = self;
|
||||||
|
let ident = &item.ident;
|
||||||
|
let body = quote! {};
|
||||||
|
append(out, quote! {
|
||||||
|
#item
|
||||||
|
|
||||||
|
impl<'a> Namespace<'a, #ident> for #state {
|
||||||
|
symbols!('a |stte| -> #ident {
|
||||||
|
#body,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
fn write <T: ToTokens> (t: T) -> TokenStream {
|
||||||
|
let mut out = TokenStream2::new();
|
||||||
|
t.to_tokens(&mut out);
|
||||||
|
out.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn append (out: &mut TokenStream2, quote: TokenStream2) {
|
||||||
|
for token in quote {
|
||||||
|
out.append(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -609,9 +609,15 @@ impl Track {
|
||||||
|
|
||||||
pub fn per <'a, T: Draw<Tui> + 'a, U: TracksSizes<'a>> (
|
pub fn per <'a, T: Draw<Tui> + 'a, U: TracksSizes<'a>> (
|
||||||
tracks: impl Fn() -> U + Send + Sync + 'a,
|
tracks: impl Fn() -> U + Send + Sync + 'a,
|
||||||
callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
|
callback: &'a (impl Fn(usize, &'a Track)->T + Send + Sync + 'a)
|
||||||
) -> impl Draw<Tui> + 'a {
|
) -> impl Draw<Tui> + 'a {
|
||||||
view_track_per(tracks, callback)
|
iter_east(move||tracks().map(|(index, track, x1, x2): (usize, &Track, usize, usize)|{
|
||||||
|
fg_bg(
|
||||||
|
track.color.lightest.term,
|
||||||
|
track.color.base.term,
|
||||||
|
callback(index, track)
|
||||||
|
).exact_w((x2 - x1) as u16)
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -948,10 +954,11 @@ impl Arrangement {
|
||||||
|
|
||||||
pub fn view_track_devices (&self, theme: ItemTheme) -> impl Draw<Tui> {
|
pub fn view_track_devices (&self, theme: ItemTheme) -> impl Draw<Tui> {
|
||||||
let height = self.devices_height();
|
let height = self.devices_height();
|
||||||
view_track_row_section(theme,
|
let btn1 = button_3("d", "evice", format!("{}", self.track().map(|t|t.devices.len()).unwrap_or(0)), false);
|
||||||
button_3("d", "evice", format!("{}", self.track().map(|t|t.devices.len()).unwrap_or(0)), false),
|
let btn2 = button_2("D", "+", false);
|
||||||
button_2("D", "+", false),
|
view_track_row_section(theme, btn1, btn2, iter_east(move||self.tracks_with_sizes()
|
||||||
iter_once(self.tracks_with_sizes(), move|(_, track, _x1, _x2), index|bg(
|
.enumerate()
|
||||||
|
.map(move|(index, (_, track, _x1, _x2))|bg(
|
||||||
track.color.dark.term,
|
track.color.dark.term,
|
||||||
iter_south(move||(0..height).map(|_|fg_bg(
|
iter_south(move||(0..height).map(|_|fg_bg(
|
||||||
ItemTheme::G[32].lightest.term,
|
ItemTheme::G[32].lightest.term,
|
||||||
|
|
@ -963,7 +970,7 @@ impl Arrangement {
|
||||||
.exact_wh(
|
.exact_wh(
|
||||||
Some(track_width(index, track)),
|
Some(track_width(index, track)),
|
||||||
Some(height + 1),
|
Some(height + 1),
|
||||||
)))
|
))))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn devices_height (&self) -> u16 {
|
fn devices_height (&self) -> u16 {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,17 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
def_command!(FileBrowserCommand: |sampler: Sampler|{
|
impl App {
|
||||||
|
/// Return reference to content browser if open.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// assert_eq!(tek::App::default().browser(), None);
|
||||||
|
/// ```
|
||||||
|
pub fn browser (&self) -> Option<&Browse> {
|
||||||
|
if let Dialog::Browse(_, ref b) = self.dialog { Some(b) } else { None }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def_command!(FileBrowserCommand: |_browse: Browse|{
|
||||||
//("begin" [] Some(Self::Begin))
|
//("begin" [] Some(Self::Begin))
|
||||||
//("cancel" [] Some(Self::Cancel))
|
//("cancel" [] Some(Self::Cancel))
|
||||||
//("confirm" [] Some(Self::Confirm))
|
//("confirm" [] Some(Self::Confirm))
|
||||||
|
|
@ -539,3 +550,20 @@ def_command!(CropCommand: |pool: Pool| {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pub fn scan (dir: &PathBuf) -> Usually<(Vec<OsString>, Vec<OsString>)> {
|
||||||
|
let (mut subdirs, mut files) = std::fs::read_dir(dir)?
|
||||||
|
.fold((vec!["..".into()], vec![]), |(mut subdirs, mut files), entry|{
|
||||||
|
let entry = entry.expect("failed to read drectory entry");
|
||||||
|
let meta = entry.metadata().expect("failed to read entry metadata");
|
||||||
|
if meta.is_file() {
|
||||||
|
files.push(entry.file_name());
|
||||||
|
} else if meta.is_dir() {
|
||||||
|
subdirs.push(entry.file_name());
|
||||||
|
}
|
||||||
|
(subdirs, files)
|
||||||
|
});
|
||||||
|
subdirs.sort();
|
||||||
|
files.sort();
|
||||||
|
Ok((subdirs, files))
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ pub fn draw_dialog (
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
|
|
||||||
pub fn get_dialog (&self, src: impl Language) -> Perhaps<Dialog> {
|
pub fn get_dialog (&self, src: impl Language) -> Perhaps<Dialog> {
|
||||||
src.word()?.map(|word|Ok(match word {
|
src.word()?.map(|word|Ok(match word {
|
||||||
":dialog/none" => Dialog::None,
|
":dialog/none" => Dialog::None,
|
||||||
|
|
@ -64,31 +65,36 @@ impl App {
|
||||||
)
|
)
|
||||||
})).transpose()
|
})).transpose()
|
||||||
}
|
}
|
||||||
/// Set modal dialog.
|
|
||||||
|
/// Set currently active modal dialog.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// let previous: tek::Dialog = tek::App::default().set_dialog(tek::Dialog::welcome());
|
/// let previous: tek::Dialog = tek::App::default().set_dialog(&tek::Dialog::welcome());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_dialog (&mut self, mut dialog: Dialog) -> Dialog {
|
pub fn set_dialog (&mut self, dialog: &Dialog) -> Dialog {
|
||||||
|
let mut dialog = dialog.clone();
|
||||||
std::mem::swap(&mut self.dialog, &mut dialog);
|
std::mem::swap(&mut self.dialog, &mut dialog);
|
||||||
dialog
|
dialog
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inc (&mut self, axis: &ControlAxis) -> Perhaps<AppCommand> {
|
pub fn inc (&mut self, axis: &ControlAxis) -> Perhaps<AppCommand> {
|
||||||
Ok(match (&self.dialog, axis) {
|
Ok(match (&self.dialog, axis) {
|
||||||
(Dialog::None, _) => todo!(),
|
(Dialog::None, _) => todo!(),
|
||||||
(Dialog::Menu(_, _), ControlAxis::Y) =>
|
(Dialog::Menu(_, _), ControlAxis::Y) =>
|
||||||
AppCommand::SetDialog { dialog: self.dialog.menu_next() }.act(self)?,
|
AppCommand::SetDialog(self.dialog.menu_next()).act(self)?,
|
||||||
_ => todo!()
|
_ => todo!()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dec (&mut self, axis: &ControlAxis) -> Perhaps<AppCommand> {
|
pub fn dec (&mut self, axis: &ControlAxis) -> Perhaps<AppCommand> {
|
||||||
Ok(match (&self.dialog, axis) {
|
Ok(match (&self.dialog, axis) {
|
||||||
(Dialog::None, _) => None,
|
(Dialog::None, _) => None,
|
||||||
(Dialog::Menu(_, _), ControlAxis::Y) =>
|
(Dialog::Menu(_, _), ControlAxis::Y) =>
|
||||||
AppCommand::SetDialog { dialog: self.dialog.menu_prev() }.act(self)?,
|
AppCommand::SetDialog(self.dialog.menu_prev()).act(self)?,
|
||||||
_ => todo!()
|
_ => todo!()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn confirm (&mut self) -> Perhaps<AppCommand> {
|
pub fn confirm (&mut self) -> Perhaps<AppCommand> {
|
||||||
Ok(match &self.dialog {
|
Ok(match &self.dialog {
|
||||||
Dialog::Menu(index, items) => {
|
Dialog::Menu(index, items) => {
|
||||||
|
|
|
||||||
108
src/tek.edn
108
src/tek.edn
|
|
@ -1,7 +1,7 @@
|
||||||
(view :logo (bsp/s (text ~~ ╓─╥─╖ ╓──╖ ╥ ╖ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ )
|
(view :logo (bsp/s (bg (rgb 100 70 40) (text ~~ ╓─╥─╖ ╓──╖ ╥ ╖ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ))
|
||||||
(bsp/s (text ~~~~ ║ ~ ╟─╌ ~╟─< ~~ heatwave is the new darkwave ~~ )
|
(bsp/s (bg (rgb 90 70 50) (text ~~~~ ║ ~ ╟─╌ ~╟─< ~~ heatwave is the new darkwave ~~ ))
|
||||||
(bsp/s (text ~~~~ ╨ ~ ╙──╜ ╨ ╜ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ )
|
(bsp/s (bg (rgb 80 70 60) (text ~~~~ ╨ ~ ╙──╜ ╨ ╜ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ))
|
||||||
(text)))))
|
(align/x (text .))))))
|
||||||
|
|
||||||
(view :browse (bsp/s
|
(view :browse (bsp/s
|
||||||
(padding 3 1 :browse-title)
|
(padding 3 1 :browse-title)
|
||||||
|
|
@ -15,51 +15,8 @@
|
||||||
|
|
||||||
(mode :menu (name Menu) (info Mode selector.) (keys :axis/y :confirm)
|
(mode :menu (name Menu) (info Mode selector.) (keys :axis/y :confirm)
|
||||||
(view (bsp/s
|
(view (bsp/s
|
||||||
(align/s (bsp/e :ports/out
|
(align/s (bsp/e :ports/out (bsp/e :transport :ports/in)))
|
||||||
(bsp/e :transport
|
(align/c (bsp/s (align/x (bg (g 36) :logo)) (bg (g 24) :dialog/menu))))))
|
||||||
:ports/in)))
|
|
||||||
(align/c (bsp/s (align/x (bg (g 36) :logo))
|
|
||||||
(bg (g 24) :dialog/menu))))))
|
|
||||||
|
|
||||||
(view :ports/out
|
|
||||||
(bsp/s (align/w (text L-AUDIO-OUT))
|
|
||||||
(bsp/e (text MIDI-OUT)
|
|
||||||
(align/e (text AUDIO-OUT-R)))))
|
|
||||||
|
|
||||||
(view :ports/in
|
|
||||||
(bsp/s (align/w (text L-AUDIO-IN))
|
|
||||||
(bsp/e (text MIDI-IN)
|
|
||||||
(align/e (text AUDIO-IN-R)))))
|
|
||||||
|
|
||||||
(mode :sequencer (name Sequencer) (info MIDI sequencer.)
|
|
||||||
(keys :editor :clock :global)
|
|
||||||
(mode browse (keys :browse))
|
|
||||||
(mode rename (keys :pool/rename))
|
|
||||||
(mode length (keys :pool/length))
|
|
||||||
(bsp/s (fixed/y 1 :transport)
|
|
||||||
(bsp/n (fixed/y 1 :status)
|
|
||||||
(fill (bsp/a (fill/xy (align/e :pool)) :editor)))))
|
|
||||||
|
|
||||||
(mode :sampler (name Sampler) (info Sample player.)
|
|
||||||
(keys :sampler/directions :sampler/record :sampler/play)
|
|
||||||
(bsp/s (fixed/y 1 :transport)
|
|
||||||
(bsp/n (fixed/y 1 :status)
|
|
||||||
(fill :samples/grid))))
|
|
||||||
|
|
||||||
(mode :groovebox (name Groovebox) (info Sequencer with sampler.)
|
|
||||||
(keys :clock :editor :sampler :global)
|
|
||||||
(mode browse (keys :browse))
|
|
||||||
(mode rename (keys :pool-rename))
|
|
||||||
(mode length (keys :pool-length))
|
|
||||||
(bsp/w :meters/output (bsp/e :meters/input (bsp/w :groove/meta :groove/editor))))
|
|
||||||
|
|
||||||
(view :groove/meta (fill/y (align/n (stack/s :midi-ins/status :midi-outs/status :audio-ins/status :audio-outs/status :pool))))
|
|
||||||
|
|
||||||
(view :groove/editor (bsp/n :groove/sample :groove/sequence))
|
|
||||||
|
|
||||||
(view :groove/sample (fixed/y :h-sample-detail (bsp/e (fill/y (fixed/x 20 (align/nw :sample-status))) :sample-viewer)))
|
|
||||||
|
|
||||||
(view :groove/sequence (bsp/e (fill/y (align/n (bsp/s :status/v :editor-status))) (bsp/e :samples/keys :editor)))
|
|
||||||
|
|
||||||
(mode :arranger (name Arranger) (info Launch grid.)
|
(mode :arranger (name Arranger) (info Launch grid.)
|
||||||
(keys (see :clock :color :launch :scenes :tracks :global)
|
(keys (see :clock :color :launch :scenes :tracks :global)
|
||||||
|
|
@ -76,7 +33,58 @@
|
||||||
(mode :track (keys :track))
|
(mode :track (keys :track))
|
||||||
(mode :scene (keys :scene))
|
(mode :scene (keys :scene))
|
||||||
(mode :mix (keys :mix))
|
(mode :mix (keys :mix))
|
||||||
(view (bg (g 64) (bsp/n :status (text test)))))
|
(view
|
||||||
|
(bsp/n (bg (g 10) (bsp/e :transport :status))
|
||||||
|
(bsp/w (bg (g 20) (exact/x 4 (align/ne :meters/output)))
|
||||||
|
(bsp/e (bg (g 30) (exact/x 4 (align/nw :meters/input)))
|
||||||
|
(full/xy (align/c (max/xy 80 80
|
||||||
|
(bsp/s (bg (g 40) (exact/y 4 :tracks/outputs))
|
||||||
|
(bsp/s (bg (g 50) (exact/y 2 :tracks/names))
|
||||||
|
(bsp/s (bg (g 60) (exact/y 4 :tracks/devices))
|
||||||
|
(bsp/s (bg (g 70) (exact/y 4 :tracks/inputs))
|
||||||
|
(either :mode/editor
|
||||||
|
(bg (g 80) (bsp/s :scenes/names :editor))
|
||||||
|
(bg (g 90) :scenes))))))))))))))
|
||||||
|
|
||||||
|
(view :ports/out
|
||||||
|
(bsp/s (align/w (text L-AUDIO-OUT))
|
||||||
|
(bsp/e (text MIDI-OUT)
|
||||||
|
(align/e (text AUDIO-OUT-R)))))
|
||||||
|
|
||||||
|
(view :ports/in
|
||||||
|
(bsp/s (align/w (text L-AUDIO-IN))
|
||||||
|
(bsp/e (text MIDI-IN)
|
||||||
|
(align/e (text AUDIO-IN-R)))))
|
||||||
|
|
||||||
|
(mode :sequencer (name Sequencer) (info MIDI sequencer.)
|
||||||
|
(keys :editor :clock :global)
|
||||||
|
(mode browse (keys :browse))
|
||||||
|
(mode rename (keys :pool/rename))
|
||||||
|
(mode length (keys :pool/length))
|
||||||
|
(bsp/s (exact/y 1 :transport)
|
||||||
|
(bsp/n (exact/y 1 :status)
|
||||||
|
(fill (bsp/a (fill/xy (align/e :pool)) :editor)))))
|
||||||
|
|
||||||
|
(mode :sampler (name Sampler) (info Sample player.)
|
||||||
|
(keys :sampler/directions :sampler/record :sampler/play)
|
||||||
|
(bsp/s (exact/y 1 :transport)
|
||||||
|
(bsp/n (exact/y 1 :status)
|
||||||
|
(fill :samples/grid))))
|
||||||
|
|
||||||
|
(mode :groovebox (name Groovebox) (info Sequencer with sampler.)
|
||||||
|
(keys :clock :editor :sampler :global)
|
||||||
|
(mode browse (keys :browse))
|
||||||
|
(mode rename (keys :pool-rename))
|
||||||
|
(mode length (keys :pool-length))
|
||||||
|
(bsp/w :meters/output (bsp/e :meters/input (bsp/w :groove/meta :groove/editor))))
|
||||||
|
|
||||||
|
(view :groove/meta (fill/y (align/n (stack/s :midi-ins/status :midi-outs/status :audio-ins/status :audio-outs/status :pool))))
|
||||||
|
|
||||||
|
(view :groove/editor (bsp/n :groove/sample :groove/sequence))
|
||||||
|
|
||||||
|
(view :groove/sample (exact/y :h-sample-detail (bsp/e (fill/y (exact/x 20 (align/nw :sample-status))) :sample-viewer)))
|
||||||
|
|
||||||
|
(view :groove/sequence (bsp/e (fill/y (align/n (bsp/s :status/v :editor-status))) (bsp/e :samples/keys :editor)))
|
||||||
|
|
||||||
(keys :back (@escape back))
|
(keys :back (@escape back))
|
||||||
(keys :confirm (@enter confirm))
|
(keys :confirm (@enter confirm))
|
||||||
|
|
|
||||||
517
src/tek.rs
517
src/tek.rs
|
|
@ -218,14 +218,26 @@ fn run_new_plain (config: Config) -> Usually<()> {
|
||||||
Clock::new(&jack, *bpm)?,
|
Clock::new(&jack, *bpm)?,
|
||||||
[].into_iter(),
|
[].into_iter(),
|
||||||
[].into_iter(),
|
[].into_iter(),
|
||||||
connect_midi_ins(&jack, &"M".to_string(), midi_from.as_ref(), Some(midi_from_re))?.into_iter(),
|
connect_midi_ins(
|
||||||
connect_midi_outs(&jack, &"M".to_string(), midi_to.as_ref(), Some(midi_to_re))?.into_iter(),
|
&jack, &"M".to_string(), midi_from.as_ref(), Some(midi_from_re)
|
||||||
|
)?.into_iter(),
|
||||||
|
connect_midi_outs(
|
||||||
|
&jack, &"M".to_string(), midi_to.as_ref(), Some(midi_to_re)
|
||||||
|
)?.into_iter(),
|
||||||
[].into_iter()
|
[].into_iter()
|
||||||
.chain(connect_audio_ins(&jack, &"L".to_string(), &left_from, None)?.into_iter())
|
.chain(
|
||||||
.chain(connect_audio_ins(&jack, &"R".to_string(), &right_from, None)?.into_iter()),
|
connect_audio_ins(&jack, &"L".to_string(), &left_from, None)?.into_iter()
|
||||||
|
)
|
||||||
|
.chain(
|
||||||
|
connect_audio_ins(&jack, &"R".to_string(), &right_from, None)?.into_iter()
|
||||||
|
),
|
||||||
[].into_iter()
|
[].into_iter()
|
||||||
.chain(connect_audio_outs(&jack, &"L".to_string(), &left_to, None)?.into_iter())
|
.chain(
|
||||||
.chain(connect_audio_outs(&jack, &"R".to_string(), &right_to, None)?.into_iter()));
|
connect_audio_outs(&jack, &"L".to_string(), &left_to, None)?.into_iter()
|
||||||
|
)
|
||||||
|
.chain(
|
||||||
|
connect_audio_outs(&jack, &"R".to_string(), &right_to, None)?.into_iter()
|
||||||
|
));
|
||||||
//&jack, Clock::new(&jack, *bpm)?, &lf, <, &rf, &rt, &mf, &mt, &mfr, &mtr)?;
|
//&jack, Clock::new(&jack, *bpm)?, &lf, <, &rf, &rt, &mf, &mt, &mfr, &mtr)?;
|
||||||
proj.tracks_add(tracks.unwrap_or(0), None, &[], &[])?;
|
proj.tracks_add(tracks.unwrap_or(0), None, &[], &[])?;
|
||||||
proj.scenes_add(scenes.unwrap_or(0))?;
|
proj.scenes_add(scenes.unwrap_or(0))?;
|
||||||
|
|
@ -476,12 +488,13 @@ mod config {
|
||||||
let tail = expr.tail()?;
|
let tail = expr.tail()?;
|
||||||
let name = tail.head()?;
|
let name = tail.head()?;
|
||||||
let body = tail.tail()?;
|
let body = tail.tail()?;
|
||||||
//println!("Config::load: {} {} {}", head.unwrap_or_default(), name.unwrap_or_default(), body.unwrap_or_default());
|
|
||||||
match head {
|
match head {
|
||||||
Some("mode") if let Some(name) = name => self.modes.add(&name, &body)?,
|
Some("mode") if let Some(name) = name => self.modes.add(&name, &body)?,
|
||||||
Some("keys") if let Some(name) = name => load_bind(&self.binds, &name, &body)?,
|
Some("keys") if let Some(name) = name => load_bind(&self.binds, &name, &body)?,
|
||||||
Some("view") if let Some(name) = name => load_view(&self.views, &name, &body)?,
|
Some("view") if let Some(name) = name => load_view(&self.views, &name, &body)?,
|
||||||
_ => return Err(format!("Config::load: expected view/keys/mode, got: {item:?}").into())
|
_ => return Err(
|
||||||
|
format!("Config::load: expected view/keys/mode, got: {item:?}").into()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -713,6 +726,7 @@ mod app {
|
||||||
/// Error, if any
|
/// Error, if any
|
||||||
pub error: Arc<RwLock<Option<Arc<str>>>>
|
pub error: Arc<RwLock<Option<Arc<str>>>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
/// Create a new application instance from a backend, project, config, and mode
|
/// Create a new application instance from a backend, project, config, and mode
|
||||||
///
|
///
|
||||||
|
|
@ -856,55 +870,32 @@ mod app {
|
||||||
})).transpose()
|
})).transpose()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// FIXME: generalize. Set picked device in device pick dialog.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// tek::App::default().device_pick(0);
|
|
||||||
/// ```
|
|
||||||
pub fn device_pick (&mut self, index: usize) {
|
|
||||||
self.dialog = Dialog::Device(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// FIXME: generalize. Add device to current track.
|
|
||||||
pub fn add_device (&mut self, index: usize) -> Usually<()> {
|
|
||||||
match index {
|
|
||||||
0 => {
|
|
||||||
let name = self.jack.with_client(|c|c.name().to_string());
|
|
||||||
let midi = self.project.track().expect("no active track").sequencer.midi_outs[0].port_name();
|
|
||||||
let track = self.track().expect("no active track");
|
|
||||||
let port = format!("{}/Sampler", &track.name);
|
|
||||||
let connect = Connect::exact(format!("{name}:{midi}"));
|
|
||||||
let sampler = if let Ok(sampler) = Sampler::new(
|
|
||||||
&self.jack, &port, &[connect], &[&[], &[]], &[&[], &[]]
|
|
||||||
) {
|
|
||||||
self.dialog = Dialog::None;
|
|
||||||
Device::Sampler(sampler)
|
|
||||||
} else {
|
|
||||||
self.dialog = Dialog::Message("Failed to add device.".into());
|
|
||||||
return Err("failed to add device".into())
|
|
||||||
};
|
|
||||||
let track = self.track_mut().expect("no active track");
|
|
||||||
track.devices.push(sampler);
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
1 => {
|
|
||||||
todo!();
|
|
||||||
//Ok(())
|
|
||||||
},
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return reference to content browser if open.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// assert_eq!(tek::App::default().browser(), None);
|
|
||||||
/// ```
|
|
||||||
pub fn browser (&self) -> Option<&Browse> {
|
|
||||||
if let Dialog::Browse(_, ref b) = self.dialog { Some(b) } else { None }
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use self::bind::*;
|
||||||
|
mod bind {
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
/// Define an enum containing commands, and implement [Command] trait for over given `State`.
|
||||||
|
#[macro_export] macro_rules! def_command (
|
||||||
|
($Command:ident: |$state:ident: $State:ty| {
|
||||||
|
// FIXME: support attrs (docstrings)
|
||||||
|
$($Variant:ident$({$($arg:ident:$Arg:ty),+ $(,)?})?=>$body:expr),* $(,)?
|
||||||
|
})=>{
|
||||||
|
#[derive(Debug)] pub enum $Command {
|
||||||
|
// FIXME: support attrs (docstrings)
|
||||||
|
$($Variant $({ $($arg: $Arg),* })?),*
|
||||||
|
}
|
||||||
|
impl ::tengri::dizzle::Act<$State> for $Command {
|
||||||
|
fn act (&self, $state: &mut $State) -> Perhaps<Self> {
|
||||||
|
match self {
|
||||||
|
$(Self::$Variant $({ $($arg),* })? => $body,)*
|
||||||
|
_ => unimplemented!("Act<{}>: {self:?}", stringify!($State)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
pub fn swap_value <T: Clone + PartialEq, U> (
|
pub fn swap_value <T: Clone + PartialEq, U> (
|
||||||
target: &mut T, value: &T, returned: impl Fn(T)->U
|
target: &mut T, value: &T, returned: impl Fn(T)->U
|
||||||
|
|
@ -930,23 +921,6 @@ mod app {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scan (dir: &PathBuf) -> Usually<(Vec<OsString>, Vec<OsString>)> {
|
|
||||||
let (mut subdirs, mut files) = std::fs::read_dir(dir)?
|
|
||||||
.fold((vec!["..".into()], vec![]), |(mut subdirs, mut files), entry|{
|
|
||||||
let entry = entry.expect("failed to read drectory entry");
|
|
||||||
let meta = entry.metadata().expect("failed to read entry metadata");
|
|
||||||
if meta.is_file() {
|
|
||||||
files.push(entry.file_name());
|
|
||||||
} else if meta.is_dir() {
|
|
||||||
subdirs.push(entry.file_name());
|
|
||||||
}
|
|
||||||
(subdirs, files)
|
|
||||||
});
|
|
||||||
subdirs.sort();
|
|
||||||
files.sort();
|
|
||||||
Ok((subdirs, files))
|
|
||||||
}
|
|
||||||
|
|
||||||
tui_keys!(self: App, input {
|
tui_keys!(self: App, input {
|
||||||
let commands = tek_commands_collect(self, input)?;
|
let commands = tek_commands_collect(self, input)?;
|
||||||
let results = tek_commands_execute(self, commands)?;
|
let results = tek_commands_execute(self, commands)?;
|
||||||
|
|
@ -1072,30 +1046,58 @@ mod app {
|
||||||
|
|
||||||
impl_debug!(Condition |self, w| { write!(w, "*") });
|
impl_debug!(Condition |self, w| { write!(w, "*") });
|
||||||
|
|
||||||
impl_default!(AppCommand: Self::Nop);
|
#[tek_proc::command(App)]
|
||||||
|
#[tek_proc::keyword(App)]
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub enum AppCommand {
|
||||||
|
#[default]
|
||||||
|
#[command(App::nop)]
|
||||||
|
#[keyword("nop")]
|
||||||
|
Nop,
|
||||||
|
|
||||||
def_command!(AppCommand: |app: App| {
|
#[command(App::cancel)]
|
||||||
Nop => Ok(None),
|
#[keyword("cancel")]
|
||||||
Cancel => todo!(), // TODO delegate:
|
Cancel,
|
||||||
Confirm => app.confirm(),
|
|
||||||
Inc { axis: ControlAxis } => app.inc(axis),
|
|
||||||
Dec { axis: ControlAxis } => app.dec(axis),
|
|
||||||
SetDialog { dialog: Dialog } => {
|
|
||||||
swap_value(&mut app.dialog, dialog, |dialog|Self::SetDialog { dialog })
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
impl<'a> Namespace<'a, AppCommand> for App {
|
#[command(App::confirm)]
|
||||||
symbols!('a |app| -> AppCommand {
|
#[keyword("confirm")]
|
||||||
"x/inc" => AppCommand::Inc { axis: ControlAxis::X },
|
Confirm,
|
||||||
"x/dec" => AppCommand::Dec { axis: ControlAxis::X },
|
|
||||||
"y/inc" => AppCommand::Inc { axis: ControlAxis::Y },
|
#[command(App::inc)]
|
||||||
"y/dec" => AppCommand::Dec { axis: ControlAxis::Y },
|
#[keyword("x/inc", ControlAxis::X)]
|
||||||
"confirm" => AppCommand::Confirm,
|
#[keyword("y/inc", ControlAxis::Y)]
|
||||||
"cancel" => AppCommand::Cancel,
|
Inc(ControlAxis),
|
||||||
});
|
|
||||||
|
#[command(App::dec)]
|
||||||
|
#[keyword("x/dec", ControlAxis::X)]
|
||||||
|
#[keyword("y/dec", ControlAxis::Y)]
|
||||||
|
Dec(ControlAxis),
|
||||||
|
|
||||||
|
#[command(App::set_dialog | Self::SetDialog)]
|
||||||
|
#[keyword("dialog")]
|
||||||
|
SetDialog(Dialog),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl App {
|
||||||
|
fn nop (&mut self) -> Perhaps<AppCommand> {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
fn cancel (&mut self) -> Perhaps<AppCommand> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//impl<'a> Namespace<'a, AppCommand> for App {
|
||||||
|
//symbols!('a |app| -> AppCommand {
|
||||||
|
//"x/inc" => AppCommand::Inc { axis: ControlAxis::X },
|
||||||
|
//"x/dec" => AppCommand::Dec { axis: ControlAxis::X },
|
||||||
|
//"y/inc" => AppCommand::Inc { axis: ControlAxis::Y },
|
||||||
|
//"y/dec" => AppCommand::Dec { axis: ControlAxis::Y },
|
||||||
|
//"confirm" => AppCommand::Confirm,
|
||||||
|
//"cancel" => AppCommand::Cancel,
|
||||||
|
//});
|
||||||
|
//}
|
||||||
|
|
||||||
/// A control axis.
|
/// A control axis.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
|
@ -1107,6 +1109,52 @@ mod app {
|
||||||
|
|
||||||
//take!(DeviceCommand|state: Arrangement, iter|state.selected_device().as_ref()
|
//take!(DeviceCommand|state: Arrangement, iter|state.selected_device().as_ref()
|
||||||
//.map(|t|Take::take(t, iter)).transpose().map(|x|x.flatten()));
|
//.map(|t|Take::take(t, iter)).transpose().map(|x|x.flatten()));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use self::device::*;
|
||||||
|
mod device {
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
impl App {
|
||||||
|
/// FIXME: generalize. Set picked device in device pick dialog.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// tek::App::default().device_pick(0);
|
||||||
|
/// ```
|
||||||
|
pub fn device_pick (&mut self, index: usize) {
|
||||||
|
self.dialog = Dialog::Device(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// FIXME: generalize. Add device to current track.
|
||||||
|
pub fn add_device (&mut self, index: usize) -> Usually<()> {
|
||||||
|
match index {
|
||||||
|
0 => {
|
||||||
|
let name = self.jack.with_client(|c|c.name().to_string());
|
||||||
|
let midi = self.project.track().expect("no active track").sequencer.midi_outs[0].port_name();
|
||||||
|
let track = self.track().expect("no active track");
|
||||||
|
let port = format!("{}/Sampler", &track.name);
|
||||||
|
let connect = Connect::exact(format!("{name}:{midi}"));
|
||||||
|
let sampler = if let Ok(sampler) = Sampler::new(
|
||||||
|
&self.jack, &port, &[connect], &[&[], &[]], &[&[], &[]]
|
||||||
|
) {
|
||||||
|
self.dialog = Dialog::None;
|
||||||
|
Device::Sampler(sampler)
|
||||||
|
} else {
|
||||||
|
self.dialog = Dialog::Message("Failed to add device.".into());
|
||||||
|
return Err("failed to add device".into())
|
||||||
|
};
|
||||||
|
let track = self.track_mut().expect("no active track");
|
||||||
|
track.devices.push(sampler);
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
1 => {
|
||||||
|
todo!();
|
||||||
|
//Ok(())
|
||||||
|
},
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl HasJack<'static> for App { fn jack (&self) -> &Jack<'static> { &self.jack } }
|
impl HasJack<'static> for App { fn jack (&self) -> &Jack<'static> { &self.jack } }
|
||||||
|
|
||||||
|
|
@ -1158,12 +1206,12 @@ mod app {
|
||||||
_ => { panic!("{event:?}"); }
|
_ => { panic!("{event:?}"); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub use self::device::*;
|
#[tek_proc::command(Device)]
|
||||||
mod device {
|
#[tek_proc::keyword(Device)]
|
||||||
use crate::*;
|
#[derive(Debug)]
|
||||||
def_command!(DeviceCommand: |device: Device| {});
|
pub enum DeviceCommand {}
|
||||||
|
|
||||||
impl Device {
|
impl Device {
|
||||||
pub fn name (&self) -> &str {
|
pub fn name (&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
|
|
@ -1196,6 +1244,7 @@ mod device {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A device that can be plugged into the chain.
|
/// A device that can be plugged into the chain.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
|
@ -1274,26 +1323,6 @@ mod device {
|
||||||
#[cfg(feature = "plugin")] pub mod plugin;
|
#[cfg(feature = "plugin")] pub mod plugin;
|
||||||
#[cfg(feature = "plugin")] pub use self::plugin::*;
|
#[cfg(feature = "plugin")] pub use self::plugin::*;
|
||||||
|
|
||||||
def_command!(AudioInputCommand: |port: AudioInput| {
|
|
||||||
Close => todo!(),
|
|
||||||
Connect { audio_out: Arc<str> } => todo!(),
|
|
||||||
});
|
|
||||||
|
|
||||||
def_command!(AudioOutputCommand: |port: AudioOutput| {
|
|
||||||
Close => todo!(),
|
|
||||||
Connect { audio_in: Arc<str> } => todo!(),
|
|
||||||
});
|
|
||||||
|
|
||||||
def_command!(MidiInputCommand: |port: MidiInput| {
|
|
||||||
Close => todo!(),
|
|
||||||
Connect { midi_out: Arc<str> } => todo!(),
|
|
||||||
});
|
|
||||||
|
|
||||||
def_command!(MidiOutputCommand: |port: MidiOutput| {
|
|
||||||
Close => todo!(),
|
|
||||||
Connect { midi_in: Arc<str> } => todo!(),
|
|
||||||
});
|
|
||||||
|
|
||||||
pub struct Junction<T: JackPort>(T);
|
pub struct Junction<T: JackPort>(T);
|
||||||
|
|
||||||
impl<T: JackPort> View<Tui> for Junction<T> {
|
impl<T: JackPort> View<Tui> for Junction<T> {
|
||||||
|
|
@ -1301,36 +1330,38 @@ mod device {
|
||||||
T::KIND
|
T::KIND
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print_status (project: &Arrangement) {
|
#[tek_proc::command(AudioInput)]
|
||||||
println!("Name: {:?}", &project.name);
|
#[tek_proc::keyword(AudioInput)]
|
||||||
println!("JACK: {:?}", &project.jack);
|
#[derive(Debug)]
|
||||||
println!("Buffer: {:?}", &project.clock.chunk);
|
pub enum AudioInputCommand {
|
||||||
println!("Sample rate: {:?}", &project.clock.timebase.sr);
|
Close,
|
||||||
println!("MIDI PPQ: {:?}", &project.clock.timebase.ppq);
|
Connect(Arc<str>),
|
||||||
println!("Tempo: {:?}", &project.clock.timebase.bpm);
|
|
||||||
println!("Quantize: {:?}", &project.clock.quant);
|
|
||||||
println!("Launch: {:?}", &project.clock.sync);
|
|
||||||
println!("Playhead: {:?}us", &project.clock.playhead.usec);
|
|
||||||
println!("Playhead: {:?}s", &project.clock.playhead.sample);
|
|
||||||
println!("Playhead: {:?}p", &project.clock.playhead.pulse);
|
|
||||||
println!("Started: {:?}", &project.clock.started);
|
|
||||||
println!("Tracks:");
|
|
||||||
for (i, t) in project.tracks.iter().enumerate() {
|
|
||||||
println!(" Track {i}: {} {} {:?} {:?}", t.name, t.width,
|
|
||||||
&t.sequencer.play_clip, &t.sequencer.next_clip);
|
|
||||||
}
|
}
|
||||||
println!("Scenes:");
|
|
||||||
for (i, t) in project.scenes.iter().enumerate() {
|
#[tek_proc::command(AudioOutput)]
|
||||||
println!(" Scene {i}: {} {:?}", &t.name, &t.clips);
|
#[tek_proc::keyword(AudioOutput)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum AudioOutputCommand {
|
||||||
|
Close,
|
||||||
|
Connect(Arc<str>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tek_proc::command(MidiInput)]
|
||||||
|
#[tek_proc::keyword(MidiInput)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum MidiInputCommand {
|
||||||
|
Close,
|
||||||
|
Connect(Arc<str>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tek_proc::command(MidiOutput)]
|
||||||
|
#[tek_proc::keyword(MidiOutput)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum MidiOutputCommand {
|
||||||
|
Close,
|
||||||
|
Connect(Arc<str>),
|
||||||
}
|
}
|
||||||
println!("MIDI Ins: {:?}", &project.midi_ins);
|
|
||||||
println!("MIDI Outs: {:?}", &project.midi_outs);
|
|
||||||
println!("Audio Ins: {:?}", &project.audio_ins);
|
|
||||||
println!("Audio Outs: {:?}", &project.audio_outs);
|
|
||||||
// TODO git integration
|
|
||||||
// TODO dawvert integration
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//pub fn tui (
|
//pub fn tui (
|
||||||
|
|
@ -1354,35 +1385,13 @@ pub fn print_status (project: &Arrangement) {
|
||||||
//})?)?
|
//})?)?
|
||||||
//}
|
//}
|
||||||
|
|
||||||
/// Define an enum containing commands, and implement [Command] trait for over given `State`.
|
|
||||||
#[macro_export] macro_rules! def_command (
|
|
||||||
($Command:ident: |$state:ident: $State:ty| {
|
|
||||||
// FIXME: support attrs (docstrings)
|
|
||||||
$($Variant:ident$({$($arg:ident:$Arg:ty),+ $(,)?})?=>$body:expr),* $(,)?
|
|
||||||
})=>{
|
|
||||||
#[derive(Debug)] pub enum $Command {
|
|
||||||
// FIXME: support attrs (docstrings)
|
|
||||||
$($Variant $({ $($arg: $Arg),* })?),*
|
|
||||||
}
|
|
||||||
impl ::tengri::dizzle::Act<$State> for $Command {
|
|
||||||
fn act (&self, $state: &mut $State) -> Perhaps<Self> {
|
|
||||||
match self {
|
|
||||||
$(Self::$Variant $({ $($arg),* })? => $body,)*
|
|
||||||
_ => unimplemented!("Act<{}>: {self:?}", stringify!($State)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
pub use self::draw::*;
|
pub use self::draw::*;
|
||||||
mod draw {
|
mod draw {
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
/// Load custom view definition.
|
/// Load custom view definition.
|
||||||
pub(crate) fn load_view (
|
pub(crate) fn load_view (
|
||||||
views: &Views,
|
views: &Views, name: &impl AsRef<str>, body: &impl Language,
|
||||||
name: &impl AsRef<str>,
|
|
||||||
body: &impl Language,
|
|
||||||
) -> Usually<()> {
|
) -> Usually<()> {
|
||||||
views.write().unwrap().insert(
|
views.write().unwrap().insert(
|
||||||
name.as_ref().into(),
|
name.as_ref().into(),
|
||||||
|
|
@ -1393,7 +1402,7 @@ mod draw {
|
||||||
|
|
||||||
impl Keywords<Tui, XYWH<u16>> for App {
|
impl Keywords<Tui, XYWH<u16>> for App {
|
||||||
fn keywords () -> impl Iterator<Item = fn(&Self, &mut Tui, &str) -> Perhaps<XYWH<u16>>> {
|
fn keywords () -> impl Iterator<Item = fn(&Self, &mut Tui, &str) -> Perhaps<XYWH<u16>>> {
|
||||||
[kw_when, kw_either, kw_split, kw_align, kw_exact, kw_min, kw_max, kw_push,
|
[kw_when, kw_either, kw_split, kw_align, kw_exact, kw_min, kw_max, kw_push, kw_full,
|
||||||
kw_tui_text, kw_tui_fg, kw_tui_bg]
|
kw_tui_text, kw_tui_fg, kw_tui_bg]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
}
|
}
|
||||||
|
|
@ -1467,11 +1476,26 @@ mod draw {
|
||||||
let mut frags = lang.src()?.unwrap().split("/");
|
let mut frags = lang.src()?.unwrap().split("/");
|
||||||
match frags.next() {
|
match frags.next() {
|
||||||
//Some(":logo") => view_logo().draw(to),
|
//Some(":logo") => view_logo().draw(to),
|
||||||
Some(":meters") => draw_meter_section(to, frags),
|
Some(":meters") => match frags.next() {
|
||||||
Some(":tracks") => draw_tracks(to, frags, self),
|
Some("input") => bg(Rgb(30, 30, 30), "Input Meters".align_s().full_h()).draw(to),
|
||||||
Some(":scenes") => draw_scenes(to, frags),
|
Some("output") => bg(Rgb(30, 30, 30), "Output Meters".align_s().full_h()).draw(to),
|
||||||
|
_ => panic!()
|
||||||
|
},
|
||||||
|
Some(":tracks") => match frags.next() {
|
||||||
|
None => "TODO tracks".draw(to),
|
||||||
|
Some("names") => self.project.view_track_names(self.color.clone()).draw(to),//bg(Rgb(40, 40, 40), full_w(align_w("Track Names")))),
|
||||||
|
Some("inputs") => self.project.view_track_inputs(self.color.clone()).draw(to),//bg(Rgb(40, 40, 40), "Track Inputs".align_w().full_w()).draw(to),
|
||||||
|
Some("devices") => self.project.view_track_devices(self.color.clone()).draw(to),//bg(Rgb(40, 40, 40), "Track Devices".align_w().full_w()).draw(to),
|
||||||
|
Some("outputs") => self.project.view_track_outputs(self.color.clone(), 0).draw(to),
|
||||||
|
_ => panic!()
|
||||||
|
},
|
||||||
|
Some(":scenes") => match frags.next() {
|
||||||
|
None => "TODO Scenes".draw(to),
|
||||||
|
Some(":scenes/names") => "TODO Scene Names".draw(to),
|
||||||
|
_ => panic!()
|
||||||
|
},
|
||||||
Some(":dialog") => draw_dialog(to, frags, self, lang),
|
Some(":dialog") => draw_dialog(to, frags, self, lang),
|
||||||
Some(":templates") => draw_templates(to, frags, self),
|
Some(":templates") => view_templates(frags, self).draw(to),
|
||||||
Some(":sessions") => view_sessions().draw(to),
|
Some(":sessions") => view_sessions().draw(to),
|
||||||
Some(":browse/title") => view_browse_title(self).draw(to),
|
Some(":browse/title") => view_browse_title(self).draw(to),
|
||||||
Some(":device") => view_device(self).draw(to),
|
Some(":device") => view_device(self).draw(to),
|
||||||
|
|
@ -1486,7 +1510,7 @@ mod draw {
|
||||||
std::mem::drop(views);
|
std::mem::drop(views);
|
||||||
self.interpret(to, &lang)
|
self.interpret(to, &lang)
|
||||||
} else {
|
} else {
|
||||||
unimplemented!("{lang:?}");
|
fg(Color::Rgb(128, 32, 32), format!("undefined: {lang:?}")).draw(to)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => unreachable!()
|
_ => unreachable!()
|
||||||
|
|
@ -1504,34 +1528,7 @@ mod draw {
|
||||||
fn width_dec (&mut self);
|
fn width_dec (&mut self);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_meter_section (to: &mut Tui, mut frags: std::str::Split<&str>) -> Drawn<u16> {
|
pub fn view_templates (_frags: std::str::Split<&str>, state: &App) -> impl Draw<Tui> {
|
||||||
match frags.next() {
|
|
||||||
Some("input") => bg(Rgb(30, 30, 30), "Input Meters".align_s().full_h()).draw(to),
|
|
||||||
Some("output") => bg(Rgb(30, 30, 30), "Output Meters".align_s().full_h()).draw(to),
|
|
||||||
_ => panic!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw_tracks (to: &mut Tui, mut frags: std::str::Split<&str>, state: &App) -> Drawn<u16> {
|
|
||||||
match frags.next() {
|
|
||||||
None => "TODO tracks".draw(to),
|
|
||||||
Some("names") => state.project.view_track_names(state.color.clone()).draw(to),//bg(Rgb(40, 40, 40), full_w(align_w("Track Names")))),
|
|
||||||
Some("inputs") => bg(Rgb(40, 40, 40), "Track Inputs".align_w().full_w()).draw(to),
|
|
||||||
Some("devices") => bg(Rgb(40, 40, 40), "Track Devices".align_w().full_w()).draw(to),
|
|
||||||
Some("outputs") => bg(Rgb(40, 40, 40), "Track Outputs".align_w().full_w()).draw(to),
|
|
||||||
_ => panic!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw_scenes (to: &mut Tui, mut frags: std::str::Split<&str>) -> Drawn<u16> {
|
|
||||||
match frags.next() {
|
|
||||||
None => "TODO Scenes".draw(to),
|
|
||||||
Some(":scenes/names") => "TODO Scene Names".draw(to),
|
|
||||||
_ => panic!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw_templates (to: &mut Tui, _frags: std::str::Split<&str>, state: &App) -> Drawn<u16> {
|
|
||||||
let height = (state.config.modes.len() * 2) as u16;
|
let height = (state.config.modes.len() * 2) as u16;
|
||||||
draw(move |to: &mut Tui|{
|
draw(move |to: &mut Tui|{
|
||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
|
|
@ -1549,29 +1546,29 @@ mod draw {
|
||||||
index += 1;
|
index += 1;
|
||||||
});
|
});
|
||||||
Ok(Some(to.area().into()))
|
Ok(Some(to.area().into()))
|
||||||
}).min_w(30).exact_h(height).draw(to)
|
}).min_w(30).exact_h(height)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn per_track <'a, T: Draw<Tui> + 'a, U: TracksSizes<'a>> (
|
//pub fn per_track <'a, T: Draw<Tui> + 'a, U: TracksSizes<'a>> (
|
||||||
tracks: impl Fn() -> U + Send + Sync + 'a,
|
//tracks: impl Fn() -> U + Send + Sync + 'a,
|
||||||
callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
|
//callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
|
||||||
) -> impl Draw<Tui> + 'a {
|
//) -> impl Draw<Tui> + 'a {
|
||||||
per_track_top(tracks, move|index, track|callback(index, track).full_h().align_y())
|
//per_track_top(tracks, move|index, track|callback(index, track).full_h().align_y())
|
||||||
}
|
//}
|
||||||
|
|
||||||
pub fn per_track_top <'a, T: Draw<Tui> + 'a, U: TracksSizes<'a>> (
|
//pub fn per_track_top <'a, T: Draw<Tui> + 'a, U: TracksSizes<'a>> (
|
||||||
tracks: impl Fn() -> U + Send + Sync + 'a,
|
//tracks: impl Fn() -> U + Send + Sync + 'a,
|
||||||
callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
|
//callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
|
||||||
) -> impl Draw<Tui> + 'a {
|
//) -> impl Draw<Tui> + 'a {
|
||||||
bg(Reset, iter_east(tracks,
|
//bg(Reset, iter_east(||tracks()
|
||||||
move|(index, track, x1, x2): (usize, &'a Track, usize, usize), _|{
|
//.map(move|(index, track, x1, x2): (usize, &'a Track, usize, usize)|{
|
||||||
fg_bg(
|
//fg_bg(
|
||||||
track.color.lightest.term,
|
//track.color.lightest.term,
|
||||||
track.color.base.term,
|
//track.color.base.term,
|
||||||
callback(index, track)
|
//callback(index, track)
|
||||||
).exact_w((x2 - x1) as u16)
|
//).exact_w((x2 - x1) as u16)
|
||||||
}).align_x())
|
//})).align_x())
|
||||||
}
|
//}
|
||||||
|
|
||||||
pub fn field_h <T: Screen> (
|
pub fn field_h <T: Screen> (
|
||||||
_theme: ItemTheme, _head: impl Draw<T>, _body: impl Draw<T>
|
_theme: ItemTheme, _head: impl Draw<T>, _body: impl Draw<T>
|
||||||
|
|
@ -1932,17 +1929,73 @@ mod draw {
|
||||||
track.map(move|track|view_ports_status(theme, "Audio outs:", &track.audio_outs()))
|
track.map(move|track|view_ports_status(theme, "Audio outs:", &track.audio_outs()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn view_track_per <'a, T: Draw<Tui> + 'a, U: TracksSizes<'a>> (
|
//pub fn view_track_per <'a, T: Draw<Tui> + 'a, U: TracksSizes<'a>> (
|
||||||
tracks: impl Fn() -> U + Send + Sync + 'a,
|
//tracks: impl Fn() -> U + Send + Sync + 'a,
|
||||||
callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
|
//callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
|
||||||
|
//) -> impl Draw<Tui> {
|
||||||
|
//}
|
||||||
|
|
||||||
|
/// ```
|
||||||
|
/// let _ = tek::button_2("", "", true);
|
||||||
|
/// let _ = tek::button_2("", "", false);
|
||||||
|
/// ```
|
||||||
|
pub fn button_2 <'a> (key: impl Draw<Tui>, label: impl Draw<Tui>, hide: bool) -> impl Draw<Tui> {
|
||||||
|
let c1 = tui_orange();
|
||||||
|
let c2 = tui_g(0);
|
||||||
|
let c3 = tui_g(96);
|
||||||
|
let c4 = tui_g(255);
|
||||||
|
bold(true, fg_bg(c1, c2,
|
||||||
|
east!(fg(c2, "▐"), key, fg(c3, "▐"), when(!hide, fg_bg(c4, c3, label)))))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ```
|
||||||
|
/// let _ = tek::button_3("", "", "", true);
|
||||||
|
/// let _ = tek::button_3("", "", "", false);
|
||||||
|
/// ```
|
||||||
|
pub fn button_3 <'a> (
|
||||||
|
key: impl Draw<Tui>, label: impl Draw<Tui>, value: impl Draw<Tui>, editing: bool,
|
||||||
) -> impl Draw<Tui> {
|
) -> impl Draw<Tui> {
|
||||||
iter_east(tracks, move|(index, track, x1, x2): (usize, &Track, usize, usize), _|{
|
let c1 = tui_orange();
|
||||||
fg_bg(
|
let c2 = tui_g(0);
|
||||||
track.color.lightest.term,
|
let c3 = tui_g(96);
|
||||||
track.color.base.term,
|
let c4 = tui_g(255);
|
||||||
callback(index, track)
|
let c5 = tui_g(128);
|
||||||
).exact_w((x2 - x1) as u16)
|
bold(true, east(
|
||||||
})
|
fg_bg(c1, c2,
|
||||||
|
east(fg(c2, "▐"), east(key, fg(if editing { c5 } else { c3 }, "▐")))),
|
||||||
|
east(
|
||||||
|
when(!editing, east(fg_bg(c4, c3, label), fg_bg(c5, c3, "▐"),)),
|
||||||
|
east(fg_bg(tui_g(224), c5, value), fg_bg(c5, Reset, "▌"), ))))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn print_status (project: &Arrangement) {
|
||||||
|
println!("Name: {:?}", &project.name);
|
||||||
|
println!("JACK: {:?}", &project.jack);
|
||||||
|
println!("Buffer: {:?}", &project.clock.chunk);
|
||||||
|
println!("Sample rate: {:?}", &project.clock.timebase.sr);
|
||||||
|
println!("MIDI PPQ: {:?}", &project.clock.timebase.ppq);
|
||||||
|
println!("Tempo: {:?}", &project.clock.timebase.bpm);
|
||||||
|
println!("Quantize: {:?}", &project.clock.quant);
|
||||||
|
println!("Launch: {:?}", &project.clock.sync);
|
||||||
|
println!("Playhead: {:?}us", &project.clock.playhead.usec);
|
||||||
|
println!("Playhead: {:?}s", &project.clock.playhead.sample);
|
||||||
|
println!("Playhead: {:?}p", &project.clock.playhead.pulse);
|
||||||
|
println!("Started: {:?}", &project.clock.started);
|
||||||
|
println!("Tracks:");
|
||||||
|
for (i, t) in project.tracks.iter().enumerate() {
|
||||||
|
println!(" Track {i}: {} {} {:?} {:?}", t.name, t.width,
|
||||||
|
&t.sequencer.play_clip, &t.sequencer.next_clip);
|
||||||
|
}
|
||||||
|
println!("Scenes:");
|
||||||
|
for (i, t) in project.scenes.iter().enumerate() {
|
||||||
|
println!(" Scene {i}: {} {:?}", &t.name, &t.clips);
|
||||||
|
}
|
||||||
|
println!("MIDI Ins: {:?}", &project.midi_ins);
|
||||||
|
println!("MIDI Outs: {:?}", &project.midi_outs);
|
||||||
|
println!("Audio Ins: {:?}", &project.audio_ins);
|
||||||
|
println!("Audio Outs: {:?}", &project.audio_outs);
|
||||||
|
// TODO git integration
|
||||||
|
// TODO dawvert integration
|
||||||
|
}
|
||||||
|
|
|
||||||
2
tengri
2
tengri
|
|
@ -1 +1 @@
|
||||||
Subproject commit 799e49762250f48778dadd92874a29495114a6af
|
Subproject commit 8a6eb19e279afefc126008c27b38cc66645e1c96
|
||||||
Loading…
Add table
Add a link
Reference in a new issue