diff --git a/Cargo.lock b/Cargo.lock index e3c86da1..91f75883 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2536,6 +2536,7 @@ dependencies = [ "backtrace", "better-panic", "clap", + "clojure-reader", "crossterm", "midly", "ratatui", diff --git a/crates/tek/src/control.rs b/crates/tek/src/control.rs index a4f95549..646cf486 100644 --- a/crates/tek/src/control.rs +++ b/crates/tek/src/control.rs @@ -41,12 +41,12 @@ fn handle_focused (state: &mut App, e: &AppEvent) -> Usually { AppFocus::Transport => state.transport.handle(e), AppFocus::Arranger => state.arranger.sequencer.handle(e), AppFocus::Sequencer => state.arranger.sequencer.handle(e), - AppFocus::Chain => Ok(if state.entered { + AppFocus::Chain => Ok(false)/*if state.entered { handle_device(state, e)? || handle_keymap(state, e, crate::control::KEYMAP_CHAIN)? } else { handle_keymap(state, e, crate::control::KEYMAP_CHAIN)? || handle_device(state, e)? - }) + })*/ } } diff --git a/crates/tek/src/edn.rs b/crates/tek/src/edn.rs index 60ccb8ab..64173758 100644 --- a/crates/tek/src/edn.rs +++ b/crates/tek/src/edn.rs @@ -69,10 +69,10 @@ impl App { } }, Some(Edn::Symbol("scene")) => { - Scene::load_edn(self, &items[1..])?; + tek_sequencer::Scene::load_edn(self, &items[1..])?; }, Some(Edn::Symbol("track")) => { - Track::load_edn(self, &items[1..])?; + tek_mixer::Track::load_edn(self, &items[1..])?; }, Some(Edn::Symbol("midi-in")) => { self.midi_ins = items[1..].iter().map(|x|match x { @@ -107,75 +107,6 @@ impl App { } } -impl Scene { - fn load_edn <'a, 'e> (app: &'a mut App, args: &[Edn<'e>]) -> Usually<&'a mut Self> { - let mut name = None; - let mut clips = vec![]; - edn!(edn in args { - Edn::Map(map) => { - let key = map.get(&Edn::Key(":name")); - if let Some(Edn::Str(n)) = key { - name = Some(*n); - } else { - panic!("unexpected key in scene '{name:?}': {key:?}") - } - }, - Edn::Symbol("_") => { - clips.push(None); - }, - Edn::Int(i) => { - clips.push(Some(*i as usize)); - }, - _ => panic!("unexpected in scene '{name:?}': {edn:?}") - }); - let scene = app.arranger.scene_add(name)?; - scene.clips = clips; - Ok(scene) - } -} - -impl Track { - fn load_edn <'a, 'e> (app: &'a mut App, args: &[Edn<'e>]) -> Usually<&'a mut Self> { - let ppq = app.transport.ppq(); - let mut name = None; - let mut _gain = 0.0f64; - let mut devices: Vec = vec![]; - let mut phrases: Vec = vec![]; - edn!(edn in args { - Edn::Map(map) => { - if let Some(Edn::Str(n)) = map.get(&Edn::Key(":name")) { - name = Some(*n); - } - if let Some(Edn::Double(g)) = map.get(&Edn::Key(":gain")) { - _gain = f64::from(*g) - } - }, - Edn::List(args) => match args.get(0) { - Some(Edn::Symbol("phrase")) => { - phrases.push(Phrase::load_edn(ppq, &args[1..])?) - }, - Some(Edn::Symbol("sampler")) => { - devices.push(Sampler::load_edn(&args[1..])?) - }, - Some(Edn::Symbol("lv2")) => { - devices.push(LV2Plugin::load_edn(&args[1..])?) - }, - None => panic!("empty list track {}", - name.unwrap_or("") - ), - _ => panic!("unexpected in track {}: {:?}", - name.unwrap_or(""), - args.get(0).unwrap() - ) - }, - _ => {} - }); - let track = app.arranger.track_add(name)?; - for phrase in phrases { track.phrases.push(Arc::new(RwLock::new(phrase))); } - for device in devices { track.add_device(device)?; } - Ok(track) - } -} impl Phrase { fn load_edn <'e> (ppq: usize, args: &[Edn<'e>]) -> Usually { diff --git a/crates/tek_core/Cargo.toml b/crates/tek_core/Cargo.toml index 5e80bc3f..4a49e7b8 100644 --- a/crates/tek_core/Cargo.toml +++ b/crates/tek_core/Cargo.toml @@ -11,3 +11,4 @@ toml = "0.8.12" better-panic = "0.3.0" midly = "0.5" clap = { version = "4.5.4", features = [ "derive" ] } +clojure-reader = "0.1.0" diff --git a/crates/tek_core/src/lib.rs b/crates/tek_core/src/lib.rs index 6273b7b9..875ffe19 100644 --- a/crates/tek_core/src/lib.rs +++ b/crates/tek_core/src/lib.rs @@ -7,6 +7,7 @@ pub use std::collections::BTreeMap; pub use crossterm::event::{Event, KeyEvent, KeyCode, KeyModifiers}; pub use ratatui::prelude::{Rect, Style, Color, Buffer}; pub use ratatui::style::Stylize; +pub use clojure_reader::{edn::{read, Edn}, error::Error as EdnError}; pub(crate) use std::error::Error; pub(crate) use std::io::{stdout}; @@ -42,6 +43,18 @@ submod! { exit render handle } +/// EDN parsing helper. +#[macro_export] macro_rules! edn { + ($edn:ident { $($pat:pat => $expr:expr),* $(,)? }) => { + match $edn { $($pat => $expr),* } + }; + ($edn:ident in $args:ident { $($pat:pat => $expr:expr),* $(,)? }) => { + for $edn in $args { + edn!($edn { $($pat => $expr),* }) + } + }; +} + /// Standard result type. pub type Usually = Result>; diff --git a/crates/tek_mixer/example.edn b/crates/tek_mixer/example.edn new file mode 100644 index 00000000..3b42a353 --- /dev/null +++ b/crates/tek_mixer/example.edn @@ -0,0 +1,12 @@ +(mixer + (track + (name "Drums") + (sampler + (dir "/home/user/Lab/Music/pak") + (sample (midi 34) (name "808 D") (file "808.wav")))) + (track + (name "Lead") + (lv2 + (name "Odin2") + (path "file:///home/user/.lv2/Odin2.lv2")) + (gain 0.0))) diff --git a/crates/tek_mixer/src/mixer_track.rs b/crates/tek_mixer/src/mixer_track.rs index 6a9e1aa2..24f0a5a8 100644 --- a/crates/tek_mixer/src/mixer_track.rs +++ b/crates/tek_mixer/src/mixer_track.rs @@ -1,4 +1,5 @@ use crate::*; +use tek_core::edn; /// TODO: A track in the mixer. (Integrate with [crate::model::Track]?) pub struct MixerTrack { @@ -52,6 +53,44 @@ impl MixerTrack { } } +impl MixerTrack { + fn load_edn <'a, 'e> (app: &'a mut App, args: &[Edn<'e>]) -> Usually<&'a mut Self> { + let ppq = app.transport.ppq(); + let mut name = None; + let mut _gain = 0.0f64; + let mut devices: Vec = vec![]; + edn!(edn in args { + Edn::Map(map) => { + if let Some(Edn::Str(n)) = map.get(&Edn::Key(":name")) { + name = Some(*n); + } + if let Some(Edn::Double(g)) = map.get(&Edn::Key(":gain")) { + _gain = f64::from(*g) + } + }, + Edn::List(args) => match args.get(0) { + Some(Edn::Symbol("sampler")) => { + devices.push(Sampler::load_edn(&args[1..])?) + }, + Some(Edn::Symbol("lv2")) => { + devices.push(LV2Plugin::load_edn(&args[1..])?) + }, + None => panic!("empty list track {}", + name.unwrap_or("") + ), + _ => panic!("unexpected in track {}: {:?}", + name.unwrap_or(""), + args.get(0).unwrap() + ) + }, + _ => {} + }); + let track = app.arranger.track_add(name)?; + for device in devices { track.add_device(device)?; } + Ok(track) + } +} + //impl Input, bool> for Mixer { //fn handle (&mut self, engine: &mut TUI) -> Result> { //Ok(None) diff --git a/crates/tek_sequencer/example.edn b/crates/tek_sequencer/example.edn new file mode 100644 index 00000000..04bdbb01 --- /dev/null +++ b/crates/tek_sequencer/example.edn @@ -0,0 +1,18 @@ +(arranger + (track + (name "Drums") + (phrase + (name "4 kicks") + (beats 4) + (steps 16) + (:00 (36 128)) + (:04 (36 100)) + (:08 (36 100)) + (:12 (36 100)))) + (track + (name "Bass") + (phrase + (beats 4) + (steps 16) + (:04 (36 100)) + (:12 (36 100))))) diff --git a/crates/tek_sequencer/src/scene_edn.rs b/crates/tek_sequencer/src/scene_edn.rs index e3937b4f..d4fb10b4 100644 --- a/crates/tek_sequencer/src/scene_edn.rs +++ b/crates/tek_sequencer/src/scene_edn.rs @@ -1,7 +1,7 @@ use crate::*; impl Scene { - fn load_edn <'a, 'e> (app: &'a mut App, args: &[Edn<'e>]) -> Usually<&'a mut Self> { + pub fn load_edn <'a, 'e> (app: &'a mut App, args: &[Edn<'e>]) -> Usually<&'a mut Self> { let mut name = None; let mut clips = vec![]; edn!(edn in args {