mirror of
https://codeberg.org/unspeaker/tek.git
synced 2026-09-18 12:56:42 +02:00
Compare commits
No commits in common. "ae496987c8bdc85e3a8880be1ec0139b55a99c26" and "4aefaf452c26fa6b583915ba155f51eb675e0ee4" have entirely different histories.
ae496987c8
...
4aefaf452c
10 changed files with 278 additions and 591 deletions
10
Cargo.lock
generated
10
Cargo.lock
generated
|
|
@ -3145,7 +3145,6 @@ dependencies = [
|
|||
"rand 0.8.7",
|
||||
"symphonia",
|
||||
"tek",
|
||||
"tek_proc",
|
||||
"tengri",
|
||||
"toml 0.9.12+spec-1.1.0",
|
||||
"uuid",
|
||||
|
|
@ -3154,15 +3153,6 @@ dependencies = [
|
|||
"xdg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tek_proc"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.27.0"
|
||||
|
|
|
|||
|
|
@ -14,8 +14,7 @@ path = "src/tek.rs"
|
|||
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
|
||||
|
||||
[dependencies]
|
||||
tek_proc = { path = "./proc" }
|
||||
tengri = { path = "./tengri", features = [ "term", "lang" ] }
|
||||
tengri = { path = "./tengri", features = [ "term", "lang" ] }
|
||||
|
||||
ansi_term = { version = "0.12.1" }
|
||||
atomic_float = { version = "1.0.0" }
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
[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
189
proc/src/lib.rs
|
|
@ -1,189 +0,0 @@
|
|||
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,15 +609,9 @@ impl Track {
|
|||
|
||||
pub fn per <'a, T: Draw<Tui> + 'a, U: TracksSizes<'a>> (
|
||||
tracks: impl Fn() -> U + Send + Sync + 'a,
|
||||
callback: &'a (impl Fn(usize, &'a Track)->T + Send + Sync + 'a)
|
||||
callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
|
||||
) -> impl Draw<Tui> + 'a {
|
||||
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)
|
||||
}))
|
||||
view_track_per(tracks, callback)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -954,11 +948,10 @@ impl Arrangement {
|
|||
|
||||
pub fn view_track_devices (&self, theme: ItemTheme) -> impl Draw<Tui> {
|
||||
let height = self.devices_height();
|
||||
let btn1 = button_3("d", "evice", format!("{}", self.track().map(|t|t.devices.len()).unwrap_or(0)), false);
|
||||
let btn2 = button_2("D", "+", false);
|
||||
view_track_row_section(theme, btn1, btn2, iter_east(move||self.tracks_with_sizes()
|
||||
.enumerate()
|
||||
.map(move|(index, (_, track, _x1, _x2))|bg(
|
||||
view_track_row_section(theme,
|
||||
button_3("d", "evice", format!("{}", self.track().map(|t|t.devices.len()).unwrap_or(0)), false),
|
||||
button_2("D", "+", false),
|
||||
iter_once(self.tracks_with_sizes(), move|(_, track, _x1, _x2), index|bg(
|
||||
track.color.dark.term,
|
||||
iter_south(move||(0..height).map(|_|fg_bg(
|
||||
ItemTheme::G[32].lightest.term,
|
||||
|
|
@ -970,7 +963,7 @@ impl Arrangement {
|
|||
.exact_wh(
|
||||
Some(track_width(index, track)),
|
||||
Some(height + 1),
|
||||
))))
|
||||
)))
|
||||
}
|
||||
|
||||
fn devices_height (&self) -> u16 {
|
||||
|
|
|
|||
|
|
@ -1,17 +1,6 @@
|
|||
use crate::*;
|
||||
|
||||
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|{
|
||||
def_command!(FileBrowserCommand: |sampler: Sampler|{
|
||||
//("begin" [] Some(Self::Begin))
|
||||
//("cancel" [] Some(Self::Cancel))
|
||||
//("confirm" [] Some(Self::Confirm))
|
||||
|
|
@ -550,20 +539,3 @@ def_command!(CropCommand: |pool: Pool| {
|
|||
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,7 +33,6 @@ pub fn draw_dialog (
|
|||
}
|
||||
|
||||
impl App {
|
||||
|
||||
pub fn get_dialog (&self, src: impl Language) -> Perhaps<Dialog> {
|
||||
src.word()?.map(|word|Ok(match word {
|
||||
":dialog/none" => Dialog::None,
|
||||
|
|
@ -65,36 +64,31 @@ impl App {
|
|||
)
|
||||
})).transpose()
|
||||
}
|
||||
|
||||
/// Set currently active modal dialog.
|
||||
/// Set 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, dialog: &Dialog) -> Dialog {
|
||||
let mut dialog = dialog.clone();
|
||||
pub fn set_dialog (&mut self, mut dialog: Dialog) -> Dialog {
|
||||
std::mem::swap(&mut self.dialog, &mut dialog);
|
||||
dialog
|
||||
}
|
||||
|
||||
pub fn inc (&mut self, axis: &ControlAxis) -> Perhaps<AppCommand> {
|
||||
Ok(match (&self.dialog, axis) {
|
||||
(Dialog::None, _) => todo!(),
|
||||
(Dialog::Menu(_, _), ControlAxis::Y) =>
|
||||
AppCommand::SetDialog(self.dialog.menu_next()).act(self)?,
|
||||
AppCommand::SetDialog { dialog: self.dialog.menu_next() }.act(self)?,
|
||||
_ => todo!()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn dec (&mut self, axis: &ControlAxis) -> Perhaps<AppCommand> {
|
||||
Ok(match (&self.dialog, axis) {
|
||||
(Dialog::None, _) => None,
|
||||
(Dialog::Menu(_, _), ControlAxis::Y) =>
|
||||
AppCommand::SetDialog(self.dialog.menu_prev()).act(self)?,
|
||||
AppCommand::SetDialog { dialog: self.dialog.menu_prev() }.act(self)?,
|
||||
_ => todo!()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn confirm (&mut self) -> Perhaps<AppCommand> {
|
||||
Ok(match &self.dialog {
|
||||
Dialog::Menu(index, items) => {
|
||||
|
|
|
|||
70
src/tek.edn
70
src/tek.edn
|
|
@ -1,7 +1,7 @@
|
|||
(view :logo (bsp/s (bg (rgb 100 70 40) (text ~~ ╓─╥─╖ ╓──╖ ╥ ╖ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ))
|
||||
(bsp/s (bg (rgb 90 70 50) (text ~~~~ ║ ~ ╟─╌ ~╟─< ~~ heatwave is the new darkwave ~~ ))
|
||||
(bsp/s (bg (rgb 80 70 60) (text ~~~~ ╨ ~ ╙──╜ ╨ ╜ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ))
|
||||
(align/x (text .))))))
|
||||
(view :logo (bsp/s (text ~~ ╓─╥─╖ ╓──╖ ╥ ╖ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ )
|
||||
(bsp/s (text ~~~~ ║ ~ ╟─╌ ~╟─< ~~ heatwave is the new darkwave ~~ )
|
||||
(bsp/s (text ~~~~ ╨ ~ ╙──╜ ╨ ╜ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ )
|
||||
(text)))))
|
||||
|
||||
(view :browse (bsp/s
|
||||
(padding 3 1 :browse-title)
|
||||
|
|
@ -15,36 +15,11 @@
|
|||
|
||||
(mode :menu (name Menu) (info Mode selector.) (keys :axis/y :confirm)
|
||||
(view (bsp/s
|
||||
(align/s (bsp/e :ports/out (bsp/e :transport :ports/in)))
|
||||
(align/c (bsp/s (align/x (bg (g 36) :logo)) (bg (g 24) :dialog/menu))))))
|
||||
|
||||
(mode :arranger (name Arranger) (info Launch grid.)
|
||||
(keys (see :clock :color :launch :scenes :tracks :global)
|
||||
(@tab project/edit) (@shift/I project/input/add) (@shift/O project/output/add)
|
||||
(@shift/D dialog/show :dialog/device))
|
||||
(mode :editor (keys :editor))
|
||||
(mode :dialog (keys :dialog))
|
||||
(mode :message (keys :message))
|
||||
(mode :add-device (keys :add-device))
|
||||
(mode :browse (keys :browse))
|
||||
(mode :rename (keys :input))
|
||||
(mode :length (keys :rename))
|
||||
(mode :clip (keys :clip))
|
||||
(mode :track (keys :track))
|
||||
(mode :scene (keys :scene))
|
||||
(mode :mix (keys :mix))
|
||||
(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))))))))))))))
|
||||
(align/s (bsp/e :ports/out
|
||||
(bsp/e :transport
|
||||
: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))
|
||||
|
|
@ -61,14 +36,14 @@
|
|||
(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)
|
||||
(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 (exact/y 1 :transport)
|
||||
(bsp/n (exact/y 1 :status)
|
||||
(bsp/s (fixed/y 1 :transport)
|
||||
(bsp/n (fixed/y 1 :status)
|
||||
(fill :samples/grid))))
|
||||
|
||||
(mode :groovebox (name Groovebox) (info Sequencer with sampler.)
|
||||
|
|
@ -82,10 +57,27 @@
|
|||
|
||||
(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/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.)
|
||||
(keys (see :clock :color :launch :scenes :tracks :global)
|
||||
(@tab project/edit) (@shift/I project/input/add) (@shift/O project/output/add)
|
||||
(@shift/D dialog/show :dialog/device))
|
||||
(mode :editor (keys :editor))
|
||||
(mode :dialog (keys :dialog))
|
||||
(mode :message (keys :message))
|
||||
(mode :add-device (keys :add-device))
|
||||
(mode :browse (keys :browse))
|
||||
(mode :rename (keys :input))
|
||||
(mode :length (keys :rename))
|
||||
(mode :clip (keys :clip))
|
||||
(mode :track (keys :track))
|
||||
(mode :scene (keys :scene))
|
||||
(mode :mix (keys :mix))
|
||||
(view (bg (g 64) (bsp/n :status (text test)))))
|
||||
|
||||
(keys :back (@escape back))
|
||||
(keys :confirm (@enter confirm))
|
||||
(keys :axis/x (@left x/dec) (@right x/inc))
|
||||
|
|
|
|||
517
src/tek.rs
517
src/tek.rs
|
|
@ -218,26 +218,14 @@ fn run_new_plain (config: Config) -> Usually<()> {
|
|||
Clock::new(&jack, *bpm)?,
|
||||
[].into_iter(),
|
||||
[].into_iter(),
|
||||
connect_midi_ins(
|
||||
&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(),
|
||||
connect_midi_ins(&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()
|
||||
.chain(
|
||||
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()
|
||||
),
|
||||
.chain(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()
|
||||
.chain(
|
||||
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()
|
||||
));
|
||||
.chain(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)?;
|
||||
proj.tracks_add(tracks.unwrap_or(0), None, &[], &[])?;
|
||||
proj.scenes_add(scenes.unwrap_or(0))?;
|
||||
|
|
@ -488,13 +476,12 @@ mod config {
|
|||
let tail = expr.tail()?;
|
||||
let name = tail.head()?;
|
||||
let body = tail.tail()?;
|
||||
//println!("Config::load: {} {} {}", head.unwrap_or_default(), name.unwrap_or_default(), body.unwrap_or_default());
|
||||
match head {
|
||||
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("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(())
|
||||
} else {
|
||||
|
|
@ -726,7 +713,6 @@ mod app {
|
|||
/// Error, if any
|
||||
pub error: Arc<RwLock<Option<Arc<str>>>>
|
||||
}
|
||||
|
||||
impl App {
|
||||
/// Create a new application instance from a backend, project, config, and mode
|
||||
///
|
||||
|
|
@ -870,32 +856,55 @@ mod app {
|
|||
})).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> (
|
||||
target: &mut T, value: &T, returned: impl Fn(T)->U
|
||||
|
|
@ -921,6 +930,23 @@ mod bind {
|
|||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
let commands = tek_commands_collect(self, input)?;
|
||||
let results = tek_commands_execute(self, commands)?;
|
||||
|
|
@ -1046,58 +1072,30 @@ mod bind {
|
|||
|
||||
impl_debug!(Condition |self, w| { write!(w, "*") });
|
||||
|
||||
#[tek_proc::command(App)]
|
||||
#[tek_proc::keyword(App)]
|
||||
#[derive(Debug, Default)]
|
||||
pub enum AppCommand {
|
||||
#[default]
|
||||
#[command(App::nop)]
|
||||
#[keyword("nop")]
|
||||
Nop,
|
||||
impl_default!(AppCommand: Self::Nop);
|
||||
|
||||
#[command(App::cancel)]
|
||||
#[keyword("cancel")]
|
||||
Cancel,
|
||||
def_command!(AppCommand: |app: App| {
|
||||
Nop => Ok(None),
|
||||
Cancel => todo!(), // TODO delegate:
|
||||
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 })
|
||||
},
|
||||
});
|
||||
|
||||
#[command(App::confirm)]
|
||||
#[keyword("confirm")]
|
||||
Confirm,
|
||||
|
||||
#[command(App::inc)]
|
||||
#[keyword("x/inc", ControlAxis::X)]
|
||||
#[keyword("y/inc", ControlAxis::Y)]
|
||||
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<'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,
|
||||
});
|
||||
}
|
||||
|
||||
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.
|
||||
///
|
||||
/// ```
|
||||
|
|
@ -1109,52 +1107,6 @@ mod bind {
|
|||
|
||||
//take!(DeviceCommand|state: Arrangement, iter|state.selected_device().as_ref()
|
||||
//.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 } }
|
||||
|
||||
|
|
@ -1206,12 +1158,12 @@ mod device {
|
|||
_ => { panic!("{event:?}"); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tek_proc::command(Device)]
|
||||
#[tek_proc::keyword(Device)]
|
||||
#[derive(Debug)]
|
||||
pub enum DeviceCommand {}
|
||||
|
||||
pub use self::device::*;
|
||||
mod device {
|
||||
use crate::*;
|
||||
def_command!(DeviceCommand: |device: Device| {});
|
||||
impl Device {
|
||||
pub fn name (&self) -> &str {
|
||||
match self {
|
||||
|
|
@ -1244,7 +1196,6 @@ mod device {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A device that can be plugged into the chain.
|
||||
///
|
||||
/// ```
|
||||
|
|
@ -1323,6 +1274,26 @@ mod device {
|
|||
#[cfg(feature = "plugin")] pub mod 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);
|
||||
|
||||
impl<T: JackPort> View<Tui> for Junction<T> {
|
||||
|
|
@ -1330,38 +1301,36 @@ mod device {
|
|||
T::KIND
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tek_proc::command(AudioInput)]
|
||||
#[tek_proc::keyword(AudioInput)]
|
||||
#[derive(Debug)]
|
||||
pub enum AudioInputCommand {
|
||||
Close,
|
||||
Connect(Arc<str>),
|
||||
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);
|
||||
}
|
||||
|
||||
#[tek_proc::command(AudioOutput)]
|
||||
#[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!("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
|
||||
}
|
||||
|
||||
//pub fn tui (
|
||||
|
|
@ -1385,13 +1354,35 @@ mod device {
|
|||
//})?)?
|
||||
//}
|
||||
|
||||
/// 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::*;
|
||||
mod draw {
|
||||
use crate::*;
|
||||
|
||||
/// Load custom view definition.
|
||||
pub(crate) fn load_view (
|
||||
views: &Views, name: &impl AsRef<str>, body: &impl Language,
|
||||
views: &Views,
|
||||
name: &impl AsRef<str>,
|
||||
body: &impl Language,
|
||||
) -> Usually<()> {
|
||||
views.write().unwrap().insert(
|
||||
name.as_ref().into(),
|
||||
|
|
@ -1402,7 +1393,7 @@ mod draw {
|
|||
|
||||
impl Keywords<Tui, XYWH<u16>> for App {
|
||||
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_full,
|
||||
[kw_when, kw_either, kw_split, kw_align, kw_exact, kw_min, kw_max, kw_push,
|
||||
kw_tui_text, kw_tui_fg, kw_tui_bg]
|
||||
.into_iter()
|
||||
}
|
||||
|
|
@ -1476,26 +1467,11 @@ mod draw {
|
|||
let mut frags = lang.src()?.unwrap().split("/");
|
||||
match frags.next() {
|
||||
//Some(":logo") => view_logo().draw(to),
|
||||
Some(":meters") => 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!()
|
||||
},
|
||||
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(":meters") => draw_meter_section(to, frags),
|
||||
Some(":tracks") => draw_tracks(to, frags, self),
|
||||
Some(":scenes") => draw_scenes(to, frags),
|
||||
Some(":dialog") => draw_dialog(to, frags, self, lang),
|
||||
Some(":templates") => view_templates(frags, self).draw(to),
|
||||
Some(":templates") => draw_templates(to, frags, self),
|
||||
Some(":sessions") => view_sessions().draw(to),
|
||||
Some(":browse/title") => view_browse_title(self).draw(to),
|
||||
Some(":device") => view_device(self).draw(to),
|
||||
|
|
@ -1510,7 +1486,7 @@ mod draw {
|
|||
std::mem::drop(views);
|
||||
self.interpret(to, &lang)
|
||||
} else {
|
||||
fg(Color::Rgb(128, 32, 32), format!("undefined: {lang:?}")).draw(to)
|
||||
unimplemented!("{lang:?}");
|
||||
}
|
||||
},
|
||||
_ => unreachable!()
|
||||
|
|
@ -1528,7 +1504,34 @@ mod draw {
|
|||
fn width_dec (&mut self);
|
||||
}
|
||||
|
||||
pub fn view_templates (_frags: std::str::Split<&str>, state: &App) -> impl Draw<Tui> {
|
||||
pub fn draw_meter_section (to: &mut Tui, mut frags: std::str::Split<&str>) -> Drawn<u16> {
|
||||
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;
|
||||
draw(move |to: &mut Tui|{
|
||||
let mut index = 0;
|
||||
|
|
@ -1546,29 +1549,29 @@ mod draw {
|
|||
index += 1;
|
||||
});
|
||||
Ok(Some(to.area().into()))
|
||||
}).min_w(30).exact_h(height)
|
||||
}).min_w(30).exact_h(height).draw(to)
|
||||
}
|
||||
|
||||
//pub fn per_track <'a, T: Draw<Tui> + 'a, U: TracksSizes<'a>> (
|
||||
//tracks: impl Fn() -> U + Send + Sync + 'a,
|
||||
//callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
|
||||
//) -> impl Draw<Tui> + 'a {
|
||||
//per_track_top(tracks, move|index, track|callback(index, track).full_h().align_y())
|
||||
//}
|
||||
pub fn per_track <'a, T: Draw<Tui> + 'a, U: TracksSizes<'a>> (
|
||||
tracks: impl Fn() -> U + Send + Sync + 'a,
|
||||
callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
|
||||
) -> impl Draw<Tui> + 'a {
|
||||
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>> (
|
||||
//tracks: impl Fn() -> U + Send + Sync + 'a,
|
||||
//callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
|
||||
//) -> impl Draw<Tui> + 'a {
|
||||
//bg(Reset, iter_east(||tracks()
|
||||
//.map(move|(index, track, x1, x2): (usize, &'a Track, usize, usize)|{
|
||||
//fg_bg(
|
||||
//track.color.lightest.term,
|
||||
//track.color.base.term,
|
||||
//callback(index, track)
|
||||
//).exact_w((x2 - x1) as u16)
|
||||
//})).align_x())
|
||||
//}
|
||||
pub fn per_track_top <'a, T: Draw<Tui> + 'a, U: TracksSizes<'a>> (
|
||||
tracks: impl Fn() -> U + Send + Sync + 'a,
|
||||
callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
|
||||
) -> impl Draw<Tui> + 'a {
|
||||
bg(Reset, iter_east(tracks,
|
||||
move|(index, track, x1, x2): (usize, &'a Track, usize, usize), _|{
|
||||
fg_bg(
|
||||
track.color.lightest.term,
|
||||
track.color.base.term,
|
||||
callback(index, track)
|
||||
).exact_w((x2 - x1) as u16)
|
||||
}).align_x())
|
||||
}
|
||||
|
||||
pub fn field_h <T: Screen> (
|
||||
_theme: ItemTheme, _head: impl Draw<T>, _body: impl Draw<T>
|
||||
|
|
@ -1929,73 +1932,17 @@ mod draw {
|
|||
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>> (
|
||||
//tracks: impl Fn() -> U + 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,
|
||||
pub fn view_track_per <'a, T: Draw<Tui> + 'a, U: TracksSizes<'a>> (
|
||||
tracks: impl Fn() -> U + Send + Sync + 'a,
|
||||
callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
|
||||
) -> impl Draw<Tui> {
|
||||
let c1 = tui_orange();
|
||||
let c2 = tui_g(0);
|
||||
let c3 = tui_g(96);
|
||||
let c4 = tui_g(255);
|
||||
let c5 = tui_g(128);
|
||||
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, "▌"), ))))
|
||||
iter_east(tracks, move|(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)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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 8a6eb19e279afefc126008c27b38cc66645e1c96
|
||||
Subproject commit 799e49762250f48778dadd92874a29495114a6af
|
||||
Loading…
Add table
Add a link
Reference in a new issue