From 84aacfea8286518f098dc7b4ac4c1928186b2492 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Sat, 9 Nov 2024 21:27:04 +0100 Subject: [PATCH] wip: refactor: cli, snd, tui --- Cargo.lock | 33 ++- Cargo.toml | 4 +- crates/tek/Cargo.toml | 4 +- crates/tek_api/Cargo.toml | 3 + crates/tek_api/src/api_edn.rs | 27 +- crates/tek_api/src/api_midi.rs | 28 --- crates/tek_api/src/lib.rs | 238 +++++++++++++++++- crates/tek_app/src/transport.rs | 105 -------- crates/{tek_app => tek_cli}/Cargo.toml | 11 +- crates/tek_cli/README.md | 1 + .../src/cli_arranger.rs} | 0 .../mixer_cli.rs => tek_cli/src/cli_mixer.rs} | 0 .../src/cli_plugin.rs} | 0 .../src/cli_sampler.rs} | 0 .../src/cli_sequencer.rs} | 0 .../src/cli_transport.rs} | 2 +- crates/tek_cli/src/lib.rs | 0 crates/tek_core/Cargo.toml | 3 +- crates/tek_snd/Cargo.toml | 8 + crates/tek_snd/README.md | 1 + crates/tek_snd/src/lib.rs | 10 + .../src/snd_arranger.rs} | 3 +- .../mixer_snd.rs => tek_snd/src/snd_mixer.rs} | 3 +- .../src/snd_plugin.rs} | 3 +- .../src/snd_sampler.rs} | 3 +- .../src/snd_sequencer.rs} | 8 +- .../src/snd_transport.rs} | 2 +- crates/tek_tui/Cargo.toml | 16 ++ crates/{tek_app => tek_tui}/README.md | 0 crates/{tek_app => tek_tui}/src/arranger.rs | 2 +- .../{tek_app => tek_tui}/src/arranger_cmd.rs | 0 .../{tek_app => tek_tui}/src/arranger_tui.rs | 0 .../src/arranger_tui_bar.rs | 0 .../src/arranger_tui_cmd.rs | 0 .../src/arranger_tui_col.rs | 0 .../src/arranger_tui_hor.rs | 0 .../src/arranger_tui_ver.rs | 0 crates/{tek_app => tek_tui}/src/lib.rs | 0 crates/{tek_app => tek_tui}/src/mixer.rs | 0 crates/{tek_app => tek_tui}/src/mixer_cmd.rs | 0 crates/{tek_app => tek_tui}/src/mixer_tui.rs | 0 crates/{tek_app => tek_tui}/src/plugin.rs | 0 crates/{tek_app => tek_tui}/src/plugin_cmd.rs | 0 crates/{tek_app => tek_tui}/src/plugin_lv2.rs | 0 .../src/plugin_lv2_gui.rs | 0 crates/{tek_app => tek_tui}/src/plugin_tui.rs | 0 .../{tek_app => tek_tui}/src/plugin_vst2.rs | 0 .../{tek_app => tek_tui}/src/plugin_vst3.rs | 0 crates/{tek_app => tek_tui}/src/sampler.rs | 0 .../{tek_app => tek_tui}/src/sampler_cmd.rs | 0 .../{tek_app => tek_tui}/src/sampler_tui.rs | 0 crates/{tek_app => tek_tui}/src/sequencer.rs | 0 .../{tek_app => tek_tui}/src/sequencer_cmd.rs | 0 .../{tek_app => tek_tui}/src/sequencer_tui.rs | 0 crates/{tek_app => tek_tui}/src/track_cli.rs | 0 crates/tek_tui/src/transport.rs | 81 ++++++ .../{tek_app => tek_tui}/src/transport_cmd.rs | 8 +- .../{tek_app => tek_tui}/src/transport_tui.rs | 0 58 files changed, 416 insertions(+), 191 deletions(-) delete mode 100644 crates/tek_api/src/api_midi.rs delete mode 100644 crates/tek_app/src/transport.rs rename crates/{tek_app => tek_cli}/Cargo.toml (71%) create mode 100644 crates/tek_cli/README.md rename crates/{tek_app/src/arranger_cli.rs => tek_cli/src/cli_arranger.rs} (100%) rename crates/{tek_app/src/mixer_cli.rs => tek_cli/src/cli_mixer.rs} (100%) rename crates/{tek_app/src/plugin_cli.rs => tek_cli/src/cli_plugin.rs} (100%) rename crates/{tek_app/src/sampler_cli.rs => tek_cli/src/cli_sampler.rs} (100%) rename crates/{tek_app/src/sequencer_cli.rs => tek_cli/src/cli_sequencer.rs} (100%) rename crates/{tek_app/src/transport_cli.rs => tek_cli/src/cli_transport.rs} (77%) create mode 100644 crates/tek_cli/src/lib.rs create mode 100644 crates/tek_snd/Cargo.toml create mode 100644 crates/tek_snd/README.md create mode 100644 crates/tek_snd/src/lib.rs rename crates/{tek_app/src/arranger_snd.rs => tek_snd/src/snd_arranger.rs} (97%) rename crates/{tek_app/src/mixer_snd.rs => tek_snd/src/snd_mixer.rs} (76%) rename crates/{tek_app/src/plugin_snd.rs => tek_snd/src/snd_plugin.rs} (98%) rename crates/{tek_app/src/sampler_snd.rs => tek_snd/src/snd_sampler.rs} (87%) rename crates/{tek_app/src/sequencer_snd.rs => tek_snd/src/snd_sequencer.rs} (99%) rename crates/{tek_app/src/transport_snd.rs => tek_snd/src/snd_transport.rs} (96%) create mode 100644 crates/tek_tui/Cargo.toml rename crates/{tek_app => tek_tui}/README.md (100%) rename crates/{tek_app => tek_tui}/src/arranger.rs (99%) rename crates/{tek_app => tek_tui}/src/arranger_cmd.rs (100%) rename crates/{tek_app => tek_tui}/src/arranger_tui.rs (100%) rename crates/{tek_app => tek_tui}/src/arranger_tui_bar.rs (100%) rename crates/{tek_app => tek_tui}/src/arranger_tui_cmd.rs (100%) rename crates/{tek_app => tek_tui}/src/arranger_tui_col.rs (100%) rename crates/{tek_app => tek_tui}/src/arranger_tui_hor.rs (100%) rename crates/{tek_app => tek_tui}/src/arranger_tui_ver.rs (100%) rename crates/{tek_app => tek_tui}/src/lib.rs (100%) rename crates/{tek_app => tek_tui}/src/mixer.rs (100%) rename crates/{tek_app => tek_tui}/src/mixer_cmd.rs (100%) rename crates/{tek_app => tek_tui}/src/mixer_tui.rs (100%) rename crates/{tek_app => tek_tui}/src/plugin.rs (100%) rename crates/{tek_app => tek_tui}/src/plugin_cmd.rs (100%) rename crates/{tek_app => tek_tui}/src/plugin_lv2.rs (100%) rename crates/{tek_app => tek_tui}/src/plugin_lv2_gui.rs (100%) rename crates/{tek_app => tek_tui}/src/plugin_tui.rs (100%) rename crates/{tek_app => tek_tui}/src/plugin_vst2.rs (100%) rename crates/{tek_app => tek_tui}/src/plugin_vst3.rs (100%) rename crates/{tek_app => tek_tui}/src/sampler.rs (100%) rename crates/{tek_app => tek_tui}/src/sampler_cmd.rs (100%) rename crates/{tek_app => tek_tui}/src/sampler_tui.rs (100%) rename crates/{tek_app => tek_tui}/src/sequencer.rs (100%) rename crates/{tek_app => tek_tui}/src/sequencer_cmd.rs (100%) rename crates/{tek_app => tek_tui}/src/sequencer_tui.rs (100%) rename crates/{tek_app => tek_tui}/src/track_cli.rs (100%) create mode 100644 crates/tek_tui/src/transport.rs rename crates/{tek_app => tek_tui}/src/transport_cmd.rs (93%) rename crates/{tek_app => tek_tui}/src/transport_tui.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 4c9cc5c6..80b4377b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2661,22 +2661,20 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" name = "tek_api" version = "0.1.0" dependencies = [ + "livi", + "symphonia", "tek_core", "uuid", + "vst", ] [[package]] -name = "tek_app" +name = "tek_cli" version = "0.1.0" dependencies = [ - "livi", - "suil-rs", - "symphonia", "tek_api", "tek_core", - "vst", - "wavers", - "winit", + "tek_tui", ] [[package]] @@ -2695,32 +2693,31 @@ dependencies = [ "palette", "rand", "ratatui", - "tek_mixer", - "tek_sequencer", "toml", ] [[package]] -name = "tek_mixer" +name = "tek_snd" +version = "0.1.0" +dependencies = [ + "tek_api", + "tek_core", +] + +[[package]] +name = "tek_tui" version = "0.1.0" dependencies = [ "livi", "suil-rs", "symphonia", + "tek_api", "tek_core", "vst", "wavers", "winit", ] -[[package]] -name = "tek_sequencer" -version = "0.1.0" -dependencies = [ - "tek_core", - "uuid", -] - [[package]] name = "thiserror" version = "1.0.63" diff --git a/Cargo.toml b/Cargo.toml index 66f78292..72911332 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,8 @@ resolver = "2" members = [ "crates/tek_core", + "crates/tek_snd", "crates/tek_api", - "crates/tek_app" + "crates/tek_cli", + "crates/tek_tui" ] diff --git a/crates/tek/Cargo.toml b/crates/tek/Cargo.toml index 6964a0f9..5130e5cd 100644 --- a/crates/tek/Cargo.toml +++ b/crates/tek/Cargo.toml @@ -8,8 +8,8 @@ clojure-reader = "0.1.0" microxdg = "0.1.2" tek_core = { path = "../tek_core" } -tek_sequencer = { path = "../tek_sequencer" } -tek_mixer = { path = "../tek_mixer" } +#tek_sequencer = { path = "../tek_sequencer" } +#tek_mixer = { path = "../tek_mixer" } #jack = "0.10" #crossterm = "0.27" #ratatui = { version = "0.26.3", features = [ "unstable-widget-ref", "underline-color" ] } diff --git a/crates/tek_api/Cargo.toml b/crates/tek_api/Cargo.toml index 23d70e54..0352a1e8 100644 --- a/crates/tek_api/Cargo.toml +++ b/crates/tek_api/Cargo.toml @@ -6,3 +6,6 @@ version = "0.1.0" [dependencies] tek_core = { path = "../tek_core" } uuid = { version = "1.10.0", features = [ "v4" ] } +vst = "0.4.0" +livi = "0.7.4" +symphonia = { version = "0.5.4", features = [ "all" ] } diff --git a/crates/tek_api/src/api_edn.rs b/crates/tek_api/src/api_edn.rs index b7f5b0ac..7d1e57ec 100644 --- a/crates/tek_api/src/api_edn.rs +++ b/crates/tek_api/src/api_edn.rs @@ -1,4 +1,5 @@ use crate::*; +use crate::midly::num::u7; impl Scene { pub fn from_edn <'a, 'e> (args: &[Edn<'e>]) -> Usually { @@ -26,29 +27,29 @@ impl Scene { } } -const SYM_NAME: &'static str = ":name"; -const SYM_GAIN: &'static str = ":gain"; -const SYM_SAMPLER: &'static str = "sampler"; -const SYM_LV2: &'static str = "lv2"; -impl Track { +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> (jack: &Arc>, args: &[Edn<'e>]) -> Usually { let mut _gain = 0.0f64; let mut track = Self::new("")?; #[allow(unused_mut)] - let mut devices: Vec> = vec![]; + let mut devices: Vec = vec![]; edn!(edn in args { Edn::Map(map) => { - if let Some(Edn::Str(n)) = map.get(&Edn::Key(SYM_NAME)) { + 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(SYM_GAIN)) { + 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(SYM_SAMPLER)) => { + Some(Edn::Symbol(Self::SYM_SAMPLER)) => { devices.push(Sampler::from_edn(jack, &args[1..])?); panic!( "unsupported in track {}: {:?}; tek_mixer not compiled with feature \"sampler\"", @@ -57,7 +58,7 @@ impl Track { ) }, // Add a LV2 plugin to the track. - Some(Edn::Symbol(SYM_LV2)) => { + Some(Edn::Symbol(Self::SYM_LV2)) => { devices.push(LV2Plugin::from_edn(jack, &args[1..])?); panic!( "unsupported in track {}: {:?}; tek_mixer not compiled with feature \"plugin\"", @@ -80,7 +81,7 @@ impl Track { } impl LV2Plugin { - pub fn from_edn <'e, E: Engine> (jack: &Arc>, args: &[Edn<'e>]) -> Usually> { + pub fn from_edn <'e> (jack: &Arc>, args: &[Edn<'e>]) -> Usually { let mut name = String::new(); let mut path = String::new(); edn!(edn in args { @@ -98,8 +99,8 @@ impl LV2Plugin { } } -impl Sampler { - pub fn from_edn <'e> (jack: &Arc>, args: &[Edn<'e>]) -> Usually> { +impl Sampler { + pub fn from_edn <'e> (jack: &Arc>, args: &[Edn<'e>]) -> Usually { let mut name = String::new(); let mut dir = String::new(); let mut samples = BTreeMap::new(); diff --git a/crates/tek_api/src/api_midi.rs b/crates/tek_api/src/api_midi.rs deleted file mode 100644 index ff984c64..00000000 --- a/crates/tek_api/src/api_midi.rs +++ /dev/null @@ -1,28 +0,0 @@ -use crate::*; -pub(crate) use tek_core::midly::MidiMessage; - -/// MIDI message structural -pub type PhraseData = Vec>; - -/// A MIDI sequence. -#[derive(Debug, Clone)] pub struct Phrase { - pub uuid: uuid::Uuid, - /// Name of phrase - pub name: String, - /// Temporal resolution in pulses per quarter note - pub ppq: usize, - /// Length of phrase in pulses - pub length: usize, - /// Notes in phrase - pub notes: PhraseData, - /// Whether to loop the phrase or play it once - pub loop_on: bool, - /// Start of loop - pub loop_start: usize, - /// Length of loop - pub loop_length: usize, - /// All notes are displayed with minimum length - pub percussive: bool, - /// Identifying color of phrase - pub color: ItemColorTriplet, -} diff --git a/crates/tek_api/src/lib.rs b/crates/tek_api/src/lib.rs index f909690f..ef4988ce 100644 --- a/crates/tek_api/src/lib.rs +++ b/crates/tek_api/src/lib.rs @@ -1,6 +1,242 @@ pub(crate) use tek_core::*; +pub(crate) use tek_core::jack::{TransportState, Port, MidiIn, MidiOut}; +pub(crate) use tek_core::midly::{MidiMessage, num::u7}; +pub(crate) use std::thread::JoinHandle; + submod! { api_cmd api_edn - api_midi +} + +/// A timer with starting point, current time, and quantization +#[derive(Default, Debug)] +pub struct Clock { + /// Playback state + pub playing: RwLock>, + /// Global sample and usec at which playback started + pub started: RwLock>, + /// Current moment in time + pub current: Instant, + /// Note quantization factor + pub quant: Quantize, + /// Launch quantization factor + pub sync: LaunchSync, +} + +impl Clock { + #[inline] pub fn timebase (&self) -> &Arc { + &self.current.timebase + } + #[inline] pub fn pulse (&self) -> f64 { + self.current.pulse.get() + } + #[inline] pub fn quant (&self) -> f64 { + self.quant.get() + } + #[inline] pub fn sync (&self) -> f64 { + self.sync.get() + } + #[inline] pub fn next_launch_pulse (&self) -> usize { + let sync = self.sync.get() as usize; + let pulse = self.current.pulse.get() as usize; + if pulse % sync == 0 { pulse } else { (pulse / sync + 1) * sync } + } +} + +pub struct TransportToolbar { + pub jack: Arc>, + /// JACK transport handle. + pub transport: jack::Transport, + /// Current sample rate, tempo, and PPQ. + pub clock: Arc, + /// Enable metronome? + pub metronome: bool, +} + +pub struct Arrangement { + /// JACK client handle (needs to not be dropped for standalone mode to work). + pub jack: Arc>, + /// Global timebase + pub clock: Arc, + /// Name of arranger + pub name: Arc>, + /// Collection of phrases. + pub phrases: Arc>>, + /// Collection of tracks. + pub tracks: Vec, + /// Collection of scenes. + pub scenes: Vec, +} + +#[derive(Debug)] +pub struct Sequencer { + /// Name of track + pub name: Arc>, + /// Preferred width of track column + pub width: usize, + /// Identifying color of track + pub color: ItemColor, + /// MIDI player/recorder + pub player: PhrasePlayer, +} + +/// Phrase player. +#[derive(Debug)] +pub struct PhrasePlayer { + /// Global timebase + pub clock: Arc, + /// Start time and phrase being played + pub phrase: Option<(Instant, Option>>)>, + /// Start time and next phrase + pub next_phrase: Option<(Instant, Option>>)>, + /// Play input through output. + pub monitoring: bool, + /// Write input to sequence. + pub recording: bool, + /// Overdub input to sequence. + pub overdub: bool, + /// Send all notes off + pub reset: bool, // TODO?: after Some(nframes) + /// Record from MIDI ports to current sequence. + pub midi_inputs: Vec>, + /// Play from current sequence to MIDI ports + pub midi_outputs: Vec>, + /// MIDI output buffer + pub midi_note: Vec, + /// MIDI output buffer + pub midi_chunk: Vec>>, + /// Notes currently held at input + pub notes_in: Arc>, + /// Notes currently held at output + pub notes_out: Arc>, +} + +#[derive(Default, Debug, Clone)] +pub struct Scene { + /// Name of scene + pub name: Arc>, + /// Clips in scene, one per track + pub clips: Vec>>>, + /// Identifying color of scene + pub color: ItemColor, +} + +/// A MIDI sequence. +#[derive(Debug, Clone, Default)] +pub struct Phrase { + pub uuid: uuid::Uuid, + /// Name of phrase + pub name: String, + /// Temporal resolution in pulses per quarter note + pub ppq: usize, + /// Length of phrase in pulses + pub length: usize, + /// Notes in phrase + pub notes: PhraseData, + /// Whether to loop the phrase or play it once + pub loop_on: bool, + /// Start of loop + pub loop_start: usize, + /// Length of loop + pub loop_length: usize, + /// All notes are displayed with minimum length + pub percussive: bool, + /// Identifying color of phrase + pub color: ItemColorTriplet, +} + +/// MIDI message structural +pub type PhraseData = Vec>; + +#[derive(Default, Debug, Clone)] +pub struct Mixer { + /// JACK client handle (needs to not be dropped for standalone mode to work). + pub jack: Arc>, + pub name: String, + pub tracks: Vec, + pub selected_track: usize, + pub selected_column: usize, +} + +/// A mixer track. +#[derive(Default, Debug, Clone)] +pub struct MixerTrack { + pub name: String, + /// Inputs and outputs of 1st and last device + pub ports: JackPorts, + /// Device chain + pub devices: Vec, + /// Device selector + pub device: usize, +} + +/// The sampler plugin plays sounds. +#[derive(Default, Debug, Clone)] +pub struct Sampler { + pub jack: Arc>, + pub name: String, + pub cursor: (usize, usize), + pub editing: Option>>, + pub mapped: BTreeMap>>, + pub unmapped: Vec>>, + pub voices: Arc>>, + pub ports: JackPorts, + pub buffer: Vec>, + pub modal: Arc>>>, + pub output_gain: f32 +} + +/// A sound sample. +#[derive(Default, Debug)] +pub struct Sample { + pub name: String, + pub start: usize, + pub end: usize, + pub channels: Vec>, + pub rate: Option, +} + +/// A currently playing instance of a sample. +#[derive(Default, Debug, Clone)] +pub struct Voice { + pub sample: Arc>, + pub after: usize, + pub position: usize, + pub velocity: f32, +} + +/// A plugin device. +#[derive(Debug)] +pub struct Plugin { + /// JACK client handle (needs to not be dropped for standalone mode to work). + pub jack: Arc>, + pub name: String, + pub path: Option, + pub plugin: Option, + pub selected: usize, + pub mapping: bool, + pub ports: JackPorts, +} + +/// Supported plugin formats. +#[derive(Default, Debug)] +pub enum PluginKind { + #[default] None, + LV2(LV2Plugin), + VST2 { + instance: ::vst::host::PluginInstance + }, + VST3, +} + +/// A LV2 plugin. +#[derive(Debug)] +pub struct LV2Plugin { + pub world: livi::World, + pub instance: livi::Instance, + pub plugin: livi::LiviPlugin, + pub features: Arc, + pub port_list: Vec, + pub input_buffer: Vec, + pub ui_thread: Option>, } diff --git a/crates/tek_app/src/transport.rs b/crates/tek_app/src/transport.rs deleted file mode 100644 index ac0561a2..00000000 --- a/crates/tek_app/src/transport.rs +++ /dev/null @@ -1,105 +0,0 @@ -use crate::*; -/// A timer with starting point, current time, and quantization -#[derive(Debug, Default)] pub struct TransportTime { - /// Playback state - pub playing: RwLock>, - /// Global sample and usec at which playback started - pub started: RwLock>, - /// Current moment in time - pub current: Instant, - /// Note quantization factor - pub quant: Quantize, - /// Launch quantization factor - pub sync: LaunchSync, -} -impl TransportTime { - #[inline] pub fn timebase (&self) -> &Arc { &self.current.timebase } - #[inline] pub fn pulse (&self) -> f64 { self.current.pulse.get() } - #[inline] pub fn quant (&self) -> f64 { self.quant.get() } - #[inline] pub fn sync (&self) -> f64 { self.sync.get() } - #[inline] pub fn next_launch_pulse (&self) -> usize { - let sync = self.sync.get() as usize; - let pulse = self.current.pulse.get() as usize; - if pulse % sync == 0 { pulse } else { (pulse / sync + 1) * sync } - } -} -/// Stores and displays time-related state. -pub struct TransportToolbar { - _engine: PhantomData, - /// JACK client handle (needs to not be dropped for standalone mode to work). - pub jack: Arc>, - /// JACK transport handle. - pub transport: Transport, - /// Current sample rate, tempo, and PPQ. - pub clock: Arc, - /// Enable metronome? - pub metronome: bool, - /// Whether the toolbar is focused - pub focused: bool, - /// Which part of the toolbar is focused - pub focus: TransportToolbarFocus, -} -/// Which item of the transport toolbar is focused -#[derive(Clone, Copy, PartialEq)] -pub enum TransportToolbarFocus { Bpm, Sync, PlayPause, Clock, Quant, } -impl TransportToolbar { - pub fn new (jack: &Arc>, clock: Option<&Arc>) -> Self { - Self { - _engine: Default::default(), - focused: false, - focus: TransportToolbarFocus::PlayPause, - metronome: false, - transport: jack.read().unwrap().transport(), - jack: jack.clone(), - clock: match clock { - Some(clock) => clock.clone(), - None => { - let timebase = Arc::new(Timebase::default()); - Arc::new(TransportTime { - playing: Some(TransportState::Stopped).into(), - quant: 24.into(), - sync: (timebase.ppq.get() * 4.).into(), - current: Instant::default(), - started: None.into(), - }) - } - } - } - } - pub fn toggle_play (&mut self) -> Usually<()> { - let playing = self.clock.playing.read().unwrap().expect("1st sample has not been processed yet"); - let playing = match playing { - TransportState::Stopped => { - self.transport.start()?; - Some(TransportState::Starting) - }, - _ => { - self.transport.stop()?; - self.transport.locate(0)?; - Some(TransportState::Stopped) - }, - }; - *self.clock.playing.write().unwrap() = playing; - Ok(()) - } -} -impl TransportToolbarFocus { - pub fn next (&mut self) { - *self = match self { - Self::PlayPause => Self::Bpm, - Self::Bpm => Self::Quant, - Self::Quant => Self::Sync, - Self::Sync => Self::Clock, - Self::Clock => Self::PlayPause, - } - } - pub fn prev (&mut self) { - *self = match self { - Self::PlayPause => Self::Clock, - Self::Bpm => Self::PlayPause, - Self::Quant => Self::Bpm, - Self::Sync => Self::Quant, - Self::Clock => Self::Sync, - } - } -} diff --git a/crates/tek_app/Cargo.toml b/crates/tek_cli/Cargo.toml similarity index 71% rename from crates/tek_app/Cargo.toml rename to crates/tek_cli/Cargo.toml index 2e82a635..e8cf13bc 100644 --- a/crates/tek_app/Cargo.toml +++ b/crates/tek_cli/Cargo.toml @@ -1,19 +1,12 @@ [package] -name = "tek_app" +name = "tek_cli" edition = "2021" version = "0.1.0" [dependencies] tek_core = { path = "../tek_core" } tek_api = { path = "../tek_api" } - -livi = "0.7.4" -suil-rs = { path = "../suil" } -symphonia = { version = "0.5.4", features = [ "all" ] } -vst = "0.4.0" -#vst3 = "0.1.0" -wavers = "1.4.3" -winit = { version = "0.30.4", features = [ "x11" ] } +tek_tui = { path = "../tek_tui" } [lib] path = "src/lib.rs" diff --git a/crates/tek_cli/README.md b/crates/tek_cli/README.md new file mode 100644 index 00000000..051bf760 --- /dev/null +++ b/crates/tek_cli/README.md @@ -0,0 +1 @@ +cli entry points diff --git a/crates/tek_app/src/arranger_cli.rs b/crates/tek_cli/src/cli_arranger.rs similarity index 100% rename from crates/tek_app/src/arranger_cli.rs rename to crates/tek_cli/src/cli_arranger.rs diff --git a/crates/tek_app/src/mixer_cli.rs b/crates/tek_cli/src/cli_mixer.rs similarity index 100% rename from crates/tek_app/src/mixer_cli.rs rename to crates/tek_cli/src/cli_mixer.rs diff --git a/crates/tek_app/src/plugin_cli.rs b/crates/tek_cli/src/cli_plugin.rs similarity index 100% rename from crates/tek_app/src/plugin_cli.rs rename to crates/tek_cli/src/cli_plugin.rs diff --git a/crates/tek_app/src/sampler_cli.rs b/crates/tek_cli/src/cli_sampler.rs similarity index 100% rename from crates/tek_app/src/sampler_cli.rs rename to crates/tek_cli/src/cli_sampler.rs diff --git a/crates/tek_app/src/sequencer_cli.rs b/crates/tek_cli/src/cli_sequencer.rs similarity index 100% rename from crates/tek_app/src/sequencer_cli.rs rename to crates/tek_cli/src/cli_sequencer.rs diff --git a/crates/tek_app/src/transport_cli.rs b/crates/tek_cli/src/cli_transport.rs similarity index 77% rename from crates/tek_app/src/transport_cli.rs rename to crates/tek_cli/src/cli_transport.rs index 4cd2278e..4ebde7b2 100644 --- a/crates/tek_app/src/transport_cli.rs +++ b/crates/tek_cli/src/cli_transport.rs @@ -2,7 +2,7 @@ include!("lib.rs"); /// Application entrypoint. pub fn main () -> Usually<()> { Tui::run(JackClient::new("tek_transport")?.activate_with(|jack|{ - let mut transport = TransportToolbar::new(jack, None); + let mut transport = TransportToolbarView::new(jack, None); transport.focused = true; Ok(transport) })?)?; diff --git a/crates/tek_cli/src/lib.rs b/crates/tek_cli/src/lib.rs new file mode 100644 index 00000000..e69de29b diff --git a/crates/tek_core/Cargo.toml b/crates/tek_core/Cargo.toml index a80ef13f..80ec514e 100644 --- a/crates/tek_core/Cargo.toml +++ b/crates/tek_core/Cargo.toml @@ -20,5 +20,4 @@ toml = "0.8.12" #no_deadlocks = "1.3.2" [dev-dependencies] -tek_mixer = { version = "0.1.0", path = "../tek_mixer" } -tek_sequencer = { version = "0.1.0", path = "../tek_sequencer" } +#tek_app = { version = "0.1.0", path = "../tek_app" } diff --git a/crates/tek_snd/Cargo.toml b/crates/tek_snd/Cargo.toml new file mode 100644 index 00000000..64634afe --- /dev/null +++ b/crates/tek_snd/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "tek_snd" +edition = "2021" +version = "0.1.0" + +[dependencies] +tek_core = { path = "../tek_core" } +tek_api = { path = "../tek_api" } diff --git a/crates/tek_snd/README.md b/crates/tek_snd/README.md new file mode 100644 index 00000000..17da7482 --- /dev/null +++ b/crates/tek_snd/README.md @@ -0,0 +1 @@ +jack-based audio engine diff --git a/crates/tek_snd/src/lib.rs b/crates/tek_snd/src/lib.rs new file mode 100644 index 00000000..097bdbbd --- /dev/null +++ b/crates/tek_snd/src/lib.rs @@ -0,0 +1,10 @@ +pub(crate) use tek_core::*; +submod! { + snd_arranger + snd_mixer + snd_plugin + snd_sampler + snd_sequencer + snd_transport +} + diff --git a/crates/tek_app/src/arranger_snd.rs b/crates/tek_snd/src/snd_arranger.rs similarity index 97% rename from crates/tek_app/src/arranger_snd.rs rename to crates/tek_snd/src/snd_arranger.rs index 74343a1d..0186cf8c 100644 --- a/crates/tek_app/src/arranger_snd.rs +++ b/crates/tek_snd/src/snd_arranger.rs @@ -1,5 +1,6 @@ use crate::*; -impl Audio for Arranger { + +impl Audio for Arranger { fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control { if let Some(ref transport) = self.transport { transport.write().unwrap().process(client, scope); diff --git a/crates/tek_app/src/mixer_snd.rs b/crates/tek_snd/src/snd_mixer.rs similarity index 76% rename from crates/tek_app/src/mixer_snd.rs rename to crates/tek_snd/src/snd_mixer.rs index eea362e2..b8593fb4 100644 --- a/crates/tek_app/src/mixer_snd.rs +++ b/crates/tek_snd/src/snd_mixer.rs @@ -1,5 +1,6 @@ use crate::*; -impl Audio for Mixer { + +impl Audio for Mixer { fn process (&mut self, _: &Client, _: &ProcessScope) -> Control { Control::Continue } diff --git a/crates/tek_app/src/plugin_snd.rs b/crates/tek_snd/src/snd_plugin.rs similarity index 98% rename from crates/tek_app/src/plugin_snd.rs rename to crates/tek_snd/src/snd_plugin.rs index 1adba53e..5ee03f29 100644 --- a/crates/tek_app/src/plugin_snd.rs +++ b/crates/tek_snd/src/snd_plugin.rs @@ -1,5 +1,6 @@ use crate::*; -impl Audio for Plugin { + +impl Audio for Plugin { fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { match self.plugin.as_mut() { Some(PluginKind::LV2(LV2Plugin { diff --git a/crates/tek_app/src/sampler_snd.rs b/crates/tek_snd/src/snd_sampler.rs similarity index 87% rename from crates/tek_app/src/sampler_snd.rs rename to crates/tek_snd/src/snd_sampler.rs index 880c1116..4ce221e6 100644 --- a/crates/tek_app/src/sampler_snd.rs +++ b/crates/tek_snd/src/snd_sampler.rs @@ -1,5 +1,6 @@ use crate::*; -impl Audio for Sampler { + +impl Audio for Sampler { fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { self.process_midi_in(scope); self.clear_output_buffer(); diff --git a/crates/tek_app/src/sequencer_snd.rs b/crates/tek_snd/src/snd_sequencer.rs similarity index 99% rename from crates/tek_app/src/sequencer_snd.rs rename to crates/tek_snd/src/snd_sequencer.rs index 8a7fbbf5..82368cf5 100644 --- a/crates/tek_app/src/sequencer_snd.rs +++ b/crates/tek_snd/src/snd_sequencer.rs @@ -1,6 +1,7 @@ use crate::*; + /// JACK process callback for sequencer app -impl Audio for Sequencer { +impl Audio for Sequencer { fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control { if let Some(ref transport) = self.transport { transport.write().unwrap().process(client, scope); @@ -9,6 +10,7 @@ impl Audio for Sequencer { Control::Continue } } + /// JACK process callback for a sequencer's phrase player/recorder. impl Audio for PhrasePlayer { fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { @@ -34,6 +36,7 @@ impl Audio for PhrasePlayer { Control::Continue } } + /// Methods used primarily by the process callback impl PhrasePlayer { fn is_rolling (&self) -> bool { @@ -199,6 +202,7 @@ impl PhrasePlayer { } } } + /// Add "all notes off" to the start of a buffer. pub fn all_notes_off (output: &mut PhraseChunk) { let mut buf = vec![]; @@ -207,6 +211,7 @@ pub fn all_notes_off (output: &mut PhraseChunk) { evt.write(&mut buf).unwrap(); output[0].push(buf); } + /// Return boxed iterator of MIDI events pub fn parse_midi_input (input: MidiIter) -> Box + '_> { Box::new(input.map(|RawMidi { time, bytes }|( @@ -215,6 +220,7 @@ pub fn parse_midi_input (input: MidiIter) -> Box Audio for TransportToolbar { +impl Audio for TransportToolbar { fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { let times = scope.cycle_times().unwrap(); let CycleTimes { current_frames, current_usecs, next_usecs: _, period_usecs: _ } = times; diff --git a/crates/tek_tui/Cargo.toml b/crates/tek_tui/Cargo.toml new file mode 100644 index 00000000..a4c6cd05 --- /dev/null +++ b/crates/tek_tui/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "tek_tui" +edition = "2021" +version = "0.1.0" + +[dependencies] +tek_core = { path = "../tek_core" } +tek_api = { path = "../tek_api" } + +livi = "0.7.4" +suil-rs = { path = "../suil" } +symphonia = { version = "0.5.4", features = [ "all" ] } +vst = "0.4.0" +#vst3 = "0.1.0" +wavers = "1.4.3" +winit = { version = "0.30.4", features = [ "x11" ] } diff --git a/crates/tek_app/README.md b/crates/tek_tui/README.md similarity index 100% rename from crates/tek_app/README.md rename to crates/tek_tui/README.md diff --git a/crates/tek_app/src/arranger.rs b/crates/tek_tui/src/arranger.rs similarity index 99% rename from crates/tek_app/src/arranger.rs rename to crates/tek_tui/src/arranger.rs index 1fdb5446..53f8bd5c 100644 --- a/crates/tek_app/src/arranger.rs +++ b/crates/tek_tui/src/arranger.rs @@ -1,7 +1,7 @@ use crate::*; /// Root level object for standalone `tek_arranger` -pub struct Arranger { +pub struct ArrangerView { /// JACK client handle (needs to not be dropped for standalone mode to work). pub jack: Arc>, /// Which view is focused diff --git a/crates/tek_app/src/arranger_cmd.rs b/crates/tek_tui/src/arranger_cmd.rs similarity index 100% rename from crates/tek_app/src/arranger_cmd.rs rename to crates/tek_tui/src/arranger_cmd.rs diff --git a/crates/tek_app/src/arranger_tui.rs b/crates/tek_tui/src/arranger_tui.rs similarity index 100% rename from crates/tek_app/src/arranger_tui.rs rename to crates/tek_tui/src/arranger_tui.rs diff --git a/crates/tek_app/src/arranger_tui_bar.rs b/crates/tek_tui/src/arranger_tui_bar.rs similarity index 100% rename from crates/tek_app/src/arranger_tui_bar.rs rename to crates/tek_tui/src/arranger_tui_bar.rs diff --git a/crates/tek_app/src/arranger_tui_cmd.rs b/crates/tek_tui/src/arranger_tui_cmd.rs similarity index 100% rename from crates/tek_app/src/arranger_tui_cmd.rs rename to crates/tek_tui/src/arranger_tui_cmd.rs diff --git a/crates/tek_app/src/arranger_tui_col.rs b/crates/tek_tui/src/arranger_tui_col.rs similarity index 100% rename from crates/tek_app/src/arranger_tui_col.rs rename to crates/tek_tui/src/arranger_tui_col.rs diff --git a/crates/tek_app/src/arranger_tui_hor.rs b/crates/tek_tui/src/arranger_tui_hor.rs similarity index 100% rename from crates/tek_app/src/arranger_tui_hor.rs rename to crates/tek_tui/src/arranger_tui_hor.rs diff --git a/crates/tek_app/src/arranger_tui_ver.rs b/crates/tek_tui/src/arranger_tui_ver.rs similarity index 100% rename from crates/tek_app/src/arranger_tui_ver.rs rename to crates/tek_tui/src/arranger_tui_ver.rs diff --git a/crates/tek_app/src/lib.rs b/crates/tek_tui/src/lib.rs similarity index 100% rename from crates/tek_app/src/lib.rs rename to crates/tek_tui/src/lib.rs diff --git a/crates/tek_app/src/mixer.rs b/crates/tek_tui/src/mixer.rs similarity index 100% rename from crates/tek_app/src/mixer.rs rename to crates/tek_tui/src/mixer.rs diff --git a/crates/tek_app/src/mixer_cmd.rs b/crates/tek_tui/src/mixer_cmd.rs similarity index 100% rename from crates/tek_app/src/mixer_cmd.rs rename to crates/tek_tui/src/mixer_cmd.rs diff --git a/crates/tek_app/src/mixer_tui.rs b/crates/tek_tui/src/mixer_tui.rs similarity index 100% rename from crates/tek_app/src/mixer_tui.rs rename to crates/tek_tui/src/mixer_tui.rs diff --git a/crates/tek_app/src/plugin.rs b/crates/tek_tui/src/plugin.rs similarity index 100% rename from crates/tek_app/src/plugin.rs rename to crates/tek_tui/src/plugin.rs diff --git a/crates/tek_app/src/plugin_cmd.rs b/crates/tek_tui/src/plugin_cmd.rs similarity index 100% rename from crates/tek_app/src/plugin_cmd.rs rename to crates/tek_tui/src/plugin_cmd.rs diff --git a/crates/tek_app/src/plugin_lv2.rs b/crates/tek_tui/src/plugin_lv2.rs similarity index 100% rename from crates/tek_app/src/plugin_lv2.rs rename to crates/tek_tui/src/plugin_lv2.rs diff --git a/crates/tek_app/src/plugin_lv2_gui.rs b/crates/tek_tui/src/plugin_lv2_gui.rs similarity index 100% rename from crates/tek_app/src/plugin_lv2_gui.rs rename to crates/tek_tui/src/plugin_lv2_gui.rs diff --git a/crates/tek_app/src/plugin_tui.rs b/crates/tek_tui/src/plugin_tui.rs similarity index 100% rename from crates/tek_app/src/plugin_tui.rs rename to crates/tek_tui/src/plugin_tui.rs diff --git a/crates/tek_app/src/plugin_vst2.rs b/crates/tek_tui/src/plugin_vst2.rs similarity index 100% rename from crates/tek_app/src/plugin_vst2.rs rename to crates/tek_tui/src/plugin_vst2.rs diff --git a/crates/tek_app/src/plugin_vst3.rs b/crates/tek_tui/src/plugin_vst3.rs similarity index 100% rename from crates/tek_app/src/plugin_vst3.rs rename to crates/tek_tui/src/plugin_vst3.rs diff --git a/crates/tek_app/src/sampler.rs b/crates/tek_tui/src/sampler.rs similarity index 100% rename from crates/tek_app/src/sampler.rs rename to crates/tek_tui/src/sampler.rs diff --git a/crates/tek_app/src/sampler_cmd.rs b/crates/tek_tui/src/sampler_cmd.rs similarity index 100% rename from crates/tek_app/src/sampler_cmd.rs rename to crates/tek_tui/src/sampler_cmd.rs diff --git a/crates/tek_app/src/sampler_tui.rs b/crates/tek_tui/src/sampler_tui.rs similarity index 100% rename from crates/tek_app/src/sampler_tui.rs rename to crates/tek_tui/src/sampler_tui.rs diff --git a/crates/tek_app/src/sequencer.rs b/crates/tek_tui/src/sequencer.rs similarity index 100% rename from crates/tek_app/src/sequencer.rs rename to crates/tek_tui/src/sequencer.rs diff --git a/crates/tek_app/src/sequencer_cmd.rs b/crates/tek_tui/src/sequencer_cmd.rs similarity index 100% rename from crates/tek_app/src/sequencer_cmd.rs rename to crates/tek_tui/src/sequencer_cmd.rs diff --git a/crates/tek_app/src/sequencer_tui.rs b/crates/tek_tui/src/sequencer_tui.rs similarity index 100% rename from crates/tek_app/src/sequencer_tui.rs rename to crates/tek_tui/src/sequencer_tui.rs diff --git a/crates/tek_app/src/track_cli.rs b/crates/tek_tui/src/track_cli.rs similarity index 100% rename from crates/tek_app/src/track_cli.rs rename to crates/tek_tui/src/track_cli.rs diff --git a/crates/tek_tui/src/transport.rs b/crates/tek_tui/src/transport.rs new file mode 100644 index 00000000..3bfc3e38 --- /dev/null +++ b/crates/tek_tui/src/transport.rs @@ -0,0 +1,81 @@ +use crate::*; +/// Stores and displays time-related state. +#[derive(Debug)] +pub struct TransportView { + _engine: PhantomData, + state: TransportToolbar, + focused: bool, + focus: TransportFocus, +} +/// Which item of the transport toolbar is focused +#[derive(Clone, Copy, PartialEq)] +pub enum TransportFocus { + Bpm, + Sync, + PlayPause, + Clock, + Quant, +} +impl TransportView { + pub fn new (jack: &Arc>, clock: Option<&Arc>) -> Self { + Self { + _engine: Default::default(), + focused: false, + focus: TransportFocus::PlayPause, + state: TransportToolbar { + metronome: false, + transport: jack.read().unwrap().transport(), + jack: jack.clone(), + clock: match clock { + Some(clock) => clock.clone(), + None => { + let timebase = Arc::new(Timebase::default()); + Arc::new(TransportTime { + playing: Some(TransportState::Stopped).into(), + quant: 24.into(), + sync: (timebase.ppq.get() * 4.).into(), + current: Instant::default(), + started: None.into(), + }) + } + }, + } + } + } + pub fn toggle_play (&mut self) -> Usually<()> { + let playing = self.clock.playing.read().unwrap().expect("1st sample has not been processed yet"); + let playing = match playing { + TransportState::Stopped => { + self.transport.start()?; + Some(TransportState::Starting) + }, + _ => { + self.transport.stop()?; + self.transport.locate(0)?; + Some(TransportState::Stopped) + }, + }; + *self.clock.playing.write().unwrap() = playing; + Ok(()) + } +} +impl TransportFocus { + pub fn next (&mut self) { + *self = match self { + Self::PlayPause => Self::Bpm, + Self::Bpm => Self::Quant, + Self::Quant => Self::Sync, + Self::Sync => Self::Clock, + Self::Clock => Self::PlayPause, + } + } + pub fn prev (&mut self) { + *self = match self { + Self::PlayPause => Self::Clock, + Self::Bpm => Self::PlayPause, + Self::Quant => Self::Bpm, + Self::Sync => Self::Quant, + Self::Clock => Self::Sync, + } + } +} diff --git a/crates/tek_app/src/transport_cmd.rs b/crates/tek_tui/src/transport_cmd.rs similarity index 93% rename from crates/tek_app/src/transport_cmd.rs rename to crates/tek_tui/src/transport_cmd.rs index b5470a60..1debf27f 100644 --- a/crates/tek_app/src/transport_cmd.rs +++ b/crates/tek_tui/src/transport_cmd.rs @@ -18,10 +18,10 @@ pub enum TransportCommand { SetQuant(f64), SetSync(f64), } -impl Command> for TransportCommand { - fn translate (self, state: &TransportToolbar) -> Self { +impl Command> for TransportCommand { + fn translate (self, state: &TransportView) -> Self { use TransportCommand::*; - use TransportToolbarFocus::*; + use TransportViewFocus::*; match self { Increment => match state.focus { Bpm => @@ -75,7 +75,7 @@ impl Command> for TransportCommand { }; return self } - fn execute (self, state: &mut TransportToolbar) -> Perhaps { + fn execute (self, state: &mut TransportView) -> Perhaps { use TransportCommand::*; match self.translate(&state) { FocusNext => diff --git a/crates/tek_app/src/transport_tui.rs b/crates/tek_tui/src/transport_tui.rs similarity index 100% rename from crates/tek_app/src/transport_tui.rs rename to crates/tek_tui/src/transport_tui.rs