mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 03:36:41 +01:00
This commit is contained in:
parent
eb0547dc37
commit
2858b01bd4
12 changed files with 379 additions and 391 deletions
|
|
@ -9,7 +9,7 @@
|
||||||
(@shift-O project output-add)
|
(@shift-O project output-add)
|
||||||
(@shift-S project scene-add)
|
(@shift-S project scene-add)
|
||||||
(@shift-T project track-add)
|
(@shift-T project track-add)
|
||||||
(@shift-D toggle-dialog :dialog-device)
|
(@shift-D dialog :dialog-device)
|
||||||
|
|
||||||
(@up select :select-scene-prev)
|
(@up select :select-scene-prev)
|
||||||
(@down select :select-scene-next)
|
(@down select :select-scene-next)
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
(@esc dialog :dialog-none)
|
(@esc dialog :dialog-none)
|
||||||
(@f1 dialog :dialog-help)
|
(@f1 dialog :dialog-help)
|
||||||
(@f6 dialog :dialog-save)
|
(@f6 dialog :dialog-save)
|
||||||
(@f8 dialog :dialog-options)
|
(@f8 dialog :dialog-options)
|
||||||
(@f9 dialog :dialog-load)
|
(@f9 dialog :dialog-load)
|
||||||
(@f10 dialog :dialog-quit)
|
(@f10 dialog :dialog-quit)
|
||||||
|
|
||||||
(@u undo 1)
|
(@u undo 1)
|
||||||
(@r redo 1)
|
(@r redo 1)
|
||||||
|
|
|
||||||
|
|
@ -1,199 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
/// Configuration
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
pub struct Configuration {
|
|
||||||
/// Path of configuration entrypoint
|
|
||||||
pub path: PathBuf,
|
|
||||||
/// Name of configuration
|
|
||||||
pub name: Option<Arc<str>>,
|
|
||||||
/// Description of configuration
|
|
||||||
pub info: Option<Arc<str>>,
|
|
||||||
/// View definition
|
|
||||||
pub view: TokenIter<'static>,
|
|
||||||
// Input keymap
|
|
||||||
pub keys: InputMap<'static, App, AppCommand, TuiIn, TokenIter<'static>>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Configuration {
|
|
||||||
|
|
||||||
pub fn new (path: &impl AsRef<Path>, _watch: bool) -> Usually<Self> {
|
|
||||||
let text = read_and_leak(path.as_ref())?;
|
|
||||||
let [name, info, view, keys] = Self::parse(TokenIter::from(text))?;
|
|
||||||
Ok(Self {
|
|
||||||
path: path.as_ref().into(),
|
|
||||||
info: info.map(Self::parse_info).flatten(),
|
|
||||||
name: name.map(Self::parse_name).flatten(),
|
|
||||||
view: Self::parse_view(view)?,
|
|
||||||
keys: Self::parse_keys(&path, keys)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse (iter: TokenIter) -> Usually<[Option<TokenIter>;4]> {
|
|
||||||
let mut name: Option<TokenIter> = None;
|
|
||||||
let mut info: Option<TokenIter> = None;
|
|
||||||
let mut view: Option<TokenIter> = None;
|
|
||||||
let mut keys: Option<TokenIter> = None;
|
|
||||||
for token in iter {
|
|
||||||
match token.value {
|
|
||||||
Value::Exp(_, mut exp) => {
|
|
||||||
let next = exp.next();
|
|
||||||
match next {
|
|
||||||
Some(Token { value: Value::Key(sym), .. }) => match sym {
|
|
||||||
"name" => name = Some(exp),
|
|
||||||
"info" => info = Some(exp),
|
|
||||||
"keys" => keys = Some(exp),
|
|
||||||
"view" => view = Some(exp),
|
|
||||||
_ => return Err(
|
|
||||||
format!("(e3) unexpected symbol {sym:?}").into()
|
|
||||||
)
|
|
||||||
},
|
|
||||||
_ => return Err(
|
|
||||||
format!("(e2) unexpected exp {:?}", next.map(|x|x.value)).into()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
t => return Err(
|
|
||||||
format!("(e1) unexpected token {token:?}").into()
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Ok([name, info, view, keys])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_info (mut iter: TokenIter) -> Option<Arc<str>> {
|
|
||||||
iter.next().and_then(|x|if let Value::Str(x) = x.value {
|
|
||||||
Some(x.into())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_name (mut iter: TokenIter) -> Option<Arc<str>> {
|
|
||||||
iter.next().and_then(|x|if let Value::Str(x) = x.value {
|
|
||||||
Some(x.into())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_view (iter: Option<TokenIter>) -> Usually<TokenIter> {
|
|
||||||
if let Some(view) = iter {
|
|
||||||
Ok(view)
|
|
||||||
} else {
|
|
||||||
Err(format!("missing view definition").into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_keys (base: &impl AsRef<Path>, iter: Option<TokenIter<'static>>)
|
|
||||||
-> Usually<InputMap<'static, App, AppCommand, TuiIn, TokenIter<'static>>>
|
|
||||||
{
|
|
||||||
if iter.is_none() {
|
|
||||||
return Err(format!("missing keys definition").into())
|
|
||||||
}
|
|
||||||
let mut keys = iter.unwrap();
|
|
||||||
let mut map = InputMap::default();
|
|
||||||
while let Some(token) = keys.next() {
|
|
||||||
if let Value::Exp(_, mut exp) = token.value {
|
|
||||||
let next = exp.next();
|
|
||||||
if let Some(Token { value: Value::Key(sym), .. }) = next {
|
|
||||||
match sym {
|
|
||||||
"layer" => {
|
|
||||||
let next = exp.next();
|
|
||||||
if let Some(Token { value: Value::Str(path), .. }) = next {
|
|
||||||
let path = base.as_ref().parent().unwrap().join(unquote(path));
|
|
||||||
if !std::fs::exists(&path)? {
|
|
||||||
return Err(format!("(e5) not found: {path:?}").into())
|
|
||||||
}
|
|
||||||
map.add_layer(read_and_leak(path)?.into());
|
|
||||||
} else {
|
|
||||||
return Err(format!("(e4) unexpected non-string {next:?}").into())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"layer-if" => {
|
|
||||||
let mut cond = None;
|
|
||||||
|
|
||||||
let next = exp.next();
|
|
||||||
if let Some(Token { value: Value::Sym(sym), .. }) = next {
|
|
||||||
cond = Some(leak(sym));
|
|
||||||
} else {
|
|
||||||
return Err(format!("(e4) unexpected non-symbol {next:?}").into())
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(Token { value: Value::Str(path), .. }) = exp.peek() {
|
|
||||||
let path = base.as_ref().parent().unwrap().join(unquote(path));
|
|
||||||
if !std::fs::exists(&path)? {
|
|
||||||
return Err(format!("(e5) not found: {path:?}").into())
|
|
||||||
}
|
|
||||||
print!("{path:?}...");
|
|
||||||
let keys = read_and_leak(path)?.into();
|
|
||||||
let cond = cond.unwrap();
|
|
||||||
print!("{exp:?}...");
|
|
||||||
println!("ok");
|
|
||||||
map.add_layer_if(
|
|
||||||
Box::new(move |state|{
|
|
||||||
let mut exp = exp.clone();
|
|
||||||
Context::get(state, &mut exp).unwrap_or(false)
|
|
||||||
}),
|
|
||||||
keys
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return Err(format!("(e4) unexpected non-symbol {next:?}").into())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_ => return Err(format!("(e3) unexpected symbol {sym:?}").into())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Err(format!("(e2) unexpected exp {:?}", next.map(|x|x.value)).into())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Err(format!("(e1) unexpected token {token:?}").into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(map)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_and_leak (path: impl AsRef<Path>) -> Usually<&'static str> {
|
|
||||||
Ok(leak(String::from_utf8(std::fs::read(path.as_ref())?)?))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn leak (x: impl AsRef<str>) -> &'static str {
|
|
||||||
Box::leak(x.as_ref().into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unquote (x: &str) -> &str {
|
|
||||||
let mut chars = x.chars();
|
|
||||||
chars.next();
|
|
||||||
//chars.next_back();
|
|
||||||
chars.as_str()
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! default_config { ($path:literal) => { ($path, include_str!($path)) }; }
|
|
||||||
pub const DEFAULT_CONFIGS: &'static [(&'static str, &'static str)] = &[
|
|
||||||
default_config!("../../../config/config_arranger.edn"),
|
|
||||||
default_config!("../../../config/config_groovebox.edn"),
|
|
||||||
default_config!("../../../config/config_sampler.edn"),
|
|
||||||
default_config!("../../../config/config_sequencer.edn"),
|
|
||||||
default_config!("../../../config/config_transport.edn"),
|
|
||||||
|
|
||||||
default_config!("../../../config/keys_arranger.edn"),
|
|
||||||
default_config!("../../../config/keys_clip.edn"),
|
|
||||||
default_config!("../../../config/keys_clock.edn"),
|
|
||||||
default_config!("../../../config/keys_editor.edn"),
|
|
||||||
default_config!("../../../config/keys_global.edn"),
|
|
||||||
default_config!("../../../config/keys_groovebox.edn"),
|
|
||||||
default_config!("../../../config/keys_length.edn"),
|
|
||||||
default_config!("../../../config/keys_mix.edn"),
|
|
||||||
default_config!("../../../config/keys_pool.edn"),
|
|
||||||
default_config!("../../../config/keys_pool_file.edn"),
|
|
||||||
default_config!("../../../config/keys_rename.edn"),
|
|
||||||
default_config!("../../../config/keys_sampler.edn"),
|
|
||||||
default_config!("../../../config/keys_scene.edn"),
|
|
||||||
default_config!("../../../config/keys_sequencer.edn"),
|
|
||||||
default_config!("../../../config/keys_track.edn"),
|
|
||||||
];
|
|
||||||
|
|
@ -36,7 +36,6 @@ pub(crate) use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering::Relaxed};
|
||||||
|
|
||||||
mod api; pub use self::api::*;
|
mod api; pub use self::api::*;
|
||||||
mod audio; pub use self::audio::*;
|
mod audio; pub use self::audio::*;
|
||||||
mod config; pub use self::config::*;
|
|
||||||
mod model; pub use self::model::*;
|
mod model; pub use self::model::*;
|
||||||
mod view; pub use self::view::*;
|
mod view; pub use self::view::*;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct App {
|
pub struct App {
|
||||||
|
|
@ -28,6 +29,7 @@ pub struct App {
|
||||||
|
|
||||||
has!(Jack: |self: App|self.jack);
|
has!(Jack: |self: App|self.jack);
|
||||||
has!(Pool: |self: App|self.pool);
|
has!(Pool: |self: App|self.pool);
|
||||||
|
has!(Option<Dialog>: |self: App|self.dialog);
|
||||||
has!(Clock: |self: App|self.project.clock);
|
has!(Clock: |self: App|self.project.clock);
|
||||||
has!(Option<MidiEditor>: |self: App|self.project.editor);
|
has!(Option<MidiEditor>: |self: App|self.project.editor);
|
||||||
has!(Selection: |self: App|self.project.selection);
|
has!(Selection: |self: App|self.project.selection);
|
||||||
|
|
@ -91,12 +93,6 @@ impl App {
|
||||||
pub(crate) fn device_pick (&mut self, index: usize) {
|
pub(crate) fn device_pick (&mut self, index: usize) {
|
||||||
self.dialog = Some(Dialog::Device(index));
|
self.dialog = Some(Dialog::Device(index));
|
||||||
}
|
}
|
||||||
pub(crate) fn device_kinds (&self) -> &'static [&'static str] {
|
|
||||||
&[
|
|
||||||
"Sampler",
|
|
||||||
"Plugin (LV2)",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
pub(crate) fn device_add (&mut self, index: usize) -> Usually<()> {
|
pub(crate) fn device_add (&mut self, index: usize) -> Usually<()> {
|
||||||
match index {
|
match index {
|
||||||
0 => self.device_add_sampler(),
|
0 => self.device_add_sampler(),
|
||||||
|
|
@ -166,35 +162,6 @@ impl App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Various possible dialog overlays
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum Dialog {
|
|
||||||
Help(usize),
|
|
||||||
Menu(usize),
|
|
||||||
Device(usize),
|
|
||||||
Message(Message),
|
|
||||||
Browser(BrowserTarget, Browser),
|
|
||||||
Options,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum BrowserTarget {
|
|
||||||
SaveProject,
|
|
||||||
LoadProject,
|
|
||||||
ImportSample(Arc<RwLock<Option<Sample>>>),
|
|
||||||
ExportSample(Arc<RwLock<Option<Sample>>>),
|
|
||||||
ImportClip(Arc<RwLock<Option<MidiClip>>>),
|
|
||||||
ExportClip(Arc<RwLock<Option<MidiClip>>>),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Various possible messages
|
|
||||||
#[derive(PartialEq, Clone, Copy, Debug)]
|
|
||||||
pub enum Message {
|
|
||||||
FailedToAddDevice,
|
|
||||||
}
|
|
||||||
|
|
||||||
content!(TuiOut: |self: Message| match self { Self::FailedToAddDevice => "Failed to add device." });
|
|
||||||
|
|
||||||
#[tengri_proc::expose]
|
#[tengri_proc::expose]
|
||||||
impl App {
|
impl App {
|
||||||
fn _todo_isize_stub (&self) -> isize {
|
fn _todo_isize_stub (&self) -> isize {
|
||||||
|
|
@ -332,16 +299,214 @@ impl App {
|
||||||
}
|
}
|
||||||
fn device_kind_prev (&self) -> usize {
|
fn device_kind_prev (&self) -> usize {
|
||||||
if let Some(Dialog::Device(index)) = self.dialog {
|
if let Some(Dialog::Device(index)) = self.dialog {
|
||||||
index.overflowing_sub(1).0.min(self.device_kinds().len().saturating_sub(1))
|
index.overflowing_sub(1).0.min(device_kinds().len().saturating_sub(1))
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn device_kind_next (&self) -> usize {
|
fn device_kind_next (&self) -> usize {
|
||||||
if let Some(Dialog::Device(index)) = self.dialog {
|
if let Some(Dialog::Device(index)) = self.dialog {
|
||||||
(index + 1) % self.device_kinds().len()
|
(index + 1) % device_kinds().len()
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Configuration
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
pub struct Configuration {
|
||||||
|
/// Path of configuration entrypoint
|
||||||
|
pub path: PathBuf,
|
||||||
|
/// Name of configuration
|
||||||
|
pub name: Option<Arc<str>>,
|
||||||
|
/// Description of configuration
|
||||||
|
pub info: Option<Arc<str>>,
|
||||||
|
/// View definition
|
||||||
|
pub view: TokenIter<'static>,
|
||||||
|
// Input keymap
|
||||||
|
pub keys: InputMap<'static, App, AppCommand, TuiIn, TokenIter<'static>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Configuration {
|
||||||
|
|
||||||
|
pub fn new (path: &impl AsRef<Path>, _watch: bool) -> Usually<Self> {
|
||||||
|
let text = read_and_leak(path.as_ref())?;
|
||||||
|
let [name, info, view, keys] = Self::parse(TokenIter::from(text))?;
|
||||||
|
Ok(Self {
|
||||||
|
path: path.as_ref().into(),
|
||||||
|
info: info.map(Self::parse_info).flatten(),
|
||||||
|
name: name.map(Self::parse_name).flatten(),
|
||||||
|
view: Self::parse_view(view)?,
|
||||||
|
keys: Self::parse_keys(&path, keys)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse (iter: TokenIter) -> Usually<[Option<TokenIter>;4]> {
|
||||||
|
let mut name: Option<TokenIter> = None;
|
||||||
|
let mut info: Option<TokenIter> = None;
|
||||||
|
let mut view: Option<TokenIter> = None;
|
||||||
|
let mut keys: Option<TokenIter> = None;
|
||||||
|
for token in iter {
|
||||||
|
match token.value {
|
||||||
|
Value::Exp(_, mut exp) => {
|
||||||
|
let next = exp.next();
|
||||||
|
match next {
|
||||||
|
Some(Token { value: Value::Key(sym), .. }) => match sym {
|
||||||
|
"name" => name = Some(exp),
|
||||||
|
"info" => info = Some(exp),
|
||||||
|
"keys" => keys = Some(exp),
|
||||||
|
"view" => view = Some(exp),
|
||||||
|
_ => return Err(
|
||||||
|
format!("(e3) unexpected symbol {sym:?}").into()
|
||||||
|
)
|
||||||
|
},
|
||||||
|
_ => return Err(
|
||||||
|
format!("(e2) unexpected exp {:?}", next.map(|x|x.value)).into()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
t => return Err(
|
||||||
|
format!("(e1) unexpected token {token:?}").into()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok([name, info, view, keys])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_info (mut iter: TokenIter) -> Option<Arc<str>> {
|
||||||
|
iter.next().and_then(|x|if let Value::Str(x) = x.value {
|
||||||
|
Some(x.into())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_name (mut iter: TokenIter) -> Option<Arc<str>> {
|
||||||
|
iter.next().and_then(|x|if let Value::Str(x) = x.value {
|
||||||
|
Some(x.into())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_view (iter: Option<TokenIter>) -> Usually<TokenIter> {
|
||||||
|
if let Some(view) = iter {
|
||||||
|
Ok(view)
|
||||||
|
} else {
|
||||||
|
Err(format!("missing view definition").into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_keys (base: &impl AsRef<Path>, iter: Option<TokenIter<'static>>)
|
||||||
|
-> Usually<InputMap<'static, App, AppCommand, TuiIn, TokenIter<'static>>>
|
||||||
|
{
|
||||||
|
if iter.is_none() {
|
||||||
|
return Err(format!("missing keys definition").into())
|
||||||
|
}
|
||||||
|
let mut keys = iter.unwrap();
|
||||||
|
let mut map = InputMap::default();
|
||||||
|
while let Some(token) = keys.next() {
|
||||||
|
if let Value::Exp(_, mut exp) = token.value {
|
||||||
|
let next = exp.next();
|
||||||
|
if let Some(Token { value: Value::Key(sym), .. }) = next {
|
||||||
|
match sym {
|
||||||
|
"layer" => {
|
||||||
|
if let Some(Token { value: Value::Str(path), .. }) = exp.peek() {
|
||||||
|
let path = base.as_ref().parent().unwrap().join(unquote(path));
|
||||||
|
if !std::fs::exists(&path)? {
|
||||||
|
return Err(format!("(e5) not found: {path:?}").into())
|
||||||
|
}
|
||||||
|
map.add_layer(read_and_leak(path)?.into());
|
||||||
|
print!("layer:\n path: {:?}...", exp.0.0.trim());
|
||||||
|
println!("ok");
|
||||||
|
} else {
|
||||||
|
return Err(format!("(e4) unexpected non-string {next:?}").into())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"layer-if" => {
|
||||||
|
let mut cond = None;
|
||||||
|
|
||||||
|
let next = exp.next();
|
||||||
|
if let Some(Token { value: Value::Sym(sym), .. }) = next {
|
||||||
|
cond = Some(leak(sym));
|
||||||
|
} else {
|
||||||
|
return Err(format!("(e4) unexpected non-symbol {next:?}").into())
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(Token { value: Value::Str(path), .. }) = exp.peek() {
|
||||||
|
let path = base.as_ref().parent().unwrap().join(unquote(path));
|
||||||
|
if !std::fs::exists(&path)? {
|
||||||
|
return Err(format!("(e5) not found: {path:?}").into())
|
||||||
|
}
|
||||||
|
print!("layer-if:\n cond: {}\n path: {path:?}...",
|
||||||
|
cond.unwrap_or_default());
|
||||||
|
let keys = read_and_leak(path)?.into();
|
||||||
|
let cond = cond.unwrap();
|
||||||
|
println!("ok");
|
||||||
|
map.add_layer_if(
|
||||||
|
Box::new(move |state|{
|
||||||
|
let mut exp = exp.clone();
|
||||||
|
Context::get(state, &mut exp).unwrap_or(false)
|
||||||
|
}),
|
||||||
|
keys
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Err(format!("(e4) unexpected non-symbol {next:?}").into())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => return Err(format!("(e3) unexpected symbol {sym:?}").into())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(format!("(e2) unexpected exp {:?}", next.map(|x|x.value)).into())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(format!("(e1) unexpected token {token:?}").into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(map)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_and_leak (path: impl AsRef<Path>) -> Usually<&'static str> {
|
||||||
|
Ok(leak(String::from_utf8(std::fs::read(path.as_ref())?)?))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn leak (x: impl AsRef<str>) -> &'static str {
|
||||||
|
Box::leak(x.as_ref().into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unquote (x: &str) -> &str {
|
||||||
|
let mut chars = x.chars();
|
||||||
|
chars.next();
|
||||||
|
//chars.next_back();
|
||||||
|
chars.as_str()
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! default_config { ($path:literal) => { ($path, include_str!($path)) }; }
|
||||||
|
pub const DEFAULT_CONFIGS: &'static [(&'static str, &'static str)] = &[
|
||||||
|
default_config!("../../../config/config_arranger.edn"),
|
||||||
|
default_config!("../../../config/config_groovebox.edn"),
|
||||||
|
default_config!("../../../config/config_sampler.edn"),
|
||||||
|
default_config!("../../../config/config_sequencer.edn"),
|
||||||
|
default_config!("../../../config/config_transport.edn"),
|
||||||
|
|
||||||
|
default_config!("../../../config/keys_arranger.edn"),
|
||||||
|
default_config!("../../../config/keys_clip.edn"),
|
||||||
|
default_config!("../../../config/keys_clock.edn"),
|
||||||
|
default_config!("../../../config/keys_editor.edn"),
|
||||||
|
default_config!("../../../config/keys_global.edn"),
|
||||||
|
default_config!("../../../config/keys_groovebox.edn"),
|
||||||
|
default_config!("../../../config/keys_length.edn"),
|
||||||
|
default_config!("../../../config/keys_mix.edn"),
|
||||||
|
default_config!("../../../config/keys_pool.edn"),
|
||||||
|
default_config!("../../../config/keys_pool_file.edn"),
|
||||||
|
default_config!("../../../config/keys_rename.edn"),
|
||||||
|
default_config!("../../../config/keys_sampler.edn"),
|
||||||
|
default_config!("../../../config/keys_scene.edn"),
|
||||||
|
default_config!("../../../config/keys_sequencer.edn"),
|
||||||
|
default_config!("../../../config/keys_track.edn"),
|
||||||
|
];
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,7 @@ impl App {
|
||||||
self.project.view_scenes_clips()
|
self.project.view_scenes_clips()
|
||||||
}
|
}
|
||||||
pub fn view_tracks_inputs <'a> (&'a self) -> impl Content<TuiOut> + use<'a> {
|
pub fn view_tracks_inputs <'a> (&'a self) -> impl Content<TuiOut> + use<'a> {
|
||||||
Fixed::y(2 + self.project.midi_ins.len() as u16, self.project.view_inputs(self.color))
|
Fixed::y(1 + self.project.midi_ins.len() as u16, self.project.view_inputs(self.color))
|
||||||
}
|
}
|
||||||
pub fn view_tracks_outputs <'a> (&'a self) -> impl Content<TuiOut> + use<'a> {
|
pub fn view_tracks_outputs <'a> (&'a self) -> impl Content<TuiOut> + use<'a> {
|
||||||
Fixed::y(1 + self.project.midi_outs.len() as u16, self.project.view_outputs(self.color))
|
Fixed::y(1 + self.project.midi_outs.len() as u16, self.project.view_outputs(self.color))
|
||||||
|
|
@ -163,100 +163,10 @@ impl App {
|
||||||
self.project.sampler().map(|s|s.view_meters_output())
|
self.project.sampler().map(|s|s.view_meters_output())
|
||||||
}
|
}
|
||||||
pub fn view_dialog (&self) -> impl Content<TuiOut> + use<'_> {
|
pub fn view_dialog (&self) -> impl Content<TuiOut> + use<'_> {
|
||||||
When(self.dialog.is_some(), Bsp::b( "",
|
self.dialog.as_ref().map(|dialog|Bsp::b("",
|
||||||
Fixed::xy(70, 23, Tui::fg_bg(Rgb(255,255,255), Rgb(16,16,16), Bsp::b(
|
Fixed::xy(70, 23, Tui::fg_bg(Rgb(255,255,255), Rgb(16,16,16), Bsp::b(
|
||||||
Repeat(" "), Outer(true, Style::default().fg(Tui::g(96)))
|
Repeat(" "), Outer(true, Style::default().fg(Tui::g(96)))
|
||||||
.enclose(self.dialog.as_ref().map(|dialog|match dialog {
|
.enclose(dialog))))))
|
||||||
Dialog::Menu(_) =>
|
|
||||||
self.view_dialog_menu().boxed(),
|
|
||||||
Dialog::Help(offset) =>
|
|
||||||
self.view_dialog_help(*offset).boxed(),
|
|
||||||
Dialog::Browser(target, browser) =>
|
|
||||||
self.view_dialog_browser(target, browser).boxed(),
|
|
||||||
Dialog::Options =>
|
|
||||||
self.view_dialog_options().boxed(),
|
|
||||||
Dialog::Device(index) =>
|
|
||||||
self.view_dialog_device(*index).boxed(),
|
|
||||||
Dialog::Message(message) =>
|
|
||||||
self.view_dialog_message(message).boxed(),
|
|
||||||
}))
|
|
||||||
)))
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl App {
|
|
||||||
pub fn view_dialog_menu (&self) -> impl Content<TuiOut> {
|
|
||||||
let options = ||["Projects", "Settings", "Help", "Quit"].iter();
|
|
||||||
let option = |a,i|Tui::fg(Rgb(255,255,255), format!("{}", a));
|
|
||||||
Bsp::s(Tui::bold(true, "tek!"), Bsp::s("", Map::south(1, options, option)))
|
|
||||||
}
|
|
||||||
pub fn view_dialog_help <'a> (&'a self, offset: usize) -> impl Content<TuiOut> + use<'a> {
|
|
||||||
Bsp::s(Tui::bold(true, "Help"), Bsp::s("", Map::south(1,
|
|
||||||
move||self.config.keys.layers.iter()
|
|
||||||
.filter_map(|a|(a.0)(self).then_some(a.1))
|
|
||||||
.flat_map(|a|a)
|
|
||||||
.filter_map(|x|if let Value::Exp(_, iter)=x.value{ Some(iter) } else { None })
|
|
||||||
.skip(offset)
|
|
||||||
.take(20),
|
|
||||||
|mut b,i|Fixed::x(60, Align::w(Bsp::e("(", Bsp::e(
|
|
||||||
b.next().map(|t|Fixed::x(16, Align::w(Tui::fg(Rgb(64,224,0), format!("{}", t.value))))),
|
|
||||||
Bsp::e(" ", Align::w(format!("{}", b.0.0.trim()))))))))))
|
|
||||||
}
|
|
||||||
pub fn view_dialog_device (&self, index: usize) -> impl Content<TuiOut> + use<'_> {
|
|
||||||
let choices = ||self.device_kinds().iter();
|
|
||||||
let choice = move|label, i|
|
|
||||||
Fill::x(Tui::bg(if i == index { Rgb(64,128,32) } else { Rgb(0,0,0) },
|
|
||||||
Bsp::e(if i == index { "[ " } else { " " },
|
|
||||||
Bsp::w(if i == index { " ]" } else { " " },
|
|
||||||
label))));
|
|
||||||
Bsp::s(Tui::bold(true, "Add device"), Map::south(1, choices, choice))
|
|
||||||
}
|
|
||||||
pub fn view_dialog_message <'a> (&'a self, message: &'a Message) -> impl Content<TuiOut> + use<'a> {
|
|
||||||
Bsp::s(message, Bsp::s("", "[ OK ]"))
|
|
||||||
}
|
|
||||||
pub fn view_dialog_browser <'a> (&'a self, target: &BrowserTarget, browser: &'a Browser) -> impl Content<TuiOut> + use<'a> {
|
|
||||||
Bsp::s(
|
|
||||||
Padding::xy(3, 1, Fill::x(Align::w(FieldV(
|
|
||||||
self.color,
|
|
||||||
match target {
|
|
||||||
BrowserTarget::SaveProject => "Save project:",
|
|
||||||
BrowserTarget::LoadProject => "Load project:",
|
|
||||||
BrowserTarget::ImportSample(_) => "Import sample:",
|
|
||||||
BrowserTarget::ExportSample(_) => "Export sample:",
|
|
||||||
BrowserTarget::ImportClip(_) => "Import clip:",
|
|
||||||
BrowserTarget::ExportClip(_) => "Export clip:",
|
|
||||||
},
|
|
||||||
Shrink::x(3, Fixed::y(1, Tui::fg(Tui::g(96), RepeatH("🭻")))))))),
|
|
||||||
Outer(true, Style::default().fg(Tui::g(96)))
|
|
||||||
.enclose(Fill::xy(browser)))
|
|
||||||
}
|
|
||||||
pub fn view_dialog_load <'a> (&'a self, browser: &'a Browser) -> impl Content<TuiOut> + use<'a> {
|
|
||||||
Bsp::s(
|
|
||||||
Fill::x(Align::w(Margin::xy(1, 1, Bsp::e(
|
|
||||||
Tui::bold(true, " Load project: "),
|
|
||||||
Shrink::x(3, Fixed::y(1, RepeatH("🭻"))))))),
|
|
||||||
Outer(true, Style::default().fg(Tui::g(96)))
|
|
||||||
.enclose(Fill::xy(browser)))
|
|
||||||
}
|
|
||||||
pub fn view_dialog_export <'a> (&'a self, browser: &'a Browser) -> impl Content<TuiOut> + use<'a> {
|
|
||||||
Bsp::s(
|
|
||||||
Fill::x(Align::w(Margin::xy(1, 1, Bsp::e(
|
|
||||||
Tui::bold(true, " Export: "),
|
|
||||||
Shrink::x(3, Fixed::y(1, RepeatH("🭻"))))))),
|
|
||||||
Outer(true, Style::default().fg(Tui::g(96)))
|
|
||||||
.enclose(Fill::xy(browser)))
|
|
||||||
}
|
|
||||||
pub fn view_dialog_import <'a> (&'a self, browser: &'a Browser) -> impl Content<TuiOut> + use<'a> {
|
|
||||||
Bsp::s(
|
|
||||||
Fill::x(Align::w(Margin::xy(1, 1, Bsp::e(
|
|
||||||
Tui::bold(true, " Import: "),
|
|
||||||
Shrink::x(3, Fixed::y(1, RepeatH("🭻"))))))),
|
|
||||||
Outer(true, Style::default().fg(Tui::g(96)))
|
|
||||||
.enclose(Fill::xy(browser)))
|
|
||||||
}
|
|
||||||
pub fn view_dialog_options <'a> (&'a self) -> impl Content<TuiOut> + use<'a> {
|
|
||||||
"TODO"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ use crate::*;
|
||||||
|
|
||||||
impl Arrangement {
|
impl Arrangement {
|
||||||
pub fn view_inputs <'a> (&'a self, theme: ItemTheme) -> impl Content<TuiOut> + 'a {
|
pub fn view_inputs <'a> (&'a self, theme: ItemTheme) -> impl Content<TuiOut> + 'a {
|
||||||
let mut h = 1u16;
|
let mut h = 0;
|
||||||
for track in self.tracks().iter() {
|
for track in self.tracks().iter() {
|
||||||
h = h.max(track.sequencer.midi_ins.len() as u16);
|
h = h.max(self.midi_ins.len() as u16);
|
||||||
}
|
}
|
||||||
let h = h + 1;
|
let h = h + 1;
|
||||||
self.view_track_row_section(
|
self.view_track_row_section(
|
||||||
|
|
@ -153,12 +153,22 @@ pub trait TracksView:
|
||||||
fn view_track_names (&self, theme: ItemTheme) -> impl Content<TuiOut> {
|
fn view_track_names (&self, theme: ItemTheme) -> impl Content<TuiOut> {
|
||||||
self.view_track_row_section(
|
self.view_track_row_section(
|
||||||
theme,
|
theme,
|
||||||
button_3("t", "rack ", if let Some(track) = self.selection().track() {
|
Bsp::s(
|
||||||
format!("{track}/{}", self.tracks().len())
|
button_3("t", "rack ", if let Some(track) = self.selection().track() {
|
||||||
} else {
|
format!("{track}/{}", self.tracks().len())
|
||||||
format!("{}", self.tracks().len())
|
} else {
|
||||||
}, false),
|
format!("{}", self.tracks().len())
|
||||||
button_2("T", "+", false),
|
}, false),
|
||||||
|
button_3("s", "cene ", if let Some(scene) = self.selection().scene() {
|
||||||
|
format!("{scene}/{}", self.scenes().len())
|
||||||
|
} else {
|
||||||
|
format!("{}", self.scenes().len())
|
||||||
|
}, false)
|
||||||
|
),
|
||||||
|
Bsp::s(
|
||||||
|
button_2("T", "+", false),
|
||||||
|
button_2("S", "+", false),
|
||||||
|
),
|
||||||
Tui::bg(theme.darker.rgb, Fixed::y(2, Fill::x(
|
Tui::bg(theme.darker.rgb, Fixed::y(2, Fill::x(
|
||||||
Stack::east(move|add: &mut dyn FnMut(&dyn Render<TuiOut>)|{
|
Stack::east(move|add: &mut dyn FnMut(&dyn Render<TuiOut>)|{
|
||||||
for (index, track, x1, x2) in self.tracks_with_sizes() {
|
for (index, track, x1, x2) in self.tracks_with_sizes() {
|
||||||
|
|
@ -256,7 +266,7 @@ pub trait ScenesView:
|
||||||
self.scenes().iter().enumerate().skip(self.scene_scroll()).map_while(move|(s, scene)|{
|
self.scenes().iter().enumerate().skip(self.scene_scroll()).map_while(move|(s, scene)|{
|
||||||
let active = editing && selected_track.is_some() && selected_scene == Some(s);
|
let active = editing && selected_track.is_some() && selected_scene == Some(s);
|
||||||
let height = if active { larger } else { height };
|
let height = if active { larger } else { height };
|
||||||
if y + height < self.clips_size().h() {
|
if y + height <= self.clips_size().h() {
|
||||||
let data = (s, scene, y, y + height);
|
let data = (s, scene, y, y + height);
|
||||||
y += height;
|
y += height;
|
||||||
Some(data)
|
Some(data)
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,16 @@ use crate::*;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum BrowserTarget {
|
||||||
|
SaveProject,
|
||||||
|
LoadProject,
|
||||||
|
ImportSample(Arc<RwLock<Option<Sample>>>),
|
||||||
|
ExportSample(Arc<RwLock<Option<Sample>>>),
|
||||||
|
ImportClip(Arc<RwLock<Option<MidiClip>>>),
|
||||||
|
ExportClip(Arc<RwLock<Option<MidiClip>>>),
|
||||||
|
}
|
||||||
|
|
||||||
/// Browses for phrase to import/export
|
/// Browses for phrase to import/export
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct Browser {
|
pub struct Browser {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,12 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
|
pub fn device_kinds () -> &'static [&'static str] {
|
||||||
|
&[
|
||||||
|
"Sampler",
|
||||||
|
"Plugin (LV2)",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: Has<Vec<Device>> + Has<Track>> HasDevices for T {
|
impl<T: Has<Vec<Device>> + Has<Track>> HasDevices for T {
|
||||||
fn devices (&self) -> &Vec<Device> {
|
fn devices (&self) -> &Vec<Device> {
|
||||||
self.get()
|
self.get()
|
||||||
|
|
|
||||||
113
crates/device/src/dialog.rs
Normal file
113
crates/device/src/dialog.rs
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
/// Various possible dialog overlays
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum Dialog {
|
||||||
|
Help(usize),
|
||||||
|
Menu(usize),
|
||||||
|
Device(usize),
|
||||||
|
Message(Message),
|
||||||
|
Browser(BrowserTarget, Browser),
|
||||||
|
Options,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Various possible messages
|
||||||
|
#[derive(PartialEq, Clone, Copy, Debug)]
|
||||||
|
pub enum Message {
|
||||||
|
FailedToAddDevice,
|
||||||
|
}
|
||||||
|
|
||||||
|
content!(TuiOut: |self: Message| match self {
|
||||||
|
Self::FailedToAddDevice => "Failed to add device."
|
||||||
|
});
|
||||||
|
|
||||||
|
content!(TuiOut: |self: Dialog| match self {
|
||||||
|
Self::Menu(_) =>
|
||||||
|
self.view_dialog_menu().boxed(),
|
||||||
|
Self::Help(offset) =>
|
||||||
|
self.view_dialog_help(*offset).boxed(),
|
||||||
|
Self::Browser(target, browser) =>
|
||||||
|
self.view_dialog_browser(target, browser).boxed(),
|
||||||
|
Self::Options =>
|
||||||
|
self.view_dialog_options().boxed(),
|
||||||
|
Self::Device(index) =>
|
||||||
|
self.view_dialog_device(*index).boxed(),
|
||||||
|
Self::Message(message) =>
|
||||||
|
self.view_dialog_message(message).boxed(),
|
||||||
|
});
|
||||||
|
|
||||||
|
impl Dialog {
|
||||||
|
pub fn view_dialog_menu (&self) -> impl Content<TuiOut> {
|
||||||
|
let options = ||["Projects", "Settings", "Help", "Quit"].iter();
|
||||||
|
let option = |a,i|Tui::fg(Rgb(255,255,255), format!("{}", a));
|
||||||
|
Bsp::s(Tui::bold(true, "tek!"), Bsp::s("", Map::south(1, options, option)))
|
||||||
|
}
|
||||||
|
pub fn view_dialog_help <'a> (&'a self, offset: usize) -> impl Content<TuiOut> + use<'a> {
|
||||||
|
Bsp::s(Tui::bold(true, "Help"), "FIXME")
|
||||||
|
//Bsp::s(Tui::bold(true, "Help"), Bsp::s("", Map::south(1,
|
||||||
|
//move||self.config.keys.layers.iter()
|
||||||
|
//.filter_map(|a|(a.0)(self).then_some(a.1))
|
||||||
|
//.flat_map(|a|a)
|
||||||
|
//.filter_map(|x|if let Value::Exp(_, iter)=x.value{ Some(iter) } else { None })
|
||||||
|
//.skip(offset)
|
||||||
|
//.take(20),
|
||||||
|
//|mut b,i|Fixed::x(60, Align::w(Bsp::e("(", Bsp::e(
|
||||||
|
//b.next().map(|t|Fixed::x(16, Align::w(Tui::fg(Rgb(64,224,0), format!("{}", t.value))))),
|
||||||
|
//Bsp::e(" ", Align::w(format!("{}", b.0.0.trim()))))))))))
|
||||||
|
}
|
||||||
|
pub fn view_dialog_device (&self, index: usize) -> impl Content<TuiOut> + use<'_> {
|
||||||
|
let choices = ||device_kinds().iter();
|
||||||
|
let choice = move|label, i|
|
||||||
|
Fill::x(Tui::bg(if i == index { Rgb(64,128,32) } else { Rgb(0,0,0) },
|
||||||
|
Bsp::e(if i == index { "[ " } else { " " },
|
||||||
|
Bsp::w(if i == index { " ]" } else { " " },
|
||||||
|
label))));
|
||||||
|
Bsp::s(Tui::bold(true, "Add device"), Map::south(1, choices, choice))
|
||||||
|
}
|
||||||
|
pub fn view_dialog_message <'a> (&'a self, message: &'a Message) -> impl Content<TuiOut> + use<'a> {
|
||||||
|
Bsp::s(message, Bsp::s("", "[ OK ]"))
|
||||||
|
}
|
||||||
|
pub fn view_dialog_browser <'a> (&'a self, target: &BrowserTarget, browser: &'a Browser) -> impl Content<TuiOut> + use<'a> {
|
||||||
|
Bsp::s(
|
||||||
|
Padding::xy(3, 1, Fill::x(Align::w(FieldV(
|
||||||
|
Default::default(),
|
||||||
|
match target {
|
||||||
|
BrowserTarget::SaveProject => "Save project:",
|
||||||
|
BrowserTarget::LoadProject => "Load project:",
|
||||||
|
BrowserTarget::ImportSample(_) => "Import sample:",
|
||||||
|
BrowserTarget::ExportSample(_) => "Export sample:",
|
||||||
|
BrowserTarget::ImportClip(_) => "Import clip:",
|
||||||
|
BrowserTarget::ExportClip(_) => "Export clip:",
|
||||||
|
},
|
||||||
|
Shrink::x(3, Fixed::y(1, Tui::fg(Tui::g(96), RepeatH("🭻")))))))),
|
||||||
|
Outer(true, Style::default().fg(Tui::g(96)))
|
||||||
|
.enclose(Fill::xy(browser)))
|
||||||
|
}
|
||||||
|
pub fn view_dialog_load <'a> (&'a self, browser: &'a Browser) -> impl Content<TuiOut> + use<'a> {
|
||||||
|
Bsp::s(
|
||||||
|
Fill::x(Align::w(Margin::xy(1, 1, Bsp::e(
|
||||||
|
Tui::bold(true, " Load project: "),
|
||||||
|
Shrink::x(3, Fixed::y(1, RepeatH("🭻"))))))),
|
||||||
|
Outer(true, Style::default().fg(Tui::g(96)))
|
||||||
|
.enclose(Fill::xy(browser)))
|
||||||
|
}
|
||||||
|
pub fn view_dialog_export <'a> (&'a self, browser: &'a Browser) -> impl Content<TuiOut> + use<'a> {
|
||||||
|
Bsp::s(
|
||||||
|
Fill::x(Align::w(Margin::xy(1, 1, Bsp::e(
|
||||||
|
Tui::bold(true, " Export: "),
|
||||||
|
Shrink::x(3, Fixed::y(1, RepeatH("🭻"))))))),
|
||||||
|
Outer(true, Style::default().fg(Tui::g(96)))
|
||||||
|
.enclose(Fill::xy(browser)))
|
||||||
|
}
|
||||||
|
pub fn view_dialog_import <'a> (&'a self, browser: &'a Browser) -> impl Content<TuiOut> + use<'a> {
|
||||||
|
Bsp::s(
|
||||||
|
Fill::x(Align::w(Margin::xy(1, 1, Bsp::e(
|
||||||
|
Tui::bold(true, " Import: "),
|
||||||
|
Shrink::x(3, Fixed::y(1, RepeatH("🭻"))))))),
|
||||||
|
Outer(true, Style::default().fg(Tui::g(96)))
|
||||||
|
.enclose(Fill::xy(browser)))
|
||||||
|
}
|
||||||
|
pub fn view_dialog_options <'a> (&'a self) -> impl Content<TuiOut> + use<'a> {
|
||||||
|
"TODO"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,54 +14,27 @@ pub(crate) use std::error::Error;
|
||||||
pub(crate) use std::ffi::OsString;
|
pub(crate) use std::ffi::OsString;
|
||||||
|
|
||||||
pub(crate) use ::tengri::{from, has, maybe_has, Usually, Perhaps, Has, MaybeHas};
|
pub(crate) use ::tengri::{from, has, maybe_has, Usually, Perhaps, Has, MaybeHas};
|
||||||
pub(crate) use ::tengri::{dsl::*, input::*, output::*, tui::{*, ratatui::prelude::*}};
|
pub(crate) use ::tengri::{dsl::*, input::*, output::{*, Margin}, tui::{*, ratatui::prelude::*}};
|
||||||
pub(crate) use ::tek_engine::*;
|
pub(crate) use ::tek_engine::*;
|
||||||
pub(crate) use ::tek_engine::midi::{u7, LiveEvent, MidiMessage};
|
pub(crate) use ::tek_engine::midi::{u7, LiveEvent, MidiMessage};
|
||||||
pub(crate) use ::tek_engine::jack::{Control, ProcessScope, MidiWriter, RawMidi};
|
pub(crate) use ::tek_engine::jack::{Control, ProcessScope, MidiWriter, RawMidi};
|
||||||
pub(crate) use ratatui::{prelude::Rect, widgets::{Widget, canvas::{Canvas, Line}}};
|
pub(crate) use ratatui::{prelude::Rect, widgets::{Widget, canvas::{Canvas, Line}}};
|
||||||
pub(crate) use Color::*;
|
pub(crate) use Color::*;
|
||||||
|
|
||||||
mod device;
|
mod device; pub use self::device::*;
|
||||||
pub use self::device::*;
|
mod dialog; pub use self::dialog::*;
|
||||||
|
|
||||||
#[cfg(feature = "arranger")] mod arranger;
|
#[cfg(feature = "arranger")] mod arranger; #[cfg(feature = "arranger")] pub use self::arranger::*;
|
||||||
#[cfg(feature = "arranger")] pub use self::arranger::*;
|
#[cfg(feature = "browser")] mod browser; #[cfg(feature = "browser")] pub use self::browser::*;
|
||||||
|
#[cfg(feature = "clock")] mod clock; #[cfg(feature = "clock")] pub use self::clock::*;
|
||||||
#[cfg(feature = "browser")] mod browser;
|
#[cfg(feature = "editor")] mod editor; #[cfg(feature = "editor")] pub use self::editor::*;
|
||||||
#[cfg(feature = "browser")] pub use self::browser::*;
|
#[cfg(feature = "pool")] mod pool; #[cfg(feature = "pool")] pub use self::pool::*;
|
||||||
|
#[cfg(feature = "sequencer")] mod sequencer; #[cfg(feature = "sequencer")] pub use self::sequencer::*;
|
||||||
#[cfg(feature = "clock")] mod clock;
|
#[cfg(feature = "sampler")] mod sampler; #[cfg(feature = "sampler")] pub use self::sampler::*;
|
||||||
#[cfg(feature = "clock")] pub use self::clock::*;
|
#[cfg(feature = "meter")] mod meter; #[cfg(feature = "meter")] pub use self::meter::*;
|
||||||
|
#[cfg(feature = "mixer")] mod mixer; #[cfg(feature = "mixer")] pub use self::mixer::*;
|
||||||
#[cfg(feature = "editor")] mod editor;
|
#[cfg(feature = "lv2")] mod lv2; #[cfg(feature = "lv2")] pub use self::lv2::*;
|
||||||
#[cfg(feature = "editor")] pub use self::editor::*;
|
#[cfg(feature = "sf2")] mod sf2; #[cfg(feature = "sf2")] pub use self::sf2::*;
|
||||||
|
#[cfg(feature = "vst2")] mod vst2; #[cfg(feature = "vst2")] pub use self::vst2::*;
|
||||||
#[cfg(feature = "pool")] mod pool;
|
#[cfg(feature = "vst3")] mod vst3; #[cfg(feature = "vst3")] pub use self::vst3::*;
|
||||||
#[cfg(feature = "pool")] pub use self::pool::*;
|
#[cfg(feature = "clap")] mod clap; #[cfg(feature = "clap")] pub use self::clap::*;
|
||||||
|
|
||||||
#[cfg(feature = "sequencer")] mod sequencer;
|
|
||||||
#[cfg(feature = "sequencer")] pub use self::sequencer::*;
|
|
||||||
|
|
||||||
#[cfg(feature = "sampler")] mod sampler;
|
|
||||||
#[cfg(feature = "sampler")] pub use self::sampler::*;
|
|
||||||
|
|
||||||
#[cfg(feature = "meter")] mod meter;
|
|
||||||
#[cfg(feature = "meter")] pub use self::meter::*;
|
|
||||||
|
|
||||||
#[cfg(feature = "mixer")] mod mixer;
|
|
||||||
#[cfg(feature = "mixer")] pub use self::mixer::*;
|
|
||||||
|
|
||||||
#[cfg(feature = "lv2")] mod lv2;
|
|
||||||
#[cfg(feature = "lv2")] pub use self::lv2::*;
|
|
||||||
|
|
||||||
#[cfg(feature = "sf2")] mod sf2;
|
|
||||||
#[cfg(feature = "sf2")] pub use self::sf2::*;
|
|
||||||
|
|
||||||
#[cfg(feature = "vst2")] mod vst2;
|
|
||||||
#[cfg(feature = "vst2")] pub use self::vst2::*;
|
|
||||||
|
|
||||||
#[cfg(feature = "vst3")] mod vst3;
|
|
||||||
#[cfg(feature = "vst3")] pub use self::vst3::*;
|
|
||||||
|
|
||||||
#[cfg(feature = "clap")] mod clap;
|
|
||||||
#[cfg(feature = "clap")] pub use self::clap::*;
|
|
||||||
|
|
|
||||||
2
deps/tengri
vendored
2
deps/tengri
vendored
|
|
@ -1 +1 @@
|
||||||
Subproject commit 12998a94ea02bc84c1a490783bc76b10789ce37f
|
Subproject commit f21781e81664e1991e3985e2377becca9c1d58cf
|
||||||
Loading…
Add table
Add a link
Reference in a new issue