diff --git a/shell.nix b/shell.nix index 2a4aa1c4..0c77854c 100644 --- a/shell.nix +++ b/shell.nix @@ -1,6 +1,7 @@ {pkgs?import{}}:pkgs.mkShell{ nativeBuildInputs = with pkgs; [ pkg-config + freetype ]; buildInputs = with pkgs; [ jack2 @@ -10,4 +11,10 @@ ]; VST3_SDK_DIR = "/home/user/Lab/Music/tek/vst3sdk/"; LIBCLANG_PATH = "${pkgs.libclang.lib.outPath}/lib"; + LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath (with pkgs; [ + pipewire.jack + # for ChowKick.lv2: + freetype + libgcc.lib + ]); } diff --git a/src/config.rs b/src/config.rs index 0082f807..6d245117 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,6 +1,7 @@ use crate::prelude::*; -const CONFIG_FILE_NAME: &'static str = "dawdle.toml"; +const CONFIG_FILE_NAME: &'static str = "tek.toml"; +const PROJECT_FILE_NAME: &'static str = "project.toml"; pub fn create_dirs (xdg: µxdg::XdgApp) -> Result<(), Box> { use std::{path::Path,fs::{File,create_dir_all}}; @@ -19,5 +20,10 @@ pub fn create_dirs (xdg: µxdg::XdgApp) -> Result<(), Box> { println!("Creating {data_dir:?}"); create_dir_all(&data_dir)?; } + let project_path = data_dir.join(PROJECT_FILE_NAME); + if !Path::new(&project_path).exists() { + println!("Creating {project_path:?}"); + File::create_new(&project_path)?; + } Ok(()) } diff --git a/src/device.rs b/src/device.rs index e16d5e96..ce15474d 100644 --- a/src/device.rs +++ b/src/device.rs @@ -21,7 +21,11 @@ pub use self::launcher::Launcher; use crossterm::event; use ::jack::{AudioIn, AudioOut, MidiIn, MidiOut, Port, PortSpec, Client}; -pub trait Device: Render + Handle + PortList + Send + Sync {} +pub trait Device: Render + Handle + PortList + Send + Sync { + fn boxed (self) -> Box where Self: Sized + 'static { + Box::new(self) + } +} pub fn run (device: impl Device + Send + Sync + 'static) -> Result<(), Box> { let device = Arc::new(Mutex::new(device)); @@ -138,7 +142,7 @@ pub trait PortList { fn midi_outs (&self) -> Usually> { Ok(vec![]) } - fn connect (&mut self, connect: bool, source: &str, target: &str) + fn connect (&mut self, _connect: bool, _source: &str, _target: &str) -> Usually<()> { Ok(()) diff --git a/src/device/launcher.rs b/src/device/launcher.rs index 060b52ff..8dbe909c 100644 --- a/src/device/launcher.rs +++ b/src/device/launcher.rs @@ -121,16 +121,35 @@ impl Launcher { ], chains: vec![ Chain::new("Chain#0000", vec![ - Box::new(Plugin::new("Plugin#000")?), + Plugin::lv2( + "Plugin#000", + "file:///home/user/.lv2/ChowKick.lv2", + &[1, 1, 0, 2] + )?.boxed(), ])?, + Chain::new("Chain#0000", vec![ - Box::new(Plugin::new("Plugin#001")?), + Plugin::lv2( + "Plugin#001", + "file:///home/user/.lv2/Helm.lv2", + &[1, 0, 0, 2] + )?.boxed(), ])?, + Chain::new("Chain#0000", vec![ - Box::new(Plugin::new("Plugin#002")?), + Plugin::lv2( + "Plugin#002", + "file:///home/user/.lv2/Helm.lv2", + &[1, 0, 0, 2] + )?.boxed(), ])?, + Chain::new("Chain#0000", vec![ - Box::new(Plugin::new("Plugin#003")?), + Plugin::lv2( + "Plugin#003", + "file:///home/user/.lv2/Odin2.lv2", + &[1, 0, 0, 2] + )?.boxed(), ])?, ], timebase, @@ -506,18 +525,27 @@ fn play_toggle (s: &mut Launcher) -> Usually { }; Ok(true) } -fn play_start (s: &mut Launcher) -> Usually { +fn play_start (_: &mut Launcher) -> Usually { unimplemented!() } fn record_toggle (s: &mut Launcher) -> Usually { s.recording = !s.recording; + for sequencer in s.tracks.iter() { + sequencer.state().recording = s.recording; + } Ok(true) } fn overdub_toggle (s: &mut Launcher) -> Usually { s.overdub = !s.overdub; + for sequencer in s.tracks.iter() { + sequencer.state().overdub = s.overdub; + } Ok(true) } fn monitor_toggle (s: &mut Launcher) -> Usually { s.monitoring = !s.monitoring; + for sequencer in s.tracks.iter() { + sequencer.state().monitoring = s.monitoring; + } Ok(true) } diff --git a/src/device/plugin.rs b/src/device/plugin.rs index 9a8ab11e..a9537f14 100644 --- a/src/device/plugin.rs +++ b/src/device/plugin.rs @@ -5,14 +5,16 @@ mod vst2; mod vst3; pub struct Plugin { - name: String, - path: String, - plugin: Option, - offset: usize, - selected: usize, - mapping: bool, - midi_in: ::jack::Port<::jack::MidiIn>, - audio_out: [::jack::Port<::jack::AudioOut>;2], + name: String, + path: Option, + plugin: Option, + offset: usize, + selected: usize, + mapping: bool, + midi_ins: Vec>, + midi_outs: Vec>, + audio_ins: Vec>, + audio_outs: Vec>, } enum PluginKind { @@ -31,44 +33,100 @@ enum PluginKind { const HELM: &'static str = "file:///nix/store/ij3sz7nqg5l7v2dygdvzy3w6cj62bd6r-helm-0.9.0/lib/lv2/helm.lv2"; impl Plugin { - pub fn new (name: &str) -> Result, Box> { + /// Load a LV2 plugin. + pub fn lv2 (name: &str, path: &str, ports: &[usize;4]) -> Usually> { + let plugin = Self::new(name, ports)?; + let mut state = plugin.state(); + state.plugin = Some(self::lv2::plug(path)?); + state.path = Some(String::from(path)); + std::mem::drop(state); + Ok(plugin) + } + pub fn new (name: &str, ports: &[usize;4]) -> Usually> { let (client, _status) = Client::new(name, ClientOptions::NO_START_SERVER)?; + let [midi_ins, midi_outs, audio_ins, audio_outs] = ports; DynamicDevice::new(render, handle, Self::process, Self { name: name.into(), - path: HELM.into(), - plugin: Some(self::lv2::plug(HELM)?), + path: None, + plugin: None, offset: 0, selected: 0, mapping: false, - midi_in: client.register_port("in", MidiIn::default())?, - audio_out: [ - client.register_port("outL", AudioOut::default())?, - client.register_port("outR", AudioOut::default())?, - ], + midi_ins: { + let mut ports = vec![]; + for i in 0..*midi_ins { + ports.push(client.register_port(&format!("midi-in-{i}"), MidiIn::default())?) + } + ports + }, + midi_outs: { + let mut ports = vec![]; + for i in 0..*midi_outs { + ports.push(client.register_port(&format!("midi-out-{i}"), MidiOut::default())?) + } + ports + }, + audio_ins: { + let mut ports = vec![]; + for i in 0..*audio_ins { + ports.push(client.register_port(&format!("audio-in-{i}"), AudioIn::default())?) + } + ports + }, + audio_outs: { + let mut ports = vec![]; + for i in 0..*audio_outs { + ports.push(client.register_port(&format!("audio-out-{i}"), AudioOut::default())?) + } + ports + }, }).activate(client) } - pub fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { match self.plugin.as_mut() { Some(PluginKind::LV2 { features, ref mut instance, .. }) => { - let mut input = ::livi::event::LV2AtomSequence::new( - &features, scope.n_frames() as usize - ); let urid = features.midi_urid(); - for event in self.midi_in.iter(scope) { - match event.bytes.len() { - 3 => input.push_midi_event::<3>( - event.time as i64, - urid, - &event.bytes[0..3] - ).unwrap(), - _ => {} + let mut inputs = vec![]; + for port in self.midi_ins.iter() { + let mut atom = ::livi::event::LV2AtomSequence::new( + &features, + scope.n_frames() as usize + ); + for event in port.iter(scope) { + match event.bytes.len() { + 3 => atom.push_midi_event::<3>( + event.time as i64, + urid, + &event.bytes[0..3] + ).unwrap(), + _ => {} + } } + inputs.push(atom); + } + let mut outputs = vec![]; + for port in self.midi_outs.iter() { + outputs.push(::livi::event::LV2AtomSequence::new( + &features, + scope.n_frames() as usize + )); } let ports = ::livi::EmptyPortConnections::new() - .with_atom_sequence_inputs(std::iter::once(&input)) - .with_audio_outputs(self.audio_out.iter_mut().map(|o|o.as_mut_slice(scope))); - unsafe { instance.run(scope.n_frames() as usize, ports).unwrap() }; + .with_atom_sequence_inputs( + inputs.iter() + ) + .with_atom_sequence_outputs( + outputs.iter_mut() + ) + .with_audio_inputs( + self.audio_ins.iter().map(|o|o.as_slice(scope)) + ) + .with_audio_outputs( + self.audio_outs.iter_mut().map(|o|o.as_mut_slice(scope)) + ); + unsafe { + instance.run(scope.n_frames() as usize, ports).unwrap() + }; }, _ => {} } @@ -77,16 +135,33 @@ impl Plugin { } impl PortList for Plugin { - fn midi_ins (&self) -> Usually> { - Ok(vec![ - self.midi_in.name()? - ]) + fn audio_ins (&self) -> Usually> { + let mut ports = vec![]; + for port in self.audio_ins.iter() { + ports.push(port.name()?); + } + Ok(ports) } fn audio_outs (&self) -> Usually> { - Ok(vec![ - self.audio_out[0].name()?, - self.audio_out[1].name()? - ]) + let mut ports = vec![]; + for port in self.audio_outs.iter() { + ports.push(port.name()?); + } + Ok(ports) + } + fn midi_ins (&self) -> Usually> { + let mut ports = vec![]; + for port in self.midi_ins.iter() { + ports.push(port.name()?); + } + Ok(ports) + } + fn midi_outs (&self) -> Usually> { + let mut ports = vec![]; + for port in self.midi_outs.iter() { + ports.push(port.name()?); + } + Ok(ports) } } @@ -100,10 +175,13 @@ pub fn render (state: &Plugin, buf: &mut Buffer, area: Rect) match &state.plugin { Some(PluginKind::LV2 { portList, instance, .. }) => { for i in 0..height - 4 { - let port = &portList[i as usize]; - let label = &format!("C·· M·· {:25} = {:03}", port.name, port.default_value); - width = width.max(label.len() as u16); - label.blit(buf, x + 2, y + 3 + i as u16, None); + if let Some(port) = portList.get(i as usize) { + let label = &format!("C·· M·· {:25} = {:03}", port.name, port.default_value); + width = width.max(label.len() as u16); + label.blit(buf, x + 2, y + 3 + i as u16, None); + } else { + break + } } draw_box(buf, Rect { x, y, width, height }); }, diff --git a/src/device/sequencer.rs b/src/device/sequencer.rs index 9d1e226e..4eb51aa7 100644 --- a/src/device/sequencer.rs +++ b/src/device/sequencer.rs @@ -44,13 +44,13 @@ pub struct Sequencer { notes_on: Vec, /// Play sequence to output. - playing: TransportState, + pub playing: TransportState, /// Play input through output. - monitoring: bool, + pub monitoring: bool, /// Write input to sequence. - recording: bool, + pub recording: bool, /// Don't delete when recording. - overdub: bool, + pub overdub: bool, /// Display mode mode: SequencerView, @@ -171,7 +171,7 @@ impl Sequencer { frame.push(event.bytes.into()) } } - if self.recording { + if self.recording && self.playing == TransportState::Rolling { let contains = sequence.contains_key(&tick); if contains { sequence.get_mut(&tick).unwrap().push(message.clone()); @@ -191,7 +191,7 @@ impl Sequencer { frame.push(event.bytes.into()) } } - if self.recording { + if self.recording && self.playing == TransportState::Rolling { let contains = sequence.contains_key(&tick); if contains { sequence.get_mut(&tick).unwrap().push(message.clone()); diff --git a/src/layout/container.rs b/src/layout/container.rs index 2e9e727f..de976d56 100644 --- a/src/layout/container.rs +++ b/src/layout/container.rs @@ -15,7 +15,7 @@ impl Column { let mut w = 0u16; let mut h = 0u16; let mut rects = vec![]; - for (i, device) in items.iter().enumerate() { + for (_i, device) in items.iter().enumerate() { let y = area.y + h; let rect = Rect { x: area.x, y, width: area.width, height: area.height - h }; let result = device.render(buf, rect)?; @@ -43,7 +43,7 @@ impl Row { let mut w = 0u16; let mut h = 0u16; let mut rects = vec![]; - for (i, device) in items.iter().enumerate() { + for (_i, device) in items.iter().enumerate() { let x = area.x + w; let rect = Rect { x, y: area.y, width: area.width - w, height: area.height }; let result = device.render(buf, rect)?; diff --git a/src/layout/focus.rs b/src/layout/focus.rs index 2f1d85d4..a1e9f92c 100644 --- a/src/layout/focus.rs +++ b/src/layout/focus.rs @@ -34,7 +34,7 @@ impl Handle for FocusColumn { impl Render for FocusColumn { fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { - let (rect, rects) = Column::draw(buf, area, self.1.0.as_ref(), 0)?; + let (rect, _rects) = Column::draw(buf, area, self.1.0.as_ref(), 0)?; //if i == self.focus { //if self.focused { //draw_box_styled(buf, result, Some(Style::default().white().not_dim())) @@ -99,7 +99,7 @@ impl Focus for FocusColumn { _ => false }, FocusEvent::Outward => match self.0 { - Some(i) => { + Some(_i) => { self.0 = None; true }, @@ -119,7 +119,7 @@ impl Handle for FocusRow { impl Render for FocusRow { fn render (&self, buf: &mut Buffer, area: Rect) -> Usually { - let (rect, rects) = Row::draw(buf, area, &self.1.0, 0)?; + let (rect, _rects) = Row::draw(buf, area, &self.1.0, 0)?; Ok(rect) } } @@ -177,7 +177,7 @@ impl Focus for FocusRow { _ => false }, FocusEvent::Outward => match self.0 { - Some(i) => { + Some(_i) => { self.0 = None; true }, diff --git a/src/main.rs b/src/main.rs index 8b94f264..5739c28a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,7 +17,7 @@ use crate::prelude::*; fn main () -> Result<(), Box> { let _cli = cli::Cli::parse(); - let xdg = microxdg::XdgApp::new("dawdle")?; + let xdg = microxdg::XdgApp::new("tek")?; crate::config::create_dirs(&xdg)?; run(Launcher::new_with_controller( "Launcher#0",