|
| 1 | +use crate::extension::ExtensionInterface; |
| 2 | +use crate::feature::*; |
| 3 | +use crate::plugin::{Plugin, PluginInfo}; |
| 4 | +use crate::port::PortCollection; |
| 5 | +use crate::port::*; |
| 6 | +use crate::prelude::LV2_Descriptor; |
| 7 | +use std::ffi::c_void; |
| 8 | +use std::os::raw::c_char; |
| 9 | +use sys::LV2_Handle; |
| 10 | +use urid::Uri; |
| 11 | + |
| 12 | +/// Plugin wrapper which translated between the host and the plugin. |
| 13 | +/// |
| 14 | +/// The host interacts with the plugin via a C API, but the plugin is implemented with ideomatic, safe Rust. To bridge this gap, this wrapper is used to translate and abstract the communcation between the host and the plugin. |
| 15 | +/// |
| 16 | +/// This struct is `repr(C)` and has the plugin as it's first field. Therefore, a valid `*mut PluginInstance<T>` is also a valid `*mut T`. |
| 17 | +#[repr(C)] |
| 18 | +pub struct PluginInstance<T: Plugin> { |
| 19 | + /// The plugin instance. |
| 20 | + instance: T, |
| 21 | + /// A temporary storage for all ports of the plugin. |
| 22 | + connections: <T::Ports as PortCollection>::Cache, |
| 23 | + /// All features that may be used in the initialization threading class. |
| 24 | + init_features: T::InitFeatures, |
| 25 | + /// All features that may be used in the audio threading class. |
| 26 | + audio_features: T::AudioFeatures, |
| 27 | +} |
| 28 | + |
| 29 | +impl<T: Plugin> PluginInstance<T> { |
| 30 | + /// Try to create a port collection from the currently collected connections. |
| 31 | + /// |
| 32 | + /// # Safety |
| 33 | + /// |
| 34 | + /// This method is unsafe since it needs to dereference raw pointers, which are only valid if the method is called in the "Audio" threading class. |
| 35 | + pub unsafe fn ports(&self, sample_count: u32) -> Option<T::Ports> { |
| 36 | + <T::Ports as PortCollection>::from_connections(&self.connections, sample_count) |
| 37 | + } |
| 38 | + |
| 39 | + /// Instantiate the plugin. |
| 40 | + /// |
| 41 | + /// This method provides a required method for the C interface of a plugin and is used by the `lv2_descriptors` macro. |
| 42 | + /// |
| 43 | + /// # Safety |
| 44 | + /// |
| 45 | + /// This method is unsafe since it derefences multiple raw pointers and is part of the C interface. |
| 46 | + pub unsafe extern "C" fn instantiate( |
| 47 | + descriptor: *const sys::LV2_Descriptor, |
| 48 | + sample_rate: f64, |
| 49 | + bundle_path: *const c_char, |
| 50 | + features: *const *const sys::LV2_Feature, |
| 51 | + ) -> LV2_Handle { |
| 52 | + // Dereference the descriptor. |
| 53 | + let descriptor = match descriptor.as_ref() { |
| 54 | + Some(descriptor) => descriptor, |
| 55 | + None => { |
| 56 | + eprintln!("Failed to initialize plugin: Descriptor points to null"); |
| 57 | + return std::ptr::null_mut(); |
| 58 | + } |
| 59 | + }; |
| 60 | + |
| 61 | + // Dereference the plugin info. |
| 62 | + let plugin_info = match PluginInfo::from_raw(descriptor, bundle_path, sample_rate) { |
| 63 | + Ok(info) => info, |
| 64 | + Err(e) => { |
| 65 | + eprintln!( |
| 66 | + "Failed to initialize plugin: Illegal info from host: {:?}", |
| 67 | + e |
| 68 | + ); |
| 69 | + return std::ptr::null_mut(); |
| 70 | + } |
| 71 | + }; |
| 72 | + |
| 73 | + // Collect the supported features. |
| 74 | + let mut init_features_cache = FeatureCache::from_raw(features); |
| 75 | + let mut audio_features_cache = init_features_cache.clone(); |
| 76 | + |
| 77 | + let mut init_features = match T::InitFeatures::from_cache( |
| 78 | + &mut init_features_cache, |
| 79 | + ThreadingClass::Instantiation, |
| 80 | + ) { |
| 81 | + Ok(f) => f, |
| 82 | + Err(e) => { |
| 83 | + eprintln!("{}", e); |
| 84 | + return std::ptr::null_mut(); |
| 85 | + } |
| 86 | + }; |
| 87 | + let audio_features = |
| 88 | + match T::AudioFeatures::from_cache(&mut audio_features_cache, ThreadingClass::Audio) { |
| 89 | + Ok(f) => f, |
| 90 | + Err(e) => { |
| 91 | + eprintln!("{}", e); |
| 92 | + return std::ptr::null_mut(); |
| 93 | + } |
| 94 | + }; |
| 95 | + |
| 96 | + // Instantiate the plugin. |
| 97 | + match T::new(&plugin_info, &mut init_features) { |
| 98 | + Some(instance) => { |
| 99 | + let instance = Box::new(Self { |
| 100 | + instance, |
| 101 | + connections: <<T::Ports as PortCollection>::Cache as Default>::default(), |
| 102 | + init_features, |
| 103 | + audio_features, |
| 104 | + }); |
| 105 | + Box::leak(instance) as *mut Self as LV2_Handle |
| 106 | + } |
| 107 | + None => std::ptr::null_mut(), |
| 108 | + } |
| 109 | + } |
| 110 | + |
| 111 | + /// Clean the plugin. |
| 112 | + /// |
| 113 | + /// This method provides a required method for the C interface of a plugin and is used by the `lv2_descriptors` macro. |
| 114 | + /// |
| 115 | + /// # Safety |
| 116 | + /// |
| 117 | + /// This method is unsafe since it derefences multiple raw pointers and is part of the C interface. |
| 118 | + pub unsafe extern "C" fn cleanup(instance: *mut c_void) { |
| 119 | + let instance = instance as *mut Self; |
| 120 | + Box::from_raw(instance); |
| 121 | + } |
| 122 | + |
| 123 | + /// Call `activate`. |
| 124 | + /// |
| 125 | + /// This method provides a required method for the C interface of a plugin and is used by the `lv2_descriptors` macro. |
| 126 | + /// |
| 127 | + /// # Safety |
| 128 | + /// |
| 129 | + /// This method is unsafe since it derefences multiple raw pointers and is part of the C interface. |
| 130 | + pub unsafe extern "C" fn activate(instance: *mut c_void) { |
| 131 | + let instance = &mut *(instance as *mut Self); |
| 132 | + instance.instance.activate(&mut instance.init_features) |
| 133 | + } |
| 134 | + |
| 135 | + /// Call `deactivate`. |
| 136 | + /// |
| 137 | + /// This method provides a required method for the C interface of a plugin and is used by the `lv2_descriptors` macro. |
| 138 | + /// |
| 139 | + /// # Safety |
| 140 | + /// |
| 141 | + /// This method is unsafe since it derefences multiple raw pointers and is part of the C interface. |
| 142 | + pub unsafe extern "C" fn deactivate(instance: *mut c_void) { |
| 143 | + let instance = &mut *(instance as *mut Self); |
| 144 | + instance.instance.deactivate(&mut instance.init_features) |
| 145 | + } |
| 146 | + |
| 147 | + /// Update a port pointer. |
| 148 | + /// |
| 149 | + /// This method provides a required method for the C interface of a plugin and is used by the `lv2_descriptors` macro. |
| 150 | + /// |
| 151 | + /// # Safety |
| 152 | + /// |
| 153 | + /// This method is unsafe since it derefences multiple raw pointers and is part of the C interface. |
| 154 | + pub unsafe extern "C" fn connect_port(instance: *mut c_void, port: u32, data: *mut c_void) { |
| 155 | + let instance = instance as *mut Self; |
| 156 | + (*instance).connections.connect(port, data) |
| 157 | + } |
| 158 | + |
| 159 | + /// Construct a port collection and call the `run` method. |
| 160 | + /// |
| 161 | + /// This method provides a required method for the C interface of a plugin and is used by the `lv2_descriptors` macro. |
| 162 | + /// |
| 163 | + /// # Safety |
| 164 | + /// |
| 165 | + /// This method is unsafe since it derefences multiple raw pointers and is part of the C interface. |
| 166 | + pub unsafe extern "C" fn run(instance: *mut c_void, sample_count: u32) { |
| 167 | + let instance = &mut *(instance as *mut Self); |
| 168 | + if let Some(mut ports) = instance.ports(sample_count) { |
| 169 | + instance |
| 170 | + .instance |
| 171 | + .run(&mut ports, &mut instance.audio_features, sample_count); |
| 172 | + } |
| 173 | + } |
| 174 | + |
| 175 | + /// Dereference the URI, call the `extension_data` function and return the pointer. |
| 176 | + /// |
| 177 | + /// This method provides a required method for the C interface of a plugin and is used by the `lv2_descriptors` macro. |
| 178 | + /// |
| 179 | + /// # Safety |
| 180 | + /// |
| 181 | + /// This method is unsafe since it derefences multiple raw pointers and is part of the C interface. |
| 182 | + pub unsafe extern "C" fn extension_data(uri: *const c_char) -> *const c_void { |
| 183 | + let uri = Uri::from_ptr(uri); |
| 184 | + |
| 185 | + T::extension_data(uri) |
| 186 | + .map(ExtensionInterface::get_ptr) |
| 187 | + .unwrap_or_else(core::ptr::null) |
| 188 | + } |
| 189 | + |
| 190 | + /// Retrieve the internal plugin. |
| 191 | + pub fn plugin_handle(&mut self) -> &mut T { |
| 192 | + &mut self.instance |
| 193 | + } |
| 194 | + |
| 195 | + /// Retrieve the required handles to execute an Initialization class method. |
| 196 | + /// |
| 197 | + /// This method can be used by extensions to call an extension method in the Initialization threading class and provide it the host features for that class. |
| 198 | + pub fn init_class_handle(&mut self) -> (&mut T, &mut T::InitFeatures) { |
| 199 | + (&mut self.instance, &mut self.init_features) |
| 200 | + } |
| 201 | + |
| 202 | + /// Retrieve the required handles to execute an Audio class method. |
| 203 | + /// |
| 204 | + /// This method can be used by extensions to call an extension method in the Audio threading class and provide it the host features for that class. |
| 205 | + pub fn audio_class_handle(&mut self) -> (&mut T, &mut T::AudioFeatures) { |
| 206 | + (&mut self.instance, &mut self.audio_features) |
| 207 | + } |
| 208 | +} |
| 209 | + |
| 210 | +#[doc(hidden)] |
| 211 | +pub unsafe trait PluginInstanceDescriptor { |
| 212 | + const DESCRIPTOR: sys::LV2_Descriptor; |
| 213 | +} |
| 214 | + |
| 215 | +unsafe impl<T: Plugin> PluginInstanceDescriptor for T { |
| 216 | + const DESCRIPTOR: LV2_Descriptor = LV2_Descriptor { |
| 217 | + URI: T::URI.as_ptr() as *const u8 as *const ::std::os::raw::c_char, |
| 218 | + instantiate: Some(PluginInstance::<T>::instantiate), |
| 219 | + connect_port: Some(PluginInstance::<T>::connect_port), |
| 220 | + activate: Some(PluginInstance::<T>::activate), |
| 221 | + run: Some(PluginInstance::<T>::run), |
| 222 | + deactivate: Some(PluginInstance::<T>::deactivate), |
| 223 | + cleanup: Some(PluginInstance::<T>::cleanup), |
| 224 | + extension_data: Some(PluginInstance::<T>::extension_data), |
| 225 | + }; |
| 226 | +} |
0 commit comments