use crate::core::*; mod lv2; pub use lv2::*; mod vst2; mod vst3; pub struct Plugin { name: String, path: Option, plugin: Option, selected: usize, mapping: bool, midi_ins: Vec>, midi_outs: Vec>, audio_ins: Vec>, audio_outs: Vec>, } 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> { 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::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> { let mut ports = vec![]; for port in self.audio_ins.iter() { ports.push(port.name()?); } Ok(ports) } fn audio_outs (&self) -> Usually> { 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) } } pub fn render (state: &Plugin, buf: &mut Buffer, area: Rect) -> Usually { 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 { 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 { 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) }] })) }