plugin ins/outs

This commit is contained in:
🪞👃🪞 2024-06-27 10:18:10 +03:00
parent da1d3220f9
commit 9351887ae6
9 changed files with 187 additions and 64 deletions

View file

@ -1,6 +1,7 @@
{pkgs?import<nixpkgs>{}}:pkgs.mkShell{ {pkgs?import<nixpkgs>{}}:pkgs.mkShell{
nativeBuildInputs = with pkgs; [ nativeBuildInputs = with pkgs; [
pkg-config pkg-config
freetype
]; ];
buildInputs = with pkgs; [ buildInputs = with pkgs; [
jack2 jack2
@ -10,4 +11,10 @@
]; ];
VST3_SDK_DIR = "/home/user/Lab/Music/tek/vst3sdk/"; VST3_SDK_DIR = "/home/user/Lab/Music/tek/vst3sdk/";
LIBCLANG_PATH = "${pkgs.libclang.lib.outPath}/lib"; LIBCLANG_PATH = "${pkgs.libclang.lib.outPath}/lib";
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath (with pkgs; [
pipewire.jack
# for ChowKick.lv2:
freetype
libgcc.lib
]);
} }

View file

@ -1,6 +1,7 @@
use crate::prelude::*; 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: &microxdg::XdgApp) -> Result<(), Box<dyn Error>> { pub fn create_dirs (xdg: &microxdg::XdgApp) -> Result<(), Box<dyn Error>> {
use std::{path::Path,fs::{File,create_dir_all}}; use std::{path::Path,fs::{File,create_dir_all}};
@ -19,5 +20,10 @@ pub fn create_dirs (xdg: &microxdg::XdgApp) -> Result<(), Box<dyn Error>> {
println!("Creating {data_dir:?}"); println!("Creating {data_dir:?}");
create_dir_all(&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(()) Ok(())
} }

View file

@ -21,7 +21,11 @@ pub use self::launcher::Launcher;
use crossterm::event; use crossterm::event;
use ::jack::{AudioIn, AudioOut, MidiIn, MidiOut, Port, PortSpec, Client}; 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<dyn Device> where Self: Sized + 'static {
Box::new(self)
}
}
pub fn run (device: impl Device + Send + Sync + 'static) -> Result<(), Box<dyn Error>> { pub fn run (device: impl Device + Send + Sync + 'static) -> Result<(), Box<dyn Error>> {
let device = Arc::new(Mutex::new(device)); let device = Arc::new(Mutex::new(device));
@ -138,7 +142,7 @@ pub trait PortList {
fn midi_outs (&self) -> Usually<Vec<String>> { fn midi_outs (&self) -> Usually<Vec<String>> {
Ok(vec![]) Ok(vec![])
} }
fn connect (&mut self, connect: bool, source: &str, target: &str) fn connect (&mut self, _connect: bool, _source: &str, _target: &str)
-> Usually<()> -> Usually<()>
{ {
Ok(()) Ok(())

View file

@ -121,16 +121,35 @@ impl Launcher {
], ],
chains: vec![ chains: vec![
Chain::new("Chain#0000", 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![ 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![ 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![ 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, timebase,
@ -506,18 +525,27 @@ fn play_toggle (s: &mut Launcher) -> Usually<bool> {
}; };
Ok(true) Ok(true)
} }
fn play_start (s: &mut Launcher) -> Usually<bool> { fn play_start (_: &mut Launcher) -> Usually<bool> {
unimplemented!() unimplemented!()
} }
fn record_toggle (s: &mut Launcher) -> Usually<bool> { fn record_toggle (s: &mut Launcher) -> Usually<bool> {
s.recording = !s.recording; s.recording = !s.recording;
for sequencer in s.tracks.iter() {
sequencer.state().recording = s.recording;
}
Ok(true) Ok(true)
} }
fn overdub_toggle (s: &mut Launcher) -> Usually<bool> { fn overdub_toggle (s: &mut Launcher) -> Usually<bool> {
s.overdub = !s.overdub; s.overdub = !s.overdub;
for sequencer in s.tracks.iter() {
sequencer.state().overdub = s.overdub;
}
Ok(true) Ok(true)
} }
fn monitor_toggle (s: &mut Launcher) -> Usually<bool> { fn monitor_toggle (s: &mut Launcher) -> Usually<bool> {
s.monitoring = !s.monitoring; s.monitoring = !s.monitoring;
for sequencer in s.tracks.iter() {
sequencer.state().monitoring = s.monitoring;
}
Ok(true) Ok(true)
} }

View file

@ -5,14 +5,16 @@ mod vst2;
mod vst3; mod vst3;
pub struct Plugin { pub struct Plugin {
name: String, name: String,
path: String, path: Option<String>,
plugin: Option<PluginKind>, plugin: Option<PluginKind>,
offset: usize, offset: usize,
selected: usize, selected: usize,
mapping: bool, mapping: bool,
midi_in: ::jack::Port<::jack::MidiIn>, midi_ins: Vec<Port<MidiIn>>,
audio_out: [::jack::Port<::jack::AudioOut>;2], midi_outs: Vec<Port<MidiOut>>,
audio_ins: Vec<Port<AudioIn>>,
audio_outs: Vec<Port<AudioOut>>,
} }
enum PluginKind { enum PluginKind {
@ -31,44 +33,100 @@ enum PluginKind {
const HELM: &'static str = "file:///nix/store/ij3sz7nqg5l7v2dygdvzy3w6cj62bd6r-helm-0.9.0/lib/lv2/helm.lv2"; const HELM: &'static str = "file:///nix/store/ij3sz7nqg5l7v2dygdvzy3w6cj62bd6r-helm-0.9.0/lib/lv2/helm.lv2";
impl Plugin { impl Plugin {
pub fn new (name: &str) -> Result<DynamicDevice<Self>, Box<dyn Error>> { /// Load a LV2 plugin.
pub fn lv2 (name: &str, path: &str, ports: &[usize;4]) -> Usually<DynamicDevice<Self>> {
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<DynamicDevice<Self>> {
let (client, _status) = Client::new(name, ClientOptions::NO_START_SERVER)?; 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 { DynamicDevice::new(render, handle, Self::process, Self {
name: name.into(), name: name.into(),
path: HELM.into(), path: None,
plugin: Some(self::lv2::plug(HELM)?), plugin: None,
offset: 0, offset: 0,
selected: 0, selected: 0,
mapping: false, mapping: false,
midi_in: client.register_port("in", MidiIn::default())?, midi_ins: {
audio_out: [ let mut ports = vec![];
client.register_port("outL", AudioOut::default())?, for i in 0..*midi_ins {
client.register_port("outR", AudioOut::default())?, 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) }).activate(client)
} }
pub fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { pub fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
match self.plugin.as_mut() { match self.plugin.as_mut() {
Some(PluginKind::LV2 { features, ref mut instance, .. }) => { 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(); let urid = features.midi_urid();
for event in self.midi_in.iter(scope) { let mut inputs = vec![];
match event.bytes.len() { for port in self.midi_ins.iter() {
3 => input.push_midi_event::<3>( let mut atom = ::livi::event::LV2AtomSequence::new(
event.time as i64, &features,
urid, scope.n_frames() as usize
&event.bytes[0..3] );
).unwrap(), 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() let ports = ::livi::EmptyPortConnections::new()
.with_atom_sequence_inputs(std::iter::once(&input)) .with_atom_sequence_inputs(
.with_audio_outputs(self.audio_out.iter_mut().map(|o|o.as_mut_slice(scope))); inputs.iter()
unsafe { instance.run(scope.n_frames() as usize, ports).unwrap() }; )
.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 { impl PortList for Plugin {
fn midi_ins (&self) -> Usually<Vec<String>> { fn audio_ins (&self) -> Usually<Vec<String>> {
Ok(vec![ let mut ports = vec![];
self.midi_in.name()? for port in self.audio_ins.iter() {
]) ports.push(port.name()?);
}
Ok(ports)
} }
fn audio_outs (&self) -> Usually<Vec<String>> { fn audio_outs (&self) -> Usually<Vec<String>> {
Ok(vec![ let mut ports = vec![];
self.audio_out[0].name()?, for port in self.audio_outs.iter() {
self.audio_out[1].name()? ports.push(port.name()?);
]) }
Ok(ports)
}
fn midi_ins (&self) -> Usually<Vec<String>> {
let mut ports = vec![];
for port in self.midi_ins.iter() {
ports.push(port.name()?);
}
Ok(ports)
}
fn midi_outs (&self) -> Usually<Vec<String>> {
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 { match &state.plugin {
Some(PluginKind::LV2 { portList, instance, .. }) => { Some(PluginKind::LV2 { portList, instance, .. }) => {
for i in 0..height - 4 { for i in 0..height - 4 {
let port = &portList[i as usize]; if let Some(port) = portList.get(i as usize) {
let label = &format!("C·· M·· {:25} = {:03}", port.name, port.default_value); let label = &format!("C·· M·· {:25} = {:03}", port.name, port.default_value);
width = width.max(label.len() as u16); width = width.max(label.len() as u16);
label.blit(buf, x + 2, y + 3 + i as u16, None); label.blit(buf, x + 2, y + 3 + i as u16, None);
} else {
break
}
} }
draw_box(buf, Rect { x, y, width, height }); draw_box(buf, Rect { x, y, width, height });
}, },

View file

@ -44,13 +44,13 @@ pub struct Sequencer {
notes_on: Vec<bool>, notes_on: Vec<bool>,
/// Play sequence to output. /// Play sequence to output.
playing: TransportState, pub playing: TransportState,
/// Play input through output. /// Play input through output.
monitoring: bool, pub monitoring: bool,
/// Write input to sequence. /// Write input to sequence.
recording: bool, pub recording: bool,
/// Don't delete when recording. /// Don't delete when recording.
overdub: bool, pub overdub: bool,
/// Display mode /// Display mode
mode: SequencerView, mode: SequencerView,
@ -171,7 +171,7 @@ impl Sequencer {
frame.push(event.bytes.into()) frame.push(event.bytes.into())
} }
} }
if self.recording { if self.recording && self.playing == TransportState::Rolling {
let contains = sequence.contains_key(&tick); let contains = sequence.contains_key(&tick);
if contains { if contains {
sequence.get_mut(&tick).unwrap().push(message.clone()); sequence.get_mut(&tick).unwrap().push(message.clone());
@ -191,7 +191,7 @@ impl Sequencer {
frame.push(event.bytes.into()) frame.push(event.bytes.into())
} }
} }
if self.recording { if self.recording && self.playing == TransportState::Rolling {
let contains = sequence.contains_key(&tick); let contains = sequence.contains_key(&tick);
if contains { if contains {
sequence.get_mut(&tick).unwrap().push(message.clone()); sequence.get_mut(&tick).unwrap().push(message.clone());

View file

@ -15,7 +15,7 @@ impl Column {
let mut w = 0u16; let mut w = 0u16;
let mut h = 0u16; let mut h = 0u16;
let mut rects = vec![]; let mut rects = vec![];
for (i, device) in items.iter().enumerate() { for (_i, device) in items.iter().enumerate() {
let y = area.y + h; let y = area.y + h;
let rect = Rect { x: area.x, y, width: area.width, height: area.height - h }; let rect = Rect { x: area.x, y, width: area.width, height: area.height - h };
let result = device.render(buf, rect)?; let result = device.render(buf, rect)?;
@ -43,7 +43,7 @@ impl Row {
let mut w = 0u16; let mut w = 0u16;
let mut h = 0u16; let mut h = 0u16;
let mut rects = vec![]; let mut rects = vec![];
for (i, device) in items.iter().enumerate() { for (_i, device) in items.iter().enumerate() {
let x = area.x + w; let x = area.x + w;
let rect = Rect { x, y: area.y, width: area.width - w, height: area.height }; let rect = Rect { x, y: area.y, width: area.width - w, height: area.height };
let result = device.render(buf, rect)?; let result = device.render(buf, rect)?;

View file

@ -34,7 +34,7 @@ impl Handle for FocusColumn {
impl Render for FocusColumn { impl Render for FocusColumn {
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> { fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
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 i == self.focus {
//if self.focused { //if self.focused {
//draw_box_styled(buf, result, Some(Style::default().white().not_dim())) //draw_box_styled(buf, result, Some(Style::default().white().not_dim()))
@ -99,7 +99,7 @@ impl Focus for FocusColumn {
_ => false _ => false
}, },
FocusEvent::Outward => match self.0 { FocusEvent::Outward => match self.0 {
Some(i) => { Some(_i) => {
self.0 = None; self.0 = None;
true true
}, },
@ -119,7 +119,7 @@ impl Handle for FocusRow {
impl Render for FocusRow { impl Render for FocusRow {
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> { fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
let (rect, rects) = Row::draw(buf, area, &self.1.0, 0)?; let (rect, _rects) = Row::draw(buf, area, &self.1.0, 0)?;
Ok(rect) Ok(rect)
} }
} }
@ -177,7 +177,7 @@ impl Focus for FocusRow {
_ => false _ => false
}, },
FocusEvent::Outward => match self.0 { FocusEvent::Outward => match self.0 {
Some(i) => { Some(_i) => {
self.0 = None; self.0 = None;
true true
}, },

View file

@ -17,7 +17,7 @@ use crate::prelude::*;
fn main () -> Result<(), Box<dyn Error>> { fn main () -> Result<(), Box<dyn Error>> {
let _cli = cli::Cli::parse(); let _cli = cli::Cli::parse();
let xdg = microxdg::XdgApp::new("dawdle")?; let xdg = microxdg::XdgApp::new("tek")?;
crate::config::create_dirs(&xdg)?; crate::config::create_dirs(&xdg)?;
run(Launcher::new_with_controller( run(Launcher::new_with_controller(
"Launcher#0", "Launcher#0",