mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
load scenes from project
This commit is contained in:
parent
f1f812d0fb
commit
dff42ca5a7
5 changed files with 202 additions and 301 deletions
15
README.md
15
README.md
|
|
@ -1,3 +1,18 @@
|
||||||
|
# tek
|
||||||
|
|
||||||
|
Tek is a MIDI sequencer, sampler, and plugin host for the Linux terminal.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
* Linux
|
||||||
|
* Rust toolchain
|
||||||
|
* JACK or Pipewire
|
||||||
|
|
||||||
|
## Recommended
|
||||||
|
|
||||||
|
* MIDI controller
|
||||||
|
* Samples
|
||||||
|
|
||||||
TODO:
|
TODO:
|
||||||
|
|
||||||
* Focus transport to set BPM/sync/quant with `.,`
|
* Focus transport to set BPM/sync/quant with `.,`
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,12 @@
|
||||||
(bpm 150)
|
(bpm 150)
|
||||||
|
|
||||||
|
(scene { :name "Intro" } _ 0 _ _)
|
||||||
|
(scene { :name "Hook" } 0 1 0 _)
|
||||||
|
(scene { :name "Verse" } 2 2 1 _)
|
||||||
|
(scene { :name "Chorus" } 1 3 2 _)
|
||||||
|
(scene { :name "Bridge" } 3 4 3 _)
|
||||||
|
(scene { :name "Outro" } _ 1 4 _)
|
||||||
|
|
||||||
(track { :name "Drums" :gain +0.0 }
|
(track { :name "Drums" :gain +0.0 }
|
||||||
(phrase { :name "4 kicks" :beats 4 :steps 16 }
|
(phrase { :name "4 kicks" :beats 4 :steps 16 }
|
||||||
(:00 (36 128))
|
(:00 (36 128))
|
||||||
|
|
|
||||||
294
src/edn.rs
294
src/edn.rs
|
|
@ -2,6 +2,17 @@ use crate::{core::*, model::*, App};
|
||||||
|
|
||||||
use clojure_reader::{edn::{read, Edn}, error::Error as EdnError};
|
use clojure_reader::{edn::{read, Edn}, error::Error as EdnError};
|
||||||
|
|
||||||
|
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 {
|
impl App {
|
||||||
pub fn load_edn (&mut self, mut src: &str) -> Usually<()> {
|
pub fn load_edn (&mut self, mut src: &str) -> Usually<()> {
|
||||||
loop {
|
loop {
|
||||||
|
|
@ -27,11 +38,7 @@ impl App {
|
||||||
fn load_edn_one <'e> (&mut self, edn: Edn<'e>) -> Usually<()> {
|
fn load_edn_one <'e> (&mut self, edn: Edn<'e>) -> Usually<()> {
|
||||||
match edn {
|
match edn {
|
||||||
Edn::List(items) => {
|
Edn::List(items) => {
|
||||||
let head = items.get(0);
|
match items.get(0) {
|
||||||
match head {
|
|
||||||
Some(Edn::Symbol("track")) => {
|
|
||||||
Track::load_edn(self, &items[1..])?;
|
|
||||||
},
|
|
||||||
Some(Edn::Symbol("bpm")) => {
|
Some(Edn::Symbol("bpm")) => {
|
||||||
match items.get(1) {
|
match items.get(1) {
|
||||||
Some(Edn::Int(b)) => self.timebase.set_bpm(*b as f64),
|
Some(Edn::Int(b)) => self.timebase.set_bpm(*b as f64),
|
||||||
|
|
@ -39,7 +46,13 @@ impl App {
|
||||||
_ => panic!("unspecified bpm")
|
_ => panic!("unspecified bpm")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => panic!("unexpected edn: {head:?}")
|
Some(Edn::Symbol("scene")) => {
|
||||||
|
Scene::load_edn(self, &items[1..])?;
|
||||||
|
},
|
||||||
|
Some(Edn::Symbol("track")) => {
|
||||||
|
Track::load_edn(self, &items[1..])?;
|
||||||
|
},
|
||||||
|
_ => panic!("unexpected edn: {:?}", items.get(0))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
|
|
@ -50,6 +63,35 @@ 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(String::from(*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:?}")
|
||||||
|
});
|
||||||
|
app.add_scene_with_clips(name.as_deref(), &clips)
|
||||||
|
//for edn in args {
|
||||||
|
//match end {
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Track {
|
impl Track {
|
||||||
fn load_edn <'a, 'e> (app: &'a mut App, args: &[Edn<'e>]) -> Usually<&'a mut Self> {
|
fn load_edn <'a, 'e> (app: &'a mut App, args: &[Edn<'e>]) -> Usually<&'a mut Self> {
|
||||||
let ppq = app.timebase.ppq() as usize;
|
let ppq = app.timebase.ppq() as usize;
|
||||||
|
|
@ -57,32 +99,30 @@ impl Track {
|
||||||
let mut gain = 0.0f64;
|
let mut gain = 0.0f64;
|
||||||
let mut devices: Vec<JackDevice> = vec![];
|
let mut devices: Vec<JackDevice> = vec![];
|
||||||
let mut phrases: Vec<Phrase> = vec![];
|
let mut phrases: Vec<Phrase> = vec![];
|
||||||
for edn in args {
|
edn!(edn in args {
|
||||||
match edn {
|
Edn::Map(map) => {
|
||||||
Edn::Map(map) => {
|
if let Some(Edn::Str(n)) = map.get(&Edn::Key(":name")) {
|
||||||
if let Some(Edn::Str(n)) = map.get(&Edn::Key(":name")) {
|
name = String::from(*n);
|
||||||
name = String::from(*n);
|
}
|
||||||
}
|
if let Some(Edn::Double(g)) = map.get(&Edn::Key(":gain")) {
|
||||||
if let Some(Edn::Double(g)) = map.get(&Edn::Key(":gain")) {
|
gain = f64::from(*g)
|
||||||
gain = f64::from(*g)
|
}
|
||||||
}
|
},
|
||||||
|
Edn::List(args) => match args.get(0) {
|
||||||
|
Some(Edn::Symbol("phrase")) => {
|
||||||
|
phrases.push(Phrase::load_edn(ppq, &args[1..])?)
|
||||||
},
|
},
|
||||||
Edn::List(args) => match args.get(0) {
|
Some(Edn::Symbol("sampler")) => {
|
||||||
Some(Edn::Symbol("phrase")) => {
|
devices.push(Sampler::load_edn(&args[1..])?)
|
||||||
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}"),
|
|
||||||
_ => panic!("unexpected in track {name}: {:?}", args.get(0).unwrap())
|
|
||||||
},
|
},
|
||||||
_ => {}
|
Some(Edn::Symbol("lv2")) => {
|
||||||
}
|
devices.push(LV2Plugin::load_edn(&args[1..])?)
|
||||||
}
|
},
|
||||||
|
None => panic!("empty list track {name}"),
|
||||||
|
_ => panic!("unexpected in track {name}: {:?}", args.get(0).unwrap())
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
});
|
||||||
let (left, right) = (app.audio_out(0), app.audio_out(1));
|
let (left, right) = (app.audio_out(0), app.audio_out(1));
|
||||||
app.add_track_with_cb(Some(name.as_str()), move|_, track|{
|
app.add_track_with_cb(Some(name.as_str()), move|_, track|{
|
||||||
for phrase in phrases {
|
for phrase in phrases {
|
||||||
|
|
@ -113,54 +153,52 @@ impl Phrase {
|
||||||
let mut beats = 0usize;
|
let mut beats = 0usize;
|
||||||
let mut steps = 0usize;
|
let mut steps = 0usize;
|
||||||
let mut data = BTreeMap::new();
|
let mut data = BTreeMap::new();
|
||||||
for edn in args {
|
edn!(edn in args {
|
||||||
match edn {
|
Edn::Map(map) => {
|
||||||
Edn::Map(map) => {
|
if let Some(Edn::Str(n)) = map.get(&Edn::Key(":name")) {
|
||||||
if let Some(Edn::Str(n)) = map.get(&Edn::Key(":name")) {
|
name = String::from(*n);
|
||||||
name = String::from(*n);
|
}
|
||||||
|
if let Some(Edn::Int(b)) = map.get(&Edn::Key(":beats")) {
|
||||||
|
beats = *b as usize;
|
||||||
|
}
|
||||||
|
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),
|
||||||
|
) {
|
||||||
|
if !data.contains_key(&time) {
|
||||||
|
data.insert(time, vec![]);
|
||||||
|
}
|
||||||
|
let (key, vel) = (
|
||||||
|
u7::from((*key as u8).min(127)),
|
||||||
|
u7::from((*vel as u8).min(127))
|
||||||
|
);
|
||||||
|
data.get_mut(&time).unwrap()
|
||||||
|
.push(MidiMessage::NoteOn { key, vel })
|
||||||
|
} else {
|
||||||
|
panic!("unexpected list in phrase '{name}'")
|
||||||
|
},
|
||||||
|
_ => panic!("unexpected in phrase '{name}': {edn:?}")
|
||||||
}
|
}
|
||||||
if let Some(Edn::Int(b)) = map.get(&Edn::Key(":beats")) {
|
}
|
||||||
beats = *b as usize;
|
},
|
||||||
}
|
_ => panic!("unexpected in phrase '{name}': {edn:?}"),
|
||||||
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),
|
|
||||||
) {
|
|
||||||
if !data.contains_key(&time) {
|
|
||||||
data.insert(time, vec![]);
|
|
||||||
}
|
|
||||||
let (key, vel) = (
|
|
||||||
u7::from((*key as u8).min(127)),
|
|
||||||
u7::from((*vel as u8).min(127))
|
|
||||||
);
|
|
||||||
data.get_mut(&time).unwrap()
|
|
||||||
.push(MidiMessage::NoteOn { key, vel })
|
|
||||||
} else {
|
|
||||||
panic!("unexpected list in phrase '{name}'")
|
|
||||||
},
|
|
||||||
_ => panic!("unexpected in phrase '{name}': {edn:?}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => panic!("unexpected in phrase '{name}': {edn:?}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Self::new(&name, beats * ppq, Some(data)))
|
Ok(Self::new(&name, beats * ppq, Some(data)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -170,30 +208,28 @@ impl Sampler {
|
||||||
let mut name = String::new();
|
let mut name = String::new();
|
||||||
let mut dir = String::new();
|
let mut dir = String::new();
|
||||||
let mut samples = BTreeMap::new();
|
let mut samples = BTreeMap::new();
|
||||||
for edn in args {
|
edn!(edn in args {
|
||||||
match edn {
|
Edn::Map(map) => {
|
||||||
Edn::Map(map) => {
|
if let Some(Edn::Str(n)) = map.get(&Edn::Key(":name")) {
|
||||||
if let Some(Edn::Str(n)) = map.get(&Edn::Key(":name")) {
|
name = String::from(*n);
|
||||||
name = String::from(*n);
|
}
|
||||||
}
|
if let Some(Edn::Str(n)) = map.get(&Edn::Key(":dir")) {
|
||||||
if let Some(Edn::Str(n)) = map.get(&Edn::Key(":dir")) {
|
dir = String::from(*n);
|
||||||
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.name);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Edn::List(args) => match args.get(0) {
|
_ => panic!("unexpected in sampler {name}: {args:?}")
|
||||||
Some(Edn::Symbol("sample")) => {
|
},
|
||||||
let (midi, sample) = Sample::load_edn(&dir, &args[1..])?;
|
_ => panic!("unexpected in sampler {name}: {edn:?}")
|
||||||
if let Some(midi) = midi {
|
});
|
||||||
samples.insert(midi, sample);
|
|
||||||
} else {
|
|
||||||
panic!("sample without midi binding: {}", sample.name);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => panic!("unexpected in sampler {name}: {args:?}")
|
|
||||||
},
|
|
||||||
_ => panic!("unexpected in sampler {name}: {edn:?}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self::new(&name, Some(samples))
|
Self::new(&name, Some(samples))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -204,25 +240,23 @@ impl Sample {
|
||||||
let mut file = String::new();
|
let mut file = String::new();
|
||||||
let mut midi = None;
|
let mut midi = None;
|
||||||
let mut start = 0usize;
|
let mut start = 0usize;
|
||||||
for edn in args.iter() {
|
edn!(edn in args {
|
||||||
match edn {
|
Edn::Map(map) => {
|
||||||
Edn::Map(map) => {
|
if let Some(Edn::Str(n)) = map.get(&Edn::Key(":name")) {
|
||||||
if let Some(Edn::Str(n)) = map.get(&Edn::Key(":name")) {
|
name = String::from(*n);
|
||||||
name = String::from(*n);
|
}
|
||||||
}
|
if let Some(Edn::Str(f)) = map.get(&Edn::Key(":file")) {
|
||||||
if let Some(Edn::Str(f)) = map.get(&Edn::Key(":file")) {
|
file = String::from(*f);
|
||||||
file = String::from(*f);
|
}
|
||||||
}
|
if let Some(Edn::Int(i)) = map.get(&Edn::Key(":start")) {
|
||||||
if let Some(Edn::Int(i)) = map.get(&Edn::Key(":start")) {
|
start = *i as usize;
|
||||||
start = *i as usize;
|
}
|
||||||
}
|
if let Some(Edn::Int(m)) = map.get(&Edn::Key(":midi")) {
|
||||||
if let Some(Edn::Int(m)) = map.get(&Edn::Key(":midi")) {
|
midi = Some(u7::from(*m as u8));
|
||||||
midi = Some(u7::from(*m as u8));
|
}
|
||||||
}
|
},
|
||||||
},
|
_ => panic!("unexpected in sample {name}"),
|
||||||
_ => panic!("unexpected in sample {name}"),
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
let (end, data) = read_sample_data(&format!("{dir}/{file}"))?;
|
let (end, data) = read_sample_data(&format!("{dir}/{file}"))?;
|
||||||
Ok((midi, Self::new(&name, start, end, data)))
|
Ok((midi, Self::new(&name, start, end, data)))
|
||||||
}
|
}
|
||||||
|
|
@ -232,19 +266,17 @@ impl LV2Plugin {
|
||||||
fn load_edn <'e> (args: &[Edn<'e>]) -> Usually<JackDevice> {
|
fn load_edn <'e> (args: &[Edn<'e>]) -> Usually<JackDevice> {
|
||||||
let mut name = String::new();
|
let mut name = String::new();
|
||||||
let mut path = String::new();
|
let mut path = String::new();
|
||||||
for edn in args.iter() {
|
edn!(edn in args {
|
||||||
match edn {
|
Edn::Map(map) => {
|
||||||
Edn::Map(map) => {
|
if let Some(Edn::Str(n)) = map.get(&Edn::Key(":name")) {
|
||||||
if let Some(Edn::Str(n)) = map.get(&Edn::Key(":name")) {
|
name = String::from(*n);
|
||||||
name = String::from(*n);
|
}
|
||||||
}
|
if let Some(Edn::Str(p)) = map.get(&Edn::Key(":path")) {
|
||||||
if let Some(Edn::Str(p)) = map.get(&Edn::Key(":path")) {
|
path = String::from(*p);
|
||||||
path = String::from(*p);
|
}
|
||||||
}
|
},
|
||||||
},
|
_ => panic!("unexpected in lv2 '{name}'"),
|
||||||
_ => panic!("unexpected in lv2 '{name}'"),
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
Plugin::lv2(&name, &path)
|
Plugin::lv2(&name, &path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
172
src/main.rs
172
src/main.rs
|
|
@ -22,9 +22,8 @@ use crate::{core::*, model::*};
|
||||||
|
|
||||||
/// Application entrypoint.
|
/// Application entrypoint.
|
||||||
pub fn main () -> Usually<()> {
|
pub fn main () -> Usually<()> {
|
||||||
|
// Construct app
|
||||||
let mut app = App::default();
|
let mut app = App::default();
|
||||||
|
|
||||||
// Load config
|
// Load config
|
||||||
let xdg = Arc::new(microxdg::XdgApp::new("tek")?);
|
let xdg = Arc::new(microxdg::XdgApp::new("tek")?);
|
||||||
app.xdg = Some(xdg.clone());
|
app.xdg = Some(xdg.clone());
|
||||||
|
|
@ -33,9 +32,7 @@ pub fn main () -> Usually<()> {
|
||||||
}
|
}
|
||||||
let midi_from = ["nanoKEY Studio.*capture.*"];
|
let midi_from = ["nanoKEY Studio.*capture.*"];
|
||||||
let audio_into = ["Komplete.+:playback_FL", "Komplete.+:playback_FR"];
|
let audio_into = ["Komplete.+:playback_FL", "Komplete.+:playback_FR"];
|
||||||
|
// Init view
|
||||||
// Init
|
|
||||||
let ppq = app.timebase.ppq() as usize;
|
|
||||||
app.track_cursor = 1;
|
app.track_cursor = 1;
|
||||||
app.scene_cursor = 1;
|
app.scene_cursor = 1;
|
||||||
app.note_start = 12;
|
app.note_start = 12;
|
||||||
|
|
@ -45,13 +42,11 @@ pub fn main () -> Usually<()> {
|
||||||
// Start main loop
|
// Start main loop
|
||||||
app.run(Some(|app: Arc<RwLock<App>>|{
|
app.run(Some(|app: Arc<RwLock<App>>|{
|
||||||
let mut state = app.write().unwrap();
|
let mut state = app.write().unwrap();
|
||||||
|
|
||||||
// Start JACK and setup device graph
|
// Start JACK and setup device graph
|
||||||
let jack = jack_run("tek", &app)?;
|
let jack = jack_run("tek", &app)?;
|
||||||
let client = jack.as_client();
|
let client = jack.as_client();
|
||||||
state.transport = Some(client.transport());
|
state.transport = Some(client.transport());
|
||||||
state.midi_in = Some(client.register_port("midi-in", MidiIn)?);
|
state.midi_in = Some(client.register_port("midi-in", MidiIn)?);
|
||||||
|
|
||||||
let _ = midi_from
|
let _ = midi_from
|
||||||
.iter()
|
.iter()
|
||||||
.map(|name|client
|
.map(|name|client
|
||||||
|
|
@ -65,7 +60,6 @@ pub fn main () -> Usually<()> {
|
||||||
})
|
})
|
||||||
.collect::<Usually<()>>())
|
.collect::<Usually<()>>())
|
||||||
.collect::<Usually<()>>()?;
|
.collect::<Usually<()>>()?;
|
||||||
|
|
||||||
state.audio_outs = audio_into
|
state.audio_outs = audio_into
|
||||||
.iter()
|
.iter()
|
||||||
.map(|name|client
|
.map(|name|client
|
||||||
|
|
@ -76,169 +70,9 @@ pub fn main () -> Usually<()> {
|
||||||
.filter_map(|x|x)
|
.filter_map(|x|x)
|
||||||
.map(Arc::new)
|
.map(Arc::new)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
state.jack = Some(jack);
|
state.jack = Some(jack);
|
||||||
|
// Load project
|
||||||
state.load_edn(include_str!("../demos/project.edn"))?;
|
state.load_edn(include_str!("../demos/project.edn"))?;
|
||||||
|
|
||||||
//state.add_track_with_cb(Some("Drums"), |_, track|{
|
|
||||||
|
|
||||||
//track.add_device_with_cb(Sampler::new("Sampler", Some(BTreeMap::from([
|
|
||||||
//sample!(34, "808", "/home/user/Lab/Music/pak/808.wav"),
|
|
||||||
//sample!(35, "Kick1", "/home/user/Lab/Music/pak/kik.wav"),
|
|
||||||
//sample!(36, "Kick2", "/home/user/Lab/Music/pak/kik2.wav"),
|
|
||||||
//sample!(38, "Snare1", "/home/user/Lab/Music/pak/sna.wav"),
|
|
||||||
//sample!(40, "Snare2", "/home/user/Lab/Music/pak/sna2.wav"),
|
|
||||||
//sample!(42, "Hihat", "/home/user/Lab/Music/pak/chh.wav"),
|
|
||||||
//sample!(44, "Hihat", "/home/user/Lab/Music/pak/chh2.wav"),
|
|
||||||
//])))?, |track, device|{
|
|
||||||
//device.connect_midi_in(0, &track.midi_out.clone_unowned())?;
|
|
||||||
//if let Some(Some(left)) = audio_outs.get(0) {
|
|
||||||
//device.connect_audio_out(0, left)?;
|
|
||||||
//}
|
|
||||||
//if let Some(Some(right)) = audio_outs.get(0) {
|
|
||||||
//device.connect_audio_out(1, right)?;
|
|
||||||
//}
|
|
||||||
//Ok(())
|
|
||||||
//})?;
|
|
||||||
|
|
||||||
////track.add_device_with_cb(Plugin::lv2(
|
|
||||||
////"Panagement",
|
|
||||||
////"file:///home/user/.lv2/Auburn Sounds Panagement 2.lv2"
|
|
||||||
////)?, |track, device|{
|
|
||||||
////device.connect_audio_in(0, &track.devices[0].audio_outs()?[0])?;
|
|
||||||
////device.connect_audio_in(0, &track.devices[0].audio_outs()?[1])?;
|
|
||||||
////if let Some(Some(left)) = audio_outs.get(0) {
|
|
||||||
////device.connect_audio_out(0, left)?;
|
|
||||||
////}
|
|
||||||
////if let Some(Some(right)) = audio_outs.get(0) {
|
|
||||||
////device.connect_audio_out(1, right)?;
|
|
||||||
////}
|
|
||||||
////Ok(())
|
|
||||||
////})?;
|
|
||||||
|
|
||||||
//track.sequence = Some(1); // FIXME
|
|
||||||
|
|
||||||
//track.add_phrase("4 kicks", ppq * 4, Some(phrase! {
|
|
||||||
//00 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() },
|
|
||||||
//04 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() },
|
|
||||||
//08 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() },
|
|
||||||
//12 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() },
|
|
||||||
//}));
|
|
||||||
//track.add_phrase("5 kicks", ppq * 4, Some(phrase! {
|
|
||||||
//00 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() },
|
|
||||||
//04 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() },
|
|
||||||
//08 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() },
|
|
||||||
//12 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() },
|
|
||||||
//14 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() },
|
|
||||||
//}));
|
|
||||||
//track.add_phrase("D-Beat", ppq * 4, Some(phrase! {
|
|
||||||
//00 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() },
|
|
||||||
//02 * ppq/4 => MidiMessage::NoteOn { key: 42.into(), vel: 100.into() },
|
|
||||||
//04 * ppq/4 => MidiMessage::NoteOn { key: 42.into(), vel: 100.into() },
|
|
||||||
//06 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() },
|
|
||||||
//08 * ppq/4 => MidiMessage::NoteOn { key: 42.into(), vel: 100.into() },
|
|
||||||
//10 * ppq/4 => MidiMessage::NoteOn { key: 42.into(), vel: 100.into() },
|
|
||||||
//12 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() },
|
|
||||||
//13 * ppq/4 => MidiMessage::NoteOn { key: 42.into(), vel: 100.into() },
|
|
||||||
//14 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() },
|
|
||||||
//15 * ppq/4 => MidiMessage::NoteOn { key: 42.into(), vel: 100.into() },
|
|
||||||
//00 * ppq/4 => MidiMessage::NoteOn { key: 34.into(), vel: 100.into() },
|
|
||||||
//04 * ppq/4 => MidiMessage::NoteOn { key: 38.into(), vel: 100.into() },
|
|
||||||
//08 * ppq/4 => MidiMessage::NoteOn { key: 34.into(), vel: 100.into() },
|
|
||||||
//10 * ppq/4 => MidiMessage::NoteOn { key: 35.into(), vel: 100.into() },
|
|
||||||
//12 * ppq/4 => MidiMessage::NoteOn { key: 40.into(), vel: 100.into() },
|
|
||||||
//}));
|
|
||||||
//track.add_phrase("Garage", ppq * 4, Some(phrase! {
|
|
||||||
//00 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() },
|
|
||||||
//01 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() },
|
|
||||||
//02 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() },
|
|
||||||
//03 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() },
|
|
||||||
//04 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() },
|
|
||||||
//06 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() },
|
|
||||||
//07 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() },
|
|
||||||
//09 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() },
|
|
||||||
//10 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() },
|
|
||||||
//12 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() },
|
|
||||||
//14 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() },
|
|
||||||
|
|
||||||
//00 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() },
|
|
||||||
//00 * ppq/4 => MidiMessage::NoteOn { key: 35.into(), vel: 100.into() },
|
|
||||||
//02 * ppq/4 => MidiMessage::NoteOn { key: 34.into(), vel: 100.into() },
|
|
||||||
//07 * ppq/4 => MidiMessage::NoteOn { key: 34.into(), vel: 100.into() },
|
|
||||||
//04 * ppq/4 => MidiMessage::NoteOn { key: 40.into(), vel: 100.into() },
|
|
||||||
//11 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() },
|
|
||||||
//11 * ppq/4 => MidiMessage::NoteOn { key: 35.into(), vel: 100.into() },
|
|
||||||
//12 * ppq/4 => MidiMessage::NoteOn { key: 40.into(), vel: 100.into() },
|
|
||||||
//}));
|
|
||||||
//Ok(())
|
|
||||||
//})?;
|
|
||||||
|
|
||||||
//state.add_track_with_cb(Some("Bass"), |_, track|{
|
|
||||||
//track.add_device_with_cb(Plugin::lv2(
|
|
||||||
//"Odin2",
|
|
||||||
//"file:///home/user/.lv2/Odin2.lv2"
|
|
||||||
//)?, |track, device|{
|
|
||||||
//device.connect_midi_in(0, &track.midi_out.clone_unowned())?;
|
|
||||||
//if let Some(Some(left)) = audio_outs.get(0) {
|
|
||||||
//device.connect_audio_out(0, left)?;
|
|
||||||
//}
|
|
||||||
//if let Some(Some(right)) = audio_outs.get(0) {
|
|
||||||
//device.connect_audio_out(1, right)?;
|
|
||||||
//}
|
|
||||||
//Ok(())
|
|
||||||
//})?;
|
|
||||||
//track.sequence = Some(0); // FIXME
|
|
||||||
//track.add_phrase("Offbeat", ppq * 4, Some(phrase! {
|
|
||||||
////00 * ppq/4 => MidiMessage::NoteOff { key: 40.into(), vel: 100.into() },
|
|
||||||
////02 * ppq/4 => MidiMessage::NoteOn { key: 40.into(), vel: 100.into() },
|
|
||||||
////04 * ppq/4 => MidiMessage::NoteOff { key: 40.into(), vel: 100.into() },
|
|
||||||
////06 * ppq/4 => MidiMessage::NoteOn { key: 40.into(), vel: 100.into() },
|
|
||||||
////08 * ppq/4 => MidiMessage::NoteOff { key: 40.into(), vel: 100.into() },
|
|
||||||
////10 * ppq/4 => MidiMessage::NoteOn { key: 40.into(), vel: 100.into() },
|
|
||||||
////12 * ppq/4 => MidiMessage::NoteOff { key: 40.into(), vel: 100.into() },
|
|
||||||
////14 * ppq/4 => MidiMessage::NoteOn { key: 40.into(), vel: 100.into() },
|
|
||||||
//}));
|
|
||||||
//track.add_phrase("Custom1", ppq * 4, None);
|
|
||||||
//track.add_phrase("Custom2", ppq * 4, None);
|
|
||||||
//track.add_phrase("Custom3", ppq * 4, None);
|
|
||||||
//track.add_phrase("Custom4", ppq * 4, None);
|
|
||||||
//Ok(())
|
|
||||||
//})?;
|
|
||||||
|
|
||||||
//state.add_track_with_cb(Some("Lead"), |_, track|{
|
|
||||||
//track.add_device_with_cb(Plugin::lv2(
|
|
||||||
//"Odin2",
|
|
||||||
//"file:///home/user/.lv2/Odin2.lv2"
|
|
||||||
//)?, |track, device|{
|
|
||||||
//device.connect_midi_in(0, &track.midi_out.clone_unowned())?;
|
|
||||||
//if let Some(Some(left)) = audio_outs.get(0) {
|
|
||||||
//device.connect_audio_out(0, left)?;
|
|
||||||
//}
|
|
||||||
//if let Some(Some(right)) = audio_outs.get(0) {
|
|
||||||
//device.connect_audio_out(1, right)?;
|
|
||||||
//}
|
|
||||||
//Ok(())
|
|
||||||
//})?;
|
|
||||||
//track.sequence = Some(0); // FIXME
|
|
||||||
//track.add_phrase("Custom0", ppq * 4, None);
|
|
||||||
//track.add_phrase("Custom1", ppq * 4, None);
|
|
||||||
//track.add_phrase("Custom2", ppq * 4, None);
|
|
||||||
//track.add_phrase("Custom3", ppq * 4, None);
|
|
||||||
//track.add_phrase("Custom4", ppq * 4, None);
|
|
||||||
//Ok(())
|
|
||||||
//})?;
|
|
||||||
|
|
||||||
state.scenes = vec![
|
|
||||||
Scene::new("Intro", vec![None, Some(0), None, None]),
|
|
||||||
Scene::new("Hook", vec![Some(0), Some(1), Some(0), None]),
|
|
||||||
Scene::new("Verse", vec![Some(2), Some(2), Some(1), None]),
|
|
||||||
Scene::new("Chorus", vec![Some(1), Some(3), Some(2), None]),
|
|
||||||
Scene::new("Bridge", vec![Some(3), Some(4), Some(3), None]),
|
|
||||||
Scene::new("Outro", vec![None, Some(1), Some(4), None]),
|
|
||||||
];
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}))
|
}))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
15
src/model.rs
15
src/model.rs
|
|
@ -185,12 +185,25 @@ impl App {
|
||||||
self.tracks.get_mut(id).map(|t|(id, t))
|
self.tracks.get_mut(id).map(|t|(id, t))
|
||||||
} }
|
} }
|
||||||
}
|
}
|
||||||
|
pub fn new_scene_name (&self) -> String {
|
||||||
|
format!("Scene {}", self.scenes.len() + 1)
|
||||||
|
}
|
||||||
pub fn add_scene (&mut self, name: Option<&str>) -> Usually<&mut Scene> {
|
pub fn add_scene (&mut self, name: Option<&str>) -> Usually<&mut Scene> {
|
||||||
let name = name.ok_or_else(||format!("Scene {}", self.scenes.len() + 1))?;
|
let name = name.ok_or_else(||self.new_scene_name())?;
|
||||||
self.scenes.push(Scene::new(&name, vec![]));
|
self.scenes.push(Scene::new(&name, vec![]));
|
||||||
self.scene_cursor = self.scenes.len();
|
self.scene_cursor = self.scenes.len();
|
||||||
Ok(&mut self.scenes[self.scene_cursor - 1])
|
Ok(&mut self.scenes[self.scene_cursor - 1])
|
||||||
}
|
}
|
||||||
|
pub fn add_scene_with_clips (
|
||||||
|
&mut self,
|
||||||
|
name: Option<&str>,
|
||||||
|
clips: &[Option<usize>]
|
||||||
|
) -> Usually<&mut Scene> {
|
||||||
|
let name = name.ok_or_else(||self.new_scene_name())?;
|
||||||
|
self.scenes.push(Scene::new(&name, Vec::from(clips)));
|
||||||
|
self.scene_cursor = self.scenes.len();
|
||||||
|
Ok(&mut self.scenes[self.scene_cursor - 1])
|
||||||
|
}
|
||||||
pub fn scene (&self) -> Option<(usize, &Scene)> {
|
pub fn scene (&self) -> Option<(usize, &Scene)> {
|
||||||
match self.scene_cursor { 0 => None, _ => {
|
match self.scene_cursor { 0 => None, _ => {
|
||||||
let id = self.scene_cursor as usize - 1;
|
let id = self.scene_cursor as usize - 1;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue