convert to workspace, add suil bindings crate

This commit is contained in:
🪞👃🪞 2024-07-24 13:21:11 +03:00
parent bd6f8ff9bf
commit dacce119c4
52 changed files with 1994 additions and 116 deletions

View file

@ -1,319 +0,0 @@
//! Project file format.
//!
//! This module `impl`s the `from_edn`, `load_edn`, etc. methods
//! of structs that are defined in other modules. See:
//!
//! * [App::from_edn]
//! * [App::load_edn]
//! * [App::load_edn_one]
//! * [Scene::load_edn]
//! * [Track::load_edn]
//! * [Phrase::load_edn]
//! * [Sampler::load_edn]
//! * [Sample::load_edn]
//! * [LV2Plugin::load_edn]
use crate::{core::*, model::*, App};
use crate::devices::{
arranger::Scene,
sequencer::Phrase,
sampler::{Sampler, Sample, read_sample_data}
};
use crate::devices::plugin::{Plugin, LV2Plugin};
use clojure_reader::{edn::{read, Edn}, error::Error as EdnError};
/// EDN parsing helper.
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),* })
}
};
}
impl App {
pub fn from_edn (src: &str) -> Usually<Self> {
let mut app = Self::new()?;
app.load_edn(src)?;
Ok(app)
}
pub fn load_edn (&mut self, mut src: &str) -> Usually<&mut Self> {
loop {
match read(src) {
Ok((edn, rest)) => {
self.load_edn_one(edn)?;
if rest.len() > 0 {
src = rest;
} else {
break
}
},
Err(EdnError { ptr: None, .. }) => {
break
},
Err(e) => {
panic!("{e:?}");
},
}
}
Ok(self)
}
fn load_edn_one <'e> (&mut self, edn: Edn<'e>) -> Usually<()> {
match edn {
Edn::List(items) => {
match items.get(0) {
Some(Edn::Symbol("bpm")) => {
match items.get(1) {
Some(Edn::Int(b)) =>
self.transport.timebase.set_bpm(*b as f64),
Some(Edn::Double(b)) =>
self.transport.timebase.set_bpm(f64::from(*b)),
_ => panic!("unspecified bpm")
}
},
Some(Edn::Symbol("scene")) => {
Scene::load_edn(self, &items[1..])?;
},
Some(Edn::Symbol("track")) => {
Track::load_edn(self, &items[1..])?;
},
Some(Edn::Symbol("midi-in")) => {
self.midi_ins = items[1..].iter().map(|x|match x {
Edn::Str(n) => n.to_string(),
_ => panic!("unexpected midi-in")
}).collect::<Vec<_>>();
},
Some(Edn::Symbol("audio-out")) => {
let client = self.client();
self.audio_outs = items[1..].iter().map(|x|match x {
Edn::Str(n) => n.to_string(),
_ => panic!("unexpected midi-in")
}).collect::<Vec<_>>()
.iter()
.map(|name|client
.ports(Some(name), None, PortFlags::empty())
.get(0)
.map(|name|client.port_by_name(name)))
.flatten()
.filter_map(|x|x)
.map(Arc::new)
.collect();
},
_ => panic!("unexpected edn: {:?}", items.get(0))
}
},
_ => {
panic!("unexpected edn: {edn:?}");
}
}
Ok(())
}
}
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<JackDevice> = vec![];
let mut phrases: Vec<Phrase> = 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<Self> {
let mut phrase = Self::default();
let mut name = String::new();
let mut beats = 0usize;
let mut steps = 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::Int(b)) = map.get(&Edn::Key(":beats")) {
beats = *b as usize;
phrase.length = ppq * beats;
for _ in phrase.notes.len()..phrase.length {
phrase.notes.push(Vec::with_capacity(16))
}
}
if let Some(Edn::Int(s)) = map.get(&Edn::Key(":steps")) {
steps = *s as usize;
}
},
Edn::List(args) => {
let time = (match args.get(0) {
Some(Edn::Key(text)) => text[1..].parse::<f64>()?,
Some(Edn::Int(i)) => *i as f64,
Some(Edn::Double(f)) => f64::from(*f),
_ => panic!("unexpected in phrase '{name}': {:?}", args.get(0)),
} * beats as f64 * ppq as f64 / steps as f64) as usize;
for edn in args[1..].iter() {
match edn {
Edn::List(args) => if let (
Some(Edn::Int(key)),
Some(Edn::Int(vel)),
) = (
args.get(0),
args.get(1),
) {
let (key, vel) = (
u7::from((*key as u8).min(127)),
u7::from((*vel as u8).min(127)),
);
phrase.notes[time].push(MidiMessage::NoteOn { key, vel })
} else {
panic!("unexpected list in phrase '{name}'")
},
_ => panic!("unexpected in phrase '{name}': {edn:?}")
}
}
},
_ => panic!("unexpected in phrase '{name}': {edn:?}"),
});
phrase.name = name;
Ok(phrase)
}
}
impl Sampler {
fn load_edn <'e> (args: &[Edn<'e>]) -> Usually<JackDevice> {
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.get(0) {
Some(Edn::Symbol("sample")) => {
let (midi, sample) = Sample::load_edn(&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(&name, Some(samples))
}
}
impl Sample {
fn load_edn <'e> (dir: &str, args: &[Edn<'e>]) -> Usually<(Option<u7>, Arc<RwLock<Self>>)> {
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) = read_sample_data(&format!("{dir}/{file}"))?;
Ok((midi, Arc::new(RwLock::new(Self::new(&name, start, end, data)))))
}
}
impl LV2Plugin {
fn load_edn <'e> (args: &[Edn<'e>]) -> Usually<JackDevice> {
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::lv2(&name, &path)
}
}