use crate::prelude::*; mod lv2; mod vst2; mod vst3; pub struct Plugin { name: String, path: String, plugin: Option, offset: usize, selected: usize, midi_map_enable: bool, } enum PluginKind { VST2(::vst::host::PluginInstance), VST3, LV2(Vec<::livi::Port>, ::livi::Instance), } 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> { let device = DynamicDevice::new(render, handle, process, Self { name: name.into(), path: HELM.into(), plugin: None, offset: 0, selected: 0, midi_map_enable: false }); device.state.lock().unwrap().plugin = Some(self::lv2::plug_in(HELM)?); Ok(device) } } impl ::vst::host::Host for Plugin {} pub fn process (_: &mut Plugin, _: &Client, _: &ProcessScope) -> Control { Control::Continue } pub fn render (state: &Plugin, buf: &mut Buffer, area: Rect) -> Usually { let header = draw_header(state, buf, area)?; let Rect { x, y, width, height } = area; let height = height - header.height; let style = Style::default().gray(); match &state.plugin { Some(PluginKind::LV2(ports, instance)) => { let mut height = 3; for (i, port) in ports .iter() .filter(|port|port.port_type == ::livi::PortType::ControlInput) .skip(state.offset) .enumerate() { if i >= 20 { break } buf.set_string(x + 2, y + 3 + i as u16, &format!("C·· M·· {:25} = {:03}", port.name, port.default_value ), if i + state.offset == state.selected { Style::default().white().bold() } else { Style::default().not_dim() }); height = height + 1; } Ok(draw_box(buf, Rect { x, y, width, height: height.max(20) })) }, _ => { buf.set_string(x + 1, y + 3, &format!(" Parameter 1 0.0"), style); buf.set_string(x + 1, y + 4, &format!(" Parameter 2 0.0"), style); buf.set_string(x + 1, y + 5, &format!(" Parameter 3 0.0"), style); buf.set_string(x + 1, y + 6, &format!(" Parameter 4 0.0"), style); Ok(draw_box(buf, Rect { x, y, width: 40, height: 7 })) } } } fn draw_header (state: &Plugin, buf: &mut Buffer, Rect {x, y, width, ..}: Rect) -> Usually { let style = Style::default().gray(); buf.set_string(x + 1, y + 1, &format!(" {}", state.name), style.white().bold()); buf.set_string(x + 13, y + 1, &format!("│ {}...", &HELM[..(width as usize - 20).min(HELM.len())]), style.not_dim()); buf.set_string(x + 0, y + 2, &format!("├{}┤", "-".repeat(width as usize - 2)), style.dim()); Ok(Rect { x, y, width, height: 3 }) } pub fn handle (s: &mut Plugin, event: &AppEvent) -> Usually { handle_keymap(s, event, keymap!(Plugin { [Up, NONE, "cursor_up", "move cursor up", |s: &mut Plugin|{ s.selected = s.selected.saturating_sub(1); if s.selected < s.offset { s.offset = s.selected; } Ok(true) }], [Down, NONE, "cursor_down", "move cursor down", |s: &mut Plugin|{ s.selected = s.selected + 1; if s.selected >= s.offset + 19 { s.offset = s.offset + 1; } Ok(true) }], [Char('m'), NONE, "toggle_midi_map", "toggle midi map mode", |s: &mut Plugin|{ s.midi_map_enable = !s.midi_map_enable; Ok(true) }] })) } impl DevicePorts for Plugin { fn midi_ins (&self) -> Usually> { Ok(vec!["in".into()]) } fn audio_outs (&self) -> Usually> { Ok(vec!["out L".into(), "out R".into()]) } }