Skip to main content

nice_plug/wrapper/clap/
wrapper.rs

1use atomic_refcell::{AtomicRefCell, AtomicRefMut};
2use clap_sys::events::{
3    CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_IS_LIVE, CLAP_EVENT_MIDI, CLAP_EVENT_MIDI_SYSEX,
4    CLAP_EVENT_NOTE_CHOKE, CLAP_EVENT_NOTE_EXPRESSION, CLAP_EVENT_NOTE_OFF, CLAP_EVENT_NOTE_ON,
5    CLAP_EVENT_PARAM_GESTURE_BEGIN, CLAP_EVENT_PARAM_GESTURE_END, CLAP_EVENT_PARAM_MOD,
6    CLAP_EVENT_PARAM_VALUE, CLAP_EVENT_TRANSPORT, CLAP_NOTE_EXPRESSION_BRIGHTNESS,
7    CLAP_NOTE_EXPRESSION_EXPRESSION, CLAP_NOTE_EXPRESSION_PAN, CLAP_NOTE_EXPRESSION_PRESSURE,
8    CLAP_NOTE_EXPRESSION_TUNING, CLAP_NOTE_EXPRESSION_VIBRATO, CLAP_NOTE_EXPRESSION_VOLUME,
9    CLAP_TRANSPORT_HAS_BEATS_TIMELINE, CLAP_TRANSPORT_HAS_SECONDS_TIMELINE,
10    CLAP_TRANSPORT_HAS_TEMPO, CLAP_TRANSPORT_HAS_TIME_SIGNATURE, CLAP_TRANSPORT_IS_LOOP_ACTIVE,
11    CLAP_TRANSPORT_IS_PLAYING, CLAP_TRANSPORT_IS_RECORDING, CLAP_TRANSPORT_IS_WITHIN_PRE_ROLL,
12    clap_event_header, clap_event_midi, clap_event_midi_sysex, clap_event_note,
13    clap_event_note_expression, clap_event_param_gesture, clap_event_param_mod,
14    clap_event_param_value, clap_event_transport, clap_input_events, clap_output_events,
15};
16use clap_sys::ext::audio_ports::{
17    CLAP_AUDIO_PORT_IS_MAIN, CLAP_EXT_AUDIO_PORTS, CLAP_PORT_MONO, CLAP_PORT_STEREO,
18    clap_audio_port_info, clap_plugin_audio_ports,
19};
20use clap_sys::ext::audio_ports_config::{
21    CLAP_EXT_AUDIO_PORTS_CONFIG, clap_audio_ports_config, clap_plugin_audio_ports_config,
22};
23use clap_sys::ext::gui::CLAP_EXT_GUI;
24#[cfg(feature = "editor")]
25use clap_sys::ext::gui::{clap_host_gui, clap_plugin_gui};
26use clap_sys::ext::latency::{CLAP_EXT_LATENCY, clap_host_latency, clap_plugin_latency};
27use clap_sys::ext::note_ports::{
28    CLAP_EXT_NOTE_PORTS, CLAP_NOTE_DIALECT_CLAP, CLAP_NOTE_DIALECT_MIDI, clap_note_port_info,
29    clap_plugin_note_ports,
30};
31use clap_sys::ext::params::{
32    CLAP_EXT_PARAMS, CLAP_PARAM_IS_AUTOMATABLE, CLAP_PARAM_IS_BYPASS, CLAP_PARAM_IS_HIDDEN,
33    CLAP_PARAM_IS_MODULATABLE, CLAP_PARAM_IS_MODULATABLE_PER_NOTE_ID, CLAP_PARAM_IS_READONLY,
34    CLAP_PARAM_IS_STEPPED, CLAP_PARAM_RESCAN_VALUES, clap_host_params, clap_param_info,
35    clap_plugin_params,
36};
37use clap_sys::ext::remote_controls::{
38    CLAP_EXT_REMOTE_CONTROLS, clap_plugin_remote_controls, clap_remote_controls_page,
39};
40use clap_sys::ext::render::{
41    CLAP_EXT_RENDER, CLAP_RENDER_OFFLINE, CLAP_RENDER_REALTIME, clap_plugin_render,
42    clap_plugin_render_mode,
43};
44use clap_sys::ext::state::{CLAP_EXT_STATE, clap_plugin_state};
45use clap_sys::ext::tail::{CLAP_EXT_TAIL, clap_plugin_tail};
46use clap_sys::ext::thread_check::{CLAP_EXT_THREAD_CHECK, clap_host_thread_check};
47use clap_sys::ext::track_info::CLAP_EXT_TRACK_INFO;
48#[cfg(feature = "editor")]
49use clap_sys::ext::track_info::{
50    CLAP_TRACK_INFO_HAS_TRACK_COLOR, CLAP_TRACK_INFO_HAS_TRACK_NAME, clap_host_track_info,
51    clap_plugin_track_info, clap_track_info,
52};
53use clap_sys::ext::voice_info::{
54    CLAP_EXT_VOICE_INFO, CLAP_VOICE_INFO_SUPPORTS_OVERLAPPING_NOTES, clap_host_voice_info,
55    clap_plugin_voice_info, clap_voice_info,
56};
57use clap_sys::fixedpoint::{CLAP_BEATTIME_FACTOR, CLAP_SECTIME_FACTOR};
58use clap_sys::host::clap_host;
59use clap_sys::id::{CLAP_INVALID_ID, clap_id};
60use clap_sys::plugin::clap_plugin;
61use clap_sys::process::{
62    CLAP_PROCESS_CONTINUE, CLAP_PROCESS_CONTINUE_IF_NOT_QUIET, CLAP_PROCESS_ERROR, clap_process,
63    clap_process_status,
64};
65use clap_sys::stream::{clap_istream, clap_ostream};
66use crossbeam::atomic::AtomicCell;
67use crossbeam::channel::{self, SendTimeoutError};
68use crossbeam::queue::ArrayQueue;
69use nice_plug_core::audio_setup::{AudioIOLayout, AuxiliaryBuffers, BufferConfig, ProcessMode};
70#[cfg(feature = "editor")]
71use nice_plug_core::context::gui::GuiContext;
72use nice_plug_core::context::process::Transport;
73#[cfg(feature = "editor")]
74use nice_plug_core::editor::{Editor, SpawnedEditor};
75use nice_plug_core::midi::{Channel, Key, MidiConfig, NoteEvent, PluginNoteEvent, VoiceID};
76use nice_plug_core::params::internals::ParamPtr;
77use nice_plug_core::params::{ParamFlags, Params};
78use nice_plug_core::plugin::{Plugin, PluginState, ProcessStatus, TaskExecutor};
79#[cfg(feature = "editor")]
80use nice_plug_core::plugin::{TrackColor, TrackInfo};
81use parking_lot::Mutex;
82#[cfg(feature = "editor")]
83use std::borrow::Borrow;
84use std::collections::{HashMap, HashSet, VecDeque};
85use std::ffi::{CStr, c_void};
86use std::mem;
87use std::num::NonZeroU32;
88use std::os::raw::c_char;
89use std::ptr::NonNull;
90use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
91use std::sync::{Arc, Weak};
92use std::thread::{self, ThreadId};
93use std::time::{Duration, Instant};
94use try_lock::TryLock;
95
96use super::context::{WrapperActivateContext, WrapperProcessContext};
97use super::descriptor::PluginDescriptor;
98use super::util::ClapPtr;
99use crate::event_loop::{BackgroundThread, EventLoop, MainThreadExecutor, TASK_QUEUE_CAPACITY};
100use crate::util::permit_alloc;
101use crate::wrapper::clap::ClapPlugin;
102use crate::wrapper::clap::context::RemoteControlPages;
103#[cfg(feature = "editor")]
104use crate::wrapper::clap::context::WrapperGuiContext;
105use crate::wrapper::clap::util::{read_stream, write_stream};
106use crate::wrapper::state::{self};
107use crate::wrapper::util::buffer_management::{BufferManager, ChannelPointers};
108use crate::wrapper::util::{clamp_input_event_timing, hash_param_id, process_wrapper, strlcpy};
109
110/// How many output parameter changes we can store in our output parameter change queue. Storing
111/// more than this many parameters at a time will cause changes to get lost.
112const OUTPUT_EVENT_QUEUE_CAPACITY: usize = 2048;
113
114/// Protect against OOM issues when loading malformed state.
115///
116/// If your plugin needs more storgage space than this, please post an issue in the nice-plug
117/// repository.
118const MAX_STATE_BYTES: u64 = 268_435_456;
119
120pub struct Wrapper<P: ClapPlugin> {
121    /// A reference to this object, upgraded to an `Arc<Self>` for the GUI context.
122    this: AtomicRefCell<Weak<Self>>,
123
124    /// The wrapped plugin instance.
125    plugin: TryLock<P>,
126    /// The plugin's background task executor closure.
127    pub task_executor: Mutex<TaskExecutor<P>>,
128    /// The plugin's parameters. These are fetched once during initialization. That way the
129    /// `ParamPtr`s are guaranteed to live at least as long as this object and we can interact with
130    /// the `Params` object without having to acquire a lock on `plugin`.
131    params: Arc<dyn Params>,
132    /// The plugin's editor, if it has one. This object does not do anything on its own, but we need
133    /// to instantiate this in advance so we don't need to lock the entire [`Plugin`] object when
134    /// creating an editor. Wrapped in an `AtomicRefCell` because it needs to be initialized late.
135    #[cfg(feature = "editor")]
136    editor: AtomicRefCell<Option<Mutex<P::Editor>>>,
137    /// A handle for the currently active editor instance. The plugin should implement `Drop` on
138    /// this handle for its closing behavior.
139    #[cfg(feature = "editor")]
140    #[allow(clippy::type_complexity)]
141    editor_window:
142        AtomicRefCell<Option<fragile::Fragile<SpawnedEditor<<P::Editor as Editor>::Handle>>>>,
143    /// The DPI scaling factor as passed to the [IPlugViewContentScaleSupport::set_scale_factor()]
144    /// function. Defaults to 1.0, and will be kept there on macOS. When reporting and handling size
145    /// the sizes communicated to and from the DAW should be scaled by this factor since nice-plug's
146    /// APIs only deal in logical pixels.
147    #[cfg(feature = "editor")]
148    fallback_scale_factor: AtomicCell<Option<f64>>,
149    is_activated: AtomicBool,
150    is_processing: AtomicBool,
151    /// The current IO configuration, modified through the `clap_plugin_audio_ports_config`
152    /// extension. Initialized to the plugin's first audio IO configuration.
153    current_audio_io_layout: AtomicCell<AudioIOLayout>,
154    /// The current buffer configuration, containing the sample rate and the maximum block size.
155    /// Will be set in `clap_plugin::activate()`.
156    current_buffer_config: AtomicCell<Option<BufferConfig>>,
157    /// The current audio processing mode. Set through the render extension. Defaults to realtime.
158    pub current_process_mode: AtomicCell<ProcessMode>,
159    /// The incoming events for the plugin, if `P::MIDI_INPUT` is set to `MidiConfig::Basic` or
160    /// higher.
161    ///
162    /// TODO: Maybe load these lazily at some point instead of needing to spool them all to this
163    ///       queue first
164    input_events: AtomicRefCell<VecDeque<PluginNoteEvent<P>>>,
165    /// The last process status returned by the plugin. This is used for tail handling.
166    last_process_status: AtomicCell<ProcessStatus>,
167    /// Whether the latency has changed since the last call to `activate`. When this is set,
168    /// `latency_changed` needs to be called in `activate` in order to inform the host of the
169    /// latency change.
170    latency_changed: AtomicBool,
171    /// The current latency in samples, as set by the plugin through the
172    /// [`ProcessContext`](nice_plug_core::context::process::ProcessContext). Uses the latency
173    /// extension.
174    pub current_latency: AtomicU32,
175    /// A data structure that helps manage and create buffers for all of the plugin's inputs and
176    /// outputs based on channel pointers provided by the host.
177    buffer_manager: AtomicRefCell<BufferManager>,
178    /// The plugin is able to restore state through a method on the `GuiContext`. To avoid changing
179    /// parameters mid-processing and running into garbled data if the host also tries to load state
180    /// at the same time the restoring happens at the end of each processing call. If this zero
181    /// capacity channel contains state data at that point, then the audio thread will take the
182    /// state out of the channel, restore the state, and then send it back through the same channel.
183    /// In other words, the GUI thread acts as a sender and then as a receiver, while the audio
184    /// thread acts as a receiver and then as a sender. That way deallocation can happen on the GUI
185    /// thread. All of this happens without any blocking on the audio thread.
186    updated_state_sender: channel::Sender<PluginState>,
187    /// The receiver belonging to [`new_state_sender`][Self::new_state_sender].
188    updated_state_receiver: channel::Receiver<PluginState>,
189
190    // We'll query all of the host's extensions upfront
191    host_callback: ClapPtr<clap_host>,
192
193    clap_plugin_audio_ports_config: clap_plugin_audio_ports_config,
194
195    // The main `clap_plugin` vtable. A pointer to this `Wrapper<P>` instance is stored in the
196    // `plugin_data` field. This pointer is set after creating the `Arc<Wrapper<P>>`.
197    pub clap_plugin: AtomicRefCell<clap_plugin>,
198    /// Needs to be boxed because the plugin object is supposed to contain a static reference to
199    /// this.
200    _plugin_descriptor: Box<PluginDescriptor>,
201
202    clap_plugin_audio_ports: clap_plugin_audio_ports,
203
204    #[cfg(feature = "editor")]
205    clap_plugin_gui: clap_plugin_gui,
206    #[cfg(feature = "editor")]
207    host_gui: AtomicRefCell<Option<ClapPtr<clap_host_gui>>>,
208
209    clap_plugin_latency: clap_plugin_latency,
210    host_latency: AtomicRefCell<Option<ClapPtr<clap_host_latency>>>,
211
212    clap_plugin_note_ports: clap_plugin_note_ports,
213
214    clap_plugin_params: clap_plugin_params,
215    host_params: AtomicRefCell<Option<ClapPtr<clap_host_params>>>,
216    // These fields are exactly the same as their VST3 wrapper counterparts.
217    //
218    /// The keys from `param_map` in a stable order.
219    param_hashes: Vec<u32>,
220    // TODO: Merge the three `*_by_hash` hashmaps at some point
221    /// A mapping from parameter ID hashes (obtained from the string parameter IDs) to pointers to
222    /// parameters belonging to the plugin. These addresses will remain stable as long as the
223    /// `params` object does not get deallocated.
224    param_by_hash: HashMap<u32, ParamPtr>,
225    /// Mappings from parameter hashes to string parameter IDs. Used for notifying the plugin's
226    /// editor about parameter changes.
227    param_id_by_hash: HashMap<u32, String>,
228    /// The group name of a parameter, indexed by the parameter's hash. Nested groups are delimited
229    /// by slashes, and they're only used to allow the DAW to display parameters in a tree
230    /// structure.
231    param_group_by_hash: HashMap<u32, String>,
232    /// Mappings from string parameter identifiers to parameter hashes. Useful for debug logging
233    /// and when storing and restoring plugin state.
234    param_id_to_hash: HashMap<String, u32>,
235    /// The inverse mapping from `param_by_hash`. This is needed to be able to have an ergonomic
236    /// parameter setting API that uses references to the parameters instead of having to add a
237    /// setter function to the parameter (or even worse, have it be completely
238    /// untyped).
239    pub param_ptr_to_hash: HashMap<ParamPtr, u32>,
240    /// For all polyphonically modulatable parameters, mappings from the parameter hash's hash to
241    /// the parameter's poly modulation ID. These IDs are then passed to the plugin, so it can
242    /// quickly refer to parameter by matching on constant IDs.
243    poly_mod_ids_by_hash: HashMap<u32, u32>,
244    /// A queue of parameter changes and gestures that should be output in either the next process
245    /// call or in the next parameter flush.
246    ///
247    /// XXX: There's no guarantee that a single parameter doesn't occur twice in this queue, but
248    ///      even if it does then that should still not be a problem because the host also reads it
249    ///      in the same order, right?
250    output_parameter_events: ArrayQueue<OutputParamEvent>,
251
252    host_thread_check: AtomicRefCell<Option<ClapPtr<clap_host_thread_check>>>,
253
254    clap_plugin_remote_controls: clap_plugin_remote_controls,
255    /// The plugin's remote control pages, if it defines any. Filled when initializing the plugin.
256    remote_control_pages: Vec<clap_remote_controls_page>,
257
258    clap_plugin_render: clap_plugin_render,
259
260    clap_plugin_state: clap_plugin_state,
261
262    clap_plugin_tail: clap_plugin_tail,
263
264    #[cfg(feature = "editor")]
265    clap_plugin_track_info: clap_plugin_track_info,
266    #[cfg(feature = "editor")]
267    host_track_info: AtomicRefCell<Option<ClapPtr<clap_host_track_info>>>,
268    /// The most recently reported track information. Hosts may send partial updates, so this is used
269    /// to merge successive track info queries.
270    #[cfg(feature = "editor")]
271    current_track_info: AtomicRefCell<TrackInfo>,
272
273    clap_plugin_voice_info: clap_plugin_voice_info,
274    host_voice_info: AtomicRefCell<Option<ClapPtr<clap_host_voice_info>>>,
275    /// If `P::CLAP_POLY_MODULATION_CONFIG` is set, then the plugin can configure the current number
276    /// of active voices using a context method called from the initialization or processing
277    /// context. This defaults to the maximum number of voices.
278    current_voice_capacity: AtomicU32,
279
280    /// A queue of tasks that still need to be performed. Because CLAP lets the plugin request a
281    /// host callback directly, we don't need to use the OsEventLoop we use in our other plugin
282    /// implementations. Instead, we'll post tasks to this queue, ask the host to call
283    /// [`on_main_thread()`][Self::on_main_thread()] on the main thread, and then continue to pop
284    /// tasks off this queue there until it is empty.
285    tasks: ArrayQueue<Task<P>>,
286    /// The ID of the main thread. In practice this is the ID of the thread that created this
287    /// object. If the host supports the thread check extension (and
288    /// [`host_thread_check`][Self::host_thread_check] thus contains a value), then that extension
289    /// is used instead.
290    main_thread_id: ThreadId,
291    /// A background thread for running tasks independently from the host'main GUI thread. Useful
292    /// for longer, blocking tasks. Initialized later as it needs a reference to the wrapper.
293    background_thread: AtomicRefCell<Option<BackgroundThread<Task<P>, Self>>>,
294}
295
296/// Tasks that can be sent from the plugin to be executed on the main thread in a non-blocking
297/// realtime-safe way. Instead of using a random thread or the OS' event loop like in the Linux
298/// implementation, this uses [`clap_host::request_callback()`] instead.
299#[allow(clippy::enum_variant_names)]
300pub enum Task<P: Plugin> {
301    /// Execute one of the plugin's background tasks.
302    PluginTask(P::BackgroundTask),
303    /// Inform the plugin that one parameter's value has changed. This uses the parameter hashes
304    /// since the task will be created from the audio thread.
305    #[cfg(feature = "editor")]
306    ParameterValueChanged(u32, f32),
307    /// Inform the plugin that one parameter's modulation offset has changed. This uses the
308    /// parameter hashes since the task will be created from the audio thread.
309    #[cfg(feature = "editor")]
310    ParameterModulationChanged(u32, f32),
311    StateChanged,
312    /// Inform the host that the latency has changed.
313    LatencyChanged,
314    /// Inform the host that the voice info has changed.
315    VoiceInfoChanged,
316    /// Tell the host that it should rescan the current parameter values.
317    RescanParamValues,
318}
319
320/// The types of CLAP parameter updates for events.
321pub enum ClapParamUpdate {
322    /// Set the parameter to this plain value. In our wrapper the plain values are the normalized
323    /// values multiplied by the step count for discrete parameters.
324    PlainValueSet(f64),
325    /// Set a normalized offset for the parameter's plain value. Subsequent modulation events
326    /// override the previous one, but `PlainValueSet`s do not override the existing modulation.
327    /// These values should also be divided by the step size.
328    PlainValueMod(f64),
329}
330
331/// A parameter event that should be output by the plugin, stored in a queue on the wrapper and
332/// written to the host either at the end of the process function or during a flush.
333#[derive(Debug, Clone)]
334pub enum OutputParamEvent {
335    /// Begin an automation gesture. This must always be sent before sending [`SetValue`].
336    BeginGesture { param_hash: u32 },
337    /// Change the value of a parameter using a plain CLAP value, aka the normalized value
338    /// multiplied by the number of steps.
339    SetValue {
340        /// The internal hash for the parameter.
341        param_hash: u32,
342        /// The 'plain' value as reported to CLAP. This is the normalized value multiplied by
343        /// [`params::step_size()`][crate::params::step_size()].
344        clap_plain_value: f64,
345    },
346    /// Begin an automation gesture. This must always be sent after sending one or more [`SetValue`]
347    /// events.
348    EndGesture { param_hash: u32 },
349}
350
351/// Because CLAP has this [`clap_host::request_host_callback()`] function, we don't need to use
352/// `OsEventLoop` and can instead just request a main thread callback directly.
353impl<P: ClapPlugin> EventLoop<Task<P>, Wrapper<P>> for Wrapper<P> {
354    fn new_and_spawn(_executor: Weak<Self>) -> Self {
355        panic!("What are you doing");
356    }
357
358    fn schedule_gui(&self, task: Task<P>) -> bool {
359        if self.is_main_thread() {
360            self.execute(task, true);
361            true
362        } else {
363            let success = self.tasks.push(task).is_ok();
364            if success {
365                // CLAP lets us use the host's event loop instead of having to implement our own
366                let host = &self.host_callback;
367                unsafe_clap_call! { host=>request_callback(&**host) };
368            }
369
370            success
371        }
372    }
373
374    fn schedule_background(&self, task: Task<P>) -> bool {
375        self.background_thread
376            .borrow()
377            .as_ref()
378            .unwrap()
379            .schedule(task)
380    }
381
382    fn is_main_thread(&self) -> bool {
383        // If the host supports the thread check interface then we'll use that, otherwise we'll
384        // check if this is the same thread as the one that created the plugin instance.
385        match &*self.host_thread_check.borrow() {
386            Some(thread_check) => {
387                unsafe_clap_call! { thread_check=>is_main_thread(&*self.host_callback) }
388            }
389            // FIXME: `thread::current()` may allocate the first time it's called, is there a safe
390            //        non-allocating version of this without using huge OS-specific libraries?
391            None => permit_alloc(|| thread::current().id() == self.main_thread_id),
392        }
393    }
394}
395
396impl<P: ClapPlugin> MainThreadExecutor<Task<P>> for Wrapper<P> {
397    fn execute(&self, task: Task<P>, is_gui_thread: bool) {
398        // This function is always called from the main thread, from [Self::on_main_thread].
399        match task {
400            Task::PluginTask(task) => (self.task_executor.lock())(task),
401            #[cfg(feature = "editor")]
402            Task::ParameterValueChanged(param_hash, normalized_value) => {
403                use nice_plug_core::editor::EditorHandle;
404
405                if let Some(window) = self.editor_window.borrow().as_ref() {
406                    let param_id = &self.param_id_by_hash[&param_hash];
407                    window
408                        .get()
409                        .handle
410                        .param_value_changed(param_id, normalized_value);
411                }
412            }
413            Task::StateChanged => {
414                #[cfg(feature = "editor")]
415                {
416                    use nice_plug_core::editor::EditorHandle;
417                    if let Some(window) = self.editor_window.borrow().as_ref() {
418                        window.get().handle.state_changed();
419                    }
420                }
421
422                if let Some(host_params) = &*self.host_params.borrow() {
423                    crate::nice_debug_assert!(is_gui_thread);
424                    unsafe_clap_call! { host_params=>rescan(&*self.host_callback, CLAP_PARAM_RESCAN_VALUES) };
425                }
426            }
427            #[cfg(feature = "editor")]
428            Task::ParameterModulationChanged(param_hash, modulation_offset) => {
429                use nice_plug_core::editor::EditorHandle;
430
431                if let Some(window) = self.editor_window.borrow().as_ref() {
432                    let param_id = &self.param_id_by_hash[&param_hash];
433                    window
434                        .get()
435                        .handle
436                        .param_modulation_changed(param_id, modulation_offset);
437                }
438            }
439            Task::LatencyChanged => match &*self.host_latency.borrow() {
440                Some(host_latency) => {
441                    crate::nice_debug_assert!(is_gui_thread);
442
443                    // The plugin needs to be deactivated in order for the latency to change. If
444                    // it's already deactivated we can notify the host immediately, otherwise we
445                    // need to request a restart and remember to notify the host of the latency
446                    // change in the `activate` function.
447                    //
448                    // In practice, ignoring the activation status would be fine for many hosts, but
449                    // following the specification is probably a good idea regardless :)
450                    if self.is_activated.load(Ordering::SeqCst) {
451                        self.latency_changed.store(true, Ordering::SeqCst);
452                        self.request_restart();
453                    } else {
454                        unsafe_clap_call! { host_latency=>changed(&*self.host_callback) };
455                    }
456                }
457                None => {
458                    #[cfg(debug_assertions)]
459                    crate::nice_warn!("Host does not support the latency extension");
460                }
461            },
462            Task::VoiceInfoChanged => match &*self.host_voice_info.borrow() {
463                Some(host_voice_info) => {
464                    crate::nice_debug_assert!(is_gui_thread);
465                    unsafe_clap_call! { host_voice_info=>changed(&*self.host_callback) };
466                }
467                None => {
468                    #[cfg(debug_assertions)]
469                    crate::nice_warn!("Host does not support the voice-info extension");
470                }
471            },
472            Task::RescanParamValues => match &*self.host_params.borrow() {
473                Some(host_params) => {
474                    crate::nice_debug_assert!(is_gui_thread);
475                    unsafe_clap_call! { host_params=>rescan(&*self.host_callback, CLAP_PARAM_RESCAN_VALUES) };
476                }
477                None => {
478                    #[cfg(debug_assertions)]
479                    crate::nice_warn!("Host does not support the parameter extension");
480                }
481            },
482        };
483    }
484}
485
486impl<P: ClapPlugin> Wrapper<P> {
487    /// # Safety
488    ///
489    /// `host_callback` needs to outlive the returned object.
490    pub unsafe fn new(host_callback: *const clap_host) -> Arc<Self> {
491        let mut plugin = P::default();
492        let task_executor = Mutex::new(plugin.task_executor());
493
494        // This is used to allow the plugin to restore preset data from its editor, see the comment
495        // on `Self::updated_state_sender`
496        let (updated_state_sender, updated_state_receiver) = channel::bounded(0);
497
498        let plugin_descriptor: Box<PluginDescriptor> =
499            Box::new(PluginDescriptor::for_plugin::<P>());
500
501        // We're not allowed to query any extensions until the init function has been called, so we
502        // need a bunch of AtomicRefCells instead
503        assert!(!host_callback.is_null());
504        let host_callback = unsafe { ClapPtr::new(host_callback) };
505
506        // This is a mapping from the parameter IDs specified by the plugin to pointers to those
507        // parameters. These pointers are assumed to be safe to dereference as long as
508        // `wrapper.plugin` is alive. The plugin API identifiers these parameters by hashes, which
509        // we'll calculate from the string ID specified by the plugin. These parameters should also
510        // remain in the same order as the one returned by the plugin.
511        let params = plugin.params();
512        let param_id_hashes_ptrs_groups: Vec<_> = params
513            .param_map()
514            .into_iter()
515            .map(|(id, ptr, group)| {
516                let hash = hash_param_id(&id);
517                (id, hash, ptr, group)
518            })
519            .collect();
520        let param_hashes = param_id_hashes_ptrs_groups
521            .iter()
522            .map(|(_, hash, _, _)| *hash)
523            .collect();
524        let param_by_hash = param_id_hashes_ptrs_groups
525            .iter()
526            .map(|(_, hash, ptr, _)| (*hash, *ptr))
527            .collect();
528        let param_id_by_hash = param_id_hashes_ptrs_groups
529            .iter()
530            .map(|(id, hash, _, _)| (*hash, id.clone()))
531            .collect();
532        let param_group_by_hash = param_id_hashes_ptrs_groups
533            .iter()
534            .map(|(_, hash, _, group)| (*hash, group.clone()))
535            .collect();
536        let param_id_to_hash = param_id_hashes_ptrs_groups
537            .iter()
538            .map(|(id, hash, _, _)| (id.clone(), *hash))
539            .collect();
540        let param_ptr_to_hash = param_id_hashes_ptrs_groups
541            .iter()
542            .map(|(_, hash, ptr, _)| (*ptr, *hash))
543            .collect();
544        let poly_mod_ids_by_hash: HashMap<u32, u32> = param_id_hashes_ptrs_groups
545            .iter()
546            .filter_map(|(_, hash, ptr, _)| unsafe {
547                ptr.poly_modulation_id().map(|id| (*hash, id))
548            })
549            .collect();
550
551        if cfg!(debug_assertions) {
552            let param_map = params.param_map();
553            let param_ids: HashSet<_> = param_id_hashes_ptrs_groups
554                .iter()
555                .map(|(id, _, _, _)| id.clone())
556                .collect();
557            crate::nice_debug_assert_eq!(
558                param_map.len(),
559                param_ids.len(),
560                "The plugin has duplicate parameter IDs, weird things may happen. Consider using \
561                 6 character parameter IDs to avoid collisions."
562            );
563
564            let poly_mod_ids: HashSet<u32> = poly_mod_ids_by_hash.values().copied().collect();
565            crate::nice_debug_assert_eq!(
566                poly_mod_ids_by_hash.len(),
567                poly_mod_ids.len(),
568                "The plugin has duplicate poly modulation IDs. Polyphonic modulation will not be \
569                 routed to the correct parameter."
570            );
571
572            let mut bypass_param_exists = false;
573            for (_, _, ptr, _) in &param_id_hashes_ptrs_groups {
574                let flags = unsafe { ptr.flags() };
575                let is_bypass = flags.contains(ParamFlags::BYPASS);
576
577                if is_bypass && bypass_param_exists {
578                    crate::nice_debug_assert_failure!(
579                        "Duplicate bypass parameters found, the host will only use the first one"
580                    );
581                }
582
583                bypass_param_exists |= is_bypass;
584            }
585        }
586
587        // Support for the remote controls extension
588        let mut remote_control_pages = Vec::new();
589        RemoteControlPages::define_remote_control_pages(
590            &plugin,
591            &mut remote_control_pages,
592            &param_ptr_to_hash,
593        );
594
595        let wrapper = Self {
596            this: AtomicRefCell::new(Weak::new()),
597
598            plugin: TryLock::new(plugin),
599            task_executor,
600            params,
601            // Initialized later as it needs a reference to the wrapper for the async executor
602            #[cfg(feature = "editor")]
603            editor: AtomicRefCell::new(None),
604            #[cfg(feature = "editor")]
605            editor_window: AtomicRefCell::new(None),
606            #[cfg(feature = "editor")]
607            fallback_scale_factor: AtomicCell::new(None),
608
609            is_activated: AtomicBool::new(false),
610            is_processing: AtomicBool::new(false),
611            current_audio_io_layout: AtomicCell::new(
612                P::AUDIO_IO_LAYOUTS.first().copied().unwrap_or_default(),
613            ),
614            current_buffer_config: AtomicCell::new(None),
615            current_process_mode: AtomicCell::new(ProcessMode::Realtime),
616            input_events: AtomicRefCell::new(VecDeque::with_capacity(P::INPUT_EVENT_CAPACITY)),
617            last_process_status: AtomicCell::new(ProcessStatus::Normal),
618            latency_changed: AtomicBool::new(false),
619            current_latency: AtomicU32::new(0),
620            // This is initialized just before calling `Plugin::activate()` so that during the
621            // process call buffers can be initialized without any allocations
622            buffer_manager: AtomicRefCell::new(BufferManager::for_audio_io_layout(
623                0,
624                AudioIOLayout::default(),
625            )),
626            updated_state_sender,
627            updated_state_receiver,
628
629            host_callback,
630
631            clap_plugin: AtomicRefCell::new(clap_plugin {
632                // This needs to live on the heap because the plugin object contains a direct
633                // reference to the manifest as a value. We could share this between instances of
634                // the plugin using an `Arc`, but this doesn't consume a lot of memory so it's not a
635                // huge deal.
636                desc: plugin_descriptor.clap_plugin_descriptor(),
637                // This pointer will be set to point at our wrapper instance later
638                plugin_data: std::ptr::null_mut(),
639                init: Some(Self::init),
640                destroy: Some(Self::destroy),
641                activate: Some(Self::activate),
642                deactivate: Some(Self::deactivate),
643                start_processing: Some(Self::start_processing),
644                stop_processing: Some(Self::stop_processing),
645                reset: Some(Self::reset),
646                process: Some(Self::process),
647                get_extension: Some(Self::get_extension),
648                on_main_thread: Some(Self::on_main_thread),
649            }),
650            _plugin_descriptor: plugin_descriptor,
651
652            clap_plugin_audio_ports_config: clap_plugin_audio_ports_config {
653                count: Some(Self::ext_audio_ports_config_count),
654                get: Some(Self::ext_audio_ports_config_get),
655                select: Some(Self::ext_audio_ports_config_select),
656            },
657
658            clap_plugin_audio_ports: clap_plugin_audio_ports {
659                count: Some(Self::ext_audio_ports_count),
660                get: Some(Self::ext_audio_ports_get),
661            },
662
663            #[cfg(feature = "editor")]
664            clap_plugin_gui: clap_sys::ext::gui::clap_plugin_gui {
665                is_api_supported: Some(Self::ext_gui_is_api_supported),
666                get_preferred_api: Some(Self::ext_gui_get_preferred_api),
667                create: Some(Self::ext_gui_create),
668                destroy: Some(Self::ext_gui_destroy),
669                set_scale: Some(Self::ext_gui_set_scale),
670                get_size: Some(Self::ext_gui_get_size),
671                can_resize: Some(Self::ext_gui_can_resize),
672                get_resize_hints: Some(Self::ext_gui_get_resize_hints),
673                adjust_size: Some(Self::ext_gui_adjust_size),
674                set_size: Some(Self::ext_gui_set_size),
675                set_parent: Some(Self::ext_gui_set_parent),
676                set_transient: Some(Self::ext_gui_set_transient),
677                suggest_title: Some(Self::ext_gui_suggest_title),
678                show: Some(Self::ext_gui_show),
679                hide: Some(Self::ext_gui_hide),
680            },
681            #[cfg(feature = "editor")]
682            host_gui: AtomicRefCell::new(None),
683
684            clap_plugin_latency: clap_plugin_latency {
685                get: Some(Self::ext_latency_get),
686            },
687            host_latency: AtomicRefCell::new(None),
688
689            clap_plugin_note_ports: clap_plugin_note_ports {
690                count: Some(Self::ext_note_ports_count),
691                get: Some(Self::ext_note_ports_get),
692            },
693
694            clap_plugin_params: clap_plugin_params {
695                count: Some(Self::ext_params_count),
696                get_info: Some(Self::ext_params_get_info),
697                get_value: Some(Self::ext_params_get_value),
698                value_to_text: Some(Self::ext_params_value_to_text),
699                text_to_value: Some(Self::ext_params_text_to_value),
700                flush: Some(Self::ext_params_flush),
701            },
702            host_params: AtomicRefCell::new(None),
703            param_hashes,
704            param_by_hash,
705            param_id_by_hash,
706            param_group_by_hash,
707            param_id_to_hash,
708            param_ptr_to_hash,
709            poly_mod_ids_by_hash,
710            output_parameter_events: ArrayQueue::new(OUTPUT_EVENT_QUEUE_CAPACITY),
711
712            host_thread_check: AtomicRefCell::new(None),
713
714            clap_plugin_remote_controls: clap_plugin_remote_controls {
715                count: Some(Self::ext_remote_controls_count),
716                get: Some(Self::ext_remote_controls_get),
717            },
718            remote_control_pages,
719
720            clap_plugin_render: clap_plugin_render {
721                has_hard_realtime_requirement: Some(Self::ext_render_has_hard_realtime_requirement),
722                set: Some(Self::ext_render_set),
723            },
724
725            clap_plugin_state: clap_plugin_state {
726                save: Some(Self::ext_state_save),
727                load: Some(Self::ext_state_load),
728            },
729
730            clap_plugin_tail: clap_plugin_tail {
731                get: Some(Self::ext_tail_get),
732            },
733
734            #[cfg(feature = "editor")]
735            clap_plugin_track_info: clap_plugin_track_info {
736                changed: Some(Self::ext_track_info_changed),
737            },
738            #[cfg(feature = "editor")]
739            host_track_info: AtomicRefCell::new(None),
740            #[cfg(feature = "editor")]
741            current_track_info: AtomicRefCell::new(TrackInfo::default()),
742
743            clap_plugin_voice_info: clap_plugin_voice_info {
744                get: Some(Self::ext_voice_info_get),
745            },
746            host_voice_info: AtomicRefCell::new(None),
747            current_voice_capacity: AtomicU32::new(
748                P::CLAP_POLY_MODULATION_CONFIG
749                    .map(|c| {
750                        crate::nice_debug_assert!(
751                            c.max_voice_capacity >= 1,
752                            "The maximum voice capacity cannot be zero"
753                        );
754                        c.max_voice_capacity
755                    })
756                    .unwrap_or(1),
757            ),
758
759            tasks: ArrayQueue::new(TASK_QUEUE_CAPACITY),
760            main_thread_id: thread::current().id(),
761            // Initialized later as it needs a reference to the wrapper for the executor
762            background_thread: AtomicRefCell::new(None),
763        };
764
765        // Finally, the wrapper needs to contain a reference to itself so we can create GuiContexts
766        // when opening plugin editors
767        let wrapper = Arc::new(wrapper);
768        *wrapper.this.borrow_mut() = Arc::downgrade(&wrapper);
769
770        // The `clap_plugin::plugin_data` field needs to point to this wrapper so we can access it
771        // from the vtable functions
772        wrapper.clap_plugin.borrow_mut().plugin_data = Arc::as_ptr(&wrapper) as *mut _;
773
774        // Initialize the background thread **before** the editor!
775        *wrapper.background_thread.borrow_mut() =
776            Some(BackgroundThread::get_or_create(Arc::downgrade(&wrapper)));
777
778        // The editor also needs to be initialized later so the Async executor can work.
779        #[cfg(feature = "editor")]
780        {
781            *wrapper.editor.borrow_mut() = wrapper
782                .plugin
783                .try_lock()
784                .unwrap()
785                .editor(nice_plug_core::context::gui::AsyncExecutor::new(
786                    Arc::new({
787                        let wrapper = Arc::downgrade(&wrapper);
788                        move |task| {
789                            let wrapper = match wrapper.upgrade() {
790                                Some(wrapper) => wrapper,
791                                None => return,
792                            };
793
794                            let task_posted = wrapper.schedule_background(Task::PluginTask(task));
795                            crate::nice_debug_assert!(
796                                task_posted,
797                                "The task queue is full, dropping task..."
798                            );
799                        }
800                    }),
801                    Arc::new({
802                        let wrapper = Arc::downgrade(&wrapper);
803                        move |task| {
804                            let wrapper = match wrapper.upgrade() {
805                                Some(wrapper) => wrapper,
806                                None => return,
807                            };
808
809                            let task_posted = wrapper.schedule_gui(Task::PluginTask(task));
810                            crate::nice_debug_assert!(
811                                task_posted,
812                                "The task queue is full, dropping task..."
813                            );
814                        }
815                    }),
816                ))
817                .map(Mutex::new);
818        }
819
820        wrapper
821    }
822
823    #[cfg(feature = "editor")]
824    fn make_gui_context(self: Arc<Self>) -> GuiContext {
825        GuiContext::new(Arc::new(WrapperGuiContext {
826            wrapper: Arc::downgrade(&self),
827            #[cfg(debug_assertions)]
828            param_gesture_checker: Default::default(),
829        }))
830    }
831
832    /// # Note
833    ///
834    /// The lock on the plugin must be dropped before this object is dropped to avoid deadlocks
835    /// caused by reentrant function calls.
836    fn make_activate_context(&self) -> WrapperActivateContext<'_, P> {
837        WrapperActivateContext {
838            wrapper: self,
839            pending_requests: Default::default(),
840        }
841    }
842
843    fn make_process_context(
844        &self,
845        transport: Transport,
846        total_buffer_len: usize,
847        current_sample_idx: usize,
848        host_out_events: *const clap_output_events,
849    ) -> WrapperProcessContext<'_, P> {
850        WrapperProcessContext {
851            wrapper: self,
852            input_events_guard: self.input_events.borrow_mut(),
853            transport,
854            total_buffer_len: total_buffer_len as u32,
855            current_sample_idx: current_sample_idx as u32,
856            host_out_events,
857        }
858    }
859
860    /// Get a parameter's ID based on a `ParamPtr`. Used in the `GuiContext` implementation for the
861    /// gesture checks.
862    #[allow(unused)]
863    pub fn param_id_from_ptr(&self, param: ParamPtr) -> Option<&str> {
864        self.param_ptr_to_hash
865            .get(&param)
866            .and_then(|hash| self.param_id_by_hash.get(hash))
867            .map(|s| s.as_str())
868    }
869
870    /// Queue a parameter output event to be sent to the host at the end of the audio processing
871    /// cycle, and request a parameter flush from the host if the plugin is not currently processing
872    /// audio. The parameter's actual value will only be updated at that point so the value won't
873    /// change in the middle of a processing call.
874    ///
875    /// Returns `false` if the parameter value queue was full and the update will not be sent to the
876    /// host (it will still be set on the plugin either way).
877    pub fn queue_parameter_event(&self, event: OutputParamEvent) -> bool {
878        let result = self.output_parameter_events.push(event).is_ok();
879
880        // Requesting a flush is fine even during audio processing. This avoids a race condition.
881        match &*self.host_params.borrow() {
882            Some(host_params) => {
883                unsafe_clap_call! { host_params=>request_flush(&*self.host_callback) }
884            }
885            None => {
886                crate::nice_debug_assert_failure!("The host does not support parameters? What?")
887            }
888        }
889
890        result
891    }
892
893    /// Convenience function for setting a value for a parameter as triggered by a CLAP parameter
894    /// update. The same rate is for updating parameter smoothing.
895    ///
896    /// # Note
897    ///
898    /// These values are CLAP plain values, which include a step count multiplier for discrete
899    /// parameter values.
900    pub fn update_plain_value_by_hash(
901        &self,
902        hash: u32,
903        update_type: ClapParamUpdate,
904        sample_rate: Option<f32>,
905    ) -> bool {
906        match self.param_by_hash.get(&hash) {
907            Some(param_ptr) => {
908                match update_type {
909                    ClapParamUpdate::PlainValueSet(clap_plain_value) => {
910                        if !clap_plain_value.is_finite() {
911                            return false;
912                        }
913
914                        let normalized_value = clap_plain_value as f32
915                            / unsafe { param_ptr.step_count() }.unwrap_or(1) as f32;
916
917                        if unsafe { param_ptr._internal_set_normalized_value(normalized_value) } {
918                            if let Some(sample_rate) = sample_rate {
919                                unsafe { param_ptr._internal_update_smoother(sample_rate, false) };
920                            }
921
922                            #[cfg(feature = "editor")]
923                            {
924                                // The GUI needs to be informed about the changed parameter value. This
925                                // triggers an `Editor::param_value_changed()` call on the GUI thread.
926                                let task_posted = self.schedule_gui(Task::ParameterValueChanged(
927                                    hash,
928                                    normalized_value,
929                                ));
930                                crate::nice_debug_assert!(
931                                    task_posted,
932                                    "The task queue is full, dropping task..."
933                                );
934                            }
935                        }
936
937                        true
938                    }
939                    ClapParamUpdate::PlainValueMod(clap_plain_delta) => {
940                        if !clap_plain_delta.is_finite() {
941                            return false;
942                        }
943
944                        let normalized_delta = clap_plain_delta as f32
945                            / unsafe { param_ptr.step_count() }.unwrap_or(1) as f32;
946
947                        if unsafe { param_ptr._internal_modulate_value(normalized_delta) } {
948                            if let Some(sample_rate) = sample_rate {
949                                unsafe { param_ptr._internal_update_smoother(sample_rate, false) };
950                            }
951
952                            #[cfg(feature = "editor")]
953                            {
954                                let task_posted = self.schedule_gui(
955                                    Task::ParameterModulationChanged(hash, normalized_delta),
956                                );
957                                crate::nice_debug_assert!(
958                                    task_posted,
959                                    "The task queue is full, dropping task..."
960                                );
961                            }
962                        }
963
964                        true
965                    }
966                }
967            }
968            _ => false,
969        }
970    }
971
972    /// Handle all incoming events from an event queue. This will clear `self.input_events` first.
973    ///
974    /// # Safety
975    ///
976    /// `in_` must contain only pointers to valid data (Clippy insists on there being a safety
977    /// section here).
978    pub unsafe fn handle_in_events(
979        &self,
980        in_: &clap_input_events,
981        current_sample_idx: usize,
982        total_buffer_len: usize,
983    ) {
984        let mut input_events = self.input_events.borrow_mut();
985        input_events.clear();
986
987        unsafe {
988            let num_events = clap_call! { in_=>size(in_) };
989            for event_idx in 0..num_events {
990                let event = clap_call! { in_=>get(in_, event_idx) };
991                self.handle_input_event(
992                    event,
993                    &mut input_events,
994                    None,
995                    current_sample_idx,
996                    total_buffer_len,
997                );
998            }
999        }
1000    }
1001
1002    /// Similar to [`handle_in_events()`][Self::handle_in_events()], but will stop just before an
1003    /// event if the predicate returns true for that events. This predicate is only called for
1004    /// events that occur after `current_sample_idx`. This is used to stop before a tempo or time
1005    /// signature change, or before next parameter change event with `raw_event.time >
1006    /// current_sample_idx` and return the **absolute** (relative to the entire buffer that's being
1007    /// split) sample index of that event along with the its index in the event queue as a
1008    /// `(sample_idx, event_idx)` tuple. This allows for splitting the audio buffer into segments
1009    /// with distinct sample values to enable sample accurate automation without modifications to the
1010    /// wrapped plugin.
1011    ///
1012    /// # Safety
1013    ///
1014    /// `in_` must contain only pointers to valid data (Clippy insists on there being a safety
1015    /// section here).
1016    pub unsafe fn handle_in_events_until(
1017        &self,
1018        in_: &clap_input_events,
1019        transport_info: &mut *const clap_event_transport,
1020        current_sample_idx: usize,
1021        total_buffer_len: usize,
1022        resume_from_event_idx: usize,
1023        stop_predicate: impl Fn(*const clap_event_header) -> bool,
1024    ) -> Option<(usize, usize)> {
1025        let mut input_events = self.input_events.borrow_mut();
1026        input_events.clear();
1027
1028        let num_events = unsafe {
1029            clap_call! { in_=>size(in_) }
1030        };
1031
1032        if resume_from_event_idx as u32 >= num_events {
1033            return None;
1034        }
1035
1036        for event_idx in (resume_from_event_idx as u32)..num_events {
1037            unsafe {
1038                let event: *const clap_event_header = clap_call! { in_=>get(in_, event_idx) };
1039                if event.is_null() {
1040                    continue;
1041                }
1042
1043                // Check the current event before applying it, including the first event in the
1044                // buffer. A later event belongs to the next process slice.
1045                if (*event).time > current_sample_idx as u32 && stop_predicate(event) {
1046                    return Some(((*event).time as usize, event_idx as usize));
1047                }
1048
1049                self.handle_input_event(
1050                    event,
1051                    &mut input_events,
1052                    Some(transport_info),
1053                    current_sample_idx,
1054                    total_buffer_len,
1055                );
1056            }
1057        }
1058
1059        None
1060    }
1061
1062    /// Write the unflushed parameter changes to the host's output event queue. The sample index is
1063    /// used as part of splitting up the input buffer for sample accurate automation changes. This
1064    /// will also modify the actual parameter values, since we should only do that while the wrapped
1065    /// plugin is not actually processing audio.
1066    ///
1067    /// The `total_buffer_len` argument is used to clamp out of bounds events to the buffer's length.
1068    ///
1069    /// # Safety
1070    ///
1071    /// `out` must be a valid object (Clippy insists on there being a safety section here).
1072    pub unsafe fn handle_out_events(&self, out: &clap_output_events, current_sample_idx: usize) {
1073        // We'll always write these events to the first sample, so even when we add note output we
1074        // shouldn't have to think about interleaving events here
1075        let sample_rate = self.current_buffer_config.load().map(|c| c.sample_rate);
1076        while let Some(change) = self.output_parameter_events.pop() {
1077            let push_successful = match change {
1078                OutputParamEvent::BeginGesture { param_hash } => {
1079                    let event = clap_event_param_gesture {
1080                        header: clap_event_header {
1081                            size: mem::size_of::<clap_event_param_gesture>() as u32,
1082                            time: current_sample_idx as u32,
1083                            space_id: CLAP_CORE_EVENT_SPACE_ID,
1084                            type_: CLAP_EVENT_PARAM_GESTURE_BEGIN,
1085                            flags: CLAP_EVENT_IS_LIVE,
1086                        },
1087                        param_id: param_hash,
1088                    };
1089
1090                    unsafe {
1091                        clap_call! { out=>try_push(out, &event.header) }
1092                    }
1093                }
1094                OutputParamEvent::SetValue {
1095                    param_hash,
1096                    clap_plain_value,
1097                } => {
1098                    self.update_plain_value_by_hash(
1099                        param_hash,
1100                        ClapParamUpdate::PlainValueSet(clap_plain_value),
1101                        sample_rate,
1102                    );
1103
1104                    let event = clap_event_param_value {
1105                        header: clap_event_header {
1106                            size: mem::size_of::<clap_event_param_value>() as u32,
1107                            time: current_sample_idx as u32,
1108                            space_id: CLAP_CORE_EVENT_SPACE_ID,
1109                            type_: CLAP_EVENT_PARAM_VALUE,
1110                            flags: CLAP_EVENT_IS_LIVE,
1111                        },
1112                        param_id: param_hash,
1113                        cookie: std::ptr::null_mut(),
1114                        port_index: -1,
1115                        note_id: -1,
1116                        channel: -1,
1117                        key: -1,
1118                        value: clap_plain_value,
1119                    };
1120
1121                    unsafe {
1122                        clap_call! { out=>try_push(out, &event.header) }
1123                    }
1124                }
1125                OutputParamEvent::EndGesture { param_hash } => {
1126                    let event = clap_event_param_gesture {
1127                        header: clap_event_header {
1128                            size: mem::size_of::<clap_event_param_gesture>() as u32,
1129                            time: current_sample_idx as u32,
1130                            space_id: CLAP_CORE_EVENT_SPACE_ID,
1131                            type_: CLAP_EVENT_PARAM_GESTURE_END,
1132                            flags: CLAP_EVENT_IS_LIVE,
1133                        },
1134                        param_id: param_hash,
1135                    };
1136
1137                    unsafe {
1138                        clap_call! { out=>try_push(out, &event.header) }
1139                    }
1140                }
1141            };
1142
1143            crate::nice_debug_assert!(push_successful);
1144        }
1145    }
1146
1147    /// Handle an incoming CLAP event. The sample index is provided to support block splitting for
1148    /// sample accurate automation. `input_events` must be cleared at the start of each process block.
1149    ///
1150    /// To save on mutex operations when handing MIDI events, the lock guard for the input events
1151    /// need to be passed into this function.
1152    ///
1153    /// If the event was a transport event and the `transport_info` argument is not `None`, then the
1154    /// pointer will be changed to point to the transport information from this event.
1155    ///
1156    /// # Safety
1157    ///
1158    /// `in_` must contain only pointers to valid data (Clippy insists on there being a safety
1159    /// section here).
1160    pub unsafe fn handle_input_event(
1161        &self,
1162        event: *const clap_event_header,
1163        input_events: &mut AtomicRefMut<VecDeque<PluginNoteEvent<P>>>,
1164        transport_info: Option<&mut *const clap_event_transport>,
1165        current_sample_idx: usize,
1166        total_buffer_len: usize,
1167    ) {
1168        let raw_event = unsafe { &*event };
1169
1170        // Out of bounds events are clamped to the buffer's size
1171        let timing = clamp_input_event_timing(
1172            raw_event.time - current_sample_idx as u32,
1173            total_buffer_len as u32,
1174        );
1175
1176        let push_event = |input_events: &mut AtomicRefMut<VecDeque<PluginNoteEvent<P>>>,
1177                          event: PluginNoteEvent<P>| {
1178            permit_alloc(|| {
1179                // In the rare case the host sends a very large amount of events at once, there
1180                // is not much we can do except to just accept the allocation.
1181                if input_events.len() == input_events.capacity() {
1182                    crate::nice_warn!(
1183                        "Input event buffer filled up! This will cause an allocation."
1184                    );
1185                }
1186                input_events.push_back(event);
1187            });
1188        };
1189
1190        fn voice_from_i32(v: i32) -> VoiceID {
1191            if v >= 0 {
1192                VoiceID::ID(v)
1193            } else {
1194                VoiceID::Wildcard
1195            }
1196        }
1197        fn channel_from_i16(c: i16) -> Channel {
1198            if (0..=15).contains(&c) {
1199                Channel::Number(c as u8)
1200            } else {
1201                Channel::Wildcard
1202            }
1203        }
1204        fn key_from_i16(k: i16) -> Key {
1205            if (0..=127).contains(&k) {
1206                Key::Number(k as u8)
1207            } else {
1208                Key::Wildcard
1209            }
1210        }
1211
1212        match (raw_event.space_id, raw_event.type_) {
1213            (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_PARAM_VALUE) => {
1214                let event = unsafe { &*(event as *const clap_event_param_value) };
1215                self.update_plain_value_by_hash(
1216                    event.param_id,
1217                    ClapParamUpdate::PlainValueSet(event.value),
1218                    self.current_buffer_config.load().map(|c| c.sample_rate),
1219                );
1220
1221                // If the parameter supports polyphonic modulation, then the plugin needs to be
1222                // informed that the parameter has been monophonically automated. This allows the
1223                // plugin to update all of its polyphonic modulation values, since polyphonic
1224                // modulation acts as an offset to the monophonic value.
1225                if let Some(poly_modulation_id) = self.poly_mod_ids_by_hash.get(&event.param_id) {
1226                    // The modulation offset needs to be normalized to account for modulated
1227                    // integer or enum parameters
1228                    let param_ptr = self.param_by_hash[&event.param_id];
1229                    let normalized_value =
1230                        event.value as f32 / unsafe { param_ptr.step_count().unwrap_or(1) as f32 };
1231
1232                    push_event(
1233                        input_events,
1234                        NoteEvent::MonoAutomation {
1235                            timing,
1236                            poly_modulation_id: *poly_modulation_id,
1237                            normalized_value,
1238                        },
1239                    );
1240                }
1241            }
1242            (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_PARAM_MOD) => {
1243                let event = unsafe { &*(event as *const clap_event_param_mod) };
1244
1245                if event.note_id != -1 && P::MIDI_INPUT >= MidiConfig::Basic {
1246                    match self.poly_mod_ids_by_hash.get(&event.param_id) {
1247                        Some(poly_modulation_id) => {
1248                            // The modulation offset needs to be normalized to account for modulated
1249                            // integer or enum parameters
1250                            let param_ptr = self.param_by_hash[&event.param_id];
1251                            let normalized_offset = event.amount as f32
1252                                / unsafe { param_ptr.step_count().unwrap_or(1) as f32 };
1253
1254                            // The host may also add key and channel information here, but it may
1255                            // also pass -1. So not having that information here at all seems like
1256                            // the safest choice.
1257                            push_event(
1258                                input_events,
1259                                NoteEvent::PolyModulation {
1260                                    timing,
1261                                    voice_id: event.note_id,
1262                                    poly_modulation_id: *poly_modulation_id,
1263                                    normalized_offset,
1264                                },
1265                            );
1266
1267                            return;
1268                        }
1269                        None => crate::nice_debug_assert_failure!(
1270                            "Polyphonic modulation sent for a parameter without a poly modulation \
1271                             ID"
1272                        ),
1273                    }
1274                }
1275
1276                self.update_plain_value_by_hash(
1277                    event.param_id,
1278                    ClapParamUpdate::PlainValueMod(event.amount),
1279                    self.current_buffer_config.load().map(|c| c.sample_rate),
1280                );
1281            }
1282            (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_TRANSPORT) => {
1283                let event = unsafe { &*(event as *const clap_event_transport) };
1284                if let Some(transport_info) = transport_info {
1285                    *transport_info = event;
1286                }
1287            }
1288            (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_NOTE_ON) => {
1289                if P::MIDI_INPUT >= MidiConfig::Basic {
1290                    let event = unsafe { &*(event as *const clap_event_note) };
1291
1292                    push_event(
1293                        input_events,
1294                        NoteEvent::NoteOn {
1295                            // When splitting up the buffer for sample accurate automation all events
1296                            // should be relative to the block
1297                            timing,
1298                            voice_id: voice_from_i32(event.note_id),
1299                            channel: channel_from_i16(event.channel),
1300                            key: key_from_i16(event.key),
1301                            velocity: event.velocity as f32,
1302                        },
1303                    );
1304                }
1305            }
1306            (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_NOTE_OFF) => {
1307                if P::MIDI_INPUT >= MidiConfig::Basic {
1308                    let event = unsafe { &*(event as *const clap_event_note) };
1309
1310                    push_event(
1311                        input_events,
1312                        NoteEvent::NoteOff {
1313                            timing,
1314                            voice_id: voice_from_i32(event.note_id),
1315                            channel: channel_from_i16(event.channel),
1316                            key: key_from_i16(event.key),
1317                            velocity: event.velocity as f32,
1318                        },
1319                    );
1320                }
1321            }
1322            (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_NOTE_CHOKE) => {
1323                if P::MIDI_INPUT >= MidiConfig::Basic {
1324                    let event = unsafe { &*(event as *const clap_event_note) };
1325
1326                    push_event(
1327                        input_events,
1328                        NoteEvent::Choke {
1329                            timing,
1330                            voice_id: voice_from_i32(event.note_id),
1331                            channel: channel_from_i16(event.channel),
1332                            key: key_from_i16(event.key),
1333                        },
1334                    );
1335                }
1336            }
1337            (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_NOTE_EXPRESSION) => {
1338                if P::MIDI_INPUT >= MidiConfig::Basic {
1339                    // TODO: Add support for the other expression types
1340                    let event = unsafe { &*(event as *const clap_event_note_expression) };
1341                    match event.expression_id {
1342                        CLAP_NOTE_EXPRESSION_PRESSURE => {
1343                            push_event(
1344                                input_events,
1345                                NoteEvent::PolyPressure {
1346                                    timing,
1347                                    voice_id: voice_from_i32(event.note_id),
1348                                    channel: channel_from_i16(event.channel),
1349                                    key: key_from_i16(event.key),
1350                                    pressure: event.value as f32,
1351                                },
1352                            );
1353                        }
1354                        CLAP_NOTE_EXPRESSION_VOLUME => {
1355                            push_event(
1356                                input_events,
1357                                NoteEvent::PolyVolume {
1358                                    timing,
1359                                    voice_id: voice_from_i32(event.note_id),
1360                                    channel: channel_from_i16(event.channel),
1361                                    key: key_from_i16(event.key),
1362                                    gain: event.value as f32,
1363                                },
1364                            );
1365                        }
1366                        CLAP_NOTE_EXPRESSION_PAN => {
1367                            push_event(
1368                                input_events,
1369                                NoteEvent::PolyPan {
1370                                    timing,
1371                                    voice_id: voice_from_i32(event.note_id),
1372                                    channel: channel_from_i16(event.channel),
1373                                    key: key_from_i16(event.key),
1374                                    // In CLAP this value goes from [0, 1] instead of [-1, 1]
1375                                    pan: (event.value as f32 * 2.0) - 1.0,
1376                                },
1377                            );
1378                        }
1379                        CLAP_NOTE_EXPRESSION_TUNING => {
1380                            push_event(
1381                                input_events,
1382                                NoteEvent::PolyTuning {
1383                                    timing,
1384                                    voice_id: voice_from_i32(event.note_id),
1385                                    channel: channel_from_i16(event.channel),
1386                                    key: key_from_i16(event.key),
1387                                    tuning: event.value as f32,
1388                                },
1389                            );
1390                        }
1391                        CLAP_NOTE_EXPRESSION_VIBRATO => {
1392                            push_event(
1393                                input_events,
1394                                NoteEvent::PolyVibrato {
1395                                    timing,
1396                                    voice_id: voice_from_i32(event.note_id),
1397                                    channel: channel_from_i16(event.channel),
1398                                    key: key_from_i16(event.key),
1399                                    vibrato: event.value as f32,
1400                                },
1401                            );
1402                        }
1403                        CLAP_NOTE_EXPRESSION_EXPRESSION => {
1404                            push_event(
1405                                input_events,
1406                                NoteEvent::PolyExpression {
1407                                    timing,
1408                                    voice_id: voice_from_i32(event.note_id),
1409                                    channel: channel_from_i16(event.channel),
1410                                    key: key_from_i16(event.key),
1411                                    expression: event.value as f32,
1412                                },
1413                            );
1414                        }
1415                        CLAP_NOTE_EXPRESSION_BRIGHTNESS => {
1416                            push_event(
1417                                input_events,
1418                                NoteEvent::PolyBrightness {
1419                                    timing,
1420                                    voice_id: voice_from_i32(event.note_id),
1421                                    channel: channel_from_i16(event.channel),
1422                                    key: key_from_i16(event.key),
1423                                    brightness: event.value as f32,
1424                                },
1425                            );
1426                        }
1427                        n => {
1428                            crate::nice_trace!("Unhandled note expression ID {}", n)
1429                        }
1430                    }
1431                }
1432            }
1433            (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_MIDI) => {
1434                // In the Basic note port type, we'll still handle note on, note off, and polyphonic
1435                // pressure events if the host sents us those. But we'll throw away any other MIDI
1436                // messages to stay consistent with the VST3 wrapper.
1437                let event = unsafe { &*(event as *const clap_event_midi) };
1438
1439                match NoteEvent::from_midi(timing, &event.data) {
1440                    Ok(
1441                        note_event @ (NoteEvent::NoteOn { .. }
1442                        | NoteEvent::NoteOff { .. }
1443                        | NoteEvent::PolyPressure { .. }),
1444                    ) if P::MIDI_INPUT >= MidiConfig::Basic => {
1445                        push_event(input_events, note_event);
1446                    }
1447                    Ok(note_event) if P::MIDI_INPUT >= MidiConfig::MidiCCs => {
1448                        push_event(input_events, note_event);
1449                    }
1450                    Ok(_) => (),
1451                    Err(n) => {
1452                        crate::nice_trace!("Unhandled MIDI message type {}", n)
1453                    }
1454                };
1455            }
1456            (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_MIDI_SYSEX)
1457                if P::MIDI_INPUT >= MidiConfig::Basic =>
1458            {
1459                let event = unsafe { &*(event as *const clap_event_midi_sysex) };
1460
1461                // `NoteEvent::from_midi` prints some tracing if parsing fails, which is not
1462                // necessarily an error
1463                assert!(!event.buffer.is_null());
1464                let sysex_buffer =
1465                    unsafe { std::slice::from_raw_parts(event.buffer, event.size as usize) };
1466                if let Ok(note_event) = NoteEvent::from_midi(timing, sysex_buffer) {
1467                    push_event(input_events, note_event);
1468                };
1469            }
1470            _ => {
1471                crate::nice_trace!(
1472                    "Unhandled CLAP event type {} for namespace {}",
1473                    raw_event.type_,
1474                    raw_event.space_id
1475                );
1476            }
1477        }
1478    }
1479
1480    /// Get the plugin's state object, may be called by the plugin's GUI as part of its own preset
1481    /// management. The wrapper doesn't use these functions and serializes and deserializes directly
1482    /// the JSON in the relevant plugin API methods instead.
1483    pub fn get_state_object(&self) -> PluginState {
1484        unsafe {
1485            state::serialize_object::<P>(
1486                self.params.clone(),
1487                state::make_params_iter(&self.param_by_hash, &self.param_id_to_hash),
1488            )
1489        }
1490    }
1491
1492    /// Update the plugin's internal state, called by the plugin itself from the GUI thread. To
1493    /// prevent corrupting data and changing parameters during processing the actual state is only
1494    /// updated at the end of the audio processing cycle.
1495    pub fn set_state_object_from_gui(&self, mut state: PluginState) {
1496        let mut did_set_state_inner = false;
1497
1498        // Use a loop and timeouts to handle the super rare edge case when this function gets called
1499        // between a process call and the host disabling the plugin
1500        loop {
1501            if self.is_processing.load(Ordering::SeqCst) {
1502                // If the plugin is currently processing audio, then we'll perform the restore
1503                // operation at the end of the audio call. This involves sending the state to the
1504                // audio thread, having the audio thread handle the state restore at the very end of
1505                // the process function, and then sending the state back to this thread so it can be
1506                // deallocated without blocking the audio thread.
1507                match self
1508                    .updated_state_sender
1509                    .send_timeout(state, Duration::from_secs(1))
1510                {
1511                    Ok(_) => {
1512                        // As mentioned above, the state object will be passed back to this thread
1513                        // so we can deallocate it without blocking.
1514                        let state = self.updated_state_receiver.recv();
1515                        drop(state);
1516                        break;
1517                    }
1518                    Err(SendTimeoutError::Timeout(value)) => {
1519                        state = value;
1520                        continue;
1521                    }
1522                    Err(SendTimeoutError::Disconnected(_)) => {
1523                        crate::nice_debug_assert_failure!("State update channel got disconnected");
1524                        return;
1525                    }
1526                }
1527            } else {
1528                // Otherwise we'll set the state right here and now, since this function should be
1529                // called from a GUI thread
1530                self.set_state_inner(&mut state);
1531                did_set_state_inner = true;
1532                break;
1533            }
1534        }
1535
1536        if !did_set_state_inner {
1537            // After the state has been updated, notify the host about the new parameter values
1538            let task_posted = self.schedule_gui(Task::RescanParamValues);
1539            crate::nice_debug_assert!(task_posted, "The task queue is full, dropping task...");
1540        } // Else the RescanParamValues task has already been sent
1541    }
1542
1543    pub fn set_latency_samples(&self, samples: u32) {
1544        // Only make a callback if it's actually needed
1545        // XXX: For CLAP we could move this handling to the Plugin struct, but it may be worthwhile
1546        //      to keep doing it this way to stay consistent with VST3.
1547        let old_latency = self.current_latency.swap(samples, Ordering::SeqCst);
1548        if old_latency != samples {
1549            let task_posted = self.schedule_gui(Task::LatencyChanged);
1550            crate::nice_debug_assert!(task_posted, "The task queue is full, dropping task...");
1551        }
1552    }
1553
1554    pub fn set_current_voice_capacity(&self, capacity: u32) {
1555        match P::CLAP_POLY_MODULATION_CONFIG {
1556            Some(config) => {
1557                let clamped_capacity = capacity.clamp(1, config.max_voice_capacity);
1558                crate::nice_debug_assert_eq!(
1559                    capacity,
1560                    clamped_capacity,
1561                    "The current voice capacity must be between 1 and the maximum capacity"
1562                );
1563
1564                if clamped_capacity != self.current_voice_capacity.load(Ordering::Relaxed) {
1565                    self.current_voice_capacity
1566                        .store(clamped_capacity, Ordering::SeqCst);
1567                    let task_posted = self.schedule_gui(Task::VoiceInfoChanged);
1568                    crate::nice_debug_assert!(
1569                        task_posted,
1570                        "The task queue is full, dropping task..."
1571                    );
1572                }
1573            }
1574            None => crate::nice_debug_assert_failure!(
1575                "Configuring the current voice capacity is only possible when \
1576                 'ClapPlugin::CLAP_POLY_MODULATION_CONFIG' is set"
1577            ),
1578        }
1579    }
1580
1581    /// Query the host for the current track information and notify the plugin if anything changed.
1582    #[cfg(feature = "editor")]
1583    fn update_track_info_from_host(&self) {
1584        let host_track_info = self.host_track_info.borrow();
1585        let Some(host_track_info) = host_track_info.as_ref() else {
1586            return;
1587        };
1588
1589        let editor = self.editor.borrow();
1590        let Some(editor) = editor.as_ref() else {
1591            return;
1592        };
1593
1594        permit_alloc(|| {
1595            let mut clap_info: clap_track_info = unsafe { mem::zeroed() };
1596            let success = unsafe_clap_call! {
1597                host_track_info=>get(&*self.host_callback, &mut clap_info)
1598            };
1599            if !success {
1600                return;
1601            }
1602
1603            let mut current_track_info = self.current_track_info.borrow_mut();
1604            let mut name = current_track_info.name().to_owned();
1605            let mut color = current_track_info.color();
1606
1607            if clap_info.flags & CLAP_TRACK_INFO_HAS_TRACK_NAME != 0 {
1608                let name_bytes = unsafe {
1609                    std::slice::from_raw_parts(
1610                        clap_info.name.as_ptr().cast::<u8>(),
1611                        clap_sys::string_sizes::CLAP_NAME_SIZE,
1612                    )
1613                };
1614                if let Ok(cstr) = CStr::from_bytes_until_nul(name_bytes) {
1615                    name = cstr.to_string_lossy().into_owned()
1616                } // Else there is no null terminator. In this case we do nothing with the name.
1617            }
1618
1619            if clap_info.flags & CLAP_TRACK_INFO_HAS_TRACK_COLOR != 0 {
1620                color = Some(TrackColor::new(
1621                    clap_info.color.red,
1622                    clap_info.color.green,
1623                    clap_info.color.blue,
1624                    clap_info.color.alpha,
1625                ));
1626            }
1627
1628            let track_info = TrackInfo::new(name, color);
1629            *current_track_info = track_info.clone();
1630
1631            editor.lock().track_info_updated(track_info);
1632        });
1633    }
1634
1635    /// Immediately set the plugin state. Returns `false` if the deserialization failed. The plugin
1636    /// state is set from a couple places, so this function aims to deduplicate that. Includes
1637    /// `permit_alloc()`s around the deserialization and initialization for the use case where
1638    /// `set_state_object_from_gui()` was called while the plugin is process audio.
1639    ///
1640    /// Implicitly emits `Task::ParameterValuesChanged`.
1641    ///
1642    /// # Notes
1643    ///
1644    /// `self.plugin` must _not_ be locked while calling this function or it will deadlock.
1645    pub fn set_state_inner(&self, state: &mut PluginState) -> bool {
1646        // FIXME: This is obviously not realtime-safe, but loading presets without doing this
1647        //        could lead to inconsistencies. `state::deserialize_object()` normally never
1648        //        allocates, but if the plugin has persistent non-parameter data then its
1649        //        `deserialize_fields()` implementation may still allocate.
1650        let success = permit_alloc(|| unsafe {
1651            state::deserialize_object::<P>(
1652                state,
1653                self.params.clone(),
1654                state::make_params_getter(&self.param_by_hash, &self.param_id_to_hash),
1655                self.current_buffer_config.load().as_ref(),
1656            )
1657        });
1658        if !success {
1659            crate::nice_debug_assert_failure!(
1660                "Deserializing plugin state from a state object failed"
1661            );
1662            return false;
1663        }
1664
1665        // Reinitialize the plugin after loading state so it can respond to the new parameter values,
1666        // and tell the host to rescan the parameter values.
1667        let task_posted = self.schedule_gui(Task::StateChanged);
1668        crate::nice_debug_assert!(task_posted, "The task queue is full, dropping task...");
1669
1670        success
1671    }
1672
1673    pub fn request_restart(&self) {
1674        unsafe_clap_call! { &*self.host_callback=>request_restart(&*self.host_callback) };
1675    }
1676
1677    unsafe extern "C" fn init(plugin: *const clap_plugin) -> bool {
1678        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
1679        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
1680
1681        // We weren't allowed to query these in the constructor, so we need to do it now instead.
1682        unsafe {
1683            #[cfg(feature = "editor")]
1684            {
1685                *wrapper.host_gui.borrow_mut() = query_host_extension::<
1686                    clap_sys::ext::gui::clap_host_gui,
1687                >(
1688                    &wrapper.host_callback, CLAP_EXT_GUI
1689                );
1690
1691                *wrapper.host_track_info.borrow_mut() = query_host_extension::<clap_host_track_info>(
1692                    &wrapper.host_callback,
1693                    CLAP_EXT_TRACK_INFO,
1694                );
1695            }
1696            *wrapper.host_latency.borrow_mut() =
1697                query_host_extension::<clap_host_latency>(&wrapper.host_callback, CLAP_EXT_LATENCY);
1698            *wrapper.host_params.borrow_mut() =
1699                query_host_extension::<clap_host_params>(&wrapper.host_callback, CLAP_EXT_PARAMS);
1700            *wrapper.host_voice_info.borrow_mut() = query_host_extension::<clap_host_voice_info>(
1701                &wrapper.host_callback,
1702                CLAP_EXT_VOICE_INFO,
1703            );
1704            *wrapper.host_thread_check.borrow_mut() = query_host_extension::<clap_host_thread_check>(
1705                &wrapper.host_callback,
1706                CLAP_EXT_THREAD_CHECK,
1707            );
1708        }
1709
1710        #[cfg(feature = "editor")]
1711        wrapper.update_track_info_from_host();
1712
1713        true
1714    }
1715
1716    unsafe extern "C" fn destroy(plugin: *const clap_plugin) {
1717        assert!(!plugin.is_null() && unsafe { !(*plugin).plugin_data.is_null() });
1718        let this = unsafe { Arc::from_raw((*plugin).plugin_data as *mut Self) };
1719        crate::nice_debug_assert_eq!(Arc::strong_count(&this), 1);
1720
1721        drop(this);
1722    }
1723
1724    unsafe extern "C" fn activate(
1725        plugin: *const clap_plugin,
1726        sample_rate: f64,
1727        min_frames_count: u32,
1728        max_frames_count: u32,
1729    ) -> bool {
1730        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
1731        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
1732
1733        let audio_io_layout = wrapper.current_audio_io_layout.load();
1734        let buffer_config = BufferConfig {
1735            sample_rate: sample_rate as f32,
1736            min_buffer_size: Some(min_frames_count),
1737            max_buffer_size: max_frames_count,
1738            process_mode: wrapper.current_process_mode.load(),
1739        };
1740
1741        // Before initializing the plugin, make sure all smoothers are set the the default values
1742        for param in wrapper.param_by_hash.values() {
1743            unsafe { param._internal_update_smoother(buffer_config.sample_rate, true) };
1744        }
1745
1746        // If this reactivation happened due to the latency changing, notify the host of that
1747        // latency change.
1748        if wrapper.latency_changed.swap(false, Ordering::SeqCst)
1749            && let Some(host_latency) = &*wrapper.host_latency.borrow()
1750        {
1751            unsafe_clap_call! { host_latency=>changed(&*wrapper.host_callback) };
1752        }
1753
1754        let mut activate_context = wrapper.make_activate_context();
1755
1756        // In the case a host misbehaves and tries to activate the plugin without waiting for the
1757        // `process` method to finish, manually wait for that method to finish.
1758        let now = Instant::now();
1759        let mut result = false;
1760        loop {
1761            if let Some(mut plugin) = wrapper.plugin.try_lock() {
1762                if plugin.activate(&audio_io_layout, &buffer_config, &mut activate_context) {
1763                    // NOTE: `Plugin::reset()` is called in `clap_plugin::start_processing()` instead of in
1764                    //       this function
1765
1766                    // Likewise, make sure that the buffers are also not currently being used by the process
1767                    // method.
1768                    let now_2 = Instant::now();
1769                    loop {
1770                        if let Ok(mut buffer_manager) = wrapper.buffer_manager.try_borrow_mut() {
1771                            // This preallocates enough space so we can transform all of the host's raw channel
1772                            // pointers into a set of `Buffer` objects for the plugin's main and auxiliary IO
1773                            *buffer_manager = BufferManager::for_audio_io_layout(
1774                                max_frames_count as usize,
1775                                audio_io_layout,
1776                            );
1777
1778                            // Also store this for later, so we can reinitialize the plugin after restoring state
1779                            wrapper.current_buffer_config.store(Some(buffer_config));
1780
1781                            wrapper.is_activated.store(true, Ordering::SeqCst);
1782
1783                            result = true;
1784
1785                            break;
1786                        } else if now_2.elapsed() > Duration::from_secs(1) {
1787                            crate::nice_error!(
1788                                "Failed to acquire lock on buffers while activating"
1789                            );
1790                            break;
1791                        } else {
1792                            std::thread::sleep(Duration::from_millis(1));
1793                        }
1794                    }
1795                }
1796
1797                break;
1798            } else if now.elapsed() > Duration::from_secs(1) {
1799                crate::nice_error!("Failed to acquire lock on plugin while activating");
1800                break;
1801            } else {
1802                std::thread::sleep(Duration::from_millis(1));
1803            }
1804        }
1805
1806        // NOTE: This needs to be dropped after the `plugin` lock to avoid deadlocks
1807        drop(activate_context);
1808
1809        result
1810    }
1811
1812    unsafe extern "C" fn deactivate(plugin: *const clap_plugin) {
1813        check_null_ptr!((), plugin, unsafe { (*plugin).plugin_data });
1814        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
1815
1816        // In the case a host misbehaves and tries to activate the plugin without waiting for the
1817        // `process` method to finish, manually wait for that method to finish.
1818        let now = Instant::now();
1819        loop {
1820            if let Some(mut plugin) = wrapper.plugin.try_lock() {
1821                plugin.deactivate();
1822                break;
1823            } else if now.elapsed() > Duration::from_secs(1) {
1824                crate::nice_error!("Failed to acquire lock on plugin while deactivating");
1825                break;
1826            } else {
1827                std::thread::sleep(Duration::from_millis(1));
1828            }
1829        }
1830
1831        wrapper.is_activated.store(false, Ordering::SeqCst);
1832    }
1833
1834    unsafe extern "C" fn start_processing(plugin: *const clap_plugin) -> bool {
1835        // We just need to keep track of our processing state so we can request a flush when
1836        // updating parameters from the GUI while the processing loop isn't running
1837        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
1838        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
1839
1840        // Always reset the processing status when the plugin gets activated or deactivated
1841        wrapper.last_process_status.store(ProcessStatus::Normal);
1842        wrapper.is_processing.store(true, Ordering::SeqCst);
1843
1844        // To be consistent with the VST3 wrapper, we'll also reset the buffers here in addition to
1845        // the dedicated `reset()` function.
1846        process_wrapper(|| {
1847            // In the case a host misbehaves and tries to activate/deactivate the plugin without
1848            // waiting for the `process` method to finish, manually wait for that method to finish.
1849            let now = Instant::now();
1850            loop {
1851                if let Some(mut plugin) = wrapper.plugin.try_lock() {
1852                    plugin.reset();
1853                    break;
1854                } else if now.elapsed() > Duration::from_millis(200) {
1855                    crate::nice_error!(
1856                        "Failed to acquire lock on plugin while starting processing"
1857                    );
1858                    break;
1859                } else {
1860                    std::thread::sleep(Duration::from_millis(1));
1861                }
1862            }
1863        });
1864
1865        true
1866    }
1867
1868    unsafe extern "C" fn stop_processing(plugin: *const clap_plugin) {
1869        check_null_ptr!((), plugin, unsafe { (*plugin).plugin_data });
1870        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
1871
1872        wrapper.is_processing.store(false, Ordering::SeqCst);
1873
1874        process_wrapper(|| {
1875            // In the case a host misbehaves and tries to activate/deactivate the plugin without
1876            // waiting for the `process` method to finish, manually wait for that method to finish.
1877            let now = Instant::now();
1878            loop {
1879                if let Some(mut plugin) = wrapper.plugin.try_lock() {
1880                    plugin.stop_processing();
1881                    break;
1882                } else if now.elapsed() > Duration::from_millis(200) {
1883                    crate::nice_error!(
1884                        "Failed to acquire lock on plugin while stopping processing"
1885                    );
1886                    break;
1887                } else {
1888                    std::thread::sleep(Duration::from_millis(1));
1889                }
1890            }
1891        });
1892    }
1893
1894    unsafe extern "C" fn reset(plugin: *const clap_plugin) {
1895        check_null_ptr!((), plugin, unsafe { (*plugin).plugin_data });
1896        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
1897
1898        process_wrapper(|| {
1899            // In the case a host misbehaves and tries to activate/deactivate the plugin without
1900            // waiting for the `process` method to finish, manually wait for that method to finish.
1901            let now = Instant::now();
1902            loop {
1903                if let Some(mut plugin) = wrapper.plugin.try_lock() {
1904                    plugin.reset();
1905                    break;
1906                } else if now.elapsed() > Duration::from_millis(200) {
1907                    crate::nice_error!("Failed to acquire lock on plugin while resetting");
1908                    break;
1909                } else {
1910                    std::thread::sleep(Duration::from_millis(1));
1911                }
1912            }
1913        });
1914    }
1915
1916    unsafe extern "C" fn process(
1917        plugin: *const clap_plugin,
1918        process: *const clap_process,
1919    ) -> clap_process_status {
1920        check_null_ptr!(
1921            CLAP_PROCESS_ERROR,
1922            plugin,
1923            unsafe { (*plugin).plugin_data },
1924            process
1925        );
1926        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
1927
1928        // Panic on allocations if the `assert_process_allocs` feature has been enabled, and make
1929        // sure that FTZ is set up correctly
1930        process_wrapper(|| {
1931            // We need to handle incoming automation and MIDI events. Since we don't support sample
1932            // accuration automation yet and there's no way to get the last event for a parameter,
1933            // we'll process every incoming event.
1934            let process = unsafe { &*process };
1935            let total_buffer_len = process.frames_count as usize;
1936
1937            let current_audio_io_layout = wrapper.current_audio_io_layout.load();
1938            let has_main_input = current_audio_io_layout.main_input_channels.is_some();
1939            let has_main_output = current_audio_io_layout.main_output_channels.is_some();
1940            let aux_input_start_idx = if has_main_input { 1 } else { 0 };
1941            let aux_output_start_idx = if has_main_output { 1 } else { 0 };
1942
1943            // If `P::SAMPLE_ACCURATE_AUTOMATION` is set, then we'll split up the audio buffer into
1944            // chunks whenever a parameter change occurs
1945            let mut block_start = 0;
1946            let mut block_end = total_buffer_len;
1947            let mut event_start_idx = 0;
1948
1949            // The host may send new transport information as an event. In that case we'll also
1950            // split the buffer.
1951            let mut transport_info = process.transport;
1952
1953            let result = loop {
1954                if !process.in_events.is_null() {
1955                    let split_result = unsafe {
1956                        wrapper.handle_in_events_until(
1957                            &*process.in_events,
1958                            &mut transport_info,
1959                            block_start,
1960                            total_buffer_len,
1961                            event_start_idx,
1962                            |next_event| {
1963                                // Always split the buffer on transport information changes (tempo, time
1964                                // signature, or position changes), and also split on parameter value
1965                                // changes after the current sample if sample accurate automation is
1966                                // enabled
1967                                if P::SAMPLE_ACCURATE_AUTOMATION {
1968                                    match ((*next_event).space_id, (*next_event).type_) {
1969                                        (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_PARAM_VALUE)
1970                                        | (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_TRANSPORT) => true,
1971                                        (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_PARAM_MOD) => {
1972                                            let next_event =
1973                                                &*(next_event as *const clap_event_param_mod);
1974
1975                                            // The buffer should not be split on polyphonic modulation
1976                                            // as those events will be converted to note events
1977                                            !(next_event.note_id >= 0
1978                                                && wrapper
1979                                                    .poly_mod_ids_by_hash
1980                                                    .contains_key(&next_event.param_id))
1981                                        }
1982                                        _ => false,
1983                                    }
1984                                } else {
1985                                    matches!(
1986                                        ((*next_event).space_id, (*next_event).type_,),
1987                                        (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_TRANSPORT)
1988                                    )
1989                                }
1990                            },
1991                        )
1992                    };
1993
1994                    // If there are any parameter changes after `block_start` and sample
1995                    // accurate automation is enabled or the host sends new transport
1996                    // information, then we'll process a new block just after that. Otherwise we can
1997                    // process all audio until the end of the buffer.
1998                    match split_result {
1999                        Some((next_param_change_sample_idx, next_param_change_event_idx)) => {
2000                            block_end = next_param_change_sample_idx;
2001                            event_start_idx = next_param_change_event_idx;
2002                        }
2003                        None => block_end = total_buffer_len,
2004                    }
2005                }
2006
2007                // After processing the events we now know where/if the block should be split, and
2008                // we can start preparing audio processing
2009                let block_len = block_end - block_start;
2010
2011                let Ok(mut buffer_manager) = wrapper.buffer_manager.try_borrow_mut() else {
2012                    // On the occasion a host misbehaves and tries to activate/deactivate a plugin
2013                    // concurrently with the process method, return an error.
2014                    crate::nice_error!(
2015                        "Host tried to activate/deactivate plugin while process method is still \
2016                         running"
2017                    );
2018
2019                    return CLAP_PROCESS_ERROR;
2020                };
2021
2022                // The buffer manager preallocated buffer slices for all the IO and storage for any
2023                // axuiliary inputs.
2024                // TODO: The audio buffers have a latency field, should we use those?
2025                // TODO: Like with VST3, should we expose some way to access or set the silence/constant
2026                //       flags?
2027                let buffers = unsafe {
2028                    buffer_manager.create_buffers(block_start, block_len, |buffer_source| {
2029                        // Explicitly take plugins with no main output that does have auxiliary
2030                        // outputs into account. Shouldn't happen, but if we just start copying
2031                        // audio here then that would result in unsoundness.
2032                        if process.audio_outputs_count > 0
2033                            && !process.audio_outputs.is_null()
2034                            && !(*process.audio_outputs).data32.is_null()
2035                            && has_main_output
2036                        {
2037                            let audio_output = &*process.audio_outputs;
2038                            let ptrs = NonNull::new(audio_output.data32).unwrap();
2039                            let num_channels = audio_output.channel_count as usize;
2040
2041                            *buffer_source.main_output_channel_pointers =
2042                                Some(ChannelPointers { ptrs, num_channels });
2043                        }
2044
2045                        if process.audio_inputs_count > 0
2046                            && !process.audio_inputs.is_null()
2047                            && !(*process.audio_inputs).data32.is_null()
2048                            && has_main_input
2049                        {
2050                            let audio_input = &*process.audio_inputs;
2051                            let ptrs = NonNull::new(audio_input.data32).unwrap();
2052                            let num_channels = audio_input.channel_count as usize;
2053
2054                            *buffer_source.main_input_channel_pointers =
2055                                Some(ChannelPointers { ptrs, num_channels });
2056                        }
2057
2058                        if !process.audio_inputs.is_null() {
2059                            for (aux_input_no, aux_input_channel_pointers) in buffer_source
2060                                .aux_input_channel_pointers
2061                                .iter_mut()
2062                                .enumerate()
2063                            {
2064                                let aux_input_idx = aux_input_no + aux_input_start_idx;
2065                                if aux_input_idx > process.audio_inputs_count as usize {
2066                                    break;
2067                                }
2068
2069                                let audio_input = &*process.audio_inputs.add(aux_input_idx);
2070                                match NonNull::new(audio_input.data32) {
2071                                    Some(ptrs) => {
2072                                        let num_channels = audio_input.channel_count as usize;
2073
2074                                        *aux_input_channel_pointers =
2075                                            Some(ChannelPointers { ptrs, num_channels });
2076                                    }
2077                                    None => continue,
2078                                }
2079                            }
2080                        }
2081
2082                        if !process.audio_outputs.is_null() {
2083                            for (aux_output_no, aux_output_channel_pointers) in buffer_source
2084                                .aux_output_channel_pointers
2085                                .iter_mut()
2086                                .enumerate()
2087                            {
2088                                let aux_output_idx = aux_output_no + aux_output_start_idx;
2089                                if aux_output_idx > process.audio_outputs_count as usize {
2090                                    break;
2091                                }
2092
2093                                let audio_output = &*process.audio_outputs.add(aux_output_idx);
2094                                match NonNull::new(audio_output.data32) {
2095                                    Some(ptrs) => {
2096                                        let num_channels = audio_output.channel_count as usize;
2097
2098                                        *aux_output_channel_pointers =
2099                                            Some(ChannelPointers { ptrs, num_channels });
2100                                    }
2101                                    None => continue,
2102                                }
2103                            }
2104                        }
2105                    })
2106                };
2107
2108                // If the host does not provide outputs or if it does not provide the required
2109                // number of channels (should not happen, but Ableton Live does this for bypassed
2110                // VST3 plugins) then we'll skip audio processing. In that case
2111                // `buffer_manager.create_buffers` will have set one or more of the output buffers
2112                // to empty slices since there is no storage to point them to. The auxiliary input
2113                // buffers always point to valid storage.
2114                let mut buffer_is_valid = true;
2115                for output_buffer_slice in buffers.main_buffer.as_slice_immutable().iter().chain(
2116                    buffers
2117                        .aux_outputs
2118                        .iter()
2119                        .flat_map(|buffer| buffer.as_slice_immutable().iter()),
2120                ) {
2121                    if output_buffer_slice.is_empty() {
2122                        buffer_is_valid = false;
2123                        break;
2124                    }
2125                }
2126
2127                crate::nice_debug_assert!(buffer_is_valid);
2128
2129                // Some of the fields are left empty because CLAP does not provide this information,
2130                // but the methods on [`Transport`] can reconstruct these values from the other
2131                // fields
2132                let sample_rate = wrapper
2133                    .current_buffer_config
2134                    .load()
2135                    .expect("Process call without prior initialization call")
2136                    .sample_rate;
2137                let mut transport = Transport::new(sample_rate);
2138                if !transport_info.is_null() {
2139                    let context = unsafe { &*transport_info };
2140
2141                    transport.playing = context.flags & CLAP_TRANSPORT_IS_PLAYING != 0;
2142                    transport.recording = context.flags & CLAP_TRANSPORT_IS_RECORDING != 0;
2143                    transport.preroll_active =
2144                        Some(context.flags & CLAP_TRANSPORT_IS_WITHIN_PRE_ROLL != 0);
2145                    if context.flags & CLAP_TRANSPORT_HAS_TEMPO != 0 {
2146                        transport.tempo = Some(context.tempo);
2147                    }
2148                    if context.flags & CLAP_TRANSPORT_HAS_TIME_SIGNATURE != 0 {
2149                        transport.time_sig_numerator = Some(context.tsig_num as i32);
2150                        transport.time_sig_denominator = Some(context.tsig_denom as i32);
2151                    }
2152                    if context.flags & CLAP_TRANSPORT_HAS_BEATS_TIMELINE != 0 {
2153                        let beats = context.song_pos_beats as f64 / CLAP_BEATTIME_FACTOR as f64;
2154
2155                        // This is a bit messy, but we'll try to compensate for the block splitting.
2156                        // We can't use the functions on the transport information object for this
2157                        // because we don't have any sample information.
2158                        if P::SAMPLE_ACCURATE_AUTOMATION
2159                            && block_start > 0
2160                            && (context.flags & CLAP_TRANSPORT_HAS_TEMPO != 0)
2161                        {
2162                            transport.pos_beats = Some(
2163                                beats
2164                                    + (block_start as f64 / sample_rate as f64 / 60.0
2165                                        * context.tempo),
2166                            );
2167                        } else {
2168                            transport.pos_beats = Some(beats);
2169                        }
2170                    }
2171                    if context.flags & CLAP_TRANSPORT_HAS_SECONDS_TIMELINE != 0 {
2172                        let seconds = context.song_pos_seconds as f64 / CLAP_SECTIME_FACTOR as f64;
2173
2174                        // Same here
2175                        if P::SAMPLE_ACCURATE_AUTOMATION
2176                            && block_start > 0
2177                            && (context.flags & CLAP_TRANSPORT_HAS_TEMPO != 0)
2178                        {
2179                            transport.pos_seconds =
2180                                Some(seconds + (block_start as f64 / sample_rate as f64));
2181                        } else {
2182                            transport.pos_seconds = Some(seconds);
2183                        }
2184                    }
2185                    // TODO: CLAP does not mention whether this is behind a flag or not
2186                    if P::SAMPLE_ACCURATE_AUTOMATION && block_start > 0 {
2187                        transport.bar_start_pos_beats = match transport.bar_start_pos_beats() {
2188                            Some(updated) => Some(updated),
2189                            None => Some(context.bar_start as f64 / CLAP_BEATTIME_FACTOR as f64),
2190                        };
2191                        transport.bar_number = match transport.bar_number() {
2192                            Some(updated) => Some(updated),
2193                            None => Some(context.bar_number),
2194                        };
2195                    } else {
2196                        transport.bar_start_pos_beats =
2197                            Some(context.bar_start as f64 / CLAP_BEATTIME_FACTOR as f64);
2198                        transport.bar_number = Some(context.bar_number);
2199                    }
2200                    // TODO: They also aren't very clear about this, but presumably if the loop is
2201                    //       active and the corresponding song transport information is available then
2202                    //       this is also available
2203                    if context.flags & CLAP_TRANSPORT_IS_LOOP_ACTIVE != 0
2204                        && context.flags & CLAP_TRANSPORT_HAS_BEATS_TIMELINE != 0
2205                    {
2206                        transport.loop_range_beats = Some((
2207                            context.loop_start_beats as f64 / CLAP_BEATTIME_FACTOR as f64,
2208                            context.loop_end_beats as f64 / CLAP_BEATTIME_FACTOR as f64,
2209                        ));
2210                    }
2211                    if context.flags & CLAP_TRANSPORT_IS_LOOP_ACTIVE != 0
2212                        && context.flags & CLAP_TRANSPORT_HAS_SECONDS_TIMELINE != 0
2213                    {
2214                        transport.loop_range_seconds = Some((
2215                            context.loop_start_seconds as f64 / CLAP_SECTIME_FACTOR as f64,
2216                            context.loop_end_seconds as f64 / CLAP_SECTIME_FACTOR as f64,
2217                        ));
2218                    }
2219                }
2220
2221                let result = if buffer_is_valid {
2222                    let Some(mut plugin) = wrapper.plugin.try_lock() else {
2223                        // On the occasion a host misbehaves and tries to activate/deactivate a plugin
2224                        // concurrently with the process method, return an error.
2225                        crate::nice_error!(
2226                            "Host tried to activate/deactivate plugin while process method is \
2227                             still running"
2228                        );
2229
2230                        return CLAP_PROCESS_ERROR;
2231                    };
2232
2233                    // SAFETY: Shortening these borrows is safe as even if the plugin overwrites the
2234                    //         slices (which it cannot do without using unsafe code), then they
2235                    //         would still be reset on the next iteration
2236                    let mut aux = AuxiliaryBuffers {
2237                        inputs: buffers.aux_inputs,
2238                        outputs: buffers.aux_outputs,
2239                    };
2240
2241                    let mut context = wrapper.make_process_context(
2242                        transport,
2243                        total_buffer_len,
2244                        block_start,
2245                        process.out_events,
2246                    );
2247
2248                    let result = plugin.process(buffers.main_buffer, &mut aux, &mut context);
2249
2250                    wrapper.last_process_status.store(result);
2251                    result
2252                } else {
2253                    ProcessStatus::Normal
2254                };
2255
2256                let clap_result = match result {
2257                    ProcessStatus::Error(err) => {
2258                        crate::nice_debug_assert_failure!("Process error: {}", err);
2259
2260                        return CLAP_PROCESS_ERROR;
2261                    }
2262                    ProcessStatus::Normal => CLAP_PROCESS_CONTINUE_IF_NOT_QUIET,
2263                    ProcessStatus::Tail(_) => CLAP_PROCESS_CONTINUE,
2264                    ProcessStatus::KeepAlive => CLAP_PROCESS_CONTINUE,
2265                };
2266
2267                if !process.out_events.is_null() && !wrapper.output_parameter_events.is_empty() {
2268                    unsafe { wrapper.handle_out_events(&*process.out_events, block_start) };
2269                }
2270
2271                // If our block ends at the end of the buffer then that means there are no more
2272                // unprocessed (parameter) events. If there are more events, we'll just keep going
2273                // through this process until we've processed the entire buffer.
2274                if block_end == total_buffer_len {
2275                    break clap_result;
2276                } else {
2277                    block_start = block_end;
2278                }
2279            };
2280
2281            // After processing audio, we'll check if the editor has sent us updated plugin state.
2282            // We'll restore that here on the audio thread to prevent changing the values during the
2283            // process call and also to prevent inconsistent state when the host also wants to load
2284            // plugin state.
2285            // FIXME: Zero capacity channels allocate on receiving, find a better alternative that
2286            //        doesn't do that
2287            let updated_state = permit_alloc(|| wrapper.updated_state_receiver.try_recv());
2288            if let Ok(mut state) = updated_state {
2289                wrapper.set_state_inner(&mut state);
2290
2291                // We'll pass the state object back to the GUI thread so deallocation can happen
2292                // there without potentially blocking the audio thread
2293                if let Err(err) = wrapper.updated_state_sender.send(state) {
2294                    crate::nice_debug_assert_failure!(
2295                        "Failed to send state object back to GUI thread: {}",
2296                        err
2297                    );
2298                };
2299            }
2300
2301            result
2302        })
2303    }
2304
2305    unsafe extern "C" fn get_extension(
2306        plugin: *const clap_plugin,
2307        id: *const c_char,
2308    ) -> *const c_void {
2309        check_null_ptr!(
2310            std::ptr::null(),
2311            plugin,
2312            unsafe { (*plugin).plugin_data },
2313            id
2314        );
2315        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2316
2317        let id = unsafe { CStr::from_ptr(id) };
2318
2319        if id == CLAP_EXT_PARAMS {
2320            &wrapper.clap_plugin_params as *const _ as *const c_void
2321        } else if id == CLAP_EXT_TAIL {
2322            &wrapper.clap_plugin_tail as *const _ as *const c_void
2323        } else if id == CLAP_EXT_GUI {
2324            #[cfg(not(feature = "editor"))]
2325            return std::ptr::null();
2326
2327            #[cfg(feature = "editor")]
2328            if wrapper.editor.borrow().is_some() {
2329                // Only report that we support this extension if the plugin has an editor
2330                &wrapper.clap_plugin_gui as *const _ as *const c_void
2331            } else {
2332                std::ptr::null()
2333            }
2334        } else if id == CLAP_EXT_AUDIO_PORTS_CONFIG {
2335            &wrapper.clap_plugin_audio_ports_config as *const _ as *const c_void
2336        } else if id == CLAP_EXT_AUDIO_PORTS {
2337            &wrapper.clap_plugin_audio_ports as *const _ as *const c_void
2338        } else if id == CLAP_EXT_LATENCY {
2339            &wrapper.clap_plugin_latency as *const _ as *const c_void
2340        } else if id == CLAP_EXT_NOTE_PORTS {
2341            if P::MIDI_INPUT >= MidiConfig::Basic || P::MIDI_OUTPUT >= MidiConfig::Basic {
2342                &wrapper.clap_plugin_note_ports as *const _ as *const c_void
2343            } else {
2344                std::ptr::null()
2345            }
2346        } else if id == CLAP_EXT_REMOTE_CONTROLS {
2347            &wrapper.clap_plugin_remote_controls as *const _ as *const c_void
2348        } else if id == CLAP_EXT_RENDER {
2349            &wrapper.clap_plugin_render as *const _ as *const c_void
2350        } else if id == CLAP_EXT_STATE {
2351            &wrapper.clap_plugin_state as *const _ as *const c_void
2352        } else if id == CLAP_EXT_TRACK_INFO {
2353            #[cfg(not(feature = "editor"))]
2354            return std::ptr::null();
2355
2356            #[cfg(feature = "editor")]
2357            return &wrapper.clap_plugin_track_info as *const _ as *const c_void;
2358        } else if id == CLAP_EXT_VOICE_INFO {
2359            if P::CLAP_POLY_MODULATION_CONFIG.is_some() {
2360                &wrapper.clap_plugin_voice_info as *const _ as *const c_void
2361            } else {
2362                std::ptr::null()
2363            }
2364        } else {
2365            crate::nice_trace!("Host tried to query unknown extension {:?}", id);
2366            std::ptr::null()
2367        }
2368    }
2369
2370    unsafe extern "C" fn on_main_thread(plugin: *const clap_plugin) {
2371        check_null_ptr!((), plugin, unsafe { (*plugin).plugin_data });
2372        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2373
2374        #[cfg(feature = "editor")]
2375        {
2376            use nice_plug_core::editor::EditorHandle;
2377
2378            if let Some(editor_window) = wrapper.editor_window.borrow().as_ref() {
2379                let editor_window = editor_window.get();
2380                editor_window
2381                    .handle
2382                    .host_main_thread_callback(&editor_window.window);
2383            }
2384        }
2385
2386        // [Self::schedule_gui] posts a task to the queue and asks the host to call this function
2387        // on the main thread, so once that's done we can just handle all requests here
2388        while let Some(task) = wrapper.tasks.pop() {
2389            wrapper.execute(task, true);
2390        }
2391    }
2392
2393    unsafe extern "C" fn ext_audio_ports_config_count(plugin: *const clap_plugin) -> u32 {
2394        check_null_ptr!(0, plugin, unsafe { (*plugin).plugin_data });
2395
2396        P::AUDIO_IO_LAYOUTS.len() as u32
2397    }
2398
2399    unsafe extern "C" fn ext_audio_ports_config_get(
2400        plugin: *const clap_plugin,
2401        index: u32,
2402        config: *mut clap_audio_ports_config,
2403    ) -> bool {
2404        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, config);
2405
2406        // This function directly maps to `P::AUDIO_IO_LAYOUTS`, and we thus also don't need to
2407        // access the `wrapper` instance
2408        match P::AUDIO_IO_LAYOUTS.get(index as usize) {
2409            Some(audio_io_layout) => {
2410                let name = audio_io_layout.name();
2411
2412                let main_input_channels = audio_io_layout.main_input_channels.map(NonZeroU32::get);
2413                let main_output_channels =
2414                    audio_io_layout.main_output_channels.map(NonZeroU32::get);
2415                let input_port_type = match main_input_channels {
2416                    Some(1) => CLAP_PORT_MONO.as_ptr(),
2417                    Some(2) => CLAP_PORT_STEREO.as_ptr(),
2418                    _ => std::ptr::null(),
2419                };
2420                let output_port_type = match main_output_channels {
2421                    Some(1) => CLAP_PORT_MONO.as_ptr(),
2422                    Some(2) => CLAP_PORT_STEREO.as_ptr(),
2423                    _ => std::ptr::null(),
2424                };
2425
2426                unsafe { *config = std::mem::zeroed() };
2427
2428                let config = unsafe { &mut *config };
2429                config.id = index;
2430                strlcpy(&mut config.name, &name);
2431                config.input_port_count = (if main_input_channels.is_some() { 1 } else { 0 }
2432                    + audio_io_layout.aux_input_ports.len())
2433                    as u32;
2434                config.output_port_count = (if main_output_channels.is_some() { 1 } else { 0 }
2435                    + audio_io_layout.aux_output_ports.len())
2436                    as u32;
2437                config.has_main_input = main_input_channels.is_some();
2438                config.main_input_channel_count = main_input_channels.unwrap_or_default();
2439                config.main_input_port_type = input_port_type;
2440                config.has_main_output = main_output_channels.is_some();
2441                config.main_output_channel_count = main_output_channels.unwrap_or_default();
2442                config.main_output_port_type = output_port_type;
2443
2444                true
2445            }
2446            None => {
2447                crate::nice_debug_assert_failure!(
2448                    "Host tried to query out of bounds audio port config {}",
2449                    index
2450                );
2451
2452                false
2453            }
2454        }
2455    }
2456
2457    unsafe extern "C" fn ext_audio_ports_config_select(
2458        plugin: *const clap_plugin,
2459        config_id: clap_id,
2460    ) -> bool {
2461        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
2462        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2463
2464        // We use the vector indices for the config ID
2465        match P::AUDIO_IO_LAYOUTS.get(config_id as usize) {
2466            Some(audio_io_layout) => {
2467                wrapper.current_audio_io_layout.store(*audio_io_layout);
2468
2469                true
2470            }
2471            None => {
2472                crate::nice_debug_assert_failure!(
2473                    "Host tried to select out of bounds audio port config {}",
2474                    config_id
2475                );
2476
2477                false
2478            }
2479        }
2480    }
2481
2482    unsafe extern "C" fn ext_audio_ports_count(plugin: *const clap_plugin, is_input: bool) -> u32 {
2483        check_null_ptr!(0, plugin, unsafe { (*plugin).plugin_data });
2484        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2485
2486        let audio_io_layout = wrapper.current_audio_io_layout.load();
2487        if is_input {
2488            let main_ports = if audio_io_layout.main_input_channels.is_some() {
2489                1
2490            } else {
2491                0
2492            };
2493            let aux_ports = audio_io_layout.aux_input_ports.len();
2494
2495            (main_ports + aux_ports) as u32
2496        } else {
2497            let main_ports = if audio_io_layout.main_output_channels.is_some() {
2498                1
2499            } else {
2500                0
2501            };
2502            let aux_ports = audio_io_layout.aux_output_ports.len();
2503
2504            (main_ports + aux_ports) as u32
2505        }
2506    }
2507
2508    unsafe extern "C" fn ext_audio_ports_get(
2509        plugin: *const clap_plugin,
2510        index: u32,
2511        is_input: bool,
2512        info: *mut clap_audio_port_info,
2513    ) -> bool {
2514        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, info);
2515        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2516
2517        let num_input_ports = unsafe { Self::ext_audio_ports_count(plugin, true) };
2518        let num_output_ports = unsafe { Self::ext_audio_ports_count(plugin, false) };
2519        if (is_input && index >= num_input_ports) || (!is_input && index >= num_output_ports) {
2520            crate::nice_debug_assert_failure!(
2521                "Host tried to query information for out of bounds audio port {} (input: {})",
2522                index,
2523                is_input
2524            );
2525
2526            return false;
2527        }
2528
2529        let current_audio_io_layout = wrapper.current_audio_io_layout.load();
2530        let has_main_input = current_audio_io_layout.main_input_channels.is_some();
2531        let has_main_output = current_audio_io_layout.main_output_channels.is_some();
2532
2533        // Whether this port is a main port or an auxiliary (sidechain) port
2534        let is_main_port =
2535            index == 0 && ((is_input && has_main_input) || (!is_input && has_main_output));
2536
2537        // We'll number the ports in a linear order from `0..num_input_ports` and
2538        // `num_input_ports..(num_input_ports + num_output_ports)`
2539        let stable_id = if is_input {
2540            index
2541        } else {
2542            index + num_input_ports
2543        };
2544
2545        // Allow processing the main input/output ports in-place if their channel count is the same.
2546        let can_process_in_place = current_audio_io_layout.main_input_channels
2547            == current_audio_io_layout.main_output_channels;
2548        let pair_stable_id = match (is_input, is_main_port) {
2549            // Ports are named linearly with inputs coming before outputs, so this is the index of
2550            // the first output port
2551            (true, true) if has_main_output && can_process_in_place => num_input_ports,
2552            (false, true) if has_main_input && can_process_in_place => 0,
2553            _ => CLAP_INVALID_ID,
2554        };
2555
2556        let channel_count = match (index, is_input) {
2557            (0, true) if has_main_input => {
2558                current_audio_io_layout.main_input_channels.unwrap().get()
2559            }
2560            (0, false) if has_main_output => {
2561                current_audio_io_layout.main_output_channels.unwrap().get()
2562            }
2563            // `index` is off by one for the auxiliary ports if the plugin has a main port
2564            (n, true) if has_main_input => {
2565                current_audio_io_layout.aux_input_ports[n as usize - 1].get()
2566            }
2567            (n, false) if has_main_output => {
2568                current_audio_io_layout.aux_output_ports[n as usize - 1].get()
2569            }
2570            (n, true) => current_audio_io_layout.aux_input_ports[n as usize].get(),
2571            (n, false) => current_audio_io_layout.aux_output_ports[n as usize].get(),
2572        };
2573
2574        let port_type = match channel_count {
2575            1 => CLAP_PORT_MONO.as_ptr(),
2576            2 => CLAP_PORT_STEREO.as_ptr(),
2577            _ => std::ptr::null(),
2578        };
2579
2580        unsafe { *info = std::mem::zeroed() };
2581
2582        let info = unsafe { &mut *info };
2583        info.id = stable_id;
2584        match (is_input, is_main_port) {
2585            (true, true) => strlcpy(&mut info.name, &current_audio_io_layout.main_input_name()),
2586            (false, true) => strlcpy(&mut info.name, &current_audio_io_layout.main_output_name()),
2587            (true, false) => {
2588                let aux_input_idx = if has_main_input { index - 1 } else { index } as usize;
2589                strlcpy(
2590                    &mut info.name,
2591                    &current_audio_io_layout
2592                        .aux_input_name(aux_input_idx)
2593                        .expect("Out of bounds auxiliary input port"),
2594                );
2595            }
2596            (false, false) => {
2597                let aux_output_idx = if has_main_output { index - 1 } else { index } as usize;
2598                strlcpy(
2599                    &mut info.name,
2600                    &current_audio_io_layout
2601                        .aux_output_name(aux_output_idx)
2602                        .expect("Out of bounds auxiliary output port"),
2603                );
2604            }
2605        };
2606        info.flags = if is_main_port {
2607            CLAP_AUDIO_PORT_IS_MAIN
2608        } else {
2609            0
2610        };
2611        info.channel_count = channel_count;
2612        info.port_type = port_type;
2613        info.in_place_pair = pair_stable_id;
2614
2615        true
2616    }
2617
2618    #[cfg(feature = "editor")]
2619    unsafe extern "C" fn ext_gui_is_api_supported(
2620        _plugin: *const clap_plugin,
2621        api: *const c_char,
2622        is_floating: bool,
2623    ) -> bool {
2624        // We don't do standalone floating windows
2625        if is_floating {
2626            return false;
2627        }
2628
2629        unsafe {
2630            #[cfg(all(target_family = "unix", not(target_os = "macos")))]
2631            if CStr::from_ptr(api) == clap_sys::ext::gui::CLAP_WINDOW_API_X11 {
2632                return true;
2633            }
2634            #[cfg(target_os = "macos")]
2635            if CStr::from_ptr(api) == clap_sys::ext::gui::CLAP_WINDOW_API_COCOA {
2636                return true;
2637            }
2638            #[cfg(target_os = "windows")]
2639            if CStr::from_ptr(api) == clap_sys::ext::gui::CLAP_WINDOW_API_WIN32 {
2640                return true;
2641            }
2642        }
2643
2644        false
2645    }
2646
2647    #[cfg(feature = "editor")]
2648    unsafe extern "C" fn ext_gui_get_preferred_api(
2649        _plugin: *const clap_plugin,
2650        api: *mut *const c_char,
2651        is_floating: *mut bool,
2652    ) -> bool {
2653        check_null_ptr!(false, api, is_floating);
2654
2655        unsafe {
2656            #[cfg(all(target_family = "unix", not(target_os = "macos")))]
2657            {
2658                *api = clap_sys::ext::gui::CLAP_WINDOW_API_X11.as_ptr();
2659            }
2660            #[cfg(target_os = "macos")]
2661            {
2662                *api = clap_sys::ext::gui::CLAP_WINDOW_API_COCOA.as_ptr();
2663            }
2664            #[cfg(target_os = "windows")]
2665            {
2666                *api = clap_sys::ext::gui::CLAP_WINDOW_API_WIN32.as_ptr();
2667            }
2668
2669            // We don't do standalone floating windows yet
2670            *is_floating = false;
2671        }
2672
2673        true
2674    }
2675
2676    #[cfg(feature = "editor")]
2677    unsafe extern "C" fn ext_gui_create(
2678        plugin: *const clap_plugin,
2679        api: *const c_char,
2680        is_floating: bool,
2681    ) -> bool {
2682        // Double check this in case the host didn't
2683        if unsafe { !Self::ext_gui_is_api_supported(plugin, api, is_floating) } {
2684            return false;
2685        }
2686
2687        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
2688        // For this function we need the underlying Arc so we can pass it to the editor
2689        let wrapper = unsafe { Arc::from_raw((*plugin).plugin_data as *const Self) };
2690
2691        let result = {
2692            if wrapper.editor_window.borrow().is_none() {
2693                use std::error::Error;
2694
2695                use nice_plug_core::editor::{
2696                    HostCallbacks, HostMainThreadCaller, HostMethods, dpi::Size,
2697                };
2698
2699                #[derive(Debug, thiserror::Error)]
2700                enum ResizeError {
2701                    #[error("Host refused window size: {0:?}")]
2702                    HostRefusedSize(Size),
2703                    #[error("Attempted to close window when plugin was closed")]
2704                    PluginClosed,
2705                }
2706
2707                struct ClapHostCallbacks<P: ClapPlugin> {
2708                    wrapper: Weak<Wrapper<P>>,
2709                    host_gui: ClapPtr<clap_host_gui>,
2710                }
2711
2712                impl<P: ClapPlugin> HostCallbacks for ClapHostCallbacks<P> {
2713                    fn request_resize(
2714                        &mut self,
2715                        new_size: Size,
2716                        scale_factor: f64,
2717                    ) -> Result<(), Box<dyn Error>> {
2718                        if let Some(wrapper) = self.wrapper.upgrade() {
2719                            use nice_plug_core::editor::dpi::NativeSize;
2720
2721                            let native_size = NativeSize::from_size(new_size, scale_factor);
2722
2723                            if unsafe_clap_call! {
2724                                &*self.host_gui=>request_resize(
2725                                    &*wrapper.host_callback,
2726                                    native_size.width,
2727                                    native_size.height,
2728                                )
2729                            } {
2730                                Ok(())
2731                            } else {
2732                                Err(ResizeError::HostRefusedSize(new_size).into())
2733                            }
2734                        } else {
2735                            Err(ResizeError::PluginClosed.into())
2736                        }
2737                    }
2738
2739                    fn destroyed(&mut self) {
2740                        if let Some(wrapper) = self.wrapper.upgrade() {
2741                            unsafe_clap_call! {
2742                                &*self.host_gui=>closed(
2743                                    &*wrapper.host_callback,
2744                                    true,
2745                                )
2746                            }
2747                        }
2748                    }
2749                }
2750
2751                let callbacks: Box<dyn HostCallbacks> = Box::new(ClapHostCallbacks {
2752                    wrapper: wrapper.this.borrow().clone(),
2753                    host_gui: ClapPtr::clone(wrapper.host_gui.borrow().as_ref().unwrap()),
2754                });
2755
2756                struct ClapHostMainThreadCaller<P: ClapPlugin> {
2757                    wrapper: Weak<Wrapper<P>>,
2758                }
2759
2760                impl<P: ClapPlugin> HostMainThreadCaller for ClapHostMainThreadCaller<P> {
2761                    fn call_main_thread(&mut self) {
2762                        if let Some(wrapper) = self.wrapper.upgrade() {
2763                            unsafe_clap_call! { &*wrapper.host_callback=>request_callback(&*wrapper.host_callback) };
2764                        }
2765                    }
2766                }
2767
2768                let main_thread_caller: Box<dyn HostMainThreadCaller> =
2769                    Box::new(ClapHostMainThreadCaller {
2770                        wrapper: wrapper.this.borrow().clone(),
2771                    });
2772
2773                let fallback_scale_factor = wrapper.fallback_scale_factor.load();
2774
2775                match wrapper.editor.borrow().as_ref().unwrap().lock().spawn(
2776                    None,
2777                    true,
2778                    fallback_scale_factor,
2779                    wrapper.clone().make_gui_context(),
2780                    Some(HostMethods {
2781                        callbacks,
2782                        main_thread_caller,
2783                    }),
2784                ) {
2785                    Ok(editor_window) => {
2786                        *wrapper.editor_window.borrow_mut() =
2787                            Some(fragile::Fragile::new(editor_window));
2788                        true
2789                    }
2790                    Err(e) => {
2791                        crate::nice_error!("Failed to open editor: {}", e);
2792                        false
2793                    }
2794                }
2795            } else {
2796                #[cfg(debug_assertions)]
2797                crate::nice_warn!("Host tried to create editor while editor is already open");
2798
2799                false
2800            }
2801        };
2802
2803        // Leak the Arc again since we only needed a clone to pass to the GuiContext
2804        let _ = Arc::into_raw(wrapper);
2805
2806        result
2807    }
2808
2809    #[cfg(feature = "editor")]
2810    unsafe extern "C" fn ext_gui_set_parent(
2811        plugin: *const clap_plugin,
2812        window: *const clap_sys::ext::gui::clap_window,
2813    ) -> bool {
2814        use nice_plug_core::editor::{EditorHandle, ParentWindowHandle};
2815        use std::ffi::c_ulong;
2816        use std::num::NonZeroIsize;
2817
2818        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, window);
2819        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2820        let window = unsafe { &*window };
2821
2822        if let Some(editor_window) = wrapper.editor_window.borrow().as_ref() {
2823            let editor_window = editor_window.get();
2824
2825            let api = unsafe { CStr::from_ptr(window.api) };
2826            let parent_handle = unsafe {
2827                if api == clap_sys::ext::gui::CLAP_WINDOW_API_X11 {
2828                    #[allow(clippy::unnecessary_cast)]
2829                    let w = window.specific.x11 as c_ulong;
2830                    ParentWindowHandle::XlibWindow(w)
2831                } else if api == clap_sys::ext::gui::CLAP_WINDOW_API_COCOA {
2832                    check_null_ptr!(false, window.specific.cocoa);
2833                    let w = NonNull::new(window.specific.cocoa).unwrap();
2834                    ParentWindowHandle::AppKitNsView(w)
2835                } else if api == clap_sys::ext::gui::CLAP_WINDOW_API_WIN32 {
2836                    check_null_ptr!(false, window.specific.win32);
2837                    let w = NonZeroIsize::new(window.specific.win32 as isize).unwrap();
2838                    ParentWindowHandle::Win32Hwnd(w)
2839                } else {
2840                    crate::nice_debug_assert_failure!("Host passed an invalid API");
2841                    return false;
2842                }
2843            };
2844
2845            if let Err(e) = editor_window
2846                .handle
2847                .set_parent(parent_handle, editor_window.window.borrow())
2848            {
2849                crate::nice_error!("Failed to set editor parent window: {}", e);
2850
2851                false
2852            } else {
2853                true
2854            }
2855        } else {
2856            #[cfg(debug_assertions)]
2857            crate::nice_warn!("Host tried to set parent window while editor is not open");
2858
2859            false
2860        }
2861    }
2862
2863    #[cfg(feature = "editor")]
2864    unsafe extern "C" fn ext_gui_destroy(plugin: *const clap_plugin) {
2865        check_null_ptr!((), plugin, unsafe { (*plugin).plugin_data });
2866        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2867
2868        let mut editor_handle = wrapper.editor_window.borrow_mut();
2869        if editor_handle.is_some() {
2870            *editor_handle = None;
2871        } else {
2872            #[cfg(debug_assertions)]
2873            crate::nice_warn!("Tried destroying editor while the editor was not active");
2874        }
2875    }
2876
2877    #[cfg(feature = "editor")]
2878    unsafe extern "C" fn ext_gui_get_size(
2879        plugin: *const clap_plugin,
2880        width: *mut u32,
2881        height: *mut u32,
2882    ) -> bool {
2883        check_null_ptr!(
2884            false,
2885            plugin,
2886            unsafe { (*plugin).plugin_data },
2887            width,
2888            height
2889        );
2890        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2891
2892        if let Some(editor) = wrapper.editor.borrow().as_ref() {
2893            let size = editor.lock().size();
2894
2895            unsafe {
2896                *width = size.width;
2897                *height = size.height;
2898            }
2899
2900            true
2901        } else {
2902            false
2903        }
2904    }
2905
2906    #[cfg(feature = "editor")]
2907    unsafe extern "C" fn ext_gui_can_resize(plugin: *const clap_plugin) -> bool {
2908        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
2909        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2910
2911        // The editor decides whether it's resizable via `Editor::resize_hint()`.
2912        match wrapper.editor.borrow().as_ref() {
2913            Some(editor) => editor.lock().resize_hint().can_resize,
2914            None => false,
2915        }
2916    }
2917
2918    #[cfg(feature = "editor")]
2919    unsafe extern "C" fn ext_gui_get_resize_hints(
2920        plugin: *const clap_plugin,
2921        hints: *mut clap_sys::ext::gui::clap_gui_resize_hints,
2922    ) -> bool {
2923        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, hints);
2924        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2925
2926        let hint = match wrapper.editor.borrow().as_ref() {
2927            Some(editor) => editor.lock().resize_hint(),
2928            None => return false,
2929        };
2930        if !hint.can_resize {
2931            return false;
2932        }
2933
2934        let hints = unsafe { &mut *hints };
2935        hints.can_resize_horizontally = hint.can_resize_horizontally;
2936        hints.can_resize_vertically = hint.can_resize_vertically;
2937        hints.preserve_aspect_ratio = hint.preserve_aspect_ratio;
2938        hints.aspect_ratio_width = hint.aspect_ratio_width;
2939        hints.aspect_ratio_height = hint.aspect_ratio_height;
2940
2941        true
2942    }
2943
2944    #[cfg(feature = "editor")]
2945    unsafe extern "C" fn ext_gui_adjust_size(
2946        plugin: *const clap_plugin,
2947        width: *mut u32,
2948        height: *mut u32,
2949    ) -> bool {
2950        use nice_plug_core::editor::EditorHandle;
2951        use nice_plug_core::editor::dpi::NativeSize;
2952
2953        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
2954        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2955
2956        if let Some(editor_window) = wrapper.editor_window.borrow().as_ref() {
2957            let editor_window = editor_window.get();
2958
2959            let size = unsafe { NativeSize::new(*width, *height) };
2960
2961            if let Some(new_size) = editor_window
2962                .handle
2963                .adjust_size(size, editor_window.window.borrow())
2964            {
2965                unsafe {
2966                    *width = new_size.width;
2967                    *height = new_size.height;
2968                }
2969
2970                true
2971            } else {
2972                false
2973            }
2974        } else {
2975            false
2976        }
2977    }
2978
2979    #[cfg(feature = "editor")]
2980    unsafe extern "C" fn ext_gui_set_scale(plugin: *const clap_plugin, scale: f64) -> bool {
2981        use nice_plug_core::editor::EditorHandle;
2982
2983        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
2984        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2985
2986        if let Some(editor_window) = wrapper.editor_window.borrow().as_ref() {
2987            let editor_window = editor_window.get();
2988
2989            if let Err(e) = editor_window
2990                .handle
2991                .set_fallback_scale_factor(scale, editor_window.window.borrow())
2992            {
2993                crate::nice_error!("Failed to set suggested scale factor: {}", e);
2994                false
2995            } else {
2996                wrapper.fallback_scale_factor.store(Some(scale));
2997                true
2998            }
2999        } else {
3000            false
3001        }
3002    }
3003
3004    #[cfg(feature = "editor")]
3005    unsafe extern "C" fn ext_gui_set_size(
3006        plugin: *const clap_plugin,
3007        width: u32,
3008        height: u32,
3009    ) -> bool {
3010        use nice_plug_core::editor::EditorHandle;
3011
3012        // The host calls this after honoring an earlier `request_resize()`, when
3013        // the user drags a host-drawn resize handle, and (on Linux) if an
3014        // asynchronous resize request fails.
3015        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
3016        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3017
3018        // Hand the new size to the editor. If there is no editor open, or the
3019        // editor doesn't support being resized, this fails and we tell the host so.
3020        if let Some(editor_window) = wrapper.editor_window.borrow().as_ref() {
3021            let editor_window = editor_window.get();
3022
3023            if let Err(e) = editor_window.handle.set_size(
3024                nice_plug_core::editor::dpi::NativeSize { width, height },
3025                editor_window.window.borrow(),
3026            ) {
3027                crate::nice_error!("Failed to resize window to ({}, {}): {}", width, height, e);
3028                false
3029            } else {
3030                true
3031            }
3032        } else {
3033            false
3034        }
3035    }
3036
3037    #[cfg(feature = "editor")]
3038    unsafe extern "C" fn ext_gui_set_transient(
3039        _plugin: *const clap_plugin,
3040        _window: *const clap_sys::ext::gui::clap_window,
3041    ) -> bool {
3042        // This is only relevant for floating windows
3043        false
3044    }
3045
3046    #[cfg(feature = "editor")]
3047    unsafe extern "C" fn ext_gui_suggest_title(_plugin: *const clap_plugin, _title: *const c_char) {
3048        // This is only relevant for floating windows
3049    }
3050
3051    #[cfg(feature = "editor")]
3052    unsafe extern "C" fn ext_gui_show(plugin: *const clap_plugin) -> bool {
3053        use nice_plug_core::editor::EditorHandle;
3054
3055        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
3056        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3057
3058        if let Some(editor_window) = wrapper.editor_window.borrow().as_ref() {
3059            let editor_window = editor_window.get();
3060
3061            if let Err(e) = editor_window.handle.show(editor_window.window.borrow()) {
3062                crate::nice_error!("Failed to show editor window: {}", e);
3063                false
3064            } else {
3065                true
3066            }
3067        } else {
3068            false
3069        }
3070    }
3071
3072    #[cfg(feature = "editor")]
3073    unsafe extern "C" fn ext_gui_hide(plugin: *const clap_plugin) -> bool {
3074        use nice_plug_core::editor::EditorHandle;
3075
3076        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
3077        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3078
3079        if let Some(editor_window) = wrapper.editor_window.borrow().as_ref() {
3080            let editor_window = editor_window.get();
3081
3082            if let Err(e) = editor_window.handle.hide(editor_window.window.borrow()) {
3083                crate::nice_error!("Failed to hide editor window: {}", e);
3084                false
3085            } else {
3086                true
3087            }
3088        } else {
3089            false
3090        }
3091    }
3092
3093    unsafe extern "C" fn ext_latency_get(plugin: *const clap_plugin) -> u32 {
3094        check_null_ptr!(0, plugin, unsafe { (*plugin).plugin_data });
3095        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3096
3097        wrapper.current_latency.load(Ordering::SeqCst)
3098    }
3099
3100    unsafe extern "C" fn ext_note_ports_count(_plugin: *const clap_plugin, is_input: bool) -> u32 {
3101        match is_input {
3102            true if P::MIDI_INPUT >= MidiConfig::Basic => 1,
3103            false if P::MIDI_OUTPUT >= MidiConfig::Basic => 1,
3104            _ => 0,
3105        }
3106    }
3107
3108    unsafe extern "C" fn ext_note_ports_get(
3109        _plugin: *const clap_plugin,
3110        index: u32,
3111        is_input: bool,
3112        info: *mut clap_note_port_info,
3113    ) -> bool {
3114        match (index, is_input) {
3115            (0, true) if P::MIDI_INPUT >= MidiConfig::Basic => {
3116                unsafe {
3117                    *info = std::mem::zeroed();
3118                }
3119
3120                let info = unsafe { &mut *info };
3121                info.id = 0;
3122                // NOTE: REAPER won't send us SysEx if we don't support the MIDI dialect
3123                // TODO: Implement MPE (would just be a toggle for the plugin to expose it) and MIDI2
3124                info.supported_dialects = CLAP_NOTE_DIALECT_CLAP | CLAP_NOTE_DIALECT_MIDI;
3125                info.preferred_dialect = CLAP_NOTE_DIALECT_CLAP;
3126                strlcpy(&mut info.name, "Note Input");
3127
3128                true
3129            }
3130            (0, false) if P::MIDI_OUTPUT >= MidiConfig::Basic => {
3131                unsafe { *info = std::mem::zeroed() };
3132
3133                let info = unsafe { &mut *info };
3134                info.id = 0;
3135                // If `P::MIDI_OUTPUT < MidiConfig::MidiCCs` we'll throw away MIDI CCs, pitch bend
3136                // messages, and other messages that are not basic note on, off and polyphonic
3137                // pressure messages. This way the behavior is the same as the VST3 wrapper.
3138                info.supported_dialects = CLAP_NOTE_DIALECT_CLAP | CLAP_NOTE_DIALECT_MIDI;
3139                info.preferred_dialect = CLAP_NOTE_DIALECT_CLAP;
3140                strlcpy(&mut info.name, "Note Output");
3141
3142                true
3143            }
3144            _ => false,
3145        }
3146    }
3147
3148    unsafe extern "C" fn ext_params_count(plugin: *const clap_plugin) -> u32 {
3149        check_null_ptr!(0, plugin, unsafe { (*plugin).plugin_data });
3150        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3151
3152        wrapper.param_hashes.len() as u32
3153    }
3154
3155    unsafe extern "C" fn ext_params_get_info(
3156        plugin: *const clap_plugin,
3157        param_index: u32,
3158        param_info: *mut clap_param_info,
3159    ) -> bool {
3160        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, param_info);
3161        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3162
3163        if param_index > unsafe { Self::ext_params_count(plugin) } {
3164            return false;
3165        }
3166
3167        let param_hash = &wrapper.param_hashes[param_index as usize];
3168        let param_group = &wrapper.param_group_by_hash[param_hash];
3169        let param_ptr = &wrapper.param_by_hash[param_hash];
3170        let default_value = unsafe { param_ptr.default_normalized_value() };
3171        let step_count = unsafe { param_ptr.step_count() };
3172        let flags = unsafe { param_ptr.flags() };
3173        let automatable = !flags.contains(ParamFlags::NON_AUTOMATABLE);
3174        let hidden = flags.contains(ParamFlags::HIDDEN);
3175        let is_bypass = flags.contains(ParamFlags::BYPASS);
3176
3177        unsafe {
3178            *param_info = std::mem::zeroed();
3179        }
3180
3181        // TODO: We don't use the cookies at this point. In theory this would be faster than the ID
3182        //       hashmap lookup, but for now we'll stay consistent with the VST3 implementation.
3183        let param_info = unsafe { &mut *param_info };
3184        param_info.id = *param_hash;
3185        // TODO: Somehow expose per note/channel/port modulation
3186        param_info.flags = 0;
3187        if automatable && !hidden {
3188            param_info.flags |= CLAP_PARAM_IS_AUTOMATABLE | CLAP_PARAM_IS_MODULATABLE;
3189            if wrapper.poly_mod_ids_by_hash.contains_key(param_hash) {
3190                param_info.flags |= CLAP_PARAM_IS_MODULATABLE_PER_NOTE_ID;
3191            }
3192        }
3193        if hidden {
3194            param_info.flags |= CLAP_PARAM_IS_HIDDEN | CLAP_PARAM_IS_READONLY;
3195        }
3196        if is_bypass {
3197            param_info.flags |= CLAP_PARAM_IS_BYPASS
3198        }
3199        if step_count.is_some() {
3200            param_info.flags |= CLAP_PARAM_IS_STEPPED
3201        }
3202        param_info.cookie = std::ptr::null_mut();
3203        strlcpy(&mut param_info.name, unsafe { param_ptr.name() });
3204        strlcpy(&mut param_info.module, param_group);
3205        // We don't use the actual minimum and maximum values here because that would not scale
3206        // with skewed integer ranges. Instead, just treat all parameters as `[0, 1]` normalized
3207        // parameters multiplied by the step size.
3208        param_info.min_value = 0.0;
3209        // Stepped parameters are unnormalized float parameters since there's no separate step
3210        // range option
3211        // TODO: This should probably be encapsulated in some way so we don't forget about this in one place
3212        param_info.max_value = step_count.unwrap_or(1) as f64;
3213        param_info.default_value = default_value as f64 * step_count.unwrap_or(1) as f64;
3214
3215        true
3216    }
3217
3218    unsafe extern "C" fn ext_params_get_value(
3219        plugin: *const clap_plugin,
3220        param_id: clap_id,
3221        value: *mut f64,
3222    ) -> bool {
3223        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, value);
3224        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3225
3226        match wrapper.param_by_hash.get(&param_id) {
3227            Some(param_ptr) => {
3228                unsafe {
3229                    *value = param_ptr.modulated_normalized_value() as f64
3230                        * param_ptr.step_count().unwrap_or(1) as f64;
3231                }
3232
3233                true
3234            }
3235            _ => false,
3236        }
3237    }
3238
3239    unsafe extern "C" fn ext_params_value_to_text(
3240        plugin: *const clap_plugin,
3241        param_id: clap_id,
3242        value: f64,
3243        display: *mut c_char,
3244        size: u32,
3245    ) -> bool {
3246        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, display);
3247        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3248
3249        let dest = unsafe { std::slice::from_raw_parts_mut(display, size as usize) };
3250
3251        match wrapper.param_by_hash.get(&param_id) {
3252            Some(param_ptr) => {
3253                unsafe {
3254                    strlcpy(
3255                        dest,
3256                        // CLAP does not have a separate unit, so we'll include the unit here
3257                        &param_ptr.normalized_value_to_string(
3258                            value as f32 / param_ptr.step_count().unwrap_or(1) as f32,
3259                            true,
3260                        ),
3261                    );
3262                }
3263
3264                true
3265            }
3266            _ => false,
3267        }
3268    }
3269
3270    unsafe extern "C" fn ext_params_text_to_value(
3271        plugin: *const clap_plugin,
3272        param_id: clap_id,
3273        display: *const c_char,
3274        value: *mut f64,
3275    ) -> bool {
3276        check_null_ptr!(
3277            false,
3278            plugin,
3279            unsafe { (*plugin).plugin_data },
3280            display,
3281            value
3282        );
3283        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3284
3285        let display = match unsafe { CStr::from_ptr(display).to_str() } {
3286            Ok(s) => s,
3287            Err(_) => return false,
3288        };
3289
3290        match wrapper.param_by_hash.get(&param_id) {
3291            Some(param_ptr) => {
3292                let normalized_value =
3293                    match unsafe { param_ptr.string_to_normalized_value(display) } {
3294                        Some(v) => v as f64,
3295                        None => return false,
3296                    };
3297                unsafe {
3298                    *value = normalized_value * param_ptr.step_count().unwrap_or(1) as f64;
3299                }
3300
3301                true
3302            }
3303            _ => false,
3304        }
3305    }
3306
3307    unsafe extern "C" fn ext_params_flush(
3308        plugin: *const clap_plugin,
3309        in_: *const clap_input_events,
3310        out: *const clap_output_events,
3311    ) {
3312        check_null_ptr!((), plugin, unsafe { (*plugin).plugin_data });
3313        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3314
3315        if !in_.is_null() {
3316            unsafe {
3317                wrapper.handle_in_events(&*in_, 0, 0);
3318            }
3319        }
3320
3321        if !out.is_null() {
3322            unsafe {
3323                wrapper.handle_out_events(&*out, 0);
3324            }
3325        }
3326    }
3327
3328    unsafe extern "C" fn ext_remote_controls_count(plugin: *const clap_plugin) -> u32 {
3329        check_null_ptr!(0, plugin, unsafe { (*plugin).plugin_data });
3330        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3331
3332        wrapper.remote_control_pages.len() as u32
3333    }
3334
3335    unsafe extern "C" fn ext_remote_controls_get(
3336        plugin: *const clap_plugin,
3337        page_index: u32,
3338        page: *mut clap_remote_controls_page,
3339    ) -> bool {
3340        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, page);
3341        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3342
3343        crate::nice_debug_assert!(page_index as usize <= wrapper.remote_control_pages.len());
3344        match wrapper.remote_control_pages.get(page_index as usize) {
3345            Some(p) => {
3346                unsafe {
3347                    *page = *p;
3348                }
3349                true
3350            }
3351            None => false,
3352        }
3353    }
3354
3355    unsafe extern "C" fn ext_render_has_hard_realtime_requirement(
3356        _plugin: *const clap_plugin,
3357    ) -> bool {
3358        P::HARD_REALTIME_ONLY
3359    }
3360
3361    unsafe extern "C" fn ext_render_set(
3362        plugin: *const clap_plugin,
3363        mode: clap_plugin_render_mode,
3364    ) -> bool {
3365        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
3366        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3367
3368        let mode = match mode {
3369            CLAP_RENDER_REALTIME => ProcessMode::Realtime,
3370            // Even if the plugin has a hard realtime requirement, we'll still honor this
3371            CLAP_RENDER_OFFLINE => ProcessMode::Offline,
3372            n => {
3373                crate::nice_error!("Unknown rendering mode '{}', defaulting to realtime", n);
3374                ProcessMode::Realtime
3375            }
3376        };
3377
3378        if wrapper.current_process_mode.swap(mode) != mode
3379            && wrapper.is_activated.load(Ordering::SeqCst)
3380        {
3381            // We may change process mode while activated. In that case, restart the audio processor
3382            // so the plugin can react to the process mode change in `Plugin::activate`.
3383            wrapper.request_restart();
3384        }
3385
3386        true
3387    }
3388
3389    unsafe extern "C" fn ext_state_save(
3390        plugin: *const clap_plugin,
3391        stream: *const clap_ostream,
3392    ) -> bool {
3393        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, stream);
3394        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3395
3396        let serialized = unsafe {
3397            state::serialize_json::<P>(
3398                wrapper.params.clone(),
3399                state::make_params_iter(&wrapper.param_by_hash, &wrapper.param_id_to_hash),
3400            )
3401        };
3402        match serialized {
3403            Ok(serialized) => {
3404                // CLAP does not provide a way to tell how much data there is left in a stream, so
3405                // we need to prepend it to our actual state data.
3406                let length_bytes = (serialized.len() as u64).to_le_bytes();
3407                if !write_stream(unsafe { &*stream }, &length_bytes) {
3408                    crate::nice_error!(
3409                        "Failed to save state: Error or end of stream while writing the state \
3410                         length"
3411                    );
3412                    return false;
3413                }
3414                if !write_stream(unsafe { &*stream }, &serialized) {
3415                    crate::nice_error!(
3416                        "Failed to save state: Error or end of stream while writing the state \
3417                         buffer"
3418                    );
3419                    return false;
3420                }
3421
3422                crate::nice_trace!("Saved state ({} bytes)", serialized.len());
3423
3424                true
3425            }
3426            Err(err) => {
3427                crate::nice_error!("Failed to save state: {}", err);
3428                false
3429            }
3430        }
3431    }
3432
3433    unsafe extern "C" fn ext_state_load(
3434        plugin: *const clap_plugin,
3435        stream: *const clap_istream,
3436    ) -> bool {
3437        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, stream);
3438        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3439
3440        // CLAP does not have a way to tell how much data there is left in a stream, so we've
3441        // prepended the size in front of our JSON state
3442        let mut length_bytes = [0u8; 8];
3443        let bytes_read = read_stream(unsafe { &*stream }, length_bytes.as_mut_slice());
3444        if bytes_read != Some(8) {
3445            crate::nice_error!(
3446                "Failed to load state: Error or end of stream while reading the state length"
3447            );
3448            return false;
3449        }
3450        let length = u64::from_le_bytes(length_bytes);
3451        // Protect against OOM errors if the metadata is malformed.
3452        if length > MAX_STATE_BYTES {
3453            crate::nice_error!("Failed to load state: Malformed length");
3454            return false;
3455        }
3456
3457        let mut read_buffer: Vec<u8> = Vec::new();
3458
3459        if read_buffer.try_reserve_exact(length as usize).is_err() {
3460            crate::nice_error!("Failed to load state: Failed to allocate buffer for state stream");
3461            return false;
3462        }
3463
3464        let bytes_read = read_stream(unsafe { &*stream }, read_buffer.spare_capacity_mut());
3465        let Some(bytes_read) = bytes_read else {
3466            crate::nice_error!("Failed to load state: Error while reading the state buffer");
3467            return false;
3468        };
3469        unsafe {
3470            read_buffer.set_len(bytes_read);
3471        }
3472
3473        match unsafe { state::deserialize_json(&read_buffer) } {
3474            Some(mut state) => {
3475                let success = wrapper.set_state_inner(&mut state);
3476                if success {
3477                    crate::nice_trace!("Loaded state ({} bytes)", read_buffer.len());
3478                }
3479
3480                success
3481            }
3482            None => false,
3483        }
3484    }
3485
3486    #[cfg(feature = "editor")]
3487    unsafe extern "C" fn ext_track_info_changed(plugin: *const clap_plugin) {
3488        check_null_ptr!((), plugin, unsafe { (*plugin).plugin_data });
3489        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3490
3491        wrapper.update_track_info_from_host();
3492    }
3493
3494    unsafe extern "C" fn ext_tail_get(plugin: *const clap_plugin) -> u32 {
3495        check_null_ptr!(0, plugin, unsafe { (*plugin).plugin_data });
3496        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3497
3498        match wrapper.last_process_status.load() {
3499            ProcessStatus::Tail(samples) => samples,
3500            ProcessStatus::KeepAlive => u32::MAX,
3501            _ => 0,
3502        }
3503    }
3504
3505    unsafe extern "C" fn ext_voice_info_get(
3506        plugin: *const clap_plugin,
3507        info: *mut clap_voice_info,
3508    ) -> bool {
3509        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, info);
3510        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3511
3512        match P::CLAP_POLY_MODULATION_CONFIG {
3513            Some(config) => {
3514                unsafe {
3515                    *info = clap_voice_info {
3516                        voice_count: wrapper.current_voice_capacity.load(Ordering::Relaxed),
3517                        voice_capacity: config.max_voice_capacity,
3518                        flags: if config.supports_overlapping_voices {
3519                            CLAP_VOICE_INFO_SUPPORTS_OVERLAPPING_NOTES
3520                        } else {
3521                            0
3522                        },
3523                    };
3524                }
3525
3526                true
3527            }
3528            None => false,
3529        }
3530    }
3531}
3532
3533/// Convenience function to query an extension from the host.
3534///
3535/// # Safety
3536///
3537/// The extension type `T` must match the extension's name `name`.
3538unsafe fn query_host_extension<T>(
3539    host_callback: &ClapPtr<clap_host>,
3540    name: &CStr,
3541) -> Option<ClapPtr<T>> {
3542    let extension_ptr = unsafe {
3543        clap_call! { host_callback=>get_extension(&**host_callback, name.as_ptr()) }
3544    };
3545    if !extension_ptr.is_null() {
3546        unsafe { Some(ClapPtr::new(extension_ptr as *const T)) }
3547    } else {
3548        None
3549    }
3550}