mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 04:06:45 +01:00
Compare commits
2 commits
3c8616deba
...
f488811767
| Author | SHA1 | Date | |
|---|---|---|---|
| f488811767 | |||
| e9f912f4d9 |
5 changed files with 164 additions and 126 deletions
80
config/profiles.edn
Normal file
80
config/profiles.edn
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
(module :transport
|
||||||
|
(name "Transport")
|
||||||
|
(info "A JACK transport controller.")
|
||||||
|
(keys :clock)
|
||||||
|
(keys :global)
|
||||||
|
:view/transport)
|
||||||
|
|
||||||
|
(module :arranger
|
||||||
|
(name "Arranger")
|
||||||
|
(info "A grid of launchable clips arranged by track and scene.")
|
||||||
|
(mode :editor (keys :editor))
|
||||||
|
(mode :dialog (keys :dialog))
|
||||||
|
(mode :message (keys :message))
|
||||||
|
(mode :device-add (keys :device-add))
|
||||||
|
(mode :browser (keys :browser))
|
||||||
|
(mode :rename (keys :pool-rename))
|
||||||
|
(mode :length (keys :pool-length))
|
||||||
|
(mode :clip (keys :clip))
|
||||||
|
(mode :track (keys :track))
|
||||||
|
(mode :scene (keys :scene))
|
||||||
|
(mode :mix (keys :mix))
|
||||||
|
(keys :clock)
|
||||||
|
(keys :arranger)
|
||||||
|
(keys :global)
|
||||||
|
:view/dialog
|
||||||
|
(bsp/w :view/meters/output
|
||||||
|
(bsp/e :view/meters/input
|
||||||
|
(stack/n (fixed/y 2 :view/status/h2) :view/tracks/inputs
|
||||||
|
(stack/s :view/tracks/devices :view/tracks/outputs :view/tracks/names
|
||||||
|
(fill/xy (either :mode/editor
|
||||||
|
(bsp/e :view/scenes/names :view/editor)
|
||||||
|
:view/scenes)))))))
|
||||||
|
|
||||||
|
(module :groovebox
|
||||||
|
(name "Groovebox")
|
||||||
|
(info "A sequencer with built-in sampler.")
|
||||||
|
(mode :browser (keys :browser))
|
||||||
|
(mode :rename (keys :pool-rename))
|
||||||
|
(mode :length (keys :pool-length))
|
||||||
|
(keys :clock)
|
||||||
|
(keys :editor)
|
||||||
|
(keys :sampler)
|
||||||
|
(keys :global)
|
||||||
|
:view/dialog
|
||||||
|
(bsp/w :view/meters/output
|
||||||
|
(bsp/e :view/meters/input
|
||||||
|
(bsp/w
|
||||||
|
(fill/y (align/n (stack/s :view/midi-ins/status
|
||||||
|
:view/midi-outs/status
|
||||||
|
:view/audio-ins/status
|
||||||
|
:view/audio-outs/status
|
||||||
|
:view/pool)))
|
||||||
|
(bsp/n (fixed/y :h-sample-detail (bsp/e (fill/y (fixed/x 20 (align/nw :view/sample-status)))
|
||||||
|
:view/sample-viewer))
|
||||||
|
(bsp/e (fill/y (align/n (bsp/s :view/status/v :view/editor-status)))
|
||||||
|
(bsp/e :view/samples/keys
|
||||||
|
:view/editor)))))))
|
||||||
|
|
||||||
|
(module :sampler
|
||||||
|
(name "Sampler")
|
||||||
|
(info "A sampling soundboard.")
|
||||||
|
(keys :sampler)
|
||||||
|
(keys :global)
|
||||||
|
:view/dialog
|
||||||
|
(bsp/s (fixed/y 1 :view/transport)
|
||||||
|
(bsp/n (fixed/y 1 :view/status)
|
||||||
|
(fill/xy :view/samples/grid))))
|
||||||
|
|
||||||
|
(module :sequencer
|
||||||
|
(name "Sequencer")
|
||||||
|
(info "A MIDI sequencer.")
|
||||||
|
(mode :browser (keys :browser))
|
||||||
|
(mode :rename (keys :pool-rename))
|
||||||
|
(mode :length (keys :pool-length))
|
||||||
|
(keys :editor)
|
||||||
|
(keys :clock)
|
||||||
|
(keys :global)
|
||||||
|
:view/dialog
|
||||||
|
(bsp/s (fixed/y 1 :view/transport) (bsp/n (fixed/y 1 :view/status)
|
||||||
|
(fill/xy (bsp/a (fill/xy (align/e :view/pool)) :view/editor)))))
|
||||||
|
|
@ -1,74 +0,0 @@
|
||||||
(module :transport
|
|
||||||
(name "Transport")
|
|
||||||
(info "A JACK transport controller.")
|
|
||||||
(bind :keys/clock)
|
|
||||||
(bind :keys/global)
|
|
||||||
:view/transport)
|
|
||||||
|
|
||||||
(module :arranger
|
|
||||||
(name "Arranger")
|
|
||||||
(info "A grid of launchable clips arranged by track and scene.")
|
|
||||||
(bind :keys/editor :focused/editor)
|
|
||||||
(bind :keys/dialog :focused/dialog)
|
|
||||||
(bind :keys/message :focused/message)
|
|
||||||
(bind :keys/device_add :focused/device-add)
|
|
||||||
(bind :keys/browser :focused/browser)
|
|
||||||
(bind :keys/rename :focused/pool-rename)
|
|
||||||
(bind :keys/length :focused/pool-length)
|
|
||||||
(bind :keys/clip :focused/clip)
|
|
||||||
(bind :keys/track :focused/track)
|
|
||||||
(bind :keys/scene :focused/scene)
|
|
||||||
(bind :keys/mix :focused/mix)
|
|
||||||
(bind :keys/clock)
|
|
||||||
(bind :keys/arranger)
|
|
||||||
(bind :keys/global)
|
|
||||||
:view/dialog
|
|
||||||
(bsp/w :view/meters/output
|
|
||||||
(bsp/e :view/meters/input
|
|
||||||
(stack/n (fixed/y 2 :view/status/h2) :view/tracks/inputs
|
|
||||||
(stack/s :view/tracks/devices :view/tracks/outputs :view/tracks/names
|
|
||||||
(fill/xy (either :focused/editor
|
|
||||||
(bsp/e :view/scenes/names :view/editor)
|
|
||||||
:view/scenes)))))))
|
|
||||||
|
|
||||||
(module :groovebox
|
|
||||||
(name "Groovebox")
|
|
||||||
(info "A sequencer with built-in sampler.")
|
|
||||||
(bind :keys/browser :focused/browser)
|
|
||||||
(bind :keys/rename :focused/pool-rename)
|
|
||||||
(bind :keys/length :focused/pool-length)
|
|
||||||
(bind :keys/clock)
|
|
||||||
(bind :keys/editor)
|
|
||||||
(bind :keys/sampler)
|
|
||||||
(bind :keys/global)
|
|
||||||
:view/dialog
|
|
||||||
(bsp/w :view/meters/output (bsp/e :view/meters/input (bsp/w (fill/y (align/n (stack/s :view/midi-ins/status
|
|
||||||
:view/midi-outs/status
|
|
||||||
:view/audio-ins/status
|
|
||||||
:view/audio-outs/status
|
|
||||||
:view/pool)))
|
|
||||||
(bsp/n (fixed/y :h-sample-detail (bsp/e (fill/y (fixed/x 20 (align/nw :view/sample-status))) :view/sample-viewer))
|
|
||||||
(bsp/e (fill/y (align/n (bsp/s :view/status/v :view/editor-status))) (bsp/e :view/samples/keys :view/editor)))))))
|
|
||||||
|
|
||||||
(module :sampler
|
|
||||||
(name "Sampler")
|
|
||||||
(info "A sampling soundboard.")
|
|
||||||
(bind :keys/sampler)
|
|
||||||
(bind :keys/global)
|
|
||||||
:view/dialog
|
|
||||||
(bsp/s (fixed/y 1 :view/transport)
|
|
||||||
(bsp/n (fixed/y 1 :view/status)
|
|
||||||
(fill/xy :view/samples/grid))))
|
|
||||||
|
|
||||||
(module :sequencer
|
|
||||||
(name "Sequencer")
|
|
||||||
(info "A MIDI sequencer.")
|
|
||||||
(bind :keys/browser :focused/browser)
|
|
||||||
(bind :keys/rename :mode/pool-rename)
|
|
||||||
(bind :keys/length :mode/pool-length)
|
|
||||||
(bind :keys/editor)
|
|
||||||
(bind :keys/clock)
|
|
||||||
(bind :keys/global)
|
|
||||||
:view/dialog
|
|
||||||
(bsp/s (fixed/y 1 :view/transport) (bsp/n (fixed/y 1 :view/status)
|
|
||||||
(fill/xy (bsp/a (fill/xy (align/e :view/pool)) :view/editor)))))
|
|
||||||
|
|
@ -1,17 +1,16 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use xdg::BaseDirectories;
|
use xdg::BaseDirectories;
|
||||||
|
/// Configuration
|
||||||
/// Configurations
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Configurations {
|
pub struct Configurations {
|
||||||
pub dirs: BaseDirectories,
|
pub dirs: BaseDirectories,
|
||||||
pub modules: Arc<RwLock<std::collections::BTreeMap<Arc<str>, Arc<str>>>>,
|
pub current: Option<Profile>,
|
||||||
pub current: Option<Configuration>
|
pub profiles: Arc<RwLock<std::collections::BTreeMap<Arc<str>, Profile>>>,
|
||||||
|
pub bindings: Arc<RwLock<std::collections::BTreeMap<Arc<str>, Arc<str>>>>,
|
||||||
}
|
}
|
||||||
|
/// Profile
|
||||||
/// Configuration
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Configuration {
|
pub struct Profile {
|
||||||
/// Path of configuration entrypoint
|
/// Path of configuration entrypoint
|
||||||
pub path: std::path::PathBuf,
|
pub path: std::path::PathBuf,
|
||||||
/// Name of configuration
|
/// Name of configuration
|
||||||
|
|
@ -23,56 +22,82 @@ pub struct Configuration {
|
||||||
// Input keymap
|
// Input keymap
|
||||||
pub keys: EventMap<TuiEvent, AppCommand>,
|
pub keys: EventMap<TuiEvent, AppCommand>,
|
||||||
}
|
}
|
||||||
|
impl Profile {
|
||||||
macro_rules! dsl_for_each (($dsl:expr => |$head:ident|$body:expr)=>{
|
fn from_dsl (dsl: impl Dsl) -> Usually<Self> {
|
||||||
let mut dsl: Arc<str> = $dsl.src().into();
|
let mut profile = Self { ..Default::default() };
|
||||||
let mut $head: Option<Arc<str>> = dsl.head()?.map(Into::into);
|
dsl.each(|dsl|{
|
||||||
let mut tail: Option<Arc<str>> = dsl.tail()?.map(Into::into);
|
let head = dsl.head();
|
||||||
loop {
|
let exp = dsl.exp();
|
||||||
if let Some($head) = $head {
|
Ok(if exp.head().key() == Ok(Some("name")) {
|
||||||
$body;
|
profile.name = Some(exp.tail()?.unwrap_or_default().into());
|
||||||
} else {
|
} else if exp.head().key() == Ok(Some("info")) {
|
||||||
break
|
profile.info = Some(exp.tail()?.unwrap_or_default().into());
|
||||||
}
|
})
|
||||||
if let Some(next) = tail {
|
})?;
|
||||||
$head = next.head()?.map(Into::into);
|
Ok(profile)
|
||||||
tail = next.tail()?.map(Into::into);
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
impl Configurations {
|
impl Configurations {
|
||||||
const DEFAULT_TEMPLATES: &'static str = include_str!("../../../config/templates.edn");
|
const PROFILES: &'static str = "profiles.edn";
|
||||||
|
const BINDINGS: &'static str = "bindings.edn";
|
||||||
|
const DEFAULT_PROFILES: &'static str = include_str!("../../../config/profiles.edn");
|
||||||
const DEFAULT_BINDINGS: &'static str = include_str!("../../../config/bindings.edn");
|
const DEFAULT_BINDINGS: &'static str = include_str!("../../../config/bindings.edn");
|
||||||
pub fn init () -> Usually<Self> {
|
pub fn init () -> Usually<Self> {
|
||||||
let mut dirs = BaseDirectories::with_profile("tek", "v0");
|
let mut dirs = BaseDirectories::with_profile("tek", "v0");
|
||||||
let mut cfgs = Self { dirs, ..Default::default() };
|
let mut cfgs = Self { dirs, ..Default::default() };
|
||||||
cfgs.init_file("templates.edn", Self::DEFAULT_TEMPLATES)?;
|
cfgs.init_file(Self::PROFILES, Self::DEFAULT_PROFILES)?;
|
||||||
cfgs.load_file("templates.edn", |head|{ Ok(()) })?;
|
cfgs.load_file(Self::PROFILES, |cfgs, dsl|{
|
||||||
cfgs.init_file("bindings.edn", Self::DEFAULT_BINDINGS)?;
|
Ok(if dsl.exp().head().key() == Ok(Some("module")) {
|
||||||
cfgs.load_file("bindings.end", |head|{ Ok(()) })?;
|
let exp = dsl.exp()?;
|
||||||
|
let tail = exp.tail()?;
|
||||||
|
let head = tail.head()?;
|
||||||
|
if let Some(id) = head.sym()? {
|
||||||
|
cfgs.profiles.write().unwrap().insert(
|
||||||
|
id.into(),
|
||||||
|
Profile::from_dsl(tail.tail()?)?
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err("unexpected: {exp:?}".into());
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
//cfgs.init_file(Self::BINDINGS, Self::DEFAULT_BINDINGS)?;
|
||||||
|
//cfgs.load_file(Self::BINDINGS, |cfgs, dsl|Ok(
|
||||||
|
//if let Some(exp) = dsl.head()?.exp()? && exp.head()?.key()? == Some("module") {
|
||||||
|
//let name = exp.tail()?.head()?.unwrap_or_default().into();
|
||||||
|
//println!("name = {name}");
|
||||||
|
//let body = exp.tail()?.tail()?.unwrap_or_default().into();
|
||||||
|
//println!("body = {body}");
|
||||||
|
//cfgs.bindings.write().unwrap().insert(name, body);
|
||||||
|
//} else {
|
||||||
|
//return Err("unexpected: {exp:?}".into());
|
||||||
|
//}
|
||||||
|
//))?;
|
||||||
|
println!("{cfgs:#?}");
|
||||||
Ok(cfgs)
|
Ok(cfgs)
|
||||||
}
|
}
|
||||||
fn init_file (&mut self, path: &str, val: &str) -> Usually<()> {
|
fn init_file (&mut self, path: &str, val: &str) -> Usually<()> {
|
||||||
if self.dirs.find_config_file("templates.edn").is_none() {
|
if self.dirs.find_config_file(path).is_none() {
|
||||||
std::fs::write(self.dirs.place_config_file("templates.edn")?, Self::DEFAULT_TEMPLATES);
|
std::fs::write(self.dirs.place_config_file("profiles.edn")?, Self::DEFAULT_PROFILES);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn load_file (&mut self, path: &str, mut each: impl FnMut(&Arc<str>)->Usually<()> ) -> Usually<()> {
|
fn load_file (
|
||||||
Ok(if let Some(path) = self.dirs.find_config_file("templates.edn") {
|
&mut self,
|
||||||
dsl_for_each!(std::fs::read_to_string(path)?.as_str() => |dsl|each(&dsl));
|
path: &str,
|
||||||
|
mut each: impl FnMut(&mut Self, &str)->Usually<()>
|
||||||
|
) -> Usually<()> {
|
||||||
|
Ok(if let Some(path) = self.dirs.find_config_file(path) {
|
||||||
|
let src = std::fs::read_to_string(&path)?;
|
||||||
|
src.as_str().each(move|item|each(self, item))?;
|
||||||
} else {
|
} else {
|
||||||
return Err(format!("{path}: not found").into())
|
return Err(format!("{path}: not found").into())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Profile {
|
||||||
impl Configuration {
|
|
||||||
fn load_template (&mut self, dsl: impl Dsl) -> Usually<&mut Self> {
|
fn load_template (&mut self, dsl: impl Dsl) -> Usually<&mut Self> {
|
||||||
dsl_for_each!(dsl => |dsl|match () {
|
dsl.src()?.unwrap_or_default().each(|item|Ok(match () {
|
||||||
_ if let Some(exp) = dsl.exp()? => match exp.head()?.key()? {
|
_ if let Some(exp) = dsl.exp()? => match exp.head()?.key()? {
|
||||||
Some("name") => match exp.tail()?.text()? {
|
Some("name") => match exp.tail()?.text()? {
|
||||||
Some(name) => self.name = Some(name.into()),
|
Some(name) => self.name = Some(name.into()),
|
||||||
|
|
@ -87,14 +112,13 @@ impl Configuration {
|
||||||
_ => return Err(format!("missing keys definition").into())
|
_ => return Err(format!("missing keys definition").into())
|
||||||
},
|
},
|
||||||
Some("view") => match exp.tail()? {
|
Some("view") => match exp.tail()? {
|
||||||
Some(tail) => self.view = tail.src().into(),
|
Some(tail) => self.view = tail.src()?.unwrap_or_default().into(),
|
||||||
_ => return Err(format!("missing view definition").into())
|
_ => return Err(format!("missing view definition").into())
|
||||||
},
|
},
|
||||||
dsl => return Err(format!("unexpected: {dsl:?}").into())
|
dsl => return Err(format!("unexpected: {dsl:?}").into())
|
||||||
},
|
},
|
||||||
|
|
||||||
_ => return Err(format!("unexpected: {dsl:?}").into())
|
_ => return Err(format!("unexpected: {dsl:?}").into())
|
||||||
});
|
}));
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
fn load_binding (&mut self, dsl: impl Dsl) -> Usually<&mut Self> {
|
fn load_binding (&mut self, dsl: impl Dsl) -> Usually<&mut Self> {
|
||||||
|
|
|
||||||
|
|
@ -38,11 +38,19 @@ impl App {
|
||||||
"nil"
|
"nil"
|
||||||
}
|
}
|
||||||
pub fn view_menu (&self) -> impl Content<TuiOut> + use<'_> {
|
pub fn view_menu (&self) -> impl Content<TuiOut> + use<'_> {
|
||||||
Stack::south(|add: &mut dyn FnMut(&dyn Render<TuiOut>)|{
|
Bsp::s(Fill::x(Fixed::y(3, Tui::bg(Rgb(33,33,33), Tui::bold(true, "tek 0.3.0-rc0")))),
|
||||||
add(&Tui::bold(true, "tek 0.3.0-rc0"));
|
Bsp::n(Fill::x(Fixed::y(3, Tui::bg(Rgb(33,33,33), "+ new session"))),
|
||||||
add(&"");
|
Fill::xy(Stack::south(|add: &mut dyn FnMut(&dyn Render<TuiOut>)|{
|
||||||
add(&"+ new session");
|
for (index, (id, profile)) in self.configs.profiles.read().unwrap().iter().enumerate() {
|
||||||
})
|
add(&Fixed::y(3, Tui::bg(if index == 0 { Rgb(64,64,64) } else { Rgb(32,32,32) }, Bsp::s(
|
||||||
|
Fill::x(Bsp::a(
|
||||||
|
Fill::x(Align::w(Tui::fg(Rgb(224,192,128), &profile.name))),
|
||||||
|
Fill::x(Align::e(Tui::fg(Rgb(224,128,32), id)))
|
||||||
|
)),
|
||||||
|
Fill::x(Align::w(&profile.info))
|
||||||
|
))));
|
||||||
|
}
|
||||||
|
}))))
|
||||||
}
|
}
|
||||||
pub fn view_dialog (&self) -> impl Content<TuiOut> + use<'_> {
|
pub fn view_dialog (&self) -> impl Content<TuiOut> + use<'_> {
|
||||||
self.dialog.as_ref().map(|dialog|Bsp::b("",
|
self.dialog.as_ref().map(|dialog|Bsp::b("",
|
||||||
|
|
|
||||||
2
deps/tengri
vendored
2
deps/tengri
vendored
|
|
@ -1 +1 @@
|
||||||
Subproject commit 9e0b7be9a9c80b5df52854ab426bc60b794931ed
|
Subproject commit 104bb1c8e76cacf249ccd340712ea7bd2d33b5f6
|
||||||
Loading…
Add table
Add a link
Reference in a new issue