clack_host/lib.rs
1#![doc(html_logo_url = "https://raw.githubusercontent.com/prokopyl/clack/main/logo.svg")]
2
3//! A low-level library to create [CLAP](https://github.com/free-audio/clap) audio hosts in safe Rust.
4//!
5//! # Plugin Lifecycle
6//!
7//! CLAP hosts and plugins go through a specific set of steps before they are able to process audio.
8//! The general lifecycle is explained below, with links to more detailed documentation for each
9//! step. Also see the [example](#example) section below for a code snippet presenting how this
10//! lifecycle can be generally implemented using Clack.
11//!
12//! Before starting to deal with the plugins themselves, CLAP hosts need implementations for all the
13//! different callbacks a plugin may use to interact with the host. This is done by implementing the
14//! [`Host`](host::HostHandlers) trait, and its associated sub-types (one for each thread specification).
15//! See the [`host`] module documentation for more information about how to implement the
16//! [`Host`](host::HostHandlers) trait, and on CLAP's thread specifications model.
17//!
18//! 1. CLAP plugins are distributed in dynamic library files. These are prebuilt
19//! dynamically-loaded libraries (with a `.clap` extension), and can contain the implementations
20//! of multiple plugins. They expose an [entry](entry::PluginEntry) that can be loaded and initialized
21//! with the [`PluginEntry::load`](entry::PluginEntry::load) method. See the [`entry`] module
22//! documentation for more information.
23//! 2. Plugins' entry points can expose multiple [factories](factory::Factory). These are singleton
24//! objects that can provide various functionality. The one of main interest is the
25//! [`PluginFactory`](factory::plugin::PluginFactory), which exposes methods to list and instantiate plugins.
26//! It can be retrieved from a [`PluginEntry`](entry::PluginEntry) using the
27//! [`PluginEntry::get_plugin_factory`](entry::PluginEntry::get_plugin_factory) method.
28//!
29//! See the [`factory`] module documentation to learn more about factories.
30//! 3. The [`PluginFactory`](factory::plugin::PluginFactory) can be used to list
31//! [`PluginDescriptor`s](plugin::PluginDescriptor), each of which contains various information
32//! (displayed name, author, etc.) about the plugins included in this entry, including their
33//! unique IDs. These can be displayed in a list for the user to chose from.
34//! 4. The selected plugin's ID can now be used to create a new
35//! [`PluginInstance`](plugin::PluginInstance) using its
36//! [`new`](plugin::PluginInstance::new) method. This is also where the [`Host`](host::HostHandlers)
37//! types come into play, as they need to be ready to handle the plugin instance's callbacks.
38//!
39//! See the [`PluginInstance::new`](plugin::PluginInstance::new) method's documentation for
40//! for more detail.
41//! 5. The plugin instance now needs to be activated for audio processing, using the
42//! [`activate`](plugin::PluginInstance::activate) method. This method receives the current
43//! [`PluginAudioConfiguration`](process::PluginAudioConfiguration), which allows it to
44//! allocate its buffers with proper sizes depending on sample rate, for instance. This is also
45//! where the host should allocate its own audio and event buffers (see
46//! [`EventBuffers`](events::io::EventBuffer) and
47//! [`AudioPorts`](process::audio_buffers::AudioPorts)), and also where the
48//! [`AudioProcessor`](host::HostHandlers::AudioProcessor) type is created to handle audio processing
49//! callbacks.
50//!
51//! If the plugin activation is successful, the plugin's
52//! [`StoppedAudioProcessor`](process::StoppedPluginAudioProcessor) is returned.
53//!
54//! All of this allocation always happens on the main thread. Also, because the allocated buffers
55//! are dependent on the given configuration, plugins have to be deactivated and then
56//! re-activated whenever it changes.
57//! 6. Once the [`StoppedAudioProcessor`](process::StoppedPluginAudioProcessor) is
58//! created and active, we can send it to another thread dedicated to audio processing, while the
59//! main instance type has to stay on the main thread (it is not [`Send`], while the audio
60//! processor is). Once there, all we need to do is to indicate that continuous processing
61//! is about to start, using the
62//! [`start_processing`](process::StoppedPluginAudioProcessor::start_processing)
63//! method, which consumes the
64//! [`StoppedAudioProcessor`](process::StoppedPluginAudioProcessor) and returns a
65//! [`StartedAudioProcessor`](process::StartedPluginAudioProcessor).
66//!
67//! Note that if this pattern of consuming the audio processors is too cumbersome, they can be
68//! converted into a [`PluginAudioProcessor`](process::PluginAudioProcessor) using
69//! the [`Into`] trait, which handles switching between both states using only a `&mut` reference,
70//! at the cost of making the
71//! [`start_processing`](process::PluginAudioProcessor::start_processing),
72//! [`process`](process::StartedPluginAudioProcessor::process), and
73//! [`stop_processing`](process::PluginAudioProcessor::stop_processing) operations
74//! fallible at runtime if the lifecycle isn't properly handled. See the
75//! [`PluginAudioProcessor`](process::PluginAudioProcessor) documentation for more
76//! information.
77//! 7. Perform the processing of a block of audio and events using the
78//! [`process`](process::StartedPluginAudioProcessor::process) method.
79//!
80//! Because CLAP Audio and Event buffers are generic, some cheap, short-lived wrappers around the
81//! audio and event buffers must be crated for each process call, in order to be passed to the
82//! plugin's [`process`](process::StartedPluginAudioProcessor::process) method.
83//!
84//! Those buffer wrappers are [`InputEvents`](events::io::InputEvents) and
85//! [`OutputEvents`](events::io::OutputEvents) for events, and
86//! [`InputAudioBuffers`](process::audio_buffers::InputAudioBuffers) and
87//! [`OutputAudioBuffers`](process::audio_buffers::OutputAudioBuffers) for audio (obtained via a call to
88//! [`AudioPorts::with_input_buffers`](process::audio_buffers::AudioPorts::with_input_buffers) and.
89//! [`AudioPorts::with_output_buffers`](process::audio_buffers::AudioPorts::with_output_buffers)
90//! respectively).
91//!
92//! See the documentation of those buffer types for more detail on what types they support, as
93//! well as the [`process`](process::StartedPluginAudioProcessor::process) method's
94//! documentation for more information.
95//! 8. Once continuous processing has stopped, the host needs to call
96//! [`start_processing`](process::StartedPluginAudioProcessor::stop_processing),
97//! in a similar fashion to Step 6 above.
98//!
99//! 9. The audio processor now has to be sent back to the main thread to be deactivated (in order to
100//! not perform de-allocations in the realtime audio thread).
101//! The [`PluginInstance::deactivate`](plugin::PluginInstance::deactivate) can then be used
102//! to consume the Audio Processor. Only then, the
103//! [`PluginInstance`](plugin::PluginInstance) itself can be dropped to destroy the instance
104//! entirely.
105//!
106//! # Example
107//!
108//! Note that this example isn't a full implementation and many details are left out: it is meant to
109//! give an overview of Clack's APIs and how they relate to a CLAP plugin's lifecycle.
110//!
111//! For more details instruction on how to create a full, correct Clack host implementation, refer
112//! to the submodules' and types' documentation on each topic linked above.
113//!
114//!```rust
115//! use clack_host::events::event_types::*;
116//! use clack_host::prelude::*;
117//!
118//! // Prepare our (extremely basic) host implementation
119//!
120//! struct MyHostShared;
121//!
122//! impl<'a> SharedHandler<'a> for MyHostShared {
123//! /* ... */
124//! # fn request_restart(&self) { unimplemented!() }
125//! # fn request_process(&self) { unimplemented!() }
126//! # fn request_callback(&self) { unimplemented!() }
127//! }
128//!
129//! struct MyHost;
130//! impl HostHandlers for MyHost {
131//! type Shared<'a> = MyHostShared;
132//!
133//! type MainThread<'a> = ();
134//! type AudioProcessor<'a> = ();
135//! }
136//! # pub fn main() -> Result<(), Box<dyn std::error::Error>> {
137//!
138//! // Information about our totally legit host.
139//! let host_info = HostInfo::new("Legit Studio", "Legit Ltd.", "https://example.com", "4.3.2")?;
140//!
141//! // Step 1: Load the entry in memory.
142//! # mod diva { include!("./entry/diva_stub.rs"); }
143//! # let entry = PluginEntry::load_from_clack::<diva::Entry>(c"/home/user/.clap/u-he/libdiva.so")?;
144//! # /*
145//! let entry = PluginEntry::load("/home/user/.clap/u-he/libdiva.so")?;
146//! # */
147//!
148//! // Step 2: Get the Plugin factory of this entry.
149//! let plugin_factory = entry.get_plugin_factory().unwrap();
150//!
151//! // Step 3: Find the descriptor of plugin we're interested in.
152//! let plugin_descriptor = plugin_factory.plugin_descriptors()
153//! // We're assuming this specific plugin is in this entry for this example.
154//! // A real host would store all descriptors in a list and show them to the user.
155//! .find(|d| d.id().unwrap().to_bytes() == b"com.u-he.diva")
156//! .unwrap();
157//!
158//! // Let's check we are indeed loading the right plugin.
159//! assert_eq!(plugin_descriptor.name().unwrap().to_bytes(), b"Diva");
160//!
161//! // Step 4: Create the plugin instance
162//! let mut plugin_instance = PluginInstance::<MyHost>::new(
163//! |_| MyHostShared,
164//! |_| (),
165//! &entry,
166//! plugin_descriptor.id().unwrap(),
167//! &host_info
168//! )?;
169//!
170//! // Step 5: Activate the plugin (allocating the audio processor),
171//! // and allocate associated buffers.
172//!
173//! // In this example, we will only process 4 samples at a time
174//! let audio_configuration = PluginAudioConfiguration {
175//! sample_rate: 48_000.0,
176//! min_frames_count: 4,
177//! max_frames_count: 4,
178//! };
179//! let audio_processor = plugin_instance.activate(|_, _| (), audio_configuration)?;
180//!
181//! // Event buffers
182//! // For this example, we'll only have a single input event.
183//! let note_on_event = NoteOnEvent::new(0, Pckn::new(0u16, 0u16, 12u16, 60u32), 4.2);
184//! let input_events_buffer = [note_on_event];
185//! let mut output_events_buffer = EventBuffer::new();
186//!
187//! // Audio buffers
188//! let mut input_audio_buffers = [[0.0f32; 4]; 2]; // 2 channels (stereo), 1 port
189//! let mut output_audio_buffers = [[0.0f32; 4]; 2];
190//!
191//! // Audio port buffers
192//! let mut input_ports = AudioPorts::with_capacity(2, 1); // 2 channels (stereo), 1 port
193//! let mut output_ports = AudioPorts::with_capacity(2, 1);
194//!
195//! // Let's send the audio processor to a dedicated audio processing thread.
196//! let audio_processor = std::thread::scope(|s| s.spawn(|| {
197//! // Step 6: Start the audio processor.
198//! let mut audio_processor = audio_processor.start_processing().unwrap();
199//!
200//! // Step 7: Process audio and events.
201//! // Borrow all buffers, and wrap them in cheap CLAP-compatible structs.
202//! let input_events = InputEvents::from_buffer(&input_events_buffer);
203//! let mut output_events = OutputEvents::from_buffer(&mut output_events_buffer);
204//!
205//! let mut input_audio = input_ports.with_input_buffers([AudioPortBuffer {
206//! latency: 0,
207//! // We only use F32 (32-bit floating point) audio
208//! channels: AudioPortBufferType::f32_input_only(
209//! // These buffers can be marked constant, as they only contain zeros
210//! input_audio_buffers.iter_mut().map(|b| InputChannel::constant(b))
211//! )
212//! }]);
213//!
214//! let mut output_audio = output_ports.with_output_buffers([AudioPortBuffer {
215//! latency: 0,
216//! channels: AudioPortBufferType::f32_output_only(
217//! output_audio_buffers.iter_mut().map(|b| b.as_mut_slice())
218//! )
219//! }]);
220//!
221//! // Finally do the processing itself.
222//! let status = audio_processor.process(
223//! &input_audio,
224//! &mut output_audio,
225//! &input_events,
226//! &mut output_events,
227//! None,
228//! None
229//! ).unwrap();
230//!
231//! // This plugin has finished processing and requests to be put to sleep.
232//! assert_eq!(status, ProcessStatus::Sleep);
233//!
234//! // Step 8: if requested by the plugin or required to by the host or user, stop processing
235//! let audio_processor = audio_processor.stop_processing();
236//!
237//! // Send the audio processor back to be deallocated by the main thread.
238//! audio_processor
239//! }).join().unwrap());
240//!
241//! // A real host implementation might forward the results to an output device,
242//! // or pass it along a processing chain.
243//! // Here, we are only going to check the results manually.
244//!
245//!
246//! // The raw audio for each channel
247//! assert_eq!(&[42.0f32, 69.0, 21.0, 34.5], &output_audio_buffers[0]);
248//! assert_eq!(&[42.0f32, 69.0, 21.0, 34.5], &output_audio_buffers[1]);
249//!
250//! // The input note event has been passed through
251//! assert_eq!(output_events_buffer.get(0).unwrap(), ¬e_on_event);
252//! assert_eq!(output_events_buffer.len(), 1);
253//!
254//! // Step 9: stop and deactivate the plugin.
255//!
256//! plugin_instance.deactivate(audio_processor);
257//! # Ok(()) }
258//! ```
259
260pub mod entry;
261pub mod extensions;
262pub use clack_common::factory;
263pub mod host;
264pub mod plugin;
265pub mod process;
266mod util;
267
268pub use clack_common::events;
269pub use clack_common::stream;
270pub use clack_common::utils;
271
272/// A helpful prelude re-exporting all the types related to host implementation.
273pub mod prelude {
274 pub use crate::{
275 entry::PluginEntry,
276 events::{
277 Event, EventHeader, Pckn, UnknownEvent,
278 io::{EventBuffer, InputEvents, OutputEvents},
279 },
280 host::{
281 AudioProcessorHandler, HostError, HostExtensions, HostHandlers, HostInfo,
282 MainThreadHandler, SharedHandler,
283 },
284 plugin::{
285 InitializedPluginHandle, InitializingPluginHandle, PluginAudioProcessorHandle,
286 PluginInstance, PluginInstanceError, PluginMainThreadHandle, PluginSharedHandle,
287 },
288 process::{
289 AudioPortProcessingInfo, PluginAudioConfiguration, PluginAudioProcessor, ProcessStatus,
290 StartedPluginAudioProcessor, StoppedPluginAudioProcessor,
291 audio_buffers::{
292 AudioPortBuffer, AudioPortBufferType, AudioPorts, InputAudioBuffers, InputChannel,
293 OutputAudioBuffers,
294 },
295 },
296 utils::ClapId,
297 };
298}
299
300#[doc = include_str!("../README.md")]
301const _README_TEST: () = {};