compact more (minus 6 files)

This commit is contained in:
🪞👃🪞 2024-07-13 23:05:57 +03:00
parent b4fbd99248
commit 81ea049773
17 changed files with 432 additions and 433 deletions

View file

@ -2,8 +2,6 @@
use crate::{core::*, handle, App, AppFocus}; use crate::{core::*, handle, App, AppFocus};
submod!{ chain focus mixer plugin }
handle!{ handle!{
App |self, e| { App |self, e| {
if let Some(ref mut modal) = self.modal { if let Some(ref mut modal) = self.modal {
@ -17,10 +15,10 @@ handle!{
Ok(if self.entered { Ok(if self.entered {
handle_focused(self, e)? handle_focused(self, e)?
|| handle_keymap(self, e, KEYMAP_GLOBAL)? || handle_keymap(self, e, KEYMAP_GLOBAL)?
|| handle_keymap(self, e, crate::control::focus::KEYMAP_FOCUS)? || handle_keymap(self, e, crate::control::KEYMAP_FOCUS)?
} else { } else {
handle_keymap(self, e, KEYMAP_GLOBAL)? handle_keymap(self, e, KEYMAP_GLOBAL)?
|| handle_keymap(self, e, crate::control::focus::KEYMAP_FOCUS)? || handle_keymap(self, e, crate::control::KEYMAP_FOCUS)?
|| handle_focused(self, e)? || handle_focused(self, e)?
}) })
} }
@ -36,9 +34,9 @@ fn handle_focused (state: &mut App, e: &AppEvent) -> Usually<bool> {
handle_keymap(state, e, crate::devices::sequencer::KEYMAP_SEQUENCER), handle_keymap(state, e, crate::devices::sequencer::KEYMAP_SEQUENCER),
AppFocus::Chain => Ok(if state.entered { AppFocus::Chain => Ok(if state.entered {
handle_device(state, e)? || handle_device(state, e)? ||
handle_keymap(state, e, crate::control::chain::KEYMAP_CHAIN)? handle_keymap(state, e, crate::control::KEYMAP_CHAIN)?
} else { } else {
handle_keymap(state, e, crate::control::chain::KEYMAP_CHAIN)? || handle_device(state, e)? handle_keymap(state, e, crate::control::KEYMAP_CHAIN)? || handle_device(state, e)?
}) })
} }
} }
@ -124,3 +122,77 @@ pub const KEYMAP_GLOBAL: &'static [KeyBinding<App>] = keymap!(App {
Ok(true) Ok(true)
}], }],
}); });
/// Key bindings for chain section.
pub const KEYMAP_CHAIN: &'static [KeyBinding<App>] = keymap!(App {
[Up, NONE, "chain_cursor_up", "move cursor up", |_: &mut App| {
Ok(true)
}],
[Down, NONE, "chain_cursor_down", "move cursor down", |_: &mut App| {
Ok(true)
}],
[Left, NONE, "chain_cursor_left", "move cursor left", |app: &mut App| {
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 App| {
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 App| {
app.chain_mode = !app.chain_mode;
Ok(true)
}],
});
/// Generic key bindings for views that support focus.
pub const KEYMAP_FOCUS: &'static [KeyBinding<App>] = keymap!(App {
[Char(';'), NONE, "command", "open command palette", |app: &mut App| {
app.modal = Some(Box::new(crate::view::HelpModal::new()));
Ok(true)
}],
[Tab, NONE, "focus_next", "focus next area", focus_next],
[Tab, SHIFT, "focus_prev", "focus previous area", focus_prev],
[Esc, NONE, "focus_exit", "unfocus", |app: &mut App|{
app.entered = false;
app.transport.entered = app.entered;
app.arranger.entered = app.entered;
app.sequencer.entered = app.entered;
Ok(true)
}],
[Enter, NONE, "focus_enter", "activate item at cursor", |app: &mut App|{
app.entered = true;
app.transport.entered = app.entered;
app.arranger.entered = app.entered;
app.sequencer.entered = app.entered;
Ok(true)
}],
});
pub fn focus_next (app: &mut App) -> Usually<bool> {
app.section.next();
app.transport.focused = app.section == AppFocus::Transport;
app.transport.entered = app.entered;
app.arranger.focused = app.section == AppFocus::Arranger;
app.arranger.entered = app.entered;
app.sequencer.focused = app.section == AppFocus::Sequencer;
app.sequencer.entered = app.entered;
Ok(true)
}
pub fn focus_prev (app: &mut App) -> Usually<bool> {
app.section.prev();
app.transport.focused = app.section == AppFocus::Transport;
app.transport.entered = app.entered;
app.arranger.focused = app.section == AppFocus::Arranger;
app.arranger.entered = app.entered;
app.sequencer.focused = app.section == AppFocus::Sequencer;
app.sequencer.entered = app.entered;
Ok(true)
}

View file

@ -1,30 +0,0 @@
use crate::{core::*, model::App};
/// Key bindings for chain section.
pub const KEYMAP_CHAIN: &'static [KeyBinding<App>] = keymap!(App {
[Up, NONE, "chain_cursor_up", "move cursor up", |_: &mut App| {
Ok(true)
}],
[Down, NONE, "chain_cursor_down", "move cursor down", |_: &mut App| {
Ok(true)
}],
[Left, NONE, "chain_cursor_left", "move cursor left", |app: &mut App| {
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 App| {
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 App| {
app.chain_mode = !app.chain_mode;
Ok(true)
}],
});

View file

@ -1,47 +0,0 @@
use crate::{core::*, model::{App, AppFocus}};
/// Generic key bindings for views that support focus.
pub const KEYMAP_FOCUS: &'static [KeyBinding<App>] = keymap!(App {
[Char(';'), NONE, "command", "open command palette", |app: &mut App| {
app.modal = Some(Box::new(crate::view::HelpModal::new()));
Ok(true)
}],
[Tab, NONE, "focus_next", "focus next area", focus_next],
[Tab, SHIFT, "focus_prev", "focus previous area", focus_prev],
[Esc, NONE, "focus_exit", "unfocus", |app: &mut App|{
app.entered = false;
app.transport.entered = app.entered;
app.arranger.entered = app.entered;
app.sequencer.entered = app.entered;
Ok(true)
}],
[Enter, NONE, "focus_enter", "activate item at cursor", |app: &mut App|{
app.entered = true;
app.transport.entered = app.entered;
app.arranger.entered = app.entered;
app.sequencer.entered = app.entered;
Ok(true)
}],
});
pub fn focus_next (app: &mut App) -> Usually<bool> {
app.section.next();
app.transport.focused = app.section == AppFocus::Transport;
app.transport.entered = app.entered;
app.arranger.focused = app.section == AppFocus::Arranger;
app.arranger.entered = app.entered;
app.sequencer.focused = app.section == AppFocus::Sequencer;
app.sequencer.entered = app.entered;
Ok(true)
}
pub fn focus_prev (app: &mut App) -> Usually<bool> {
app.section.prev();
app.transport.focused = app.section == AppFocus::Transport;
app.transport.entered = app.entered;
app.arranger.focused = app.section == AppFocus::Arranger;
app.arranger.entered = app.entered;
app.sequencer.focused = app.section == AppFocus::Sequencer;
app.sequencer.entered = app.entered;
Ok(true)
}

View file

@ -1,59 +0,0 @@
use crate::{core::*, model::*};
// 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?
//pub const ACTIONS: [(&'static str, &'static str);2] = [
//("+/-", "Adjust"),
//("Ins/Del", "Add/remove track"),
//];
pub fn handle_mixer (state: &mut Mixer, event: &AppEvent) -> Usually<bool> {
if let AppEvent::Input(crossterm::event::Event::Key(event)) = event {
match event.code {
//KeyCode::Char('c') => {
//if event.modifiers == KeyModifiers::CONTROL {
//state.exit();
//}
//},
KeyCode::Down => {
state.selected_track = (state.selected_track + 1) % state.tracks.len();
println!("{}", state.selected_track);
return Ok(true)
},
KeyCode::Up => {
if state.selected_track == 0 {
state.selected_track = state.tracks.len() - 1;
} else {
state.selected_track = state.selected_track - 1;
}
println!("{}", state.selected_track);
return Ok(true)
},
KeyCode::Left => {
if state.selected_column == 0 {
state.selected_column = 6
} else {
state.selected_column = state.selected_column - 1;
}
return Ok(true)
},
KeyCode::Right => {
if state.selected_column == 6 {
state.selected_column = 0
} else {
state.selected_column = state.selected_column + 1;
}
return Ok(true)
},
_ => {
println!("\n{event:?}");
}
}
}
Ok(false)
}

View file

@ -1,55 +0,0 @@
use crate::{core::*, model::*};
pub fn handle_plugin (state: &mut Plugin, event: &AppEvent) -> Usually<bool> {
handle_keymap(state, event, KEYMAP_PLUGIN)
}
/// Key bindings for plugin device.
pub const KEYMAP_PLUGIN: &'static [KeyBinding<Plugin>] = keymap!(Plugin {
[Up, NONE, "cursor_up", "move cursor up", |s: &mut Plugin|{
s.selected = s.selected.saturating_sub(1);
Ok(true)
}],
[Down, NONE, "cursor_down", "move cursor down", |s: &mut Plugin|{
s.selected = (s.selected + 1).min(match &s.plugin {
Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => port_list.len() - 1,
_ => unimplemented!()
});
Ok(true)
}],
[PageUp, NONE, "cursor_up", "move cursor up", |s: &mut Plugin|{
s.selected = s.selected.saturating_sub(8);
Ok(true)
}],
[PageDown, NONE, "cursor_down", "move cursor down", |s: &mut Plugin|{
s.selected = (s.selected + 10).min(match &s.plugin {
Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => port_list.len() - 1,
_ => unimplemented!()
});
Ok(true)
}],
[Char(','), NONE, "decrement", "decrement value", |s: &mut Plugin|{
match s.plugin.as_mut() {
Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => {
let index = port_list[s.selected].index;
if let Some(value) = instance.control_input(index) {
instance.set_control_input(index, value - 0.01);
}
},
_ => {}
}
Ok(true)
}],
[Char('.'), NONE, "increment", "increment value", |s: &mut Plugin|{
match s.plugin.as_mut() {
Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => {
let index = port_list[s.selected].index;
if let Some(value) = instance.control_input(index) {
instance.set_control_input(index, value + 0.01);
}
},
_ => {}
}
Ok(true)
}],
});

View file

@ -1 +1,11 @@
crate::core::pubmod! { arranger sampler sequencer transport } //! Music-making apparatuses.
//!
//! The following devices are provided:
//!
//! - Transport controller
//! - Arranger (clip launcher)
//! - Sequencer (phrase editor)
//! - Plugin (currently LV2 only)
//! - Sampler (currently 16bit samples only)
crate::core::pubmod!{arranger looper mixer plugin sampler sequencer transport}

View file

@ -8,3 +8,4 @@ render!(Looper);
handle!(Looper); handle!(Looper);
process!(Looper); process!(Looper);
ports!(Looper); ports!(Looper);

View file

@ -1,4 +1,14 @@
use crate::core::*; use crate::{core::*, model::*};
// 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?
//pub const ACTIONS: [(&'static str, &'static str);2] = [
//("+/-", "Adjust"),
//("Ins/Del", "Add/remove track"),
//];
/// TODO: audio mixer. /// TODO: audio mixer.
pub struct Mixer { pub struct Mixer {
@ -8,7 +18,7 @@ pub struct Mixer {
pub selected_column: usize, pub selected_column: usize,
} }
//render!(Mixer = crate::view::mixer::render); //render!(Mixer = crate::view::mixer::render);
handle!(Mixer = crate::control::handle_mixer); handle!(Mixer = handle_mixer);
process!(Mixer = process); process!(Mixer = process);
impl Mixer { impl Mixer {
@ -130,3 +140,50 @@ impl MixerTrack {
//} //}
//} //}
pub fn handle_mixer (state: &mut Mixer, event: &AppEvent) -> Usually<bool> {
if let AppEvent::Input(crossterm::event::Event::Key(event)) = event {
match event.code {
//KeyCode::Char('c') => {
//if event.modifiers == KeyModifiers::CONTROL {
//state.exit();
//}
//},
KeyCode::Down => {
state.selected_track = (state.selected_track + 1) % state.tracks.len();
println!("{}", state.selected_track);
return Ok(true)
},
KeyCode::Up => {
if state.selected_track == 0 {
state.selected_track = state.tracks.len() - 1;
} else {
state.selected_track = state.selected_track - 1;
}
println!("{}", state.selected_track);
return Ok(true)
},
KeyCode::Left => {
if state.selected_column == 0 {
state.selected_column = 6
} else {
state.selected_column = state.selected_column - 1;
}
return Ok(true)
},
KeyCode::Right => {
if state.selected_column == 6 {
state.selected_column = 0
} else {
state.selected_column = state.selected_column + 1;
}
return Ok(true)
},
_ => {
println!("\n{event:?}");
}
}
}
Ok(false)
}

278
src/devices/plugin.rs Normal file
View file

@ -0,0 +1,278 @@
use crate::core::*;
use ::livi::{
World,
Instance,
Plugin as LiviPlugin,
Features,
FeaturesBuilder,
Port,
event::LV2AtomSequence,
};
pub fn handle_plugin (state: &mut Plugin, event: &AppEvent) -> Usually<bool> {
handle_keymap(state, event, KEYMAP_PLUGIN)
}
/// Key bindings for plugin device.
pub const KEYMAP_PLUGIN: &'static [KeyBinding<Plugin>] = keymap!(Plugin {
[Up, NONE, "cursor_up", "move cursor up", |s: &mut Plugin|{
s.selected = s.selected.saturating_sub(1);
Ok(true)
}],
[Down, NONE, "cursor_down", "move cursor down", |s: &mut Plugin|{
s.selected = (s.selected + 1).min(match &s.plugin {
Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => port_list.len() - 1,
_ => unimplemented!()
});
Ok(true)
}],
[PageUp, NONE, "cursor_up", "move cursor up", |s: &mut Plugin|{
s.selected = s.selected.saturating_sub(8);
Ok(true)
}],
[PageDown, NONE, "cursor_down", "move cursor down", |s: &mut Plugin|{
s.selected = (s.selected + 10).min(match &s.plugin {
Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => port_list.len() - 1,
_ => unimplemented!()
});
Ok(true)
}],
[Char(','), NONE, "decrement", "decrement value", |s: &mut Plugin|{
match s.plugin.as_mut() {
Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => {
let index = port_list[s.selected].index;
if let Some(value) = instance.control_input(index) {
instance.set_control_input(index, value - 0.01);
}
},
_ => {}
}
Ok(true)
}],
[Char('.'), NONE, "increment", "increment value", |s: &mut Plugin|{
match s.plugin.as_mut() {
Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => {
let index = port_list[s.selected].index;
if let Some(value) = instance.control_input(index) {
instance.set_control_input(index, value + 0.01);
}
},
_ => {}
}
Ok(true)
}],
});
/// A plugin device.
pub struct Plugin {
pub name: String,
pub path: Option<String>,
pub plugin: Option<PluginKind>,
pub selected: usize,
pub mapping: bool,
pub ports: JackPorts,
}
render!(Plugin = render_plugin);
handle!(Plugin = handle_plugin);
process!(Plugin = Plugin::process);
/// Supported plugin formats.
pub enum PluginKind {
LV2(LV2Plugin),
VST2 {
instance: ::vst::host::PluginInstance
},
VST3,
}
impl Plugin {
/// Create a plugin host device.
pub fn new (name: &str) -> Usually<Self> {
Ok(Self {
name: name.into(),
path: None,
plugin: None,
selected: 0,
mapping: false,
ports: JackPorts::default()
})
}
pub fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
match self.plugin.as_mut() {
Some(PluginKind::LV2(LV2Plugin {
features,
ref mut instance,
ref mut input_buffer,
..
})) => {
let urid = features.midi_urid();
input_buffer.clear();
for port in self.ports.midi_ins.values() {
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(),
_ => {}
}
}
input_buffer.push(atom);
}
let mut outputs = vec![];
for _ in self.ports.midi_outs.iter() {
outputs.push(::livi::event::LV2AtomSequence::new(
&features,
scope.n_frames() as usize
));
}
let ports = ::livi::EmptyPortConnections::new()
.with_atom_sequence_inputs(
input_buffer.iter()
)
.with_atom_sequence_outputs(
outputs.iter_mut()
)
.with_audio_inputs(
self.ports.audio_ins.values().map(|o|o.as_slice(scope))
)
.with_audio_outputs(
self.ports.audio_outs.values_mut().map(|o|o.as_mut_slice(scope))
);
unsafe {
instance.run(scope.n_frames() as usize, ports).unwrap()
};
},
_ => {}
}
Control::Continue
}
}
pub fn render_plugin (state: &Plugin, buf: &mut Buffer, area: Rect)
-> Usually<Rect>
{
let Rect { x, y, height, .. } = area;
let mut width = 20u16;
match &state.plugin {
Some(PluginKind::LV2(LV2Plugin { port_list, instance, .. })) => {
let start = state.selected.saturating_sub((height as usize / 2).saturating_sub(1));
let end = start + height as usize - 2;
//draw_box(buf, Rect { x, y, width, height });
for i in start..end {
if let Some(port) = port_list.get(i) {
let value = if let Some(value) = instance.control_input(port.index) {
value
} else {
port.default_value
};
//let label = &format!("C·· M·· {:25} = {value:.03}", port.name);
let label = &format!("{:25} = {value:.03}", port.name);
width = width.max(label.len() as u16 + 4);
label.blit(buf, x + 2, y + 1 + i as u16 - start as u16, if i == state.selected {
Some(Style::default().green())
} else {
None
})?;
} else {
break
}
}
},
_ => {}
};
draw_header(state, buf, area.x, area.y, width)?;
Ok(Rect { width, ..area })
}
fn draw_header (state: &Plugin, buf: &mut Buffer, x: u16, y: u16, w: u16) -> Usually<Rect> {
let style = Style::default().gray();
let label1 = format!(" {}", state.name);
label1.blit(buf, x + 1, y, Some(style.white().bold()))?;
if let Some(ref path) = state.path {
let label2 = format!("{}", &path[..((w as usize - 10).min(path.len()))]);
label2.blit(buf, x + 2 + label1.len() as u16, y, Some(style.not_dim()))?;
}
Ok(Rect { x, y, width: w, height: 1 })
}
/// A LV2 plugin.
pub struct LV2Plugin {
pub world: World,
pub instance: Instance,
pub plugin: LiviPlugin,
pub features: Arc<Features>,
pub port_list: Vec<Port>,
pub input_buffer: Vec<LV2AtomSequence>
}
impl LV2Plugin {
pub fn new (uri: &str) -> Usually<Self> {
// Get 1st plugin at URI
let world = World::with_load_bundle(&uri);
let features = FeaturesBuilder { min_block_length: 1, max_block_length: 65536 };
let features = world.build_features(features);
let mut plugin = None;
for p in world.iter_plugins() {
plugin = Some(p);
break
}
let plugin = plugin.unwrap();
let err = &format!("init {uri}");
// Instantiate
Ok(Self {
world,
instance: unsafe {
plugin
.instantiate(features.clone(), 48000.0)
.expect(&err)
},
port_list: {
let mut port_list = vec![];
for port in plugin.ports() {
port_list.push(port);
}
port_list
},
plugin,
features,
input_buffer: Vec::with_capacity(1024)
})
}
}
impl Plugin {
pub fn lv2 (name: &str, path: &str) -> Usually<JackDevice> {
let plugin = LV2Plugin::new(path)?;
Jack::new(name)?
.ports_from_lv2(&plugin.plugin)
.run(|ports|Box::new(Self {
name: name.into(),
path: Some(String::from(path)),
plugin: Some(PluginKind::LV2(plugin)),
selected: 0,
mapping: false,
ports
}))
}
}
impl ::vst::host::Host for Plugin {}
fn set_vst_plugin (host: &Arc<Mutex<Plugin>>, _path: &str) -> Usually<PluginKind> {
let mut loader = ::vst::host::PluginLoader::load(
&std::path::Path::new("/nix/store/ij3sz7nqg5l7v2dygdvzy3w6cj62bd6r-helm-0.9.0/lib/lxvst/helm.so"),
host.clone()
)?;
Ok(PluginKind::VST2 {
instance: loader.instance()?
})
}

View file

@ -13,7 +13,9 @@
//! * [Sample::load_edn] //! * [Sample::load_edn]
//! * [LV2Plugin::load_edn] //! * [LV2Plugin::load_edn]
use crate::{core::*, model::*, App, devices::sampler::{Sampler, Sample, read_sample_data}}; use crate::{core::*, model::*, App};
use crate::devices::sampler::{Sampler, Sample, read_sample_data};
use crate::devices::plugin::{Plugin, LV2Plugin};
use clojure_reader::{edn::{read, Edn}, error::Error as EdnError}; use clojure_reader::{edn::{read, Edn}, error::Error as EdnError};
/// EDN parsing helper. /// EDN parsing helper.

View file

@ -2,7 +2,7 @@
use crate::{core::*, devices::{arranger::*, sequencer::*, transport::*}}; use crate::{core::*, devices::{arranger::*, sequencer::*, transport::*}};
submod! { axis looper mixer phrase plugin scene track } submod! { axis phrase scene track }
/// Root of application state. /// Root of application state.
pub struct App { pub struct App {

View file

@ -1,94 +0,0 @@
use crate::core::*;
submod! { lv2 vst2 vst3 }
/// A plugin device.
pub struct Plugin {
pub name: String,
pub path: Option<String>,
pub plugin: Option<PluginKind>,
pub selected: usize,
pub mapping: bool,
pub ports: JackPorts,
}
render!(Plugin = crate::view::render_plugin);
handle!(Plugin = crate::control::handle_plugin);
process!(Plugin = Plugin::process);
/// Supported plugin formats.
pub enum PluginKind {
LV2(LV2Plugin),
VST2 {
instance: ::vst::host::PluginInstance
},
VST3,
}
impl Plugin {
/// Create a plugin host device.
pub fn new (name: &str) -> Usually<Self> {
Ok(Self {
name: name.into(),
path: None,
plugin: None,
selected: 0,
mapping: false,
ports: JackPorts::default()
})
}
pub fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
match self.plugin.as_mut() {
Some(PluginKind::LV2(LV2Plugin {
features,
ref mut instance,
ref mut input_buffer,
..
})) => {
let urid = features.midi_urid();
input_buffer.clear();
for port in self.ports.midi_ins.values() {
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(),
_ => {}
}
}
input_buffer.push(atom);
}
let mut outputs = vec![];
for _ in self.ports.midi_outs.iter() {
outputs.push(::livi::event::LV2AtomSequence::new(
&features,
scope.n_frames() as usize
));
}
let ports = ::livi::EmptyPortConnections::new()
.with_atom_sequence_inputs(
input_buffer.iter()
)
.with_atom_sequence_outputs(
outputs.iter_mut()
)
.with_audio_inputs(
self.ports.audio_ins.values().map(|o|o.as_slice(scope))
)
.with_audio_outputs(
self.ports.audio_outs.values_mut().map(|o|o.as_mut_slice(scope))
);
unsafe {
instance.run(scope.n_frames() as usize, ports).unwrap()
};
},
_ => {}
}
Control::Continue
}
}

View file

@ -1,73 +0,0 @@
use crate::core::*;
use ::livi::{
World,
Instance,
Plugin,
Features,
FeaturesBuilder,
Port,
event::LV2AtomSequence,
};
/// A LV2 plugin.
pub struct LV2Plugin {
pub world: World,
pub instance: Instance,
pub plugin: Plugin,
pub features: Arc<Features>,
pub port_list: Vec<Port>,
pub input_buffer: Vec<LV2AtomSequence>
}
impl LV2Plugin {
pub fn new (uri: &str) -> Usually<Self> {
// Get 1st plugin at URI
let world = World::with_load_bundle(&uri);
let features = FeaturesBuilder { min_block_length: 1, max_block_length: 65536 };
let features = world.build_features(features);
let mut plugin = None;
for p in world.iter_plugins() {
plugin = Some(p);
break
}
let plugin = plugin.unwrap();
let err = &format!("init {uri}");
// Instantiate
Ok(Self {
world,
instance: unsafe {
plugin
.instantiate(features.clone(), 48000.0)
.expect(&err)
},
port_list: {
let mut port_list = vec![];
for port in plugin.ports() {
port_list.push(port);
}
port_list
},
plugin,
features,
input_buffer: Vec::with_capacity(1024)
})
}
}
impl super::Plugin {
pub fn lv2 (name: &str, path: &str) -> Usually<JackDevice> {
let plugin = LV2Plugin::new(path)?;
Jack::new(name)?
.ports_from_lv2(&plugin.plugin)
.run(|ports|Box::new(Self {
name: name.into(),
path: Some(String::from(path)),
plugin: Some(super::PluginKind::LV2(plugin)),
selected: 0,
mapping: false,
ports
}))
}
}

View file

@ -1,15 +0,0 @@
use crate::core::*;
use super::*;
impl ::vst::host::Host for Plugin {}
fn set_vst_plugin (host: &Arc<Mutex<Plugin>>, _path: &str) -> Usually<PluginKind> {
let mut loader = ::vst::host::PluginLoader::load(
&std::path::Path::new("/nix/store/ij3sz7nqg5l7v2dygdvzy3w6cj62bd6r-helm-0.9.0/lib/lxvst/helm.so"),
host.clone()
)?;
Ok(PluginKind::VST2 {
instance: loader.instance()?
})
}

View file

@ -2,7 +2,7 @@
use crate::{render, App, core::*}; use crate::{render, App, core::*};
submod! { border chain help plugin split theme } submod! { border chain help split theme }
render!(App |self, buf, area| { render!(App |self, buf, area| {
Split::down([ Split::down([

View file

@ -1,48 +0,0 @@
use crate::{core::*, model::*};
pub fn render_plugin (state: &Plugin, buf: &mut Buffer, area: Rect)
-> Usually<Rect>
{
let Rect { x, y, height, .. } = area;
let mut width = 20u16;
match &state.plugin {
Some(PluginKind::LV2(LV2Plugin { port_list, instance, .. })) => {
let start = state.selected.saturating_sub((height as usize / 2).saturating_sub(1));
let end = start + height as usize - 2;
//draw_box(buf, Rect { x, y, width, height });
for i in start..end {
if let Some(port) = port_list.get(i) {
let value = if let Some(value) = instance.control_input(port.index) {
value
} else {
port.default_value
};
//let label = &format!("C·· M·· {:25} = {value:.03}", port.name);
let label = &format!("{:25} = {value:.03}", port.name);
width = width.max(label.len() as u16 + 4);
label.blit(buf, x + 2, y + 1 + i as u16 - start as u16, if i == state.selected {
Some(Style::default().green())
} else {
None
})?;
} else {
break
}
}
},
_ => {}
};
draw_header(state, buf, area.x, area.y, width)?;
Ok(Rect { width, ..area })
}
fn draw_header (state: &Plugin, buf: &mut Buffer, x: u16, y: u16, w: u16) -> Usually<Rect> {
let style = Style::default().gray();
let label1 = format!(" {}", state.name);
label1.blit(buf, x + 1, y, Some(style.white().bold()))?;
if let Some(ref path) = state.path {
let label2 = format!("{}", &path[..((w as usize - 10).min(path.len()))]);
label2.blit(buf, x + 2 + label1.len() as u16, y, Some(style.not_dim()))?;
}
Ok(Rect { x, y, width: w, height: 1 })
}