1use lilv_sys::*;
2
3use std::ffi::{ CStr };
4use std::ptr;
5use std::ffi;
6use std::collections::HashMap;
7use std::pin::Pin;
8
9use urid::*;
10use lv2_urid::*;
11
12const LILV_URI_CONNECTION_OPTIONAL: &[u8; 48usize] = b"http://lv2plug.in/ns/lv2core#connectionOptional\0";
20pub const LV2_URID_MAP: &[u8; 34usize] = b"http://lv2plug.in/ns/ext/urid#map\0";
21
22pub struct Lv2Host{
23 world: *mut LilvWorld,
24 lilv_plugins: *const LilvPlugins,
25 plugin_cap: usize,
26 plugins: Vec<Plugin>,
27 uri_home: Vec<String>,
28 plugin_names: HashMap<String, usize>,
29 dead_list: Vec<usize>,
30 #[allow(dead_code)]
31 buffer_len: usize,
32 sr: f64,
33 in_buf: Vec<f32>,
34 out_buf: Vec<f32>,
35 atom_buf: [u8; 1024],
36 atom_urid_bytes: [u8; 4],
37 midi_urid_bytes: [u8; 4],
38 #[allow(dead_code)]
39 host_map: Pin<Box<HostMap<HashURIDMapper>>>,
40 pub map_interface: lv2_sys::LV2_URID_Map,
41}
42
43#[derive(Clone, Copy, PartialEq, Eq, Debug)]
44pub enum AddPluginError{
45 CapacityReached,
46 MoreThanTwoInOrOutAudioPorts(usize, usize),
47 MoreThanOneAtomPort(usize),
48 WorldIsNull,
49 PluginIsNull,
50 PortNeitherInputOrOutput,
51 PortNeitherControlOrAudioOrOptional,
52}
53
54#[derive(Clone, Copy, PartialEq, Eq, Debug)]
55pub enum ApplyError{
56 PluginIndexOutOfBound,
57 LeftRightInputLenUnequal,
58 MoreFramesThanBufferLen,
59}
60
61impl Lv2Host{
62 pub fn new(plugin_cap: usize, buffer_len: usize, sample_rate: usize) -> Self{
63 let mut host_map: Pin<Box<HostMap<HashURIDMapper>>> = Box::pin(HashURIDMapper::new().into());
65 let map_interface = host_map.as_mut().make_map_interface();
66 let map = LV2Map::new(&map_interface);
67 let midi_urid_bytes = map.map_str("http://lv2plug.in/ns/ext/midi#MidiEvent").unwrap().get().to_le_bytes();
68 let atom_urid_bytes = map.map_str("http://lv2plug.in/ns/ext/atom#Sequence").unwrap().get().to_le_bytes();
69
70 let (world, lilv_plugins) = unsafe{
71 let world = lilv_world_new();
72 lilv_world_load_all(world);
73 let lilv_plugins = lilv_world_get_all_plugins(world);
74 (world, lilv_plugins)
75 };
76 Lv2Host{
77 world,
78 lilv_plugins,
79 plugin_cap,
80 plugins: Vec::with_capacity(plugin_cap), uri_home: Vec::new(), plugin_names: HashMap::new(),
83 dead_list: Vec::new(),
84 buffer_len,
85 sr: sample_rate as f64,
86 in_buf: vec![0.0; buffer_len * 2], out_buf: vec![0.0; buffer_len * 2], atom_buf: [0; 1024],
89 atom_urid_bytes,
90 midi_urid_bytes,
91 host_map,
92 map_interface,
93 }
94 }
95
96 pub fn get_index(&self, name: &str) -> Option<usize>{
97 self.plugin_names.get(name).copied()
98 }
99
100 #[allow(clippy::not_unsafe_ptr_arg_deref)]
101 pub fn add_plugin(&mut self, uri: &str, name: String) -> Result<usize, AddPluginError>{
102 let feature_vec = vec![lv2_raw::core::LV2Feature {
103 uri: LV2_URID_MAP.as_ptr() as *const i8,
104 data: &mut self.map_interface as *mut lv2_sys::LV2_URID_Map as *mut std::ffi::c_void,
105 }];
106 let mapfp = feature_vec.as_ptr() as *const lv2_raw::core::LV2Feature;
107 let features = vec![mapfp, std::ptr::null::<lv2_raw::core::LV2Feature>()];
108
109 let features_ptr = features.as_ptr() as *const *const lv2_raw::core::LV2Feature;
110 let replace_index = self.dead_list.pop();
111 if self.plugins.len() == self.plugin_cap && replace_index == None{
112 return Err(AddPluginError::CapacityReached);
113 }
114 let mut uri_src = uri.to_owned();
115 uri_src.push('\0'); self.uri_home.push(uri_src);
117 let plugin = unsafe{
118 let uri = lilv_new_uri(self.world, (&self.uri_home[self.uri_home.len() - 1]).as_ptr() as *const i8);
119 let plugin = lilv_plugins_get_by_uri(self.lilv_plugins, uri);
120 lilv_node_free(uri);
121 plugin
122 };
123
124 let (mut ports, port_names, n_audio_in, n_audio_out, n_atom_in) = unsafe {
125 create_ports(self.world, plugin)?
126 };
127
128 if n_audio_in > 2 || n_audio_out > 2 {
129 return Err(AddPluginError::MoreThanTwoInOrOutAudioPorts(n_audio_in, n_audio_out));
130 }
131
132 if n_atom_in > 1 {
133 return Err(AddPluginError::MoreThanOneAtomPort(n_atom_in));
134 }
135
136 let instance = unsafe{
137 let instance = lilv_plugin_instantiate(plugin, self.sr, features_ptr);
141 let (mut i, mut o) = (0, 0);
142 for p in &mut ports{
143 match p.ptype{
144 PortType::Control => {
145 lilv_instance_connect_port(instance, p.index, &mut p.value as *mut f32 as *mut ffi::c_void);
146 },
147 PortType::Audio => {
148 if p.is_input {
149 lilv_instance_connect_port(instance, p.index, self.in_buf.as_ptr().offset(i*self.buffer_len as isize) as *mut ffi::c_void);
150 i += 1;
151 } else {
152 lilv_instance_connect_port(instance, p.index, self.out_buf.as_ptr().offset(o*self.buffer_len as isize) as *mut ffi::c_void);
153 o += 1;
154 }
155 },
156 PortType::Atom => {
157 lilv_instance_connect_port(instance, p.index, self.atom_buf.as_ptr() as *mut ffi::c_void);
158 }
159 PortType::Other => {
160 lilv_instance_connect_port(instance, p.index, ptr::null_mut());
161 }
162 }
163 }
164
165 lilv_instance_activate(instance);
166 instance
167 };
168
169 let p = Plugin{
170 instance,
171 port_names,
172 ports,
173 };
174
175 if let Some(i) = replace_index{
176 self.plugins[i] = p;
177 self.plugin_names.insert(name, i);
178 } else {
179 self.plugins.push(p);
180 self.plugin_names.insert(name, self.plugins.len() - 1);
181 }
182
183 Ok(self.plugins.len() - 1)
184 }
185
186 pub fn remove_plugin(&mut self, name: &str) -> bool{
187 if let Some(index) = self.plugin_names.get(name){
188 unsafe{
189 lilv_instance_deactivate(self.plugins[*index].instance);
190 }
193 self.dead_list.push(*index);
194 self.plugin_names.remove(name);
195 true
196 } else {
197 false
198 }
199 }
200
201 fn set_port_value(plug: &mut Plugin, port: &str, value: Option<f32>) -> bool{
202 let port_index = plug.port_names.get(port);
203 if port_index.is_none() { return false; }
204 let port_index = port_index.unwrap();
205 let min = plug.ports[*port_index].min;
206 let max = plug.ports[*port_index].max;
207 let value = if let Some(v) = value { v }
208 else { plug.ports[*port_index].def };
209 plug.ports[*port_index].value = value.max(min).min(max);
210 true
211 }
212
213 pub fn set_value(&mut self, plugin: &str, port: &str, value: f32) -> bool{
214 if let Some(index) = self.plugin_names.get(plugin){
215 let plug = &mut self.plugins[*index];
216 Self::set_port_value(plug, port, Some(value))
217 } else {
218 false
219 }
220 }
221
222 pub fn reset_value(&mut self, plugin: &str, port: &str) -> bool{
223 if let Some(index) = self.plugin_names.get(plugin){
224 let plug = &mut self.plugins[*index];
225 Self::set_port_value(plug, port, None)
226 } else {
227 false
228 }
229 }
230
231 pub fn get_plugin_sheet(&self, index: usize) -> PluginSheet{
232 let plug = &self.plugins[index];
233 let mut ains = 0;
234 let mut aouts = 0;
235 let mut controls = Vec::new();
236 for port in &plug.ports{
237 if port.ptype == PortType::Audio{
238 if port.is_input{
239 ains += 1;
240 } else {
241 aouts += 1;
242 }
243 } else if port.ptype == PortType::Control && port.is_input{
244 controls.push((port.name.clone(), port.def, port.min, port.max));
245 }
246 }
247 PluginSheet{
248 audio_ins: ains,
249 audio_outs: aouts,
250 controls,
251 }
252 }
253
254 pub fn apply(&mut self, index: usize, input: [u8; 3], input_frame: (f32, f32)) -> (f32, f32) {
255 if index >= self.plugins.len() { return (0.0, 0.0); }
256 self.in_buf[0] = input_frame.0;
257 self.in_buf[1] = input_frame.1;
258 let plugin = &mut self.plugins[index];
259 midi_into_atom_buffer(self.midi_urid_bytes, self.atom_urid_bytes, vec![(0, input)], &mut self.atom_buf);
260 unsafe {
261 lilv_instance_run(plugin.instance, 1);
262 }
263 (self.out_buf[0], self.out_buf[1])
264 }
265
266 pub fn apply_multi(&mut self, index: usize, input: Vec<(u64, [u8; 3])>, input_frame: [&[f32]; 2]) -> Result<[&[f32]; 2], ApplyError>{
267 midi_into_atom_buffer(self.midi_urid_bytes, self.atom_urid_bytes, input, &mut self.atom_buf);
268 if index >= self.plugins.len() { return Err(ApplyError::PluginIndexOutOfBound); }
269 let frames = input_frame[0].len();
270 if frames != input_frame[1].len(){
271 return Err(ApplyError::LeftRightInputLenUnequal);
272 }
273 if frames > self.buffer_len{
274 return Err(ApplyError::MoreFramesThanBufferLen);
275 }
276 for (i, (l, r)) in input_frame[0].iter().zip(input_frame[1].iter()).enumerate(){
277 self.in_buf[i] = *l;
278 self.in_buf[i + self.buffer_len] = *r;
279 }
280 let plugin = &mut self.plugins[index];
281 unsafe{
282 lilv_instance_run(plugin.instance, frames as u32);
283 }
284 Ok([&self.out_buf[..frames], &self.out_buf[self.buffer_len..][..frames]])
285 }
286}
287
288fn midi_into_atom_buffer(typebytes: [u8; 4], seqbytes: [u8; 4], midibytes: Vec<(u64, [u8; 3])>, atom_buf: &mut [u8; 1024]) {
289 let mut pos = 4; copy_bytes(atom_buf, &seqbytes, &mut pos); copy_bytes(atom_buf, &[0,0,0,0,0,0,0,0,], &mut pos); for (ea_time, ea_midi) in midibytes {
297 copy_bytes(atom_buf, &ea_time.to_le_bytes(), &mut pos); copy_bytes(atom_buf, &3_u32.to_le_bytes(), &mut pos); copy_bytes(atom_buf, &typebytes, &mut pos); copy_bytes(atom_buf, &ea_midi, &mut pos);
303 let extra = 8 - (pos % 8);
304 if extra != 8 {
305 for idx in 0..extra {
306 atom_buf[pos..][idx] = 0;
307 }
308 pos += extra;
309 }
310 }
311 pos -= 8; copy_bytes(atom_buf, &(pos as u32).to_le_bytes(), &mut 0); }
314
315fn copy_bytes(to: &mut [u8], from: &[u8], at: &mut usize) {
316 for (to, from) in to[*at..].iter_mut().zip(from.iter()) {
317 *to = *from;
318 }
319 *at += from.len();
320}
321
322
323impl Drop for Lv2Host{
324 fn drop(&mut self){
325 unsafe{
326 for (i, plugin) in self.plugins.iter().enumerate(){
327 if self.dead_list.contains(&i){
328 continue;
329 }
330 lilv_instance_deactivate(plugin.instance);
331 lilv_instance_free(plugin.instance);
332 }
333 lilv_world_free(self.world);
334 }
335 }
336}
337
338struct Plugin{
339 instance: *mut LilvInstance,
340 port_names: HashMap<String, usize>,
341 ports: Vec<Port>,
342}
343
344#[derive(Debug)]
345pub struct PluginSheet{
346 pub audio_ins: usize,
347 pub audio_outs: usize,
348 pub controls: Vec<(String, f32, f32, f32)>,
349}
350
351#[derive(PartialEq,Eq,Debug)]
352enum PortType{ Control, Audio, Atom, Other }
353
354#[derive(Debug)]
355struct Port{
356 lilv_port: *const LilvPort,
357 index: u32,
358 optional: bool,
359 is_input: bool,
360 ptype: PortType,
361 value: f32,
362 def: f32,
363 min: f32,
364 max: f32,
365 name: String,
366}
367
368type CreatePortsRes = (Vec<Port>, HashMap<String, usize>, usize, usize, usize);
369
370unsafe fn create_ports(world: *mut LilvWorld, plugin: *const LilvPluginImpl) -> Result<CreatePortsRes, AddPluginError>{
372 if world.is_null() { return Err(AddPluginError::WorldIsNull); }
373 if plugin.is_null() { return Err(AddPluginError::PluginIsNull); }
374
375 let mut ports = Vec::new();
376 let mut names = HashMap::new();
377 let mut n_audio_in = 0;
378 let mut n_audio_out = 0;
379 let mut n_atom_in = 0;
380
381 let n_ports = lilv_plugin_get_num_ports(plugin);
382
383 let mins = vec![0.0f32; n_ports as usize];
384 let maxs = vec![0.0f32; n_ports as usize];
385 let defs = vec![0.0f32; n_ports as usize];
386 lilv_plugin_get_port_ranges_float(plugin, mins.as_ptr() as *mut f32, maxs.as_ptr() as *mut f32, defs.as_ptr() as *mut f32);
387
388 let lv2_input_port = lilv_new_uri(world, LILV_URI_INPUT_PORT.as_ptr() as *const i8);
389 let lv2_output_port = lilv_new_uri(world, LILV_URI_OUTPUT_PORT.as_ptr() as *const i8);
390 let lv2_audio_port = lilv_new_uri(world, LILV_URI_AUDIO_PORT.as_ptr() as *const i8);
391 let lv2_control_port = lilv_new_uri(world, LILV_URI_CONTROL_PORT.as_ptr() as *const i8);
392 let lv2_atom_port = lilv_new_uri(world, LILV_URI_ATOM_PORT.as_ptr() as *const i8);
393 let lv2_connection_optional = lilv_new_uri(world, LILV_URI_CONNECTION_OPTIONAL.as_ptr() as *const i8);
394
395 for i in 0..n_ports{
396 let lport = lilv_plugin_get_port_by_index(plugin, i);
397 let def = defs[i as usize];
398 let min = mins[i as usize];
399 let max = maxs[i as usize];
400 let value = if def.is_nan() { 0.0 } else { def };
401
402 let lilv_name = lilv_port_get_name(plugin, lport);
403 let lilv_str = lilv_node_as_string(lilv_name);
404 let c_str = CStr::from_ptr(lilv_str as *const i8);
405 let name = c_str.to_str().expect("Lv2hm: could not build port name string.").to_owned();
406
407 names.insert(name.clone(), i as usize);
408
409 let optional = lilv_port_has_property(plugin, lport, lv2_connection_optional);
410
411 let is_input = if lilv_port_is_a(plugin, lport, lv2_input_port) { true }
412 else if !lilv_port_is_a(plugin, lport, lv2_output_port) && !optional { return Err(AddPluginError::PortNeitherInputOrOutput); }
413 else { false };
414
415 let ptype = if lilv_port_is_a(plugin, lport, lv2_control_port) { PortType::Control }
416 else if lilv_port_is_a(plugin, lport, lv2_audio_port) {
417 if is_input{
418 n_audio_in += 1;
419 } else {
420 n_audio_out += 1;
421 }
422 PortType::Audio
423 }
424 else if lilv_port_is_a(plugin, lport, lv2_atom_port) && is_input{
425 n_atom_in += 1;
426 PortType::Atom
427 }
428 else if !optional { return Err(AddPluginError::PortNeitherControlOrAudioOrOptional); }
429 else { PortType::Other };
430
431 ports.push(Port{
432 lilv_port: lport,
433 index: i,
434 ptype, is_input, optional, value, def, min, max, name,
435 });
436 }
437
438 lilv_node_free(lv2_connection_optional);
439 lilv_node_free(lv2_atom_port);
440 lilv_node_free(lv2_control_port);
441 lilv_node_free(lv2_audio_port);
442 lilv_node_free(lv2_output_port);
443 lilv_node_free(lv2_input_port);
444
445 Ok((ports, names, n_audio_in, n_audio_out, n_atom_in))
446}