From eeb2faf06475cbf300770d6aeb0a14ee91d60a5b Mon Sep 17 00:00:00 2001 From: unspeaker Date: Mon, 8 Jul 2024 17:27:58 +0300 Subject: [PATCH] load project from file --- demos/project.edn | 101 ++++++++--------- demos/scratch.edn | 52 +++++++++ src/core/time.rs | 5 +- src/edn.rs | 116 ++++++++++--------- src/main.rs | 268 ++++++++++++++++++++++---------------------- src/model.rs | 7 +- src/model/phrase.rs | 1 + src/view.rs | 13 ++- src/view/grid.rs | 2 +- 9 files changed, 320 insertions(+), 245 deletions(-) create mode 100644 demos/scratch.edn diff --git a/demos/project.edn b/demos/project.edn index 63036e75..117e3c83 100644 --- a/demos/project.edn +++ b/demos/project.edn @@ -1,5 +1,7 @@ +(bpm 150) + (track { :name "Drums" :gain +0.0 } - + (sampler { :name "DrumKit1" :dir "/home/user/Lab/Music/pak" } (sample { :midi 34 :name "808" :file "808.wav" }) (sample { :midi 35 :name "KC1" :file "kik.wav" }) @@ -14,54 +16,53 @@ (:04 (36 128)) (:08 (36 128)) (:12 (36 128))) - -) + (phrase { :name "5 kicks" :beats 4 :steps 16 } + (:00 (36 128)) + (:04 (36 128)) + (:08 (36 128)) + (:12 (36 128)) + (:14 (36 128))) + (phrase { :name "D Beat" :beats 4 }) + ;(:00 (44 :100) (:34 :100) (:35 :100)) + ;(:02 (42 :100) ) + ;(:04 (42 :080) (:38 :100) ) + ;(:06 (44 :120) ) + ;(:08 (42 :100) (:34 :100) (:35 :100)) + ;(:10 (42 :100) (:34 :100) (:35 :100)) + ;(:12 (44 :100) (:40 :100) ) + ;(:13 (44 :100) ) + ;(:14 (44 :100) ) + ;(:15 (42 :100) ))) + (phrase { :name "Garage" :beats 4 } + (:00 (44 100) (36 100) (35 100)) + (:01 (44 100)) + (:02 (44 100) (35 100)) + (:03 (44 100)) + (:04 (44 100) (40 100)) + (:06 (44 100)) + (:07 (44 100) (34 100)) + (:09 (44 100)) + (:10 (44 100)) + (:11 (35 100) (36 100)) + (:12 (44 100) (40 100)) + (:14 (44 100)))) - ;(phrase { - ;:name "5 kicks" - ;:beats 4 - ;} (:00 (36 128)) - ;(:04 (36 128)) - ;(:08 (36 128)) - ;(:12 (36 128)) - ;(:14 (36 128))) +(track { :name "Bass" :gain +0.0 } + (phrase { :name "Bass 1" :beats 4 }) + (phrase { :name "Bass 2" :beats 4 }) + (phrase { :name "Bass 3" :beats 4 }) + (phrase { :name "Bass 4" :beats 4 }) + (phrase { :name "Bass 5" :beats 4 }) + (phrase { :name "Bass 6" :beats 4 }) + (phrase { :name "Bass 7" :beats 4 }) + (phrase { :name "Bass 8" :beats 4 })) - ;(phrase { - ;:name "D Beat" - ;:beats 4 - ;} (:00 (:44 :100) (:34 :100) (:35 :100)) - ;(:02 (:42 :100) ) - ;(:04 (:42 :080) (:38 :100) ) - ;(:06 (:44 :120) ) - ;(:08 (:42 :100) (:34 :100) (:35 :100)) - ;(:10 (:42 :100) (:34 :100) (:35 :100)) - ;(:12 (:44 :100) (:40 :100) ) - ;(:13 (:44 :100) ) - ;(:14 (:44 :100) ) - ;(:15 (:42 :100) )) - - ;(phrase { - ;:name "Garage" - ;:beats 4 - ;} (:00 (44 100) (36 100) (35 100)) - ;(:01 (44 100)) - ;(:02 (44 100) (35 100)) - ;(:03 (44 100)) - ;(:04 (44 100) (40 100)) - ;(:06 (44 100)) - ;(:07 (44 100) (34 100)) - ;(:09 (44 100)) - ;(:10 (44 100)) - ;(:11 (35 100) (36 100)) - ;(:12 (44 100) (40 100)) - ;(:14 (44 100)))) - -;(track "Bass" - ;(lv2-plugin "Odin2") - ;(phrase { :name "Empty" :beats 4 }) - ;(phrase { :name "Empty" :beats 4 })) - -;(track "Lead" - ;(lv2-plugin "Odin2") - ;(phrase { :name "Empty" :beats 4 }) - ;(phrase { :name "Empty" :beats 4 })) +(track { :name "Lead" :gain +0.0 } + (phrase { :name "Lead 1" :beats 4 }) + (phrase { :name "Lead 2" :beats 4 }) + (phrase { :name "Lead 3" :beats 4 }) + (phrase { :name "Lead 4" :beats 4 }) + (phrase { :name "Lead 5" :beats 4 }) + (phrase { :name "Lead 6" :beats 4 }) + (phrase { :name "Lead 7" :beats 4 }) + (phrase { :name "Lead 8" :beats 4 })) diff --git a/demos/scratch.edn b/demos/scratch.edn new file mode 100644 index 00000000..341bb74e --- /dev/null +++ b/demos/scratch.edn @@ -0,0 +1,52 @@ + + + +;(:bpm 150) + +;(:track#01 "Drums") +;(:track#02 "Bass") +;(:track#03 "Lead") + +;(:scene#01 "Intro" (:01 --) (:02 :01) (:03 :01) ) +;(:scene#02 "Hook" ) +;(:scene#03 "Verse" ) +;(:scene#04 "Chorus") +;(:scene#05 "Bridge") + +;(midi-in "nanoKEY Studio.*capture.*") +;(audio-out ["Komplete.+:playback_FL", "Komplete.+:playback_FR"]) + + + ;(sampler { :name "DrumKit1" :dir "/home/user/Lab/Music/pak" } + ;(sample { :midi 34 :name "808" :file "808.wav" }) + ;(sample { :midi 35 :name "KC1" :file "kik.wav" }) + ;(sample { :midi 36 :name "KC2" :file "kik2.wav" }) + ;(sample { :midi 38 :name "SN1" :file "sna.wav" }) + ;(sample { :midi 40 :name "SN2" :file "sna2.wav" }) + ;(sample { :midi 42 :name "HH1" :file "chh.wav" }) + ;(sample { :midi 44 :name "HH2" :file "chh2.wav" })) + + ;(phrase { :name "4 kicks" :beats 4 :steps 16 } + ;(:00 (36 128)) + ;(:04 (36 128)) + ;(:08 (36 128)) + ;(:12 (36 128)))) + + ;(phrase { + ;:name "5 kicks" + ;:beats 4 + ;} (:00 (36 128)) + ;(:04 (36 128)) + ;(:08 (36 128)) + ;(:12 (36 128)) + ;(:14 (36 128))) + +;(track "Bass" + ;(lv2-plugin "Odin2") + ;(phrase { :name "Empty" :beats 4 }) + ;(phrase { :name "Empty" :beats 4 })) + +;(track "Lead" + ;(lv2-plugin "Odin2") + ;(phrase { :name "Empty" :beats 4 }) + ;(phrase { :name "Empty" :beats 4 })) diff --git a/src/core/time.rs b/src/core/time.rs index 14a5ce71..8d62fdc3 100644 --- a/src/core/time.rs +++ b/src/core/time.rs @@ -14,7 +14,7 @@ impl Default for Timebase { fn default () -> Self { Self { rate: 48000f64.into(), - bpm: 100f64.into(), + bpm: 150f64.into(), ppq: 96f64.into(), } } @@ -39,6 +39,9 @@ impl Timebase { #[inline] pub fn bpm (&self) -> f64 { self.bpm.load(Ordering::Relaxed) } + #[inline] pub fn set_bpm (&self, bpm: f64) { + self.bpm.store(bpm, Ordering::Relaxed) + } #[inline] fn usec_per_beat (&self) -> f64 { 60_000_000f64 / self.bpm() as f64 } diff --git a/src/edn.rs b/src/edn.rs index 50cd94d4..7c006408 100644 --- a/src/edn.rs +++ b/src/edn.rs @@ -1,70 +1,84 @@ use crate::{core::*, model::*, App}; -use clojure_reader::edn::Edn; +use clojure_reader::{edn::{read, Edn}, error::Error as EdnError}; impl App { - pub fn load_edn (&mut self, mut src: &str) { + pub fn load_edn (&mut self, mut src: &str) -> Usually<()> { loop { match clojure_reader::edn::read(src) { Ok((edn, rest)) => { - self.load_edn_one(edn); + self.load_edn_one(edn)?; if rest.len() > 0 { src = rest; } else { break } }, + Err(EdnError { ptr: None, .. }) => { + break + }, Err(e) => { panic!("{e:?}"); - } + }, } } + Ok(()) } - fn load_edn_one <'e> (&mut self, edn: Edn<'e>) { + fn load_edn_one <'e> (&mut self, edn: Edn<'e>) -> Usually<()> { match edn { Edn::List(items) => { let head = items.get(0); match head { - Some(Edn::Symbol("track")) => Track::load_edn(self, &items[1..]), + Some(Edn::Symbol("track")) => { + Track::load_edn(self, &items[1..])?; + }, + Some(Edn::Symbol("bpm")) => { + match items.get(1) { + Some(Edn::Int(b)) => self.timebase.set_bpm(*b as f64), + Some(Edn::Double(b)) => self.timebase.set_bpm(f64::from(*b)), + _ => panic!("unspecified bpm") + } + }, _ => panic!("unexpected edn: {head:?}") } }, _ => { panic!("unexpected edn: {edn:?}"); } - }; + } + Ok(()) } } impl Track { - fn load_edn <'a, 'e> (app: &'a mut App, items: &[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 mut name = String::new(); + let mut name = app.new_track_name(); let mut gain = 0.0f64; let mut devices: Vec = vec![]; let mut phrases: Vec = vec![]; - for edn in items[1..].iter() { + for edn in args { match edn { 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); } - 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) } }, - Edn::List(items) => match items.get(0) { + Edn::List(args) => match args.get(0) { Some(Edn::Symbol("phrase")) => { - phrases.push(Phrase::load_edn(ppq, items)?) + phrases.push(Phrase::load_edn(ppq, &args[1..])?) }, Some(Edn::Symbol("sampler")) => { - devices.push(Sampler::load_edn(items)?) + devices.push(Sampler::load_edn(&args[1..])?) }, Some(Edn::Symbol("lv2")) => { - devices.push(LV2Plugin::load_edn(items)?) + devices.push(LV2Plugin::load_edn(&args[1..])?) }, None => panic!("empty list track {name}"), - _ => panic!("unexpected in track {name}: {:?}", items.get(0).unwrap()) + _ => panic!("unexpected in track {name}: {:?}", args.get(0).unwrap()) }, _ => {} } @@ -82,39 +96,39 @@ impl Track { } impl Phrase { - fn load_edn <'e> (ppq: usize, items: &[Edn<'e>]) -> Usually { + fn load_edn <'e> (ppq: usize, args: &[Edn<'e>]) -> Usually { let mut name = String::new(); let mut beats = 0usize; let mut steps = 0usize; let mut data = BTreeMap::new(); - for edn in items[1..].iter() { + for edn in args { match edn { 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); } - if let Some(Edn::Int(b)) = map.get(&Edn::Key("beats")) { + 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")) { + if let Some(Edn::Int(s)) = map.get(&Edn::Key(":steps")) { steps = *s as usize; } }, - Edn::List(items) => { - let time = (match items.get(0) { - Some(Edn::Symbol(text)) => text.parse::()?, + Edn::List(args) => { + let time = (match args.get(0) { + Some(Edn::Key(text)) => text[1..].parse::()?, Some(Edn::Int(i)) => *i as f64, Some(Edn::Double(f)) => f64::from(*f), - _ => panic!("unexpected in phrase {name}: {:?}", items.get(0)), + _ => panic!("unexpected in phrase '{name}': {:?}", args.get(0)), } * beats as f64 * ppq as f64 / steps as f64) as usize; - for edn in items[1..].iter() { + for edn in args[1..].iter() { match edn { - Edn::List(items) => if let ( + Edn::List(args) => if let ( Some(Edn::Int(key)), Some(Edn::Int(vel)), ) = ( - items.get(0), - items.get(1), + args.get(0), + args.get(1), ) { if !data.contains_key(&time) { data.insert(time, vec![]); @@ -124,13 +138,13 @@ impl Phrase { vel: u7::from(*vel as u8), }); } else { - panic!("unexpected list in phrase {name}") + panic!("unexpected list in phrase '{name}'") }, - _ => panic!("unexpected in phrase {name}: {edn:?}") + _ => panic!("unexpected in phrase '{name}': {edn:?}") } } }, - _ => panic!("unexpected in phrase {name}: {edn:?}"), + _ => panic!("unexpected in phrase '{name}': {edn:?}"), } } Ok(Self::new(&name, beats * ppq, Some(data))) @@ -138,30 +152,30 @@ impl Phrase { } impl Sampler { - fn load_edn <'e> (items: &[Edn<'e>]) -> Usually { + fn load_edn <'e> (args: &[Edn<'e>]) -> Usually { let mut name = String::new(); let mut dir = String::new(); let mut samples = BTreeMap::new(); - for edn in items[1..].iter() { + for edn in args { match edn { 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); } - 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); } }, - Edn::List(items) => match items.get(0) { + Edn::List(args) => match args.get(0) { Some(Edn::Symbol("sample")) => { - let (midi, sample) = Sample::load_edn(&items[1..])?; + 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); } }, - _ => panic!("unexpected in sampler {name}: {items:?}") + _ => panic!("unexpected in sampler {name}: {args:?}") }, _ => panic!("unexpected in sampler {name}: {edn:?}") } @@ -171,46 +185,46 @@ impl Sampler { } impl Sample { - fn load_edn <'e> (items: &[Edn<'e>]) -> Usually<(Option, Arc)> { + fn load_edn <'e> (dir: &str, args: &[Edn<'e>]) -> Usually<(Option, Arc)> { let mut name = String::new(); let mut file = String::new(); let mut midi = None; let mut start = 0usize; - for edn in items[1..].iter() { + for edn in args.iter() { match edn { 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); } - 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); } - 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; } - 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)); } }, _ => panic!("unexpected in sample {name}"), } } - let (end, data) = read_sample_data(&file)?; + let (end, data) = read_sample_data(&format!("{dir}/{file}"))?; Ok((midi, Self::new(&name, start, end, data))) } } impl LV2Plugin { - fn load_edn <'e> (items: &[Edn<'e>]) -> Usually { + fn load_edn <'e> (args: &[Edn<'e>]) -> Usually { let mut name = String::new(); let mut path = String::new(); - for edn in items[1..].iter() { + for edn in args.iter() { match edn { 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); } - 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); } }, diff --git a/src/main.rs b/src/main.rs index 53f5f12e..2da6b1f4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -77,35 +77,20 @@ pub fn main () -> Usually<()> { state.jack = Some(jack); - state.load_edn(include_str!("../demos/project.edn")); + state.load_edn(include_str!("../demos/project.edn"))?; - state.add_track_with_cb(Some("Drums"), |_, track|{ + //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])?; + //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)?; //} @@ -115,117 +100,132 @@ pub fn main () -> Usually<()> { //Ok(()) //})?; - track.sequence = Some(1); // FIXME + ////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.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() }, + //track.sequence = Some(1); // FIXME - 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(()) - })?; + //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() }, - 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(()) - })?; + //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("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.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]), diff --git a/src/model.rs b/src/model.rs index e55ff67c..22330ecc 100644 --- a/src/model.rs +++ b/src/model.rs @@ -146,11 +146,14 @@ impl App { }; Ok(()) } + pub fn new_track_name (&self) -> String { + format!("Track {}", self.tracks.len() + 1) + } pub fn add_track ( &mut self, name: Option<&str>, ) -> Usually<&mut Track> { - let name = name.ok_or_else(||format!("Track {}", self.tracks.len() + 1))?; + let name = name.ok_or_else(||self.new_track_name())?; self.tracks.push(Track::new(&name, self.client(), None, None)?); self.track_cursor = self.tracks.len(); Ok(&mut self.tracks[self.track_cursor - 1]) @@ -160,7 +163,7 @@ impl App { name: Option<&str>, init: impl FnOnce(&Client, &mut Track)->Usually<()>, ) -> Usually<&mut Track> { - let name = name.ok_or_else(||format!("Track {}", self.tracks.len() + 1))?; + let name = name.ok_or_else(||self.new_track_name())?; let mut track = Track::new(&name, self.client(), None, None)?; init(self.client(), &mut track)?; self.tracks.push(track); diff --git a/src/model/phrase.rs b/src/model/phrase.rs index 7b1bfc9d..12dfc765 100644 --- a/src/model/phrase.rs +++ b/src/model/phrase.rs @@ -11,6 +11,7 @@ use crate::core::*; }} } +#[derive(Debug)] pub struct Phrase { pub name: String, pub length: usize, diff --git a/src/view.rs b/src/view.rs index 03bd8554..5ce14ce5 100644 --- a/src/view.rs +++ b/src/view.rs @@ -143,12 +143,13 @@ impl App { note_cursor: self.note_cursor, note_start: self.note_start, }.render(buf, area)?; - let track = self.tracks.get(self.track_cursor - 1).unwrap(); - if phrase.is_none() && self.section == AppSection::Sequencer { - let label = format!("[ENTER] Create new clip: {}", track.name); - let x = area.x + seq_area.width / 2 - (label.len() / 2) as u16; - let y = area.y + seq_area.height / 2; - label.blit(buf, x, y, Some(Style::default().white())); + if let Some(track) = self.tracks.get(self.track_cursor - 1) { + if phrase.is_none() && self.section == AppSection::Sequencer { + let label = format!("[ENTER] Create new clip: {}", track.name); + let x = area.x + seq_area.width / 2 - (label.len() / 2) as u16; + let y = area.y + seq_area.height / 2; + label.blit(buf, x, y, Some(Style::default().white())); + } } Ok(seq_area) } diff --git a/src/view/grid.rs b/src/view/grid.rs index 7cfc73a9..6fe2e653 100644 --- a/src/view/grid.rs +++ b/src/view/grid.rs @@ -166,7 +166,7 @@ impl<'a> SceneGridViewVertical<'a> { " " }, phrase.name) } else { - format!("????") + format!(" ??? ") } } else { format!(" ·········")