lv2_core/plugin/mod.rs
1//! Types to create plugins.
2pub(crate) mod info;
3
4pub use info::PluginInfo;
5pub use lv2_core_derive::*;
6
7use crate::feature::*;
8use crate::port::*;
9use std::any::Any;
10use std::ffi::c_void;
11use std::os::raw::c_char;
12use sys::LV2_Handle;
13use urid::{Uri, UriBound};
14
15/// The central trait to describe LV2 plugins.
16///
17/// This trait and the structs that implement it are the centre of every plugin project, since it hosts the `run` method. This method is called by the host for every processing cycle.
18///
19/// However, the host will not directly talk to the plugin. Instead, it will create and talk to the [`PluginInstance`](struct.PluginInstance.html), which dereferences raw pointers, does safety checks and then calls the corresponding plugin methods. However, it guarantees that a valid `sys::LV2_Handle` is always a valid `*mut MyPlugin`, where `MyPlugin` is your plugin's name.
20pub trait Plugin: UriBound + Sized + Send + Sync + 'static {
21 /// The type of the port collection.
22 type Ports: PortCollection;
23
24 /// The host features used by this plugin in the "Initialization" thread class.
25 ///
26 /// This collection will be created by the framework when the plugin is initialized and every
27 /// method in the "Initialization" threading class has access to it via a mutable reference.
28 ///
29 /// If a host feature is missing, the plugin creation simply fails and your plugin host will tell you so. However, this collection may only contain features that are usable in the "Initialization" thread class. Otherwise, the backend may panic during initialization. Please consult each feature's documentation.
30 type InitFeatures: FeatureCollection<'static>;
31
32 /// The host features used by this plugin in the "Audio" thread class.
33 ///
34 /// This collection will be created by the framework when the plugin is initialized and every
35 /// method in the "Audio" threading class has access to it via a mutable reference.
36 ///
37 /// If a host feature is missing, the plugin creation simply fails and your plugin host will tell you so. However, this collection may only contain features that are usable in the "Audio" thread class. Otherwise, the backend may panic during initialization. Please consult each feature's documentation.
38 type AudioFeatures: FeatureCollection<'static>;
39
40 /// Create a new plugin instance.
41 ///
42 /// This method only creates an instance of the plugin, it does not reset or set up it's internal state. This is done by the `activate` method.
43 fn new(plugin_info: &PluginInfo, features: &mut Self::InitFeatures) -> Option<Self>;
44
45 /// Run a processing step.
46 ///
47 /// The host will always call this method after `active` has been called and before `deactivate` has been called.
48 ///
49 /// The sample count is the number of frames covered by this `run` call. Audio and CV ports will contain exactly `sample_count` frames. Please note that `sample_count` may be differ between calls.
50 fn run(
51 &mut self,
52 ports: &mut Self::Ports,
53 features: &mut Self::AudioFeatures,
54 sample_count: u32,
55 );
56
57 /// Reset and initialize the complete internal state of the plugin.
58 ///
59 /// This method will be called if the plugin has just been created of if the plugin has been deactivated. Also, a host's `activate` call will be as close as possible to the first `run` call.
60 fn activate(&mut self, _features: &mut Self::InitFeatures) {}
61
62 /// Deactivate the plugin.
63 ///
64 /// The host will always call this method when it wants to shut the plugin down. After `deactivate` has been called, `run` will not be called until `activate` has been called again.
65 fn deactivate(&mut self, _features: &mut Self::InitFeatures) {}
66
67 /// Return additional, extension-specific data.
68 ///
69 /// Sometimes, the methods from the `Plugin` trait aren't enough to support additional LV2 specifications. For these cases, extension exist. In most cases and for Rust users, an extension is simply a trait that can be implemented for a plugin.
70 ///
71 /// However, these implemented methods must be passed to the host. This is where this method comes into play: The host will call it with a URI for an extension. Then, it is the plugin's responsibilty to return the extension data to the host.
72 ///
73 /// In most cases, you can simply use the [`match_extensions`](../macro.match_extensions.html) macro to generate an appropiate method body.
74 fn extension_data(_uri: &Uri) -> Option<&'static dyn Any> {
75 None
76 }
77}
78
79/// Plugin wrapper which translated between the host and the plugin.
80///
81/// 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.
82///
83/// 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`.
84#[repr(C)]
85pub struct PluginInstance<T: Plugin> {
86 /// The plugin instance.
87 instance: T,
88 /// A temporary storage for all ports of the plugin.
89 connections: <T::Ports as PortCollection>::Cache,
90 /// All features that may be used in the initialization threading class.
91 init_features: T::InitFeatures,
92 /// All features that may be used in the audio threading class.
93 audio_features: T::AudioFeatures,
94}
95
96impl<T: Plugin> PluginInstance<T> {
97 /// Try to create a port collection from the currently collected connections.
98 ///
99 /// # Safety
100 ///
101 /// 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.
102 pub unsafe fn ports(&self, sample_count: u32) -> Option<T::Ports> {
103 <T::Ports as PortCollection>::from_connections(&self.connections, sample_count)
104 }
105
106 /// Instantiate the plugin.
107 ///
108 /// This method provides a required method for the C interface of a plugin and is used by the `lv2_descriptors` macro.
109 ///
110 /// # Safety
111 ///
112 /// This method is unsafe since it derefences multiple raw pointers and is part of the C interface.
113 pub unsafe extern "C" fn instantiate(
114 descriptor: *const sys::LV2_Descriptor,
115 sample_rate: f64,
116 bundle_path: *const c_char,
117 features: *const *const sys::LV2_Feature,
118 ) -> LV2_Handle {
119 // Dereference the descriptor.
120 let descriptor = match descriptor.as_ref() {
121 Some(descriptor) => descriptor,
122 None => {
123 eprintln!("Failed to initialize plugin: Descriptor points to null");
124 return std::ptr::null_mut();
125 }
126 };
127
128 // Dereference the plugin info.
129 let plugin_info = match PluginInfo::from_raw(descriptor, bundle_path, sample_rate) {
130 Ok(info) => info,
131 Err(e) => {
132 eprintln!(
133 "Failed to initialize plugin: Illegal info from host: {:?}",
134 e
135 );
136 return std::ptr::null_mut();
137 }
138 };
139
140 // Collect the supported features.
141 let mut init_features_cache = FeatureCache::from_raw(features);
142 let mut audio_features_cache = init_features_cache.clone();
143
144 let mut init_features = match T::InitFeatures::from_cache(
145 &mut init_features_cache,
146 ThreadingClass::Instantiation,
147 ) {
148 Ok(f) => f,
149 Err(e) => {
150 eprintln!("{}", e);
151 return std::ptr::null_mut();
152 }
153 };
154 let audio_features =
155 match T::AudioFeatures::from_cache(&mut audio_features_cache, ThreadingClass::Audio) {
156 Ok(f) => f,
157 Err(e) => {
158 eprintln!("{}", e);
159 return std::ptr::null_mut();
160 }
161 };
162
163 // Instantiate the plugin.
164 match T::new(&plugin_info, &mut init_features) {
165 Some(instance) => {
166 let instance = Box::new(Self {
167 instance,
168 connections: <<T::Ports as PortCollection>::Cache as Default>::default(),
169 init_features,
170 audio_features,
171 });
172 Box::leak(instance) as *mut Self as LV2_Handle
173 }
174 None => std::ptr::null_mut(),
175 }
176 }
177
178 /// Clean the plugin.
179 ///
180 /// This method provides a required method for the C interface of a plugin and is used by the `lv2_descriptors` macro.
181 ///
182 /// # Safety
183 ///
184 /// This method is unsafe since it derefences multiple raw pointers and is part of the C interface.
185 pub unsafe extern "C" fn cleanup(instance: *mut c_void) {
186 let instance = instance as *mut Self;
187 Box::from_raw(instance);
188 }
189
190 /// Call `activate`.
191 ///
192 /// This method provides a required method for the C interface of a plugin and is used by the `lv2_descriptors` macro.
193 ///
194 /// # Safety
195 ///
196 /// This method is unsafe since it derefences multiple raw pointers and is part of the C interface.
197 pub unsafe extern "C" fn activate(instance: *mut c_void) {
198 let instance = &mut *(instance as *mut Self);
199 instance.instance.activate(&mut instance.init_features)
200 }
201
202 /// Call `deactivate`.
203 ///
204 /// This method provides a required method for the C interface of a plugin and is used by the `lv2_descriptors` macro.
205 ///
206 /// # Safety
207 ///
208 /// This method is unsafe since it derefences multiple raw pointers and is part of the C interface.
209 pub unsafe extern "C" fn deactivate(instance: *mut c_void) {
210 let instance = &mut *(instance as *mut Self);
211 instance.instance.deactivate(&mut instance.init_features)
212 }
213
214 /// Update a port pointer.
215 ///
216 /// This method provides a required method for the C interface of a plugin and is used by the `lv2_descriptors` macro.
217 ///
218 /// # Safety
219 ///
220 /// This method is unsafe since it derefences multiple raw pointers and is part of the C interface.
221 pub unsafe extern "C" fn connect_port(instance: *mut c_void, port: u32, data: *mut c_void) {
222 let instance = instance as *mut Self;
223 (*instance).connections.connect(port, data)
224 }
225
226 /// Construct a port collection and call the `run` method.
227 ///
228 /// This method provides a required method for the C interface of a plugin and is used by the `lv2_descriptors` macro.
229 ///
230 /// # Safety
231 ///
232 /// This method is unsafe since it derefences multiple raw pointers and is part of the C interface.
233 pub unsafe extern "C" fn run(instance: *mut c_void, sample_count: u32) {
234 let instance = &mut *(instance as *mut Self);
235 if let Some(mut ports) = instance.ports(sample_count) {
236 instance
237 .instance
238 .run(&mut ports, &mut instance.audio_features, sample_count);
239 }
240 }
241
242 /// Dereference the URI, call the `extension_data` function and return the pointer.
243 ///
244 /// This method provides a required method for the C interface of a plugin and is used by the `lv2_descriptors` macro.
245 ///
246 /// # Safety
247 ///
248 /// This method is unsafe since it derefences multiple raw pointers and is part of the C interface.
249 pub unsafe extern "C" fn extension_data(uri: *const c_char) -> *const c_void {
250 let uri = Uri::from_ptr(uri);
251 if let Some(data) = T::extension_data(uri) {
252 data as *const _ as *const c_void
253 } else {
254 std::ptr::null()
255 }
256 }
257
258 /// Retrieve the internal plugin.
259 pub fn plugin_handle(&mut self) -> &mut T {
260 &mut self.instance
261 }
262
263 /// Retrieve the required handles to execute an Initialization class method.
264 ///
265 /// 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.
266 pub fn init_class_handle(&mut self) -> (&mut T, &mut T::InitFeatures) {
267 (&mut self.instance, &mut self.init_features)
268 }
269
270 /// Retrieve the required handles to execute an Audio class method.
271 ///
272 /// 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.
273 pub fn audio_class_handle(&mut self) -> (&mut T, &mut T::AudioFeatures) {
274 (&mut self.instance, &mut self.audio_features)
275 }
276}
277
278#[doc(hidden)]
279pub unsafe trait PluginInstanceDescriptor: Plugin {
280 const DESCRIPTOR: sys::LV2_Descriptor;
281}