From 8d519c53dcd9ef79a777985121e337f583bfb2c6 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Mon, 6 Jan 2025 20:58:16 +0100 Subject: [PATCH] docs: update readme --- README.md | 32 +++------ edn/src/edn_view.rs | 2 +- edn/src/lib.rs | 4 ++ src/audio.rs | 1 - src/edn.rs | 168 -------------------------------------------- src/mixer.rs | 86 +++++++++++++++++++++++ src/plugin.rs | 17 +++++ src/sampler.rs | 64 +++++++++++++++++ 8 files changed, 181 insertions(+), 193 deletions(-) delete mode 100644 src/audio.rs delete mode 100644 src/edn.rs diff --git a/README.md b/README.md index 61cf10fe..f5022158 100644 --- a/README.md +++ b/README.md @@ -69,28 +69,14 @@ if you want to see this buildable with stable/beta. ## design goals -### lightweight +* inspired by trackers and hardware sequencers, + but with the critical feature that 90s samplers lack: + able to **resample, i.e. record while playing!** -* pop-up scratchpad for musical ideas -* low resource consumption, can stay open in background -* advanced toolset allows quickly expanding on compositions +* **pop-up scratchpad for musical ideas.** + low resource consumption, can stay open in background. + but flexible enough to allow expanding on compositions -### flexible - -* inspired by trackers and hardware sequencers -* able to record while playing! - -### programmable - -* human-readable project format -* command architecture allows for scripting and remapping - ---- - -> [!NOTE] -> Your moral support means a lot to me. -> Feel free to [contact me on Mastodon](https://mastodon.social/@unspeaker)![^0] -> -> Love, -> 🤫 -> (a rogue knowledge worker in a cyberpunk dystopia) +* **human- and machine- readable project format** + simple representation for project data + enable scripting and remapping. diff --git a/edn/src/edn_view.rs b/edn/src/edn_view.rs index 26a160d5..b6adbfd8 100644 --- a/edn/src/edn_view.rs +++ b/edn/src/edn_view.rs @@ -75,7 +75,7 @@ impl + Send + Sync> Content for EdnView { (Key("fixed/x"), [x, a]) => Fixed::x(s.get_unit(x.to_ref()), s.get_content(a.to_ref())).boxed(), (Key("fixed/y"), [y, a]) => Fixed::y(s.get_unit(y.to_ref()), s.get_content(a.to_ref())).boxed(), - (Key("fixed/xy"), [xy, a]) => Fixed::xy(s.get_unit(xy.to_ref()), s.get_content(a.to_ref())).boxed(), + (Key("fixed/xy"), [x, y, a]) => Fixed::xy(s.get_unit(x.to_ref()), s.get_unit(y.to_ref()), s.get_content(a.to_ref())).boxed(), (Key("max/x"), [x, a]) => Max::x(s.get_unit(x.to_ref()), s.get_content(a.to_ref())).boxed(), (Key("max/y"), [y, a]) => Max::y(s.get_unit(y.to_ref()), s.get_content(a.to_ref())).boxed(), diff --git a/edn/src/lib.rs b/edn/src/lib.rs index 28e540ce..7ade422c 100644 --- a/edn/src/lib.rs +++ b/edn/src/lib.rs @@ -40,3 +40,7 @@ mod edn_view; pub use self::edn_view::*; //let content = >::from(&layout); Ok(()) } + +#[macro_export] macro_rules! from_edn { + ($($x:tt)*) => {} +} diff --git a/src/audio.rs b/src/audio.rs deleted file mode 100644 index c7b7e813..00000000 --- a/src/audio.rs +++ /dev/null @@ -1 +0,0 @@ -use crate::*; diff --git a/src/edn.rs b/src/edn.rs deleted file mode 100644 index 931fcdc3..00000000 --- a/src/edn.rs +++ /dev/null @@ -1,168 +0,0 @@ -use crate::*; - -from_edn!("sampler" => |jack: &Arc>, args| -> crate::Sampler { - let mut name = String::new(); - let mut dir = String::new(); - let mut samples = BTreeMap::new(); - edn!(edn in args { - Edn::Map(map) => { - if let Some(Edn::Str(n)) = map.get(&Edn::Key(":name")) { - name = String::from(*n); - } - if let Some(Edn::Str(n)) = map.get(&Edn::Key(":dir")) { - dir = String::from(*n); - } - }, - Edn::List(args) => match args.first() { - Some(Edn::Symbol("sample")) => { - let (midi, sample) = MidiSample::from_edn((jack, &dir), &args[1..])?; - if let Some(midi) = midi { - samples.insert(midi, sample); - } else { - panic!("sample without midi binding: {}", sample.read().unwrap().name); - } - }, - _ => panic!("unexpected in sampler {name}: {args:?}") - }, - _ => panic!("unexpected in sampler {name}: {edn:?}") - }); - Self::new(jack, &name) -}); - -type MidiSample = (Option, Arc>); - -from_edn!("sample" => |(_jack, dir): (&Arc>, &str), args| -> MidiSample { - let mut name = String::new(); - let mut file = String::new(); - let mut midi = None; - let mut start = 0usize; - edn!(edn in args { - Edn::Map(map) => { - if let Some(Edn::Str(n)) = map.get(&Edn::Key(":name")) { - name = String::from(*n); - } - if let Some(Edn::Str(f)) = map.get(&Edn::Key(":file")) { - file = String::from(*f); - } - if let Some(Edn::Int(i)) = map.get(&Edn::Key(":start")) { - start = *i as usize; - } - if let Some(Edn::Int(m)) = map.get(&Edn::Key(":midi")) { - midi = Some(u7::from(*m as u8)); - } - }, - _ => panic!("unexpected in sample {name}"), - }); - let (end, data) = Sample::read_data(&format!("{dir}/{file}"))?; - Ok((midi, Arc::new(RwLock::new(crate::Sample { - name, - start, - end, - channels: data, - rate: None, - gain: 1.0 - })))) -}); - -from_edn!("plugin/lv2" => |jack: &Arc>, args| -> Plugin { - let mut name = String::new(); - let mut path = String::new(); - edn!(edn in args { - Edn::Map(map) => { - if let Some(Edn::Str(n)) = map.get(&Edn::Key(":name")) { - name = String::from(*n); - } - if let Some(Edn::Str(p)) = map.get(&Edn::Key(":path")) { - path = String::from(*p); - } - }, - _ => panic!("unexpected in lv2 '{name}'"), - }); - Plugin::new_lv2(jack, &name, &path) -}); - -const SYM_NAME: &str = ":name"; -const SYM_GAIN: &str = ":gain"; -const SYM_SAMPLER: &str = "sampler"; -const SYM_LV2: &str = "lv2"; - -from_edn!("mixer/track" => |jack: &Arc>, args| -> MixerTrack { - let mut _gain = 0.0f64; - let mut track = MixerTrack { - name: String::new(), - audio_ins: vec![], - audio_outs: vec![], - devices: vec![], - }; - edn!(edn in args { - Edn::Map(map) => { - if let Some(Edn::Str(n)) = map.get(&Edn::Key(SYM_NAME)) { - track.name = n.to_string(); - } - if let Some(Edn::Double(g)) = map.get(&Edn::Key(SYM_GAIN)) { - _gain = f64::from(*g); - } - }, - Edn::List(args) => match args.first() { - // Add a sampler device to the track - Some(Edn::Symbol(SYM_SAMPLER)) => { - track.devices.push( - Box::new(Sampler::from_edn(jack, &args[1..])?) as Box - ); - panic!( - "unsupported in track {}: {:?}; tek_mixer not compiled with feature \"sampler\"", - &track.name, - args.first().unwrap() - ) - }, - // Add a LV2 plugin to the track. - Some(Edn::Symbol(SYM_LV2)) => { - track.devices.push( - Box::new(Plugin::from_edn(jack, &args[1..])?) as Box - ); - panic!( - "unsupported in track {}: {:?}; tek_mixer not compiled with feature \"plugin\"", - &track.name, - args.first().unwrap() - ) - }, - None => - panic!("empty list track {}", &track.name), - _ => - panic!("unexpected in track {}: {:?}", &track.name, args.first().unwrap()) - }, - _ => {} - }); - Ok(track) -}); - -//impl ArrangerScene { - - ////TODO - ////pub fn from_edn <'a, 'e> (args: &[Edn<'e>]) -> Usually { - ////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:?}") - ////}); - ////Ok(ArrangerScene { - ////name: Arc::new(name.unwrap_or("").to_string().into()), - ////color: ItemColor::random(), - ////clips, - ////}) - ////} -//} diff --git a/src/mixer.rs b/src/mixer.rs index 94d3afcd..48772111 100644 --- a/src/mixer.rs +++ b/src/mixer.rs @@ -258,3 +258,89 @@ pub trait MixerTrackDevice: Debug + Send + Sync { impl MixerTrackDevice for Sampler {} impl MixerTrackDevice for Plugin {} + +const SYM_NAME: &str = ":name"; +const SYM_GAIN: &str = ":gain"; +const SYM_SAMPLER: &str = "sampler"; +const SYM_LV2: &str = "lv2"; + +from_edn!("mixer/track" => |jack: &Arc>, args| -> MixerTrack { + let mut _gain = 0.0f64; + let mut track = MixerTrack { + name: String::new(), + audio_ins: vec![], + audio_outs: vec![], + devices: vec![], + }; + edn!(edn in args { + Edn::Map(map) => { + if let Some(Edn::Str(n)) = map.get(&Edn::Key(SYM_NAME)) { + track.name = n.to_string(); + } + if let Some(Edn::Double(g)) = map.get(&Edn::Key(SYM_GAIN)) { + _gain = f64::from(*g); + } + }, + Edn::List(args) => match args.first() { + // Add a sampler device to the track + Some(Edn::Symbol(SYM_SAMPLER)) => { + track.devices.push( + Box::new(Sampler::from_edn(jack, &args[1..])?) as Box + ); + panic!( + "unsupported in track {}: {:?}; tek_mixer not compiled with feature \"sampler\"", + &track.name, + args.first().unwrap() + ) + }, + // Add a LV2 plugin to the track. + Some(Edn::Symbol(SYM_LV2)) => { + track.devices.push( + Box::new(Plugin::from_edn(jack, &args[1..])?) as Box + ); + panic!( + "unsupported in track {}: {:?}; tek_mixer not compiled with feature \"plugin\"", + &track.name, + args.first().unwrap() + ) + }, + None => + panic!("empty list track {}", &track.name), + _ => + panic!("unexpected in track {}: {:?}", &track.name, args.first().unwrap()) + }, + _ => {} + }); + Ok(track) +}); + +//impl ArrangerScene { + + ////TODO + ////pub fn from_edn <'a, 'e> (args: &[Edn<'e>]) -> Usually { + ////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:?}") + ////}); + ////Ok(ArrangerScene { + ////name: Arc::new(name.unwrap_or("").to_string().into()), + ////color: ItemColor::random(), + ////clips, + ////}) + ////} +//} diff --git a/src/plugin.rs b/src/plugin.rs index 33c4e60d..6c05aadc 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -259,3 +259,20 @@ handle!(TuiIn: |self:Plugin, from|{ _ => Ok(None) } }); + +from_edn!("plugin/lv2" => |jack: &Arc>, args| -> Plugin { + let mut name = String::new(); + let mut path = String::new(); + edn!(edn in args { + Edn::Map(map) => { + if let Some(Edn::Str(n)) = map.get(&Edn::Key(":name")) { + name = String::from(*n); + } + if let Some(Edn::Str(p)) = map.get(&Edn::Key(":path")) { + path = String::from(*p); + } + }, + _ => panic!("unexpected in lv2 '{name}'"), + }); + Plugin::new_lv2(jack, &name, &path) +}); diff --git a/src/sampler.rs b/src/sampler.rs index eded59f7..0fb8dbe5 100644 --- a/src/sampler.rs +++ b/src/sampler.rs @@ -90,3 +90,67 @@ impl Sampler { } } } + +from_edn!("sampler" => |jack: &Arc>, args| -> crate::Sampler { + let mut name = String::new(); + let mut dir = String::new(); + let mut samples = BTreeMap::new(); + edn!(edn in args { + Edn::Map(map) => { + if let Some(Edn::Str(n)) = map.get(&Edn::Key(":name")) { + name = String::from(*n); + } + if let Some(Edn::Str(n)) = map.get(&Edn::Key(":dir")) { + dir = String::from(*n); + } + }, + Edn::List(args) => match args.first() { + Some(Edn::Symbol("sample")) => { + let (midi, sample) = MidiSample::from_edn((jack, &dir), &args[1..])?; + if let Some(midi) = midi { + samples.insert(midi, sample); + } else { + panic!("sample without midi binding: {}", sample.read().unwrap().name); + } + }, + _ => panic!("unexpected in sampler {name}: {args:?}") + }, + _ => panic!("unexpected in sampler {name}: {edn:?}") + }); + Self::new(jack, &name) +}); + +type MidiSample = (Option, Arc>); + +from_edn!("sample" => |(_jack, dir): (&Arc>, &str), args| -> MidiSample { + let mut name = String::new(); + let mut file = String::new(); + let mut midi = None; + let mut start = 0usize; + edn!(edn in args { + Edn::Map(map) => { + if let Some(Edn::Str(n)) = map.get(&Edn::Key(":name")) { + name = String::from(*n); + } + if let Some(Edn::Str(f)) = map.get(&Edn::Key(":file")) { + file = String::from(*f); + } + if let Some(Edn::Int(i)) = map.get(&Edn::Key(":start")) { + start = *i as usize; + } + if let Some(Edn::Int(m)) = map.get(&Edn::Key(":midi")) { + midi = Some(u7::from(*m as u8)); + } + }, + _ => panic!("unexpected in sample {name}"), + }); + let (end, data) = Sample::read_data(&format!("{dir}/{file}"))?; + Ok((midi, Arc::new(RwLock::new(crate::Sample { + name, + start, + end, + channels: data, + rate: None, + gain: 1.0 + })))) +});