mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
281 lines
9.7 KiB
Rust
281 lines
9.7 KiB
Rust
use crate::core::*;
|
|
|
|
mod lv2; pub use lv2::*;
|
|
mod vst2;
|
|
mod vst3;
|
|
|
|
pub struct Plugin {
|
|
name: String,
|
|
path: Option<String>,
|
|
plugin: Option<PluginKind>,
|
|
selected: usize,
|
|
mapping: bool,
|
|
midi_ins: Vec<Port<MidiIn>>,
|
|
midi_outs: Vec<Port<MidiOut>>,
|
|
audio_ins: Vec<Port<AudioIn>>,
|
|
audio_outs: Vec<Port<AudioOut>>,
|
|
}
|
|
|
|
enum PluginKind {
|
|
LV2(LV2Plugin),
|
|
VST2 {
|
|
instance: ::vst::host::PluginInstance
|
|
},
|
|
VST3,
|
|
}
|
|
|
|
const HELM: &'static str = "file:///nix/store/ij3sz7nqg5l7v2dygdvzy3w6cj62bd6r-helm-0.9.0/lib/lv2/helm.lv2";
|
|
|
|
impl Plugin {
|
|
/// Load a LV2 plugin.
|
|
pub fn lv2 (name: &str, path: &str) -> Usually<DynamicDevice<Self>> {
|
|
let host = Self::new(name)?;
|
|
let plugin = LV2Plugin::new(path)?;
|
|
let mut state = host.state();
|
|
let client = host.client.as_ref().unwrap().as_client();
|
|
let (midi_ins, midi_outs, audio_ins, audio_outs) = (
|
|
plugin.plugin.port_counts().atom_sequence_inputs,
|
|
plugin.plugin.port_counts().atom_sequence_outputs,
|
|
plugin.plugin.port_counts().audio_inputs,
|
|
plugin.plugin.port_counts().audio_outputs,
|
|
);
|
|
state.midi_ins = {
|
|
let mut ports = vec![];
|
|
for i in 0..midi_ins {
|
|
ports.push(client.register_port(&format!("midi-in-{i}"), MidiIn::default())?)
|
|
}
|
|
ports
|
|
};
|
|
state.midi_outs = {
|
|
let mut ports = vec![];
|
|
for i in 0..midi_outs {
|
|
ports.push(client.register_port(&format!("midi-out-{i}"), MidiOut::default())?)
|
|
}
|
|
ports
|
|
};
|
|
state.audio_ins = {
|
|
let mut ports = vec![];
|
|
for i in 0..audio_ins {
|
|
ports.push(client.register_port(&format!("audio-in-{i}"), AudioIn::default())?)
|
|
}
|
|
ports
|
|
};
|
|
state.audio_outs = {
|
|
let mut ports = vec![];
|
|
for i in 0..audio_outs {
|
|
ports.push(client.register_port(&format!("audio-out-{i}"), AudioOut::default())?)
|
|
}
|
|
ports
|
|
};
|
|
state.plugin = Some(PluginKind::LV2(plugin));
|
|
state.path = Some(String::from(path));
|
|
std::mem::drop(state);
|
|
Ok(host)
|
|
}
|
|
pub fn new (name: &str) -> Usually<DynamicDevice<Self>> {
|
|
DynamicDevice::new(render, handle, Self::process, Self {
|
|
name: name.into(),
|
|
path: None,
|
|
plugin: None,
|
|
selected: 0,
|
|
mapping: false,
|
|
midi_ins: vec![],
|
|
midi_outs: vec![],
|
|
audio_ins: vec![],
|
|
audio_outs: vec![],
|
|
}).activate(Client::new(name, ClientOptions::NO_START_SERVER)?.0)
|
|
}
|
|
pub fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
|
|
match self.plugin.as_mut() {
|
|
Some(PluginKind::LV2(LV2Plugin { features, ref mut instance, .. })) => {
|
|
let urid = features.midi_urid();
|
|
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 _ 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(
|
|
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()
|
|
};
|
|
},
|
|
_ => {}
|
|
}
|
|
Control::Continue
|
|
}
|
|
}
|
|
|
|
impl PortList for Plugin {
|
|
fn audio_ins (&self) -> Usually<Vec<String>> {
|
|
let mut ports = vec![];
|
|
for port in self.audio_ins.iter() {
|
|
ports.push(port.name()?);
|
|
}
|
|
Ok(ports)
|
|
}
|
|
fn audio_outs (&self) -> Usually<Vec<String>> {
|
|
let mut ports = vec![];
|
|
for port in self.audio_outs.iter() {
|
|
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)
|
|
}
|
|
}
|
|
|
|
pub fn render (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()));
|
|
let label2 = format!("{}…", &HELM[..(w as usize - 10).min(HELM.len())]);
|
|
label2.blit(buf, x + 2 + label1.len() as u16, y, Some(style.not_dim()));
|
|
Ok(Rect { x, y, width: w, height: 1 })
|
|
}
|
|
|
|
pub fn handle (s: &mut Plugin, event: &AppEvent) -> Usually<bool> {
|
|
handle_keymap(s, event, keymap!(Plugin {
|
|
|
|
[Up, NONE, "cursor_up", "move cursor up",
|
|
|s: &mut Plugin|{
|
|
if s.selected > 0 {
|
|
s.selected = s.selected - 1
|
|
} else {
|
|
s.selected = match &s.plugin {
|
|
Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => port_list.len() - 1,
|
|
_ => 0
|
|
}
|
|
}
|
|
Ok(true)
|
|
}],
|
|
|
|
[Down, NONE, "cursor_down", "move cursor down",
|
|
|s: &mut Plugin|{
|
|
s.selected = s.selected + 1;
|
|
match &s.plugin {
|
|
Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => {
|
|
if s.selected >= port_list.len() {
|
|
s.selected = 0;
|
|
}
|
|
},
|
|
_ => {}
|
|
}
|
|
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)
|
|
}],
|
|
|
|
[Char('m'), NONE, "toggle_midi_map", "toggle midi map mode",
|
|
|s: &mut Plugin|{
|
|
s.mapping = !s.mapping;
|
|
Ok(true)
|
|
}]
|
|
|
|
}))
|
|
}
|