Skip to main content

nice_plug/wrapper/clap/
wrapper.rs

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