closer and closer to testing it away

This commit is contained in:
stop screaming 2026-02-21 18:13:36 +02:00
parent 817d2a722c
commit 4aef21f60d
10 changed files with 1782 additions and 2028 deletions

82
Cargo.lock generated
View file

@ -140,9 +140,9 @@ dependencies = [
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.101" version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]] [[package]]
name = "approx" name = "approx"
@ -398,7 +398,7 @@ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.116", "syn 2.0.117",
] ]
[[package]] [[package]]
@ -476,7 +476,7 @@ checksum = "eb0240417fe20ccf13397fa25e6f0a987dbbfaf27d9e13532419df7f593e65e8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.116", "syn 2.0.117",
"unicode-xid", "unicode-xid",
] ]
@ -613,7 +613,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501"
dependencies = [ dependencies = [
"quote", "quote",
"syn 2.0.116", "syn 2.0.117",
] ]
[[package]] [[package]]
@ -642,7 +642,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"strsim", "strsim",
"syn 2.0.116", "syn 2.0.117",
] ]
[[package]] [[package]]
@ -653,7 +653,7 @@ checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d"
dependencies = [ dependencies = [
"darling_core", "darling_core",
"quote", "quote",
"syn 2.0.116", "syn 2.0.117",
] ]
[[package]] [[package]]
@ -675,7 +675,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"rustc_version", "rustc_version",
"syn 2.0.116", "syn 2.0.117",
] ]
[[package]] [[package]]
@ -815,7 +815,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.116", "syn 2.0.117",
] ]
[[package]] [[package]]
@ -880,7 +880,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.116", "syn 2.0.117",
] ]
[[package]] [[package]]
@ -1046,7 +1046,7 @@ dependencies = [
"indoc", "indoc",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.116", "syn 2.0.117",
] ]
[[package]] [[package]]
@ -1148,9 +1148,9 @@ dependencies = [
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.85" version = "0.3.87"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" checksum = "93f0862381daaec758576dcc22eb7bbf4d7efd67328553f3b45a412a51a3fb21"
dependencies = [ dependencies = [
"once_cell", "once_cell",
"wasm-bindgen", "wasm-bindgen",
@ -1416,7 +1416,7 @@ dependencies = [
"proc-macro-crate", "proc-macro-crate",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.116", "syn 2.0.117",
] ]
[[package]] [[package]]
@ -1684,7 +1684,7 @@ dependencies = [
"by_address", "by_address",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.116", "syn 2.0.117",
] ]
[[package]] [[package]]
@ -1777,7 +1777,7 @@ dependencies = [
"phf_shared", "phf_shared",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.116", "syn 2.0.117",
] ]
[[package]] [[package]]
@ -1806,7 +1806,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.116", "syn 2.0.117",
] ]
[[package]] [[package]]
@ -1851,7 +1851,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"syn 2.0.116", "syn 2.0.117",
] ]
[[package]] [[package]]
@ -1899,7 +1899,7 @@ checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.116", "syn 2.0.117",
] ]
[[package]] [[package]]
@ -2253,7 +2253,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.116", "syn 2.0.117",
] ]
[[package]] [[package]]
@ -2404,7 +2404,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"rustversion", "rustversion",
"syn 2.0.116", "syn 2.0.117",
] ]
[[package]] [[package]]
@ -2615,9 +2615,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.116" version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3df424c70518695237746f84cede799c9c58fcb37450d7b23716568cc8bc69cb" checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2704,7 +2704,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.116", "syn 2.0.117",
] ]
[[package]] [[package]]
@ -2715,7 +2715,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.116", "syn 2.0.117",
] ]
[[package]] [[package]]
@ -2946,9 +2946,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.108" version = "0.2.110"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" checksum = "1de241cdc66a9d91bd84f097039eb140cdc6eec47e0cdbaf9d932a1dd6c35866"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"once_cell", "once_cell",
@ -2959,9 +2959,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-futures" name = "wasm-bindgen-futures"
version = "0.4.58" version = "0.4.60"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" checksum = "a42e96ea38f49b191e08a1bab66c7ffdba24b06f9995b39a9dd60222e5b6f1da"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"futures-util", "futures-util",
@ -2973,9 +2973,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro" name = "wasm-bindgen-macro"
version = "0.2.108" version = "0.2.110"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" checksum = "e12fdf6649048f2e3de6d7d5ff3ced779cdedee0e0baffd7dff5cdfa3abc8a52"
dependencies = [ dependencies = [
"quote", "quote",
"wasm-bindgen-macro-support", "wasm-bindgen-macro-support",
@ -2983,22 +2983,22 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro-support" name = "wasm-bindgen-macro-support"
version = "0.2.108" version = "0.2.110"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" checksum = "0e63d1795c565ac3462334c1e396fd46dbf481c40f51f5072c310717bc4fb309"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.116", "syn 2.0.117",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
[[package]] [[package]]
name = "wasm-bindgen-shared" name = "wasm-bindgen-shared"
version = "0.2.108" version = "0.2.110"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" checksum = "e9f9cdac23a5ce71f6bf9f8824898a501e511892791ea2a0c6b8568c68b9cb53"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@ -3161,9 +3161,9 @@ dependencies = [
[[package]] [[package]]
name = "web-sys" name = "web-sys"
version = "0.3.85" version = "0.3.87"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" checksum = "f2c7c5718134e770ee62af3b6b4a84518ec10101aad610c024b64d6ff29bb1ff"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",
@ -3464,7 +3464,7 @@ dependencies = [
"heck", "heck",
"indexmap", "indexmap",
"prettyplease", "prettyplease",
"syn 2.0.116", "syn 2.0.117",
"wasm-metadata", "wasm-metadata",
"wit-bindgen-core", "wit-bindgen-core",
"wit-component", "wit-component",
@ -3480,7 +3480,7 @@ dependencies = [
"prettyplease", "prettyplease",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.116", "syn 2.0.117",
"wit-bindgen-core", "wit-bindgen-core",
"wit-bindgen-rust", "wit-bindgen-rust",
] ]
@ -3602,7 +3602,7 @@ checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.116", "syn 2.0.117",
] ]
[[package]] [[package]]

View file

@ -13,8 +13,8 @@ bacon:
check: check:
reset && cargo check reset && cargo check
test: test +ARGS="":
cargo test --workspace --exclude jack cargo test --workspace --exclude jack --exclude jack-sys {{ARGS}}
covfig := "CARGO_INCREMENTAL=0 RUSTFLAGS='-Cinstrument-coverage' RUSTDOCFLAGS='-Cinstrument-coverage' LLVM_PROFILE_FILE='cov/cargo-test-%p-%m.profraw'" covfig := "CARGO_INCREMENTAL=0 RUSTFLAGS='-Cinstrument-coverage' RUSTDOCFLAGS='-Cinstrument-coverage' LLVM_PROFILE_FILE='cov/cargo-test-%p-%m.profraw'"
grcov-binary := "--binary-path ./target/coverage/deps/" grcov-binary := "--binary-path ./target/coverage/deps/"
@ -130,3 +130,7 @@ plugin:
rg 'TODO' app/ | cat rg 'TODO' app/ | cat
rg 'TODO' app/ | wc -l rg 'TODO' app/ | wc -l
echo echo
new:
cargo build
target/debug/tek new

View file

@ -1076,3 +1076,64 @@
//} //}
//}; //};
//} //}
//take!(MidiInputCommand |state: Arrangement, iter|state.selected_midi_in().as_ref()
//.map(|t|Take::take(t, iter)).transpose().map(|x|x.flatten()));
//take!(MidiOutputCommand |state: Arrangement, iter|state.selected_midi_out().as_ref()
//.map(|t|Take::take(t, iter)).transpose().map(|x|x.flatten()));
//impl<T, U: Has<Option<T>>> MaybeHas<T> for U {
//fn get (&self) -> Option<&T> {
//Has::<Option<T>>::get(self).as_ref()
//}
//}
//
//// DRAW SAMPLE ADD:
//let area = to.area();
//to.make_dim();
//let area = center_box(
//area,
//64.max(area.w().saturating_sub(8)),
//20.max(area.w().saturating_sub(8)),
//);
//to.fill_fg(area, Color::Reset);
//to.fill_bg(area, Nord::bg_lo(true, true));
//to.fill_char(area, ' ');
//to.blit(&format!("{}", &self.dir.to_string_lossy()), area.x()+2, area.y()+1, Some(Style::default().bold()))?;
//to.blit(&"Select sample:", area.x()+2, area.y()+2, Some(Style::default().bold()))?;
//for (i, (is_dir, name)) in self.subdirs.iter()
//.map(|path|(true, path))
//.chain(self.files.iter().map(|path|(false, path)))
//.enumerate()
//.skip(self.offset)
//{
//if i >= area.h() as usize - 4 {
//break
//}
//let t = if is_dir { "" } else { "" };
//let line = format!("{t} {}", name.to_string_lossy());
//let line = &line[..line.len().min(area.w() as usize - 4)];
//to.blit(&line, area.x() + 2, area.y() + 3 + i as u16, Some(if i == self.cursor {
//Style::default().green()
//} else {
//Style::default().white()
//}))?;
//}
//Lozenge(Style::default()).draw(to)
//let cells_x = 8u16;
//let cells_y = 8u16;
//let cell_width = 10u16;
//let cell_height = 2u16;
//let width = cells_x * cell_width;
//let height = cells_y * cell_height;
//let cols = Map::east(
//cell_width,
//move||0..cells_x,
//move|x, _|Map::south(
//cell_height,
//move||0..cells_y,
//move|y, _|self.view_grid_cell("........", x, y, cell_width, cell_height)
//)
//);
//cols
//Thunk::new(|to: &mut TuiOut|{
//})

View file

@ -22,6 +22,10 @@ pub(crate) use ::midly::{Smf, TrackEventKind, MidiMessage, Error as MidiError, n
pub extern crate tengri; pub extern crate tengri;
pub(crate) use tengri::{ pub(crate) use tengri::{
*, *,
dizzle::{
self,
*
},
ratatui::{ ratatui::{
self, self,
prelude::{Rect, Style, Stylize, Buffer, Modifier, buffer::Cell, Color::{self, *}}, prelude::{Rect, Style, Stylize, Buffer, Modifier, buffer::Cell, Color::{self, *}},
@ -76,9 +80,10 @@ pub(crate) use JackState::*;
/// ///
/// ``` /// ```
/// let jack = tek::Jack::new(&"test_tek").expect("failed to connect to jack"); /// let jack = tek::Jack::new(&"test_tek").expect("failed to connect to jack");
/// let proj = Default::default(); /// let proj = tek::Arrangement::default();
/// let conf = Default::default(); /// let mut conf = tek::Config::default();
/// let tek = tek::tek(&jack, proj, conf, "mode-doctest"); /// conf.add("(mode hello)");
/// let tek = tek::tek(&jack, proj, conf, "hello");
/// ``` /// ```
pub fn tek ( pub fn tek (
jack: &Jack<'static>, project: Arrangement, config: Config, mode: impl AsRef<str> jack: &Jack<'static>, project: Arrangement, config: Config, mode: impl AsRef<str>
@ -124,55 +129,22 @@ fn tek_dec (state: &mut App, axis: &ControlAxis) -> Perhaps<AppCommand> {
}) })
} }
pub fn load_view (views: &Views, name: &impl AsRef<str>, body: &impl Language) -> Usually<()> { pub(crate) fn load_view (views: &Views, name: &impl AsRef<str>, body: &impl Language) -> Usually<()> {
views.write().unwrap().insert(name.as_ref().into(), body.src()?.unwrap_or_default().into()); views.write().unwrap().insert(name.as_ref().into(), body.src()?.unwrap_or_default().into());
Ok(()) Ok(())
} }
pub(crate) fn load_mode (modes: &Modes, name: &impl AsRef<str>, body: &impl Language) -> Usually<()> {
pub fn load_mode (modes: &Modes, name: &impl AsRef<str>, body: &impl Language) -> Usually<()> {
let mut mode = Mode::default(); let mut mode = Mode::default();
body.each(|item|mode.add(item))?; body.each(|item|mode.add(item))?;
modes.write().unwrap().insert(name.as_ref().into(), Arc::new(mode)); modes.write().unwrap().insert(name.as_ref().into(), Arc::new(mode));
Ok(()) Ok(())
} }
pub(crate) fn load_bind (binds: &Binds, name: &impl AsRef<str>, body: &impl Language) -> Usually<()> {
pub fn load_bind (binds: &Binds, name: &impl AsRef<str>, body: &impl Language) -> Usually<()> { binds.write().unwrap().insert(name.as_ref().into(), Bind::load(body)?);
let mut map = Bind::new();
body.each(|item|if item.expr().head() == Ok(Some("see")) {
// TODO
Ok(())
} else if let Ok(Some(_word)) = item.expr().head().word() {
if let Some(key) = TuiEvent::from_dsl(item.expr()?.head()?)? {
map.add(key, Binding {
commands: [item.expr()?.tail()?.unwrap_or_default().into()].into(),
condition: None,
description: None,
source: None
});
Ok(())
} else if Some(":char") == item.expr()?.head()? {
// TODO
return Ok(())
} else {
return Err(format!("Config::load_bind: invalid key: {:?}", item.expr()?.head()?).into())
}
} else {
return Err(format!("Config::load_bind: unexpected: {item:?}").into())
})?;
binds.write().unwrap().insert(name.as_ref().into(), map);
Ok(()) Ok(())
} }
/// CLI banner. fn collect_commands (app: &App, input: &TuiIn) -> Usually<Vec<AppCommand>> {
pub(crate) const HEADER: &'static str = r#"
~ ~~~ ~ ~ ~~ ~ ~ ~ ~~ ~ ~ ~ ~
~ v0.4.0, 2026 winter (or is it) ~
~ ~ ~~~ ~ ~ ~ ~ ~~~ ~~~ ~ ~~ "#;
fn collect_commands (
app: &App, input: &TuiIn
) -> Usually<Vec<AppCommand>> {
let mut commands = vec![]; let mut commands = vec![];
for id in app.mode.keys.iter() { for id in app.mode.keys.iter() {
if let Some(event_map) = app.config.binds.clone().read().unwrap().get(id.as_ref()) if let Some(event_map) = app.config.binds.clone().read().unwrap().get(id.as_ref())
@ -335,42 +307,6 @@ pub fn tek_print_status (project: &Arrangement) {
// TODO dawvert integration // TODO dawvert integration
} }
pub const DEFAULT_PPQ: f64 = 96.0;
/// FIXME: remove this and use PPQ from timebase everywhere:
pub const PPQ: usize = 96;
/// (pulses, name), assuming 96 PPQ
pub const NOTE_DURATIONS: [(usize, &str);26] = [
(1, "1/384"), (2, "1/192"),
(3, "1/128"), (4, "1/96"),
(6, "1/64"), (8, "1/48"),
(12, "1/32"), (16, "1/24"),
(24, "1/16"), (32, "1/12"),
(48, "1/8"), (64, "1/6"),
(96, "1/4"), (128, "1/3"),
(192, "1/2"), (256, "2/3"),
(384, "1/1"), (512, "4/3"),
(576, "3/2"), (768, "2/1"),
(1152, "3/1"), (1536, "4/1"),
(2304, "6/1"), (3072, "8/1"),
(3456, "9/1"), (6144, "16/1"),
];
pub const NOTE_NAMES: [&str; 128] = [
"C0", "C#0", "D0", "D#0", "E0", "F0", "F#0", "G0", "G#0", "A0", "A#0", "B0",
"C1", "C#1", "D1", "D#1", "E1", "F1", "F#1", "G1", "G#1", "A1", "A#1", "B1",
"C2", "C#2", "D2", "D#2", "E2", "F2", "F#2", "G2", "G#2", "A2", "A#2", "B2",
"C3", "C#3", "D3", "D#3", "E3", "F3", "F#3", "G3", "G#3", "A3", "A#3", "B3",
"C4", "C#4", "D4", "D#4", "E4", "F4", "F#4", "G4", "G#4", "A4", "A#4", "B4",
"C5", "C#5", "D5", "D#5", "E5", "F5", "F#5", "G5", "G#5", "A5", "A#5", "B5",
"C6", "C#6", "D6", "D#6", "E6", "F6", "F#6", "G6", "G#6", "A6", "A#6", "B6",
"C7", "C#7", "D7", "D#7", "E7", "F7", "F#7", "G7", "G#7", "A7", "A#7", "B7",
"C8", "C#8", "D8", "D#8", "E8", "F8", "F#8", "G8", "G#8", "A8", "A#8", "B8",
"C9", "C#9", "D9", "D#9", "E9", "F9", "F#9", "G9", "G#9", "A9", "A#9", "B9",
"C10", "C#10", "D10", "D#10", "E10", "F10", "F#10", "G10",
];
/// Return boxed iterator of MIDI events /// Return boxed iterator of MIDI events
pub fn parse_midi_input <'a> (input: ::jack::MidiIter<'a>) pub fn parse_midi_input <'a> (input: ::jack::MidiIter<'a>)
-> Box<dyn Iterator<Item=(usize, LiveEvent<'a>, &'a [u8])> + 'a> -> Box<dyn Iterator<Item=(usize, LiveEvent<'a>, &'a [u8])> + 'a>
@ -1261,15 +1197,49 @@ mod view {
} }
} }
pub(crate) fn sampler_jack_process (
state: &mut Sampler, _: &Client, scope: &ProcessScope
) -> Control {
if let Some(midi_in) = &state.midi_in {
for midi in midi_in.port().iter(scope) {
sampler_midi_in(&state.samples, &state.voices, midi)
}
}
state.process_audio_out(scope);
state.process_audio_in(scope);
Control::Continue
}
/// Create [Voice]s from [Sample]s in response to MIDI input.
pub(crate) fn sampler_midi_in (
samples: &SampleKit<128>, voices: &Arc<RwLock<Vec<Voice>>>, RawMidi { time, bytes }: RawMidi
) {
if let Ok(LiveEvent::Midi { message, .. }) = LiveEvent::parse(bytes) {
match message {
MidiMessage::NoteOn { ref key, ref vel } => {
if let Some(sample) = samples.get(key.as_int() as usize) {
voices.write().unwrap().push(Sample::play(sample, time as usize, vel));
}
},
MidiMessage::Controller { controller: _, value: _ } => {
// TODO
}
_ => {}
}
}
}
#[cfg(test)] mod test_view_meter { #[cfg(test)] mod test_view_meter {
use super::*; use super::*;
use proptest::prelude::*; use proptest::prelude::*;
proptest! { proptest! {
#[test] fn proptest_view_meter ( #[test] fn proptest_view_meter (
label in "\\PC*", value in f32::MIN..f32::MAX label in "\\PC*", value in f32::MIN..f32::MAX
) { ) {
let _ = view_meter(&label, value); let _ = view_meter(&label, value);
} }
#[test] fn proptest_view_meters ( #[test] fn proptest_view_meters (
value1 in f32::MIN..f32::MAX, value1 in f32::MIN..f32::MAX,
value2 in f32::MIN..f32::MAX value2 in f32::MIN..f32::MAX
@ -1278,3 +1248,46 @@ mod view {
} }
} }
} }
pub const DEFAULT_PPQ: f64 = 96.0;
/// FIXME: remove this and use PPQ from timebase everywhere:
pub const PPQ: usize = 96;
/// (pulses, name), assuming 96 PPQ
pub const NOTE_DURATIONS: [(usize, &str);26] = [
(1, "1/384"), (2, "1/192"),
(3, "1/128"), (4, "1/96"),
(6, "1/64"), (8, "1/48"),
(12, "1/32"), (16, "1/24"),
(24, "1/16"), (32, "1/12"),
(48, "1/8"), (64, "1/6"),
(96, "1/4"), (128, "1/3"),
(192, "1/2"), (256, "2/3"),
(384, "1/1"), (512, "4/3"),
(576, "3/2"), (768, "2/1"),
(1152, "3/1"), (1536, "4/1"),
(2304, "6/1"), (3072, "8/1"),
(3456, "9/1"), (6144, "16/1"),
];
pub const NOTE_NAMES: [&str; 128] = [
"C0", "C#0", "D0", "D#0", "E0", "F0", "F#0", "G0", "G#0", "A0", "A#0", "B0",
"C1", "C#1", "D1", "D#1", "E1", "F1", "F#1", "G1", "G#1", "A1", "A#1", "B1",
"C2", "C#2", "D2", "D#2", "E2", "F2", "F#2", "G2", "G#2", "A2", "A#2", "B2",
"C3", "C#3", "D3", "D#3", "E3", "F3", "F#3", "G3", "G#3", "A3", "A#3", "B3",
"C4", "C#4", "D4", "D#4", "E4", "F4", "F#4", "G4", "G#4", "A4", "A#4", "B4",
"C5", "C#5", "D5", "D#5", "E5", "F5", "F#5", "G5", "G#5", "A5", "A#5", "B5",
"C6", "C#6", "D6", "D#6", "E6", "F6", "F#6", "G6", "G#6", "A6", "A#6", "B6",
"C7", "C#7", "D7", "D#7", "E7", "F7", "F#7", "G7", "G#7", "A7", "A#7", "B7",
"C8", "C#8", "D8", "D#8", "E8", "F8", "F#8", "G8", "G#8", "A8", "A#8", "B8",
"C9", "C#9", "D9", "D#9", "E9", "F9", "F#9", "G9", "G#9", "A9", "A#9", "B9",
"C10", "C#10", "D10", "D#10", "E10", "F10", "F#10", "G10",
];
/// CLI banner.
pub(crate) const HEADER: &'static str = r#"
~ ~~~ ~ ~ ~~ ~ ~ ~ ~~ ~ ~ ~ ~
~ v0.4.0, 2026 winter (or is it) ~
~ ~ ~~~ ~ ~ ~ ~ ~~~ ~~~ ~ ~~ "#;

File diff suppressed because it is too large Load diff

View file

@ -147,10 +147,13 @@ pub struct JackNotify<T: Fn(JackEvent) + Send>(pub T);
pub modes: Modes, pub modes: Modes,
} }
/// An input binding. /// An map of input events (e.g. [TuiEvent]) to [Binding]s.
/// ///
/// ``` /// ```
/// let bind = tek::Bind::<(), ()>::default(); /// let lang = "(@x (nop)) (@y (nop) (nop))";
/// let bind = tek::Bind::<tek::tengri::TuiEvent, std::sync::Arc<str>>::load(&lang).unwrap();
/// assert_eq!(bind.query(&'x'.into()).map(|x|x.len()), Some(1));
/// //assert_eq!(bind.query(&'y'.into()).map(|x|x.len()), Some(2));
/// ``` /// ```
#[derive(Debug)] pub struct Bind<E, C>( #[derive(Debug)] pub struct Bind<E, C>(
/// Map of each event (e.g. key combination) to /// Map of each event (e.g. key combination) to
@ -159,10 +162,12 @@ pub struct JackNotify<T: Fn(JackEvent) + Send>(pub T);
pub BTreeMap<E, Vec<Binding<C>>> pub BTreeMap<E, Vec<Binding<C>>>
); );
/// An input binding. /// A sequence of zero or more commands (e.g. [AppCommand]),
/// optionally filtered by [Condition] to form layers.
/// ///
/// ``` /// ```
/// let binding: tek::Binding<()> = Default::default(); /// //FIXME: Why does it overflow?
/// //let binding: Binding<()> = tek::Binding { ..Default::default() };
/// ``` /// ```
#[derive(Debug, Clone)] pub struct Binding<C> { #[derive(Debug, Clone)] pub struct Binding<C> {
pub commands: Arc<[C]>, pub commands: Arc<[C]>,
@ -302,7 +307,7 @@ pub struct JackNotify<T: Fn(JackEvent) + Send>(pub T);
/// Temporal resolutions: sample rate, tempo, MIDI pulses per quaver (beat) /// Temporal resolutions: sample rate, tempo, MIDI pulses per quaver (beat)
/// ///
/// ``` /// ```
/// /// let _ = tek::Timebase::default();
/// ``` /// ```
#[derive(Debug, Clone)] pub struct Timebase { #[derive(Debug, Clone)] pub struct Timebase {
/// Audio samples per second /// Audio samples per second
@ -316,15 +321,28 @@ pub struct JackNotify<T: Fn(JackEvent) + Send>(pub T);
/// Iterator that emits subsequent ticks within a range. /// Iterator that emits subsequent ticks within a range.
/// ///
/// ``` /// ```
/// let iter = tek::TicksIterator::default(); /// let iter = tek::Ticker::default();
/// ``` /// ```
#[derive(Debug, Default)] pub struct TicksIterator { #[derive(Debug, Default)] pub struct Ticker {
pub spp: f64, pub spp: f64,
pub sample: usize, pub sample: usize,
pub start: usize, pub start: usize,
pub end: usize, pub end: usize,
} }
///
/// ```
/// let _ = tek::MidiCursor::default();
/// ```
#[derive(Debug, Clone)] pub struct MidiCursor {
/// Time coordinate of cursor
pub time_pos: Arc<AtomicUsize>,
/// Note coordinate of cursor
pub note_pos: Arc<AtomicUsize>,
/// Length of note that will be inserted, in pulses
pub note_len: Arc<AtomicUsize>,
}
/// ///
/// ``` /// ```
/// use tek::{TimeRange, NoteRange}; /// use tek::{TimeRange, NoteRange};
@ -341,19 +359,6 @@ pub struct JackNotify<T: Fn(JackEvent) + Send>(pub T);
/// let _ = model.get_note_axis(); /// let _ = model.get_note_axis();
/// let _ = model.get_note_hi(); /// let _ = model.get_note_hi();
/// ``` /// ```
#[derive(Debug, Clone)] pub struct MidiCursor {
/// Time coordinate of cursor
pub time_pos: Arc<AtomicUsize>,
/// Note coordinate of cursor
pub note_pos: Arc<AtomicUsize>,
/// Length of note that will be inserted, in pulses
pub note_len: Arc<AtomicUsize>,
}
///
/// ```
///
/// ```
#[derive(Debug, Clone, Default)] pub struct MidiSelection { #[derive(Debug, Clone, Default)] pub struct MidiSelection {
pub time_len: Arc<AtomicUsize>, pub time_len: Arc<AtomicUsize>,
/// Length of visible time axis /// Length of visible time axis
@ -373,7 +378,7 @@ pub struct JackNotify<T: Fn(JackEvent) + Send>(pub T);
/// A point in time in all time scales (microsecond, sample, MIDI pulse) /// A point in time in all time scales (microsecond, sample, MIDI pulse)
/// ///
/// ``` /// ```
/// /// let _ = tek::Moment::default();
/// ``` /// ```
#[derive(Debug, Default, Clone)] pub struct Moment { #[derive(Debug, Default, Clone)] pub struct Moment {
pub timebase: Arc<Timebase>, pub timebase: Arc<Timebase>,
@ -387,7 +392,7 @@ pub struct JackNotify<T: Fn(JackEvent) + Send>(pub T);
/// ///
/// ``` /// ```
/// /// let _ = tek::Moment2::default();
/// ``` /// ```
#[derive(Debug, Clone, Default)] pub enum Moment2 { #[derive(Debug, Clone, Default)] pub enum Moment2 {
#[default] None, #[default] None,
@ -834,28 +839,18 @@ pub struct PoolView<'a>(pub &'a Pool);
pub name: Arc<str>, pub name: Arc<str>,
/// Device color. /// Device color.
pub color: ItemTheme, pub color: ItemTheme,
/// Audio input ports. Samples get recorded here.
#[cfg(feature = "port")] pub audio_ins: Vec<AudioInput>,
/// Audio input meters.
#[cfg(feature = "meter")] pub input_meters: Vec<f32>,
/// Sample currently being recorded. /// Sample currently being recorded.
pub recording: Option<(usize, Option<Arc<RwLock<Sample>>>)>, pub recording: Option<(usize, Option<Arc<RwLock<Sample>>>)>,
/// Recording buffer. /// Recording buffer.
pub buffer: Vec<Vec<f32>>, pub buffer: Vec<Vec<f32>>,
/// Samples mapped to MIDI notes. /// Samples mapped to MIDI notes.
pub samples: SampleKit<128>, pub samples: SampleKit<128>,
/// Samples that are not mapped to MIDI notes.
pub unmapped: Vec<Arc<RwLock<Sample>>>,
/// Sample currently being edited.
pub editing: Option<Arc<RwLock<Sample>>>,
/// MIDI input port. Triggers sample playback.
#[cfg(feature = "port")] pub midi_in: Option<MidiInput>,
/// Collection of currently playing instances of samples. /// Collection of currently playing instances of samples.
pub voices: Arc<RwLock<Vec<Voice>>>, pub voices: Arc<RwLock<Vec<Voice>>>,
/// Audio output ports. Voices get played here. /// Samples that are not mapped to MIDI notes.
#[cfg(feature = "port")] pub audio_outs: Vec<AudioOutput>, pub unmapped: Vec<Arc<RwLock<Sample>>>,
/// Audio output meters. /// Sample currently being edited.
#[cfg(feature = "meter")] pub output_meters: Vec<f32>, pub editing: Option<Arc<RwLock<Sample>>>,
/// How to mix the voices. /// How to mix the voices.
pub mixing_mode: MixingMode, pub mixing_mode: MixingMode,
/// How to meter the inputs and outputs. /// How to meter the inputs and outputs.
@ -872,6 +867,16 @@ pub struct PoolView<'a>(pub &'a Pool);
pub note_pt: AtomicUsize, pub note_pt: AtomicUsize,
/// Selected note as row/col. /// Selected note as row/col.
pub cursor: (AtomicUsize, AtomicUsize), pub cursor: (AtomicUsize, AtomicUsize),
/// Audio input meters.
#[cfg(feature = "meter")] pub input_meters: Vec<f32>,
/// Audio input ports. Samples are recorded from here.
#[cfg(feature = "port")] pub audio_ins: Vec<AudioInput>,
/// MIDI input port. Sampler are triggered from here.
#[cfg(feature = "port")] pub midi_in: Option<MidiInput>,
/// Audio output ports. Voices are played into here.
#[cfg(feature = "port")] pub audio_outs: Vec<AudioOutput>,
/// Audio output meters.
#[cfg(feature = "meter")] pub output_meters: Vec<f32>,
} }
/// Collection of samples, one per slot, fixed number of slots. /// Collection of samples, one per slot, fixed number of slots.
@ -909,7 +914,7 @@ pub struct PoolView<'a>(pub &'a Pool);
pub velocity: f32, pub velocity: f32,
} }
pub struct AddSampleModal { #[derive(Default, Debug)] pub struct SampleAdd {
pub exited: bool, pub exited: bool,
pub dir: PathBuf, pub dir: PathBuf,
pub subdirs: Vec<OsString>, pub subdirs: Vec<OsString>,

View file

@ -318,9 +318,14 @@ pub trait HasScenes: Has<Vec<Scene>> + Send + Sync {
/// ``` /// ```
/// use tek::{MidiEditor, HasEditor, tengri::Has}; /// use tek::{MidiEditor, HasEditor, tengri::Has};
/// struct TestEditorHost(Option<MidiEditor>); ///
/// tek::tengri::has!(Option<MidiEditor>: |self: TestEditorHost|self.0);
/// let mut host = TestEditorHost(Some(MidiEditor::default())); /// let mut host = TestEditorHost(Some(MidiEditor::default()));
/// struct TestEditorHost(Option<MidiEditor>);
/// impl Has<Option<MidiEditor>> for TestEditorHost {
/// fn get (&self) -> &Option<MidiEditor> { &self.0 }
/// fn get_mut (&mut self) -> &mut Option<MidiEditor> { &mut self.0 }
/// }
///
/// let _ = host.editor(); /// let _ = host.editor();
/// let _ = host.editor_mut(); /// let _ = host.editor_mut();
/// let _ = host.is_editing(); /// let _ = host.is_editing();

View file

@ -24,7 +24,7 @@ command = ["cargo", "clippy", "--all-targets"]
need_stdout = false need_stdout = false
watch = ["dizzle", "tengri", "app"] watch = ["dizzle", "tengri", "app"]
[jobs.test] [jobs.test]
command = ["cargo", "test", "--workspace", "--exclude", "jack"] command = ["cargo", "test", "--workspace", "--exclude", "jack", "--exclude", "jack-sys"]
need_stdout = true need_stdout = true
watch = ["dizzle", "tengri", "app"] watch = ["dizzle", "tengri", "app"]
[jobs.nextest] [jobs.nextest]

2
dizzle

@ -1 +1 @@
Subproject commit 624ac97274fbf974f1196864bb93093c31cb0fd7 Subproject commit f6fb8c844a31b35f5739297272250ff9c7fee345

2
tengri

@ -1 +1 @@
Subproject commit c1011ddb7f8061c5fe242b6eab62a66537ae671b Subproject commit b294f2e62b970cf4e1cb133346132a0a113af575