From 430c51e3059ddc8abfa3ee9dad4e5498025b2f28 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Fri, 9 Aug 2024 21:59:14 +0300 Subject: [PATCH] and it once again compiles --- Cargo.lock | 10 - crates/tek/Cargo.toml | 1 - crates/tek/src/app.rs | 18 +- crates/tek/src/control.rs | 4 +- crates/tek/src/edn.rs | 4 +- crates/tek/src/main.rs | 10 +- crates/tek_chain/Cargo.toml | 15 -- crates/tek_chain/README.md | 1 - crates/tek_chain/src/chain.rs | 86 ------- crates/tek_chain/src/chain_edn.rs | 45 ---- crates/tek_chain/src/lib.rs | 5 - crates/tek_chain/src/main.rs | 5 - crates/tek_core/src/lib.rs | 13 +- crates/tek_jack/src/ports.rs | 5 +- crates/tek_mixer/Cargo.toml | 11 +- crates/tek_mixer/README.md | 3 +- crates/tek_mixer/src/lib.rs | 3 +- crates/tek_mixer/src/main.rs | 6 - crates/tek_mixer/src/mixer.rs | 7 +- crates/tek_mixer/src/mixer_main.rs | 6 + crates/tek_mixer/src/mixer_track.rs | 160 ------------ crates/tek_mixer/src/track.rs | 168 +++++++++++++ crates/tek_mixer/src/track_main.rs | 6 + .../src/track_view.rs} | 23 +- crates/tek_sequencer/src/arranger.rs | 237 ++++++++++++------ crates/tek_sequencer/src/arranger_phrase.rs | 57 ----- crates/tek_sequencer/src/lib.rs | 3 +- crates/tek_sequencer/src/phrase.rs | 54 ++++ crates/tek_sequencer/src/phrase_edn.rs | 115 --------- .../src/{arranger_scene.rs => scene.rs} | 51 ++-- crates/tek_sequencer/src/scene_edn.rs | 28 --- 31 files changed, 466 insertions(+), 694 deletions(-) delete mode 100644 crates/tek_chain/Cargo.toml delete mode 100644 crates/tek_chain/README.md delete mode 100644 crates/tek_chain/src/chain.rs delete mode 100644 crates/tek_chain/src/chain_edn.rs delete mode 100644 crates/tek_chain/src/lib.rs delete mode 100644 crates/tek_chain/src/main.rs create mode 100644 crates/tek_mixer/src/mixer_main.rs delete mode 100644 crates/tek_mixer/src/mixer_track.rs create mode 100644 crates/tek_mixer/src/track.rs create mode 100644 crates/tek_mixer/src/track_main.rs rename crates/{tek_chain/src/chain_view.rs => tek_mixer/src/track_view.rs} (71%) delete mode 100644 crates/tek_sequencer/src/arranger_phrase.rs delete mode 100644 crates/tek_sequencer/src/phrase_edn.rs rename crates/tek_sequencer/src/{arranger_scene.rs => scene.rs} (60%) delete mode 100644 crates/tek_sequencer/src/scene_edn.rs diff --git a/Cargo.lock b/Cargo.lock index 01ffc1a4..6ebe7c5e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2511,7 +2511,6 @@ version = "0.1.0" dependencies = [ "clojure-reader", "microxdg", - "tek_chain", "tek_core", "tek_jack", "tek_mixer", @@ -2521,14 +2520,6 @@ dependencies = [ "tek_timer", ] -[[package]] -name = "tek_chain" -version = "0.1.0" -dependencies = [ - "tek_core", - "tek_jack", -] - [[package]] name = "tek_core" version = "0.1.0" @@ -2556,7 +2547,6 @@ dependencies = [ name = "tek_mixer" version = "0.1.0" dependencies = [ - "tek_chain", "tek_core", "tek_jack", "tek_plugin", diff --git a/crates/tek/Cargo.toml b/crates/tek/Cargo.toml index da7b78a8..8abb2a4f 100644 --- a/crates/tek/Cargo.toml +++ b/crates/tek/Cargo.toml @@ -13,7 +13,6 @@ tek_plugin = { path = "../tek_plugin" } tek_sampler = { path = "../tek_sampler" } tek_sequencer = { path = "../tek_sequencer" } tek_timer = { path = "../tek_timer" } -tek_chain = { path = "../tek_chain" } tek_mixer = { path = "../tek_mixer", features = ["standalone_devices"] } #jack = "0.10" #crossterm = "0.27" diff --git a/crates/tek/src/app.rs b/crates/tek/src/app.rs index d73c8435..8f332739 100644 --- a/crates/tek/src/app.rs +++ b/crates/tek/src/app.rs @@ -1,6 +1,8 @@ use crate::*; +use tek_core::Direction; use tek_timer::TransportToolbar; use tek_sequencer::Arranger; +use tek_mixer::Mixer; /// Root of application state. pub struct App { @@ -10,8 +12,10 @@ pub struct App { pub section: AppFocus, /// Transport model and view. pub transport: TransportToolbar, - /// Arranger/sequencer + /// Arranger (contains sequencers) pub arranger: Arranger, + /// Mixer (contains tracks) + pub mixer: Mixer, /// Main JACK client. pub jack: Option, /// Map of external MIDI outs in the jack graph @@ -37,13 +41,14 @@ impl App { Client::new("tek", ClientOptions::NO_START_SERVER)?.0 ); *MODAL.lock().unwrap() = first_run.then(||{ - Exit::boxed(SetupModal(Some(xdg.clone()), false)) + ExitableComponent::boxed(SetupModal(Some(xdg.clone()), false)) }); Ok(Self { entered: true, section: AppFocus::default(), transport: TransportToolbar::new(Some(jack.transport())), arranger: Arranger::new(), + mixer: Mixer::new("")?, jack: Some(jack), audio_outs: vec![], chain_mode: false, @@ -97,8 +102,13 @@ render!(App |self, buf, area| { &self.transport, &self.arranger, &If(self.arranger.selected.is_clip(), &Split::right([ - &tek_chain::ChainView::vertical(&self), - &self.sequencer, + &tek_mixer::TrackView { + direction: Direction::Down, + entered: self.entered, + focused: self.section == AppFocus::Chain, + chain: self.mixer.track() + }, + &self.arranger.sequencer, ])) ]).render(buf, area)?; if let Some(ref modal) = *MODAL.lock().unwrap() { diff --git a/crates/tek/src/control.rs b/crates/tek/src/control.rs index 0a5e8aeb..bf23d36a 100644 --- a/crates/tek/src/control.rs +++ b/crates/tek/src/control.rs @@ -51,7 +51,7 @@ fn handle_focused (state: &mut App, e: &AppEvent) -> Usually { } fn handle_device (state: &mut App, e: &AppEvent) -> Usually { - state.arranger.track() + state.mixer.track() .and_then(|track|track.device_mut()) .map(|mut device|device.handle(e)) .transpose() @@ -99,7 +99,7 @@ pub const KEYMAP_GLOBAL: &'static [KeyBinding] = keymap!(App { phrase.notes = notes; phrase.length = phrase.length * 2; }); - app.arranger.sequencer.show(app.arranger.phrase())?; + //app.arranger.show_phrase()?; Ok(true) }], [Char('l'), NONE, "loop_toggle", "toggle looping", |_app: &mut App| { diff --git a/crates/tek/src/edn.rs b/crates/tek/src/edn.rs index 67fffbca..b67febf8 100644 --- a/crates/tek/src/edn.rs +++ b/crates/tek/src/edn.rs @@ -59,10 +59,10 @@ impl App { } }, Some(Edn::Symbol("scene")) => { - tek_sequencer::Scene::from_edn(self, &items[1..])?; + tek_sequencer::Scene::from_edn(&items[1..])?; }, Some(Edn::Symbol("track")) => { - tek_mixer::MixerTrack::from_edn(self, &items[1..])?; + tek_mixer::Track::from_edn(&items[1..])?; }, Some(Edn::Symbol("midi-in")) => { self.midi_ins = items[1..].iter().map(|x|match x { diff --git a/crates/tek/src/main.rs b/crates/tek/src/main.rs index 5b12c852..a2c1f866 100644 --- a/crates/tek/src/main.rs +++ b/crates/tek/src/main.rs @@ -24,7 +24,7 @@ submod! { } /// Global modal dialog -pub static MODAL: Lazy>>>> = +pub static MODAL: Lazy>>>> = Lazy::new(||Arc::new(Mutex::new(None))); /// Application entrypoint. @@ -55,10 +55,10 @@ pub fn main () -> Usually<()> { for (index, track) in app.arranger.tracks.iter_mut().enumerate() { track.midi_out = midi_outs[index].take(); } - for track in app.arranger.tracks.iter() { - track.connect_first_device()?; - track.connect_last_device(&app)?; - } + //for track in app.arranger.tracks.iter() { + //track.connect_first_device()?; + //track.connect_last_device(&app)?; + //} }; })))?)?; Ok(()) diff --git a/crates/tek_chain/Cargo.toml b/crates/tek_chain/Cargo.toml deleted file mode 100644 index 0ace170c..00000000 --- a/crates/tek_chain/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "tek_chain" -edition = "2021" -version = "0.1.0" - -[dependencies] -tek_core = { path = "../tek_core" } -tek_jack = { path = "../tek_jack" } - -[lib] -path = "src/lib.rs" - -[[bin]] -name = "tek_chain" -path = "src/main.rs" diff --git a/crates/tek_chain/README.md b/crates/tek_chain/README.md deleted file mode 100644 index 242ae7ca..00000000 --- a/crates/tek_chain/README.md +++ /dev/null @@ -1 +0,0 @@ -# `tek_chain` diff --git a/crates/tek_chain/src/chain.rs b/crates/tek_chain/src/chain.rs deleted file mode 100644 index 40e02484..00000000 --- a/crates/tek_chain/src/chain.rs +++ /dev/null @@ -1,86 +0,0 @@ -use crate::*; - -/// A sequencer track. -#[derive(Debug)] -pub struct Chain { - pub name: String, - /// Device chain - pub devices: Vec, - /// Device selector - pub device: usize, -} - -handle!(Chain |self, event| handle_keymap(self, event, KEYMAP_CHAIN)); - -render!(Chain |self, buf, area| ChainView { - chain: Some(&self), - direction: tek_core::Direction::Right, - focused: true, - entered: true, -}.render(buf, area)); - -impl Chain { - pub fn new (name: &str) -> Usually { - Ok(Self { - name: name.to_string(), - devices: vec![], - device: 0, - }) - } - fn get_device_mut (&self, i: usize) -> Option>> { - self.devices.get(i).map(|d|d.state.write().unwrap()) - } - pub fn device_mut (&self) -> Option>> { - self.get_device_mut(self.device) - } - /// Add a device to the end of the chain. - pub fn append_device (&mut self, device: JackDevice) -> Usually<&mut JackDevice> { - self.devices.push(device); - let index = self.devices.len() - 1; - Ok(&mut self.devices[index]) - } - //pub fn connect_first_device (&self) -> Usually<()> { - //if let (Some(port), Some(device)) = (&self.midi_out, self.devices.get(0)) { - //device.client.as_client().connect_ports(&port, &device.midi_ins()?[0])?; - //} - //Ok(()) - //} - //pub fn connect_last_device (&self, app: &Chain) -> Usually<()> { - //Ok(match self.devices.get(self.devices.len().saturating_sub(1)) { - //Some(device) => { - //app.audio_out(0).map(|left|device.connect_audio_out(0, &left)).transpose()?; - //app.audio_out(1).map(|right|device.connect_audio_out(1, &right)).transpose()?; - //() - //}, - //None => () - //}) - //} -} - -/// Key bindings for chain section. -pub const KEYMAP_CHAIN: &'static [KeyBinding] = keymap!(Chain { - [Up, NONE, "chain_cursor_up", "move cursor up", |_: &mut Chain| { - Ok(true) - }], - [Down, NONE, "chain_cursor_down", "move cursor down", |_: &mut Chain| { - Ok(true) - }], - [Left, NONE, "chain_cursor_left", "move cursor left", |app: &mut Chain| { - //if let Some(track) = app.arranger.track_mut() { - //track.device = track.device.saturating_sub(1); - //return Ok(true) - //} - Ok(false) - }], - [Right, NONE, "chain_cursor_right", "move cursor right", |app: &mut Chain| { - //if let Some(track) = app.arranger.track_mut() { - //track.device = (track.device + 1).min(track.devices.len().saturating_sub(1)); - //return Ok(true) - //} - Ok(false) - }], - [Char('`'), NONE, "chain_mode_switch", "switch the display mode", |app: &mut Chain| { - //app.chain_mode = !app.chain_mode; - Ok(true) - }], -}); diff --git a/crates/tek_chain/src/chain_edn.rs b/crates/tek_chain/src/chain_edn.rs deleted file mode 100644 index 72651114..00000000 --- a/crates/tek_chain/src/chain_edn.rs +++ /dev/null @@ -1,45 +0,0 @@ -use crate::*; - -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) - } -} - diff --git a/crates/tek_chain/src/lib.rs b/crates/tek_chain/src/lib.rs deleted file mode 100644 index fa096377..00000000 --- a/crates/tek_chain/src/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub(crate) use tek_core::*; -pub(crate) use tek_core::ratatui::prelude::*; -pub(crate) use tek_jack::*; -pub(crate) use std::sync::RwLockWriteGuard; -submod! { chain chain_view } diff --git a/crates/tek_chain/src/main.rs b/crates/tek_chain/src/main.rs deleted file mode 100644 index f5920ff4..00000000 --- a/crates/tek_chain/src/main.rs +++ /dev/null @@ -1,5 +0,0 @@ -include!("lib.rs"); -pub fn main () -> Usually<()> { - tek_core::run(Arc::new(RwLock::new(crate::Chain::new("todo")?)))?; - Ok(()) -} diff --git a/crates/tek_core/src/lib.rs b/crates/tek_core/src/lib.rs index c156a32e..de05a0bf 100644 --- a/crates/tek_core/src/lib.rs +++ b/crates/tek_core/src/lib.rs @@ -2,7 +2,7 @@ pub use ratatui; pub use crossterm; pub use midly; pub use clap; -pub use std::sync::{Arc, Mutex, RwLock}; +pub use std::sync::{Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard}; pub use std::collections::BTreeMap; pub use crossterm::event::{Event, KeyEvent, KeyCode, KeyModifiers}; pub use ratatui::prelude::{Rect, Style, Color, Buffer}; @@ -67,9 +67,18 @@ pub trait Component: Render + Handle + Sync { } } -/// Anything that implements `Render` + `Handle` can be used as a UI component. impl Component for T {} +/// Marker trait for [Component]s that can [Exit] +pub trait ExitableComponent: Exit + Component { + /// Perform type erasure for collecting heterogeneous components. + fn boxed (self) -> Box where Self: Sized + 'static { + Box::new(self) + } +} + +impl ExitableComponent for T {} + /// Run the main loop. pub fn run (state: Arc>) -> Usually>> where T: Render + Handle + Send + Sync + Sized + 'static diff --git a/crates/tek_jack/src/ports.rs b/crates/tek_jack/src/ports.rs index 9d2550dd..52a7583b 100644 --- a/crates/tek_jack/src/ports.rs +++ b/crates/tek_jack/src/ports.rs @@ -1,8 +1,8 @@ //! Handling JACK ports. use super::*; -#[derive(Default)] /// Collection of JACK ports as [AudioIn]/[AudioOut]/[MidiIn]/[MidiOut]. +#[derive(Default, Debug)] pub struct JackPorts { pub audio_ins: BTreeMap>, pub midi_ins: BTreeMap>, @@ -10,9 +10,8 @@ pub struct JackPorts { pub midi_outs: BTreeMap>, } -#[derive(Default)] /// Collection of JACK ports as [Unowned]. -#[derive(Debug)] +#[derive(Default, Debug)] pub struct UnownedJackPorts { pub audio_ins: BTreeMap>, pub midi_ins: BTreeMap>, diff --git a/crates/tek_mixer/Cargo.toml b/crates/tek_mixer/Cargo.toml index 1c993154..b7f1284c 100644 --- a/crates/tek_mixer/Cargo.toml +++ b/crates/tek_mixer/Cargo.toml @@ -6,16 +6,21 @@ version = "0.1.0" [dependencies] tek_core = { path = "../tek_core" } tek_jack = { path = "../tek_jack" } -tek_chain = { path = "../tek_chain" } tek_sampler = { path = "../tek_sampler", optional = true } tek_plugin = { path = "../tek_plugin", optional = true } [features] -standalone_devices = [ "tek_sampler", "tek_plugin" ] +standalone_devices = [ "sampler", "plugin" ] +sampler = [ "tek_sampler" ] +plugin = [ "tek_plugin" ] [lib] path = "src/lib.rs" [[bin]] name = "tek_mixer" -path = "src/main.rs" +path = "src/mixer_main.rs" + +[[bin]] +name = "tek_track" +path = "src/track_main.rs" diff --git a/crates/tek_mixer/README.md b/crates/tek_mixer/README.md index e7dd39c2..bdfcfdd4 100644 --- a/crates/tek_mixer/README.md +++ b/crates/tek_mixer/README.md @@ -1,7 +1,6 @@ -# `tek_mixer` +# `tek_mixer` and `tek_track` // TODO: // - Meters: propagate clipping: // - If one stage clips, all stages after it are marked red // - If one track clips, all tracks that feed from it are marked red? - diff --git a/crates/tek_mixer/src/lib.rs b/crates/tek_mixer/src/lib.rs index bae34ee1..8299026f 100644 --- a/crates/tek_mixer/src/lib.rs +++ b/crates/tek_mixer/src/lib.rs @@ -4,5 +4,6 @@ pub(crate) use tek_core::crossterm::event::{KeyCode, KeyModifiers}; pub(crate) use tek_jack::{*, jack::*}; submod! { mixer - mixer_track + track + track_view } diff --git a/crates/tek_mixer/src/main.rs b/crates/tek_mixer/src/main.rs index 643c2c6d..e69de29b 100644 --- a/crates/tek_mixer/src/main.rs +++ b/crates/tek_mixer/src/main.rs @@ -1,6 +0,0 @@ -//! Multi-track mixer -include!("lib.rs"); -pub fn main () -> Usually<()> { - tek_core::run(Arc::new(RwLock::new(crate::Mixer::new("")?)))?; - Ok(()) -} diff --git a/crates/tek_mixer/src/mixer.rs b/crates/tek_mixer/src/mixer.rs index a317241b..7081a1f0 100644 --- a/crates/tek_mixer/src/mixer.rs +++ b/crates/tek_mixer/src/mixer.rs @@ -7,7 +7,7 @@ use crate::*; pub struct Mixer { pub name: String, - pub tracks: Vec, + pub tracks: Vec, pub selected_track: usize, pub selected_column: usize, } @@ -27,10 +27,13 @@ impl Mixer { }) } pub fn add_track (&mut self, name: &str, channels: usize) -> Usually<&mut Self> { - let track = MixerTrack::new(name, channels)?; + let track = Track::new(name)?; self.tracks.push(track); Ok(self) } + pub fn track (&self) -> Option<&Track> { + self.tracks.get(self.selected_track) + } } fn process ( diff --git a/crates/tek_mixer/src/mixer_main.rs b/crates/tek_mixer/src/mixer_main.rs new file mode 100644 index 00000000..643c2c6d --- /dev/null +++ b/crates/tek_mixer/src/mixer_main.rs @@ -0,0 +1,6 @@ +//! Multi-track mixer +include!("lib.rs"); +pub fn main () -> Usually<()> { + tek_core::run(Arc::new(RwLock::new(crate::Mixer::new("")?)))?; + Ok(()) +} diff --git a/crates/tek_mixer/src/mixer_track.rs b/crates/tek_mixer/src/mixer_track.rs deleted file mode 100644 index 050d151f..00000000 --- a/crates/tek_mixer/src/mixer_track.rs +++ /dev/null @@ -1,160 +0,0 @@ -use crate::*; -use tek_core::edn; -#[cfg(feature = "standalone_devices")] -use tek_sampler::*; -#[cfg(feature = "standalone_devices")] -use tek_plugin::*; - -/// A track in the mixer. -pub struct MixerTrack { - pub name: String, - pub ports: JackPorts, - pub devices: Vec, - //pub channels: u8, - //pub input_ports: Vec>, - //pub pre_gain_meter: f64, - //pub gain: f64, - //pub insert_ports: Vec>, - //pub return_ports: Vec>, - //pub post_gain_meter: f64, - //pub post_insert_meter: f64, - //pub level: f64, - //pub pan: f64, - //pub output_ports: Vec>, - //pub post_fader_meter: f64, - //pub route: String, -} - -impl MixerTrack { - - const SYM_NAME: &'static str = ":name"; - const SYM_GAIN: &'static str = ":gain"; - const SYM_SAMPLER: &'static str = "sampler"; - const SYM_LV2: &'static str = "lv2"; - - pub fn from_edn <'a, 'e> (args: &[Edn<'e>]) -> Usually { - let mut _gain = 0.0f64; - let mut track = MixerTrack::new("", 0)?; - #[allow(unused_mut)] - let mut devices: Vec = vec![]; - edn!(edn in args { - Edn::Map(map) => { - if let Some(Edn::Str(n)) = map.get(&Edn::Key(Self::SYM_NAME)) { - track.name = n.to_string(); - } - if let Some(Edn::Double(g)) = map.get(&Edn::Key(Self::SYM_GAIN)) { - _gain = f64::from(*g); - } - }, - Edn::List(args) => match args.get(0) { - // Add a sampler device to the track - Some(Edn::Symbol(Self::SYM_SAMPLER)) => { - #[cfg(feature = "standalone_devices")] - devices.push(Sampler::from_edn(&args[1..])?); - #[cfg(not(feature = "standalone_devices"))] - panic!( - "unsupported in track {}: {:?}; tek_mixer not compiled with feature \"sampler\"", - &track.name, - args.get(0).unwrap() - ) - }, - // Add a LV2 plugin to the track. - Some(Edn::Symbol(Self::SYM_LV2)) => { - #[cfg(feature = "standalone_devices")] - devices.push(LV2Plugin::from_edn(&args[1..])?); - #[cfg(not(feature = "standalone_devices"))] - panic!( - "unsupported in track {}: {:?}; tek_mixer not compiled with feature \"plugin\"", - &track.name, - args.get(0).unwrap() - ) - }, - None => - panic!("empty list track {}", &track.name), - _ => - panic!("unexpected in track {}: {:?}", &track.name, args.get(0).unwrap()) - }, - _ => {} - }); - for device in devices { - track.add_device(device); - } - Ok(track) - } - - pub fn new (name: &str, channels: usize) -> Usually { - Ok(Self { - name: name.into(), - ports: JackPorts::default(), - devices: vec![], - //channels, - //input_ports, - //pre_gain_meter: 0.0, - //gain: 0.0, - //post_gain_meter: 0.0, - //insert_ports, - //return_ports, - //post_insert_meter: 0.0, - //level: 0.0, - //pan: 0.0, - //post_fader_meter: 0.0, - //route: "---".into(), - //output_ports, - }) - //let mut input_ports = vec![]; - //let mut insert_ports = vec![]; - //let mut return_ports = vec![]; - //let mut output_ports = vec![]; - //for channel in 1..=channels { - //input_ports.push(jack.register_port(&format!("{name} [input {channel}]"), AudioIn::default())?); - //output_ports.push(jack.register_port(&format!("{name} [out {channel}]"), AudioOut::default())?); - //let insert_port = jack.register_port(&format!("{name} [pre {channel}]"), AudioOut::default())?; - //let return_port = jack.register_port(&format!("{name} [insert {channel}]"), AudioIn::default())?; - //jack.connect_ports(&insert_port, &return_port)?; - //insert_ports.push(insert_port); - //return_ports.push(return_port); - //} - } - - pub fn add_device (&mut self, device: JackDevice) { - self.devices.push(device); - } -} - -//impl Input, bool> for Mixer { - //fn handle (&mut self, engine: &mut TUI) -> Result> { - //Ok(None) - //} -// - -//impl Output, [u16;2]> for Mixer { - //fn render (&self, envine: &mut TUI) -> Result> { - - //let tracks_table = Columns::new() - //.add(titles) - //.add(input_meters) - //.add(gains) - //.add(gain_meters) - //.add(pres) - //.add(pre_meters) - //.add(levels) - //.add(pans) - //.add(pan_meters) - //.add(posts) - //.add(routes) - - //Rows::new() - //.add(Columns::new() - //.add(Rows::new() - //.add("[Arrows]".bold()) - //.add("Navigate")) - //.add(Rows::new() - //.add("[+/-]".bold()) - //.add("Adjust")) - //.add(Rows::new() - //.add("[Ins/Del]".bold()) - //.add("Add/remove track"))) - //.add(tracks_table) - //.render(engine) - //} -//} diff --git a/crates/tek_mixer/src/track.rs b/crates/tek_mixer/src/track.rs new file mode 100644 index 00000000..1d0790e3 --- /dev/null +++ b/crates/tek_mixer/src/track.rs @@ -0,0 +1,168 @@ +use crate::*; +use tek_core::edn; +#[cfg(feature = "standalone_devices")] +use tek_sampler::*; +#[cfg(feature = "standalone_devices")] +use tek_plugin::*; + +/// A sequencer track. +#[derive(Debug)] +pub struct Track { + pub name: String, + /// Inputs and outputs of 1st and last device + pub ports: JackPorts, + /// Device chain + pub devices: Vec, + /// Device selector + pub device: usize, +} + +handle!(Track |self, event| handle_keymap(self, event, KEYMAP_CHAIN)); + +render!(Track |self, buf, area| TrackView { + chain: Some(&self), + direction: tek_core::Direction::Right, + focused: true, + entered: true, + //pub channels: u8, + //pub input_ports: Vec>, + //pub pre_gain_meter: f64, + //pub gain: f64, + //pub insert_ports: Vec>, + //pub return_ports: Vec>, + //pub post_gain_meter: f64, + //pub post_insert_meter: f64, + //pub level: f64, + //pub pan: f64, + //pub output_ports: Vec>, + //pub post_fader_meter: f64, + //pub route: String, +}.render(buf, area)); + +impl Track { + + pub fn new (name: &str) -> Usually { + Ok(Self { + name: name.to_string(), + ports: JackPorts::default(), + devices: vec![], + device: 0, + }) + } + + fn get_device_mut (&self, i: usize) -> Option>> { + self.devices.get(i).map(|d|d.state.write().unwrap()) + } + pub fn device_mut (&self) -> Option>> { + self.get_device_mut(self.device) + } + /// Add a device to the end of the chain. + pub fn append_device (&mut self, device: JackDevice) -> Usually<&mut JackDevice> { + self.devices.push(device); + let index = self.devices.len() - 1; + Ok(&mut self.devices[index]) + } + //pub fn connect_first_device (&self) -> Usually<()> { + //if let (Some(port), Some(device)) = (&self.midi_out, self.devices.get(0)) { + //device.client.as_client().connect_ports(&port, &device.midi_ins()?[0])?; + //} + //Ok(()) + //} + //pub fn connect_last_device (&self, app: &Track) -> Usually<()> { + //Ok(match self.devices.get(self.devices.len().saturating_sub(1)) { + //Some(device) => { + //app.audio_out(0).map(|left|device.connect_audio_out(0, &left)).transpose()?; + //app.audio_out(1).map(|right|device.connect_audio_out(1, &right)).transpose()?; + //() + //}, + //None => () + //}) + //} + + pub fn from_edn <'a, 'e> (args: &[Edn<'e>]) -> Usually { + let mut _gain = 0.0f64; + let mut track = Self::new("")?; + #[allow(unused_mut)] + let mut devices: Vec = 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.get(0) { + // Add a sampler device to the track + Some(Edn::Symbol(SYM_SAMPLER)) => { + #[cfg(feature = "standalone_devices")] + devices.push(Sampler::from_edn(&args[1..])?); + #[cfg(not(feature = "standalone_devices"))] + panic!( + "unsupported in track {}: {:?}; tek_mixer not compiled with feature \"sampler\"", + &track.name, + args.get(0).unwrap() + ) + }, + // Add a LV2 plugin to the track. + Some(Edn::Symbol(SYM_LV2)) => { + #[cfg(feature = "standalone_devices")] + devices.push(LV2Plugin::from_edn(&args[1..])?); + #[cfg(not(feature = "standalone_devices"))] + panic!( + "unsupported in track {}: {:?}; tek_mixer not compiled with feature \"plugin\"", + &track.name, + args.get(0).unwrap() + ) + }, + None => + panic!("empty list track {}", &track.name), + _ => + panic!("unexpected in track {}: {:?}", &track.name, args.get(0).unwrap()) + }, + _ => {} + }); + for device in devices { + track.add_device(device); + } + Ok(track) + } + + pub fn add_device (&mut self, device: JackDevice) { + self.devices.push(device); + } +} + +const SYM_NAME: &'static str = ":name"; +const SYM_GAIN: &'static str = ":gain"; +const SYM_SAMPLER: &'static str = "sampler"; +const SYM_LV2: &'static str = "lv2"; + +/// Key bindings for chain section. +pub const KEYMAP_CHAIN: &'static [KeyBinding] = keymap!(Track { + [Up, NONE, "chain_cursor_up", "move cursor up", |_: &mut Track| { + Ok(true) + }], + [Down, NONE, "chain_cursor_down", "move cursor down", |_: &mut Track| { + Ok(true) + }], + [Left, NONE, "chain_cursor_left", "move cursor left", |app: &mut Track| { + //if let Some(track) = app.arranger.track_mut() { + //track.device = track.device.saturating_sub(1); + //return Ok(true) + //} + Ok(false) + }], + [Right, NONE, "chain_cursor_right", "move cursor right", |app: &mut Track| { + //if let Some(track) = app.arranger.track_mut() { + //track.device = (track.device + 1).min(track.devices.len().saturating_sub(1)); + //return Ok(true) + //} + Ok(false) + }], + [Char('`'), NONE, "chain_mode_switch", "switch the display mode", |app: &mut Track| { + //app.chain_mode = !app.chain_mode; + Ok(true) + }], +}); diff --git a/crates/tek_mixer/src/track_main.rs b/crates/tek_mixer/src/track_main.rs new file mode 100644 index 00000000..ecb93cb7 --- /dev/null +++ b/crates/tek_mixer/src/track_main.rs @@ -0,0 +1,6 @@ +//! Multi-track mixer +include!("lib.rs"); +pub fn main () -> Usually<()> { + tek_core::run(Arc::new(RwLock::new(crate::Track::new("")?)))?; + Ok(()) +} diff --git a/crates/tek_chain/src/chain_view.rs b/crates/tek_mixer/src/track_view.rs similarity index 71% rename from crates/tek_chain/src/chain_view.rs rename to crates/tek_mixer/src/track_view.rs index cf89c4f4..19dd0623 100644 --- a/crates/tek_chain/src/chain_view.rs +++ b/crates/tek_mixer/src/track_view.rs @@ -1,14 +1,14 @@ use crate::*; use tek_core::Direction; -pub struct ChainView<'a> { - pub chain: Option<&'a Chain>, +pub struct TrackView<'a> { + pub chain: Option<&'a Track>, pub direction: Direction, pub focused: bool, pub entered: bool, } -impl<'a> Render for ChainView<'a> { +impl<'a> Render for TrackView<'a> { fn render (&self, buf: &mut Buffer, mut area: Rect) -> Usually { if let Some(chain) = self.chain { match self.direction { @@ -41,20 +41,3 @@ impl<'a> Render for ChainView<'a> { } } } - -//impl<'a> ChainView<'a> { - //pub fn horizontal (app: &'a App) -> Self { - //Self::new(app, Direction::Right) - //} - //pub fn vertical (app: &'a App) -> Self { - //Self::new(app, Direction::Down) - //} - //pub fn new (app: &'a App, direction: Direction) -> Self { - //Self { - //direction, - //entered: app.entered, - //focused: app.section == AppFocus::Chain, - //chain: app.arranger.chain() - //} - //} -//} diff --git a/crates/tek_sequencer/src/arranger.rs b/crates/tek_sequencer/src/arranger.rs index a2ecd7dc..ff01585b 100644 --- a/crates/tek_sequencer/src/arranger.rs +++ b/crates/tek_sequencer/src/arranger.rs @@ -2,6 +2,160 @@ use crate::*; +/// Represents the tracks and scenes of the composition. +pub struct Arranger { + /// Display mode of arranger + pub mode: ArrangerViewMode, + /// Currently selected element. + pub selected: ArrangerFocus, + /// Collection of tracks. + pub tracks: Vec, + /// Collection of scenes. + pub scenes: Vec, + pub focused: bool, + pub entered: bool, + pub fixed_height: bool, + pub sequencer: Sequencer, +} + +/// Display mode of arranger +pub enum ArrangerViewMode { + Vertical, + VerticalCompact, + Horizontal, +} + +impl ArrangerViewMode { + fn to_next (&mut self) { + *self = match self { + Self::Vertical => Self::VerticalCompact, + Self::VerticalCompact => Self::Horizontal, + Self::Horizontal => Self::Vertical, + } + } +} + +impl Arranger { + pub fn new () -> Self { + Self { + mode: ArrangerViewMode::Vertical, + selected: ArrangerFocus::Clip(0, 0), + scenes: vec![], + tracks: vec![], + entered: true, + focused: true, + fixed_height: false, + sequencer: Sequencer::new(), + } + } + pub fn activate (&mut self) { + match self.selected { + ArrangerFocus::Scene(s) => { + for (track_index, track) in self.tracks.iter_mut().enumerate() { + track.sequence = self.scenes[s].clips[track_index]; + track.reset = true; + } + }, + ArrangerFocus::Clip(t, s) => { + self.tracks[t].sequence = self.scenes[s].clips[t]; + self.tracks[t].reset = true; + }, + _ => {} + } + } + fn show_phrase (&mut self) -> Usually<()> { + unimplemented!() + //let phrase = self.phrase(); + //self.sequencer.show(phrase) + } + pub fn phrase (&self) -> Option<&Arc>> { + let track_id = self.selected.track()?; + self.tracks.get(track_id)?.phrases.get((*self.scene()?.clips.get(track_id)?)?) + } + pub fn phrase_next (&mut self) { + let track_index = self.selected.track(); + let scene_index = self.selected.scene(); + track_index + .and_then(|index|self.tracks.get_mut(index).map(|track|(index, track))) + .and_then(|(track_index, track)|{ + let phrases = track.phrases.len(); + scene_index + .and_then(|index|self.scenes.get_mut(index)) + .and_then(|scene|{ + if let Some(phrase_index) = scene.clips[track_index] { + if phrase_index >= phrases - 1 { + scene.clips[track_index] = None; + } else { + scene.clips[track_index] = Some(phrase_index + 1); + } + } else if phrases > 0 { + scene.clips[track_index] = Some(0); + } + Some(()) + }) + }); + } + pub fn phrase_prev (&mut self) { + let track_index = self.selected.track(); + let scene_index = self.selected.scene(); + track_index + .and_then(|index|self.tracks.get_mut(index).map(|track|(index, track))) + .and_then(|(track_index, track)|{ + let phrases = track.phrases.len(); + scene_index + .and_then(|index|self.scenes.get_mut(index)) + .and_then(|scene|{ + if let Some(phrase_index) = scene.clips[track_index] { + scene.clips[track_index] = if phrase_index == 0 { + None + } else { + Some(phrase_index - 1) + }; + } else if phrases > 0 { + scene.clips[track_index] = Some(phrases - 1); + } + Some(()) + }) + }); + } + pub fn scene (&self) -> Option<&Scene> { + self.selected.scene().map(|s|self.scenes.get(s)).flatten() + } + pub fn scene_mut (&mut self) -> Option<&mut Scene> { + self.selected.scene().map(|s|self.scenes.get_mut(s)).flatten() + } + pub fn scene_next (&mut self) { + self.selected.scene_next(self.scenes.len() - 1) + } + pub fn scene_prev (&mut self) { + self.selected.scene_prev() + } + pub fn scene_add (&mut self, name: Option<&str>) -> Usually<&mut Scene> { + let clips = vec![None;self.tracks.len()]; + self.scenes.push(match name { + Some(name) => Scene::new(name, clips), + None => Scene::new(&self.scene_default_name(), clips), + }); + let index = self.scenes.len() - 1; + Ok(&mut self.scenes[index]) + } + pub fn scene_del (&mut self) { + unimplemented!("Arranger::scene_del"); + } + pub fn scene_default_name (&self) -> String { + format!("Scene {}", self.scenes.len() + 1) + } +} + +render!(Arranger |self, buf, area| match self.mode { + ArrangerViewMode::Horizontal => + super::arranger_view_h::draw(self, buf, area), + ArrangerViewMode::Vertical => + super::arranger_view_v::draw_expanded(self, buf, area), + ArrangerViewMode::VerticalCompact => + super::arranger_view_v::draw_compact(self, buf, area), +}); + /// Key bindings for arranger section. pub const KEYMAP_ARRANGER: &'static [KeyBinding] = keymap!(Arranger { [Char('`'), NONE, "arranger_mode_switch", "switch the display mode", |app: &mut Arranger| { @@ -55,86 +209,3 @@ pub const KEYMAP_ARRANGER: &'static [KeyBinding] = keymap!(Arranger { Ok(true) }], }); - -/// Represents the tracks and scenes of the composition. -pub struct Arranger { - /// Display mode of arranger - pub mode: ArrangerViewMode, - /// Currently selected element. - pub selected: ArrangerFocus, - /// Collection of tracks. - pub tracks: Vec, - /// Collection of scenes. - pub scenes: Vec, - - pub focused: bool, - pub entered: bool, - pub fixed_height: bool, - - pub sequencer: Sequencer, -} - -/// Display mode of arranger -pub enum ArrangerViewMode { - Vertical, - VerticalCompact, - Horizontal, -} - -impl ArrangerViewMode { - fn to_next (&mut self) { - *self = match self { - Self::Vertical => Self::VerticalCompact, - Self::VerticalCompact => Self::Horizontal, - Self::Horizontal => Self::Vertical, - } - } -} - -impl Arranger { - - pub fn new () -> Self { - Self { - mode: ArrangerViewMode::Vertical, - selected: ArrangerFocus::Clip(0, 0), - scenes: vec![], - tracks: vec![], - entered: true, - focused: true, - fixed_height: false, - sequencer: Sequencer::new(), - } - } - - pub fn activate (&mut self) { - match self.selected { - ArrangerFocus::Scene(s) => { - for (track_index, track) in self.tracks.iter_mut().enumerate() { - track.sequence = self.scenes[s].clips[track_index]; - track.reset = true; - } - }, - ArrangerFocus::Clip(t, s) => { - self.tracks[t].sequence = self.scenes[s].clips[t]; - self.tracks[t].reset = true; - }, - _ => {} - } - } - - fn show_phrase (&mut self) -> Usually<()> { - unimplemented!() - //let phrase = self.phrase(); - //self.sequencer.show(phrase) - } -} - -render!(Arranger |self, buf, area| match self.mode { - ArrangerViewMode::Horizontal => - super::arranger_view_h::draw(self, buf, area), - ArrangerViewMode::Vertical => - super::arranger_view_v::draw_expanded(self, buf, area), - ArrangerViewMode::VerticalCompact => - super::arranger_view_v::draw_compact(self, buf, area), -}); - diff --git a/crates/tek_sequencer/src/arranger_phrase.rs b/crates/tek_sequencer/src/arranger_phrase.rs deleted file mode 100644 index 71cadcff..00000000 --- a/crates/tek_sequencer/src/arranger_phrase.rs +++ /dev/null @@ -1,57 +0,0 @@ -use crate::*; - -use super::Arranger; - -/// Phrase management methods -impl Arranger { - pub fn phrase (&self) -> Option<&Arc>> { - let track_id = self.selected.track()?; - self.tracks.get(track_id)?.phrases.get((*self.scene()?.clips.get(track_id)?)?) - } - pub fn phrase_next (&mut self) { - let track_index = self.selected.track(); - let scene_index = self.selected.scene(); - track_index - .and_then(|index|self.tracks.get_mut(index).map(|track|(index, track))) - .and_then(|(track_index, track)|{ - let phrases = track.phrases.len(); - scene_index - .and_then(|index|self.scenes.get_mut(index)) - .and_then(|scene|{ - if let Some(phrase_index) = scene.clips[track_index] { - if phrase_index >= phrases - 1 { - scene.clips[track_index] = None; - } else { - scene.clips[track_index] = Some(phrase_index + 1); - } - } else if phrases > 0 { - scene.clips[track_index] = Some(0); - } - Some(()) - }) - }); - } - pub fn phrase_prev (&mut self) { - let track_index = self.selected.track(); - let scene_index = self.selected.scene(); - track_index - .and_then(|index|self.tracks.get_mut(index).map(|track|(index, track))) - .and_then(|(track_index, track)|{ - let phrases = track.phrases.len(); - scene_index - .and_then(|index|self.scenes.get_mut(index)) - .and_then(|scene|{ - if let Some(phrase_index) = scene.clips[track_index] { - scene.clips[track_index] = if phrase_index == 0 { - None - } else { - Some(phrase_index - 1) - }; - } else if phrases > 0 { - scene.clips[track_index] = Some(phrases - 1); - } - Some(()) - }) - }); - } -} diff --git a/crates/tek_sequencer/src/lib.rs b/crates/tek_sequencer/src/lib.rs index a83f1b05..118406ba 100644 --- a/crates/tek_sequencer/src/lib.rs +++ b/crates/tek_sequencer/src/lib.rs @@ -17,9 +17,8 @@ submod! { sequencer_track arranger arranger_focus - arranger_phrase - arranger_scene arranger_track + scene } pubmod! { diff --git a/crates/tek_sequencer/src/phrase.rs b/crates/tek_sequencer/src/phrase.rs index 5578f083..2aff4403 100644 --- a/crates/tek_sequencer/src/phrase.rs +++ b/crates/tek_sequencer/src/phrase.rs @@ -79,6 +79,60 @@ impl Phrase { } } } + fn from_edn <'e> (ppq: usize, args: &[Edn<'e>]) -> Usually { + 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::()?, + 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) + } } /// Define a MIDI phrase. diff --git a/crates/tek_sequencer/src/phrase_edn.rs b/crates/tek_sequencer/src/phrase_edn.rs deleted file mode 100644 index 111dbeca..00000000 --- a/crates/tek_sequencer/src/phrase_edn.rs +++ /dev/null @@ -1,115 +0,0 @@ -use crate::*; - -impl Phrase { - fn load_edn <'e> (ppq: usize, args: &[Edn<'e>]) -> Usually { - 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::()?, - 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 Phrase { - //fn from_edn <'e> (ppq: usize, args: &[Edn<'e>]) -> Usually { - //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::()?, - //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) - //} -//} diff --git a/crates/tek_sequencer/src/arranger_scene.rs b/crates/tek_sequencer/src/scene.rs similarity index 60% rename from crates/tek_sequencer/src/arranger_scene.rs rename to crates/tek_sequencer/src/scene.rs index b4c6e019..94e3e9ba 100644 --- a/crates/tek_sequencer/src/arranger_scene.rs +++ b/crates/tek_sequencer/src/scene.rs @@ -1,7 +1,5 @@ use crate::*; -use super::Arranger; - /// A collection of phrases to play on each track. pub struct Scene { pub name: String, @@ -48,33 +46,28 @@ pub fn scene_ppqs (tracks: &[SequencerTrack], scenes: &[Scene]) -> Vec<(usize, u scenes } -/// Scene management methods -impl Arranger { - pub fn scene (&self) -> Option<&Scene> { - self.selected.scene().map(|s|self.scenes.get(s)).flatten() - } - pub fn scene_mut (&mut self) -> Option<&mut Scene> { - self.selected.scene().map(|s|self.scenes.get_mut(s)).flatten() - } - pub fn scene_next (&mut self) { - self.selected.scene_next(self.scenes.len() - 1) - } - pub fn scene_prev (&mut self) { - self.selected.scene_prev() - } - pub fn scene_add (&mut self, name: Option<&str>) -> Usually<&mut Scene> { - let clips = vec![None;self.tracks.len()]; - self.scenes.push(match name { - Some(name) => Scene::new(name, clips), - None => Scene::new(&self.scene_default_name(), clips), +impl Scene { + 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:?}") }); - let index = self.scenes.len() - 1; - Ok(&mut self.scenes[index]) - } - pub fn scene_del (&mut self) { - unimplemented!("Arranger::scene_del"); - } - pub fn scene_default_name (&self) -> String { - format!("Scene {}", self.scenes.len() + 1) + let scene = Self::new(name.unwrap_or(""), clips); + Ok(scene) } } diff --git a/crates/tek_sequencer/src/scene_edn.rs b/crates/tek_sequencer/src/scene_edn.rs deleted file mode 100644 index d4fb10b4..00000000 --- a/crates/tek_sequencer/src/scene_edn.rs +++ /dev/null @@ -1,28 +0,0 @@ -use crate::*; - -impl Scene { - 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 { - 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) - } -}