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        let mut did_set_state_inner = false;
1792
1793        // Use a loop and timeouts to handle the super rare edge case when this function gets called
1794        // between a process call and the host disabling the plugin
1795        loop {
1796            if self.is_processing.load(Ordering::SeqCst) {
1797                // If the plugin is currently processing audio, then we'll perform the restore
1798                // operation at the end of the audio call. This involves sending the state to the
1799                // audio thread, having the audio thread handle the state restore at the very end of
1800                // the process function, and then sending the state back to this thread so it can be
1801                // deallocated without blocking the audio thread.
1802                match self
1803                    .updated_state_sender
1804                    .send_timeout(state, Duration::from_secs(1))
1805                {
1806                    Ok(_) => {
1807                        // As mentioned above, the state object will be passed back to this thread
1808                        // so we can deallocate it without blocking.
1809                        let state = self.updated_state_receiver.recv();
1810                        drop(state);
1811                        break;
1812                    }
1813                    Err(SendTimeoutError::Timeout(value)) => {
1814                        state = value;
1815                        continue;
1816                    }
1817                    Err(SendTimeoutError::Disconnected(_)) => {
1818                        crate::nice_debug_assert_failure!("State update channel got disconnected");
1819                        return;
1820                    }
1821                }
1822            } else {
1823                // Otherwise we'll set the state right here and now, since this function should be
1824                // called from a GUI thread
1825                self.set_state_inner(&mut state);
1826                did_set_state_inner = true;
1827                break;
1828            }
1829        }
1830
1831        if !did_set_state_inner {
1832            // After the state has been updated, notify the host about the new parameter values
1833            let task_posted = self.schedule_gui(Task::RescanParamValues);
1834            crate::nice_debug_assert!(task_posted, "The task queue is full, dropping task...");
1835        } // Else the RescanParamValues task has already been sent
1836    }
1837
1838    pub fn set_latency_samples(&self, samples: u32) {
1839        // Only make a callback if it's actually needed
1840        // XXX: For CLAP we could move this handling to the Plugin struct, but it may be worthwhile
1841        //      to keep doing it this way to stay consistent with VST3.
1842        let old_latency = self.current_latency.swap(samples, Ordering::SeqCst);
1843        if old_latency != samples {
1844            let task_posted = self.schedule_gui(Task::LatencyChanged);
1845            crate::nice_debug_assert!(task_posted, "The task queue is full, dropping task...");
1846        }
1847    }
1848
1849    pub fn set_current_voice_capacity(&self, capacity: u32) {
1850        match P::CLAP_POLY_MODULATION_CONFIG {
1851            Some(config) => {
1852                let clamped_capacity = capacity.clamp(1, config.max_voice_capacity);
1853                crate::nice_debug_assert_eq!(
1854                    capacity,
1855                    clamped_capacity,
1856                    "The current voice capacity must be between 1 and the maximum capacity"
1857                );
1858
1859                if clamped_capacity != self.current_voice_capacity.load(Ordering::Relaxed) {
1860                    self.current_voice_capacity
1861                        .store(clamped_capacity, Ordering::Relaxed);
1862                    let task_posted = self.schedule_gui(Task::VoiceInfoChanged);
1863                    crate::nice_debug_assert!(
1864                        task_posted,
1865                        "The task queue is full, dropping task..."
1866                    );
1867                }
1868            }
1869            None => crate::nice_debug_assert_failure!(
1870                "Configuring the current voice capacity is only possible when \
1871                 'ClapPlugin::CLAP_POLY_MODULATION_CONFIG' is set"
1872            ),
1873        }
1874    }
1875
1876    /// Query the host for the current track information and notify the plugin if anything changed.
1877    fn update_track_info_from_host(&self) {
1878        let host_track_info = self.host_track_info.borrow();
1879        let Some(host_track_info) = host_track_info.as_ref() else {
1880            return;
1881        };
1882
1883        permit_alloc(|| {
1884            let mut clap_info: clap_track_info = unsafe { mem::zeroed() };
1885            let success = unsafe_clap_call! {
1886                host_track_info=>get(&*self.host_callback, &mut clap_info)
1887            };
1888            if !success {
1889                return;
1890            }
1891
1892            let mut current_track_info = self.current_track_info.borrow_mut();
1893            let mut name = current_track_info.name().to_owned();
1894            let mut color = current_track_info.color();
1895
1896            if clap_info.flags & CLAP_TRACK_INFO_HAS_TRACK_NAME != 0 {
1897                let name_bytes = unsafe {
1898                    std::slice::from_raw_parts(
1899                        clap_info.name.as_ptr().cast::<u8>(),
1900                        clap_sys::string_sizes::CLAP_NAME_SIZE,
1901                    )
1902                };
1903                if let Ok(cstr) = CStr::from_bytes_until_nul(name_bytes) {
1904                    name = cstr.to_string_lossy().into_owned()
1905                } // Else there is no null terminator. In this case we do nothing with the name.
1906            }
1907
1908            if clap_info.flags & CLAP_TRACK_INFO_HAS_TRACK_COLOR != 0 {
1909                color = Some(TrackColor::new(
1910                    clap_info.color.red,
1911                    clap_info.color.green,
1912                    clap_info.color.blue,
1913                    clap_info.color.alpha,
1914                ));
1915            }
1916
1917            let track_info = TrackInfo::new(name, color);
1918            *current_track_info = track_info.clone();
1919            self.plugin.lock().track_info_updated(track_info);
1920        });
1921    }
1922
1923    /// Immediately set the plugin state. Returns `false` if the deserialization failed. The plugin
1924    /// state is set from a couple places, so this function aims to deduplicate that. Includes
1925    /// `permit_alloc()`s around the deserialization and initialization for the use case where
1926    /// `set_state_object_from_gui()` was called while the plugin is process audio.
1927    ///
1928    /// Implicitly emits `Task::ParameterValuesChanged`.
1929    ///
1930    /// # Notes
1931    ///
1932    /// `self.plugin` must _not_ be locked while calling this function or it will deadlock.
1933    pub fn set_state_inner(&self, state: &mut PluginState) -> bool {
1934        let audio_io_layout = self.current_audio_io_layout.load();
1935        let buffer_config = self.current_buffer_config.load();
1936
1937        // FIXME: This is obviously not realtime-safe, but loading presets without doing this could
1938        //        lead to inconsistencies. It's the plugin's responsibility to not perform any
1939        //        realtime-unsafe work when the initialize function is called a second time if it
1940        //        supports runtime preset loading.  `state::deserialize_object()` normally never
1941        //        allocates, but if the plugin has persistent non-parameter data then its
1942        //        `deserialize_fields()` implementation may still allocate.
1943        let mut success = permit_alloc(|| unsafe {
1944            state::deserialize_object::<P>(
1945                state,
1946                self.params.clone(),
1947                state::make_params_getter(&self.param_by_hash, &self.param_id_to_hash),
1948                self.current_buffer_config.load().as_ref(),
1949            )
1950        });
1951        if !success {
1952            crate::nice_debug_assert_failure!(
1953                "Deserializing plugin state from a state object failed"
1954            );
1955            return false;
1956        }
1957
1958        // If the plugin was already initialized then it needs to be reinitialized
1959        if let Some(buffer_config) = buffer_config {
1960            // NOTE: This needs to be dropped after the `plugin` lock to avoid deadlocks
1961            let mut init_context = self.make_init_context();
1962            let mut plugin = self.plugin.lock();
1963
1964            // See above
1965            success = permit_alloc(|| {
1966                plugin.initialize(&audio_io_layout, &buffer_config, &mut init_context)
1967            });
1968            if success {
1969                process_wrapper(|| plugin.reset());
1970            }
1971        }
1972
1973        crate::nice_debug_assert!(
1974            success,
1975            "Plugin returned false when reinitializing after loading state"
1976        );
1977
1978        // Reinitialize the plugin after loading state so it can respond to the new parameter values
1979        let task_posted = self.schedule_gui(Task::RescanParamValues);
1980        crate::nice_debug_assert!(task_posted, "The task queue is full, dropping task...");
1981        let task_posted = self.schedule_gui(Task::ParameterValuesChanged);
1982        crate::nice_debug_assert!(task_posted, "The task queue is full, dropping task...");
1983
1984        // TODO: Right now there's no way to know if loading the state changed the GUI's size. We
1985        //       could keep track of the last known size and compare the GUI's current size against
1986        //       that but that also seems brittle.
1987        if self.editor_handle.lock().is_some() {
1988            self.request_resize();
1989        }
1990
1991        success
1992    }
1993
1994    unsafe extern "C" fn init(plugin: *const clap_plugin) -> bool {
1995        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
1996        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
1997
1998        // We weren't allowed to query these in the constructor, so we need to do it now instead.
1999        unsafe {
2000            *wrapper.host_gui.borrow_mut() =
2001                query_host_extension::<clap_host_gui>(&wrapper.host_callback, CLAP_EXT_GUI);
2002            *wrapper.host_latency.borrow_mut() =
2003                query_host_extension::<clap_host_latency>(&wrapper.host_callback, CLAP_EXT_LATENCY);
2004            *wrapper.host_params.borrow_mut() =
2005                query_host_extension::<clap_host_params>(&wrapper.host_callback, CLAP_EXT_PARAMS);
2006            *wrapper.host_voice_info.borrow_mut() = query_host_extension::<clap_host_voice_info>(
2007                &wrapper.host_callback,
2008                CLAP_EXT_VOICE_INFO,
2009            );
2010            *wrapper.host_thread_check.borrow_mut() = query_host_extension::<clap_host_thread_check>(
2011                &wrapper.host_callback,
2012                CLAP_EXT_THREAD_CHECK,
2013            );
2014            *wrapper.host_track_info.borrow_mut() = query_host_extension::<clap_host_track_info>(
2015                &wrapper.host_callback,
2016                CLAP_EXT_TRACK_INFO,
2017            );
2018        }
2019
2020        wrapper.update_track_info_from_host();
2021
2022        true
2023    }
2024
2025    unsafe extern "C" fn destroy(plugin: *const clap_plugin) {
2026        assert!(!plugin.is_null() && unsafe { !(*plugin).plugin_data.is_null() });
2027        let this = unsafe { Arc::from_raw((*plugin).plugin_data as *mut Self) };
2028        crate::nice_debug_assert_eq!(Arc::strong_count(&this), 1);
2029
2030        drop(this);
2031    }
2032
2033    unsafe extern "C" fn activate(
2034        plugin: *const clap_plugin,
2035        sample_rate: f64,
2036        min_frames_count: u32,
2037        max_frames_count: u32,
2038    ) -> bool {
2039        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
2040        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2041
2042        let audio_io_layout = wrapper.current_audio_io_layout.load();
2043        let buffer_config = BufferConfig {
2044            sample_rate: sample_rate as f32,
2045            min_buffer_size: Some(min_frames_count),
2046            max_buffer_size: max_frames_count,
2047            process_mode: wrapper.current_process_mode.load(),
2048        };
2049
2050        // Before initializing the plugin, make sure all smoothers are set the the default values
2051        for param in wrapper.param_by_hash.values() {
2052            unsafe { param._internal_update_smoother(buffer_config.sample_rate, true) };
2053        }
2054
2055        // If this reactivation happened due to the latency changing, notify the host of that
2056        // latency change.
2057        if wrapper.latency_changed.swap(false, Ordering::SeqCst) {
2058            if let Some(host_latency) = &*wrapper.host_latency.borrow() {
2059                unsafe_clap_call! { host_latency=>changed(&*wrapper.host_callback) };
2060            }
2061        }
2062
2063        // NOTE: This needs to be dropped after the `plugin` lock to avoid deadlocks
2064        let mut init_context = wrapper.make_init_context();
2065        let mut plugin = wrapper.plugin.lock();
2066        if plugin.initialize(&audio_io_layout, &buffer_config, &mut init_context) {
2067            // NOTE: `Plugin::reset()` is called in `clap_plugin::start_processing()` instead of in
2068            //       this function
2069
2070            // This preallocates enough space so we can transform all of the host's raw channel
2071            // pointers into a set of `Buffer` objects for the plugin's main and auxiliary IO
2072            *wrapper.buffer_manager.borrow_mut() =
2073                BufferManager::for_audio_io_layout(max_frames_count as usize, audio_io_layout);
2074
2075            // Also store this for later, so we can reinitialize the plugin after restoring state
2076            wrapper.current_buffer_config.store(Some(buffer_config));
2077
2078            wrapper.is_activated.store(true, Ordering::SeqCst);
2079
2080            true
2081        } else {
2082            false
2083        }
2084    }
2085
2086    unsafe extern "C" fn deactivate(plugin: *const clap_plugin) {
2087        check_null_ptr!((), plugin, unsafe { (*plugin).plugin_data });
2088        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2089
2090        wrapper.plugin.lock().deactivate();
2091
2092        wrapper.is_activated.store(false, Ordering::SeqCst);
2093    }
2094
2095    unsafe extern "C" fn start_processing(plugin: *const clap_plugin) -> bool {
2096        // We just need to keep track of our processing state so we can request a flush when
2097        // updating parameters from the GUI while the processing loop isn't running
2098        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
2099        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2100
2101        // Always reset the processing status when the plugin gets activated or deactivated
2102        wrapper.last_process_status.store(ProcessStatus::Normal);
2103        wrapper.is_processing.store(true, Ordering::SeqCst);
2104
2105        // To be consistent with the VST3 wrapper, we'll also reset the buffers here in addition to
2106        // the dedicated `reset()` function.
2107        process_wrapper(|| wrapper.plugin.lock().reset());
2108
2109        true
2110    }
2111
2112    unsafe extern "C" fn stop_processing(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        wrapper.is_processing.store(false, Ordering::SeqCst);
2117    }
2118
2119    unsafe extern "C" fn reset(plugin: *const clap_plugin) {
2120        check_null_ptr!((), plugin, unsafe { (*plugin).plugin_data });
2121        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2122
2123        process_wrapper(|| wrapper.plugin.lock().reset());
2124    }
2125
2126    unsafe extern "C" fn process(
2127        plugin: *const clap_plugin,
2128        process: *const clap_process,
2129    ) -> clap_process_status {
2130        check_null_ptr!(
2131            CLAP_PROCESS_ERROR,
2132            plugin,
2133            unsafe { (*plugin).plugin_data },
2134            process
2135        );
2136        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2137
2138        // Panic on allocations if the `assert_process_allocs` feature has been enabled, and make
2139        // sure that FTZ is set up correctly
2140        process_wrapper(|| {
2141            // We need to handle incoming automation and MIDI events. Since we don't support sample
2142            // accuration automation yet and there's no way to get the last event for a parameter,
2143            // we'll process every incoming event.
2144            let process = unsafe { &*process };
2145            let total_buffer_len = process.frames_count as usize;
2146
2147            let current_audio_io_layout = wrapper.current_audio_io_layout.load();
2148            let has_main_input = current_audio_io_layout.main_input_channels.is_some();
2149            let has_main_output = current_audio_io_layout.main_output_channels.is_some();
2150            let aux_input_start_idx = if has_main_input { 1 } else { 0 };
2151            let aux_output_start_idx = if has_main_output { 1 } else { 0 };
2152
2153            // If `P::SAMPLE_ACCURATE_AUTOMATION` is set, then we'll split up the audio buffer into
2154            // chunks whenever a parameter change occurs
2155            let mut block_start = 0;
2156            let mut block_end = total_buffer_len;
2157            let mut event_start_idx = 0;
2158
2159            // The host may send new transport information as an event. In that case we'll also
2160            // split the buffer.
2161            let mut transport_info = process.transport;
2162
2163            let result = loop {
2164                if !process.in_events.is_null() {
2165                    let split_result = unsafe {
2166                        wrapper.handle_in_events_until(
2167                            &*process.in_events,
2168                            &mut transport_info,
2169                            block_start,
2170                            total_buffer_len,
2171                            event_start_idx,
2172                            |next_event| {
2173                                // Always split the buffer on transport information changes (tempo, time
2174                                // signature, or position changes), and also split on parameter value
2175                                // changes after the current sample if sample accurate automation is
2176                                // enabled
2177                                if P::SAMPLE_ACCURATE_AUTOMATION {
2178                                    match ((*next_event).space_id, (*next_event).type_) {
2179                                        (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_PARAM_VALUE)
2180                                        | (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_TRANSPORT) => true,
2181                                        (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_PARAM_MOD) => {
2182                                            let next_event =
2183                                                &*(next_event as *const clap_event_param_mod);
2184
2185                                            // The buffer should not be split on polyphonic modulation
2186                                            // as those events will be converted to note events
2187                                            !(next_event.note_id != -1
2188                                                && wrapper
2189                                                    .poly_mod_ids_by_hash
2190                                                    .contains_key(&next_event.param_id))
2191                                        }
2192                                        _ => false,
2193                                    }
2194                                } else {
2195                                    matches!(
2196                                        ((*next_event).space_id, (*next_event).type_,),
2197                                        (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_TRANSPORT)
2198                                    )
2199                                }
2200                            },
2201                        )
2202                    };
2203
2204                    // If there are any parameter changes after `block_start` and sample
2205                    // accurate automation is enabled or the host sends new transport
2206                    // information, then we'll process a new block just after that. Otherwise we can
2207                    // process all audio until the end of the buffer.
2208                    match split_result {
2209                        Some((next_param_change_sample_idx, next_param_change_event_idx)) => {
2210                            block_end = next_param_change_sample_idx;
2211                            event_start_idx = next_param_change_event_idx;
2212                        }
2213                        None => block_end = total_buffer_len,
2214                    }
2215                }
2216
2217                // After processing the events we now know where/if the block should be split, and
2218                // we can start preparing audio processing
2219                let block_len = block_end - block_start;
2220
2221                // The buffer manager preallocated buffer slices for all the IO and storage for any
2222                // axuiliary inputs.
2223                // TODO: The audio buffers have a latency field, should we use those?
2224                // TODO: Like with VST3, should we expose some way to access or set the silence/constant
2225                //       flags?
2226                let mut buffer_manager = wrapper.buffer_manager.borrow_mut();
2227                let buffers = unsafe {
2228                    buffer_manager.create_buffers(block_start, block_len, |buffer_source| {
2229                        // Explicitly take plugins with no main output that does have auxiliary
2230                        // outputs into account. Shouldn't happen, but if we just start copying
2231                        // audio here then that would result in unsoundness.
2232                        if process.audio_outputs_count > 0
2233                            && !process.audio_outputs.is_null()
2234                            && !(*process.audio_outputs).data32.is_null()
2235                            && has_main_output
2236                        {
2237                            let audio_output = &*process.audio_outputs;
2238                            let ptrs = NonNull::new(audio_output.data32).unwrap();
2239                            let num_channels = audio_output.channel_count as usize;
2240
2241                            *buffer_source.main_output_channel_pointers =
2242                                Some(ChannelPointers { ptrs, num_channels });
2243                        }
2244
2245                        if process.audio_inputs_count > 0
2246                            && !process.audio_inputs.is_null()
2247                            && !(*process.audio_inputs).data32.is_null()
2248                            && has_main_input
2249                        {
2250                            let audio_input = &*process.audio_inputs;
2251                            let ptrs = NonNull::new(audio_input.data32).unwrap();
2252                            let num_channels = audio_input.channel_count as usize;
2253
2254                            *buffer_source.main_input_channel_pointers =
2255                                Some(ChannelPointers { ptrs, num_channels });
2256                        }
2257
2258                        if !process.audio_inputs.is_null() {
2259                            for (aux_input_no, aux_input_channel_pointers) in buffer_source
2260                                .aux_input_channel_pointers
2261                                .iter_mut()
2262                                .enumerate()
2263                            {
2264                                let aux_input_idx = aux_input_no + aux_input_start_idx;
2265                                if aux_input_idx > process.audio_inputs_count as usize {
2266                                    break;
2267                                }
2268
2269                                let audio_input = &*process.audio_inputs.add(aux_input_idx);
2270                                match NonNull::new(audio_input.data32) {
2271                                    Some(ptrs) => {
2272                                        let num_channels = audio_input.channel_count as usize;
2273
2274                                        *aux_input_channel_pointers =
2275                                            Some(ChannelPointers { ptrs, num_channels });
2276                                    }
2277                                    None => continue,
2278                                }
2279                            }
2280                        }
2281
2282                        if !process.audio_outputs.is_null() {
2283                            for (aux_output_no, aux_output_channel_pointers) in buffer_source
2284                                .aux_output_channel_pointers
2285                                .iter_mut()
2286                                .enumerate()
2287                            {
2288                                let aux_output_idx = aux_output_no + aux_output_start_idx;
2289                                if aux_output_idx > process.audio_outputs_count as usize {
2290                                    break;
2291                                }
2292
2293                                let audio_output = &*process.audio_outputs.add(aux_output_idx);
2294                                match NonNull::new(audio_output.data32) {
2295                                    Some(ptrs) => {
2296                                        let num_channels = audio_output.channel_count as usize;
2297
2298                                        *aux_output_channel_pointers =
2299                                            Some(ChannelPointers { ptrs, num_channels });
2300                                    }
2301                                    None => continue,
2302                                }
2303                            }
2304                        }
2305                    })
2306                };
2307
2308                // If the host does not provide outputs or if it does not provide the required
2309                // number of channels (should not happen, but Ableton Live does this for bypassed
2310                // VST3 plugins) then we'll skip audio processing. In that case
2311                // `buffer_manager.create_buffers` will have set one or more of the output buffers
2312                // to empty slices since there is no storage to point them to. The auxiliary input
2313                // buffers always point to valid storage.
2314                let mut buffer_is_valid = true;
2315                for output_buffer_slice in buffers.main_buffer.as_slice_immutable().iter().chain(
2316                    buffers
2317                        .aux_outputs
2318                        .iter()
2319                        .flat_map(|buffer| buffer.as_slice_immutable().iter()),
2320                ) {
2321                    if output_buffer_slice.is_empty() {
2322                        buffer_is_valid = false;
2323                        break;
2324                    }
2325                }
2326
2327                crate::nice_debug_assert!(buffer_is_valid);
2328
2329                // Some of the fields are left empty because CLAP does not provide this information,
2330                // but the methods on [`Transport`] can reconstruct these values from the other
2331                // fields
2332                let sample_rate = wrapper
2333                    .current_buffer_config
2334                    .load()
2335                    .expect("Process call without prior initialization call")
2336                    .sample_rate;
2337                let mut transport = Transport::new(sample_rate);
2338                if !transport_info.is_null() {
2339                    let context = unsafe { &*transport_info };
2340
2341                    transport.playing = context.flags & CLAP_TRANSPORT_IS_PLAYING != 0;
2342                    transport.recording = context.flags & CLAP_TRANSPORT_IS_RECORDING != 0;
2343                    transport.preroll_active =
2344                        Some(context.flags & CLAP_TRANSPORT_IS_WITHIN_PRE_ROLL != 0);
2345                    if context.flags & CLAP_TRANSPORT_HAS_TEMPO != 0 {
2346                        transport.tempo = Some(context.tempo);
2347                    }
2348                    if context.flags & CLAP_TRANSPORT_HAS_TIME_SIGNATURE != 0 {
2349                        transport.time_sig_numerator = Some(context.tsig_num as i32);
2350                        transport.time_sig_denominator = Some(context.tsig_denom as i32);
2351                    }
2352                    if context.flags & CLAP_TRANSPORT_HAS_BEATS_TIMELINE != 0 {
2353                        let beats = context.song_pos_beats as f64 / CLAP_BEATTIME_FACTOR as f64;
2354
2355                        // This is a bit messy, but we'll try to compensate for the block splitting.
2356                        // We can't use the functions on the transport information object for this
2357                        // because we don't have any sample information.
2358                        if P::SAMPLE_ACCURATE_AUTOMATION
2359                            && block_start > 0
2360                            && (context.flags & CLAP_TRANSPORT_HAS_TEMPO != 0)
2361                        {
2362                            transport.pos_beats = Some(
2363                                beats
2364                                    + (block_start as f64 / sample_rate as f64 / 60.0
2365                                        * context.tempo),
2366                            );
2367                        } else {
2368                            transport.pos_beats = Some(beats);
2369                        }
2370                    }
2371                    if context.flags & CLAP_TRANSPORT_HAS_SECONDS_TIMELINE != 0 {
2372                        let seconds = context.song_pos_seconds as f64 / CLAP_SECTIME_FACTOR as f64;
2373
2374                        // Same here
2375                        if P::SAMPLE_ACCURATE_AUTOMATION
2376                            && block_start > 0
2377                            && (context.flags & CLAP_TRANSPORT_HAS_TEMPO != 0)
2378                        {
2379                            transport.pos_seconds =
2380                                Some(seconds + (block_start as f64 / sample_rate as f64));
2381                        } else {
2382                            transport.pos_seconds = Some(seconds);
2383                        }
2384                    }
2385                    // TODO: CLAP does not mention whether this is behind a flag or not
2386                    if P::SAMPLE_ACCURATE_AUTOMATION && block_start > 0 {
2387                        transport.bar_start_pos_beats = match transport.bar_start_pos_beats() {
2388                            Some(updated) => Some(updated),
2389                            None => Some(context.bar_start as f64 / CLAP_BEATTIME_FACTOR as f64),
2390                        };
2391                        transport.bar_number = match transport.bar_number() {
2392                            Some(updated) => Some(updated),
2393                            None => Some(context.bar_number),
2394                        };
2395                    } else {
2396                        transport.bar_start_pos_beats =
2397                            Some(context.bar_start as f64 / CLAP_BEATTIME_FACTOR as f64);
2398                        transport.bar_number = Some(context.bar_number);
2399                    }
2400                    // TODO: They also aren't very clear about this, but presumably if the loop is
2401                    //       active and the corresponding song transport information is available then
2402                    //       this is also available
2403                    if context.flags & CLAP_TRANSPORT_IS_LOOP_ACTIVE != 0
2404                        && context.flags & CLAP_TRANSPORT_HAS_BEATS_TIMELINE != 0
2405                    {
2406                        transport.loop_range_beats = Some((
2407                            context.loop_start_beats as f64 / CLAP_BEATTIME_FACTOR as f64,
2408                            context.loop_end_beats as f64 / CLAP_BEATTIME_FACTOR as f64,
2409                        ));
2410                    }
2411                    if context.flags & CLAP_TRANSPORT_IS_LOOP_ACTIVE != 0
2412                        && context.flags & CLAP_TRANSPORT_HAS_SECONDS_TIMELINE != 0
2413                    {
2414                        transport.loop_range_seconds = Some((
2415                            context.loop_start_seconds as f64 / CLAP_SECTIME_FACTOR as f64,
2416                            context.loop_end_seconds as f64 / CLAP_SECTIME_FACTOR as f64,
2417                        ));
2418                    }
2419                }
2420
2421                let result = if buffer_is_valid {
2422                    let mut plugin = wrapper.plugin.lock();
2423                    // SAFETY: Shortening these borrows is safe as even if the plugin overwrites the
2424                    //         slices (which it cannot do without using unsafe code), then they
2425                    //         would still be reset on the next iteration
2426                    let mut aux = AuxiliaryBuffers {
2427                        inputs: buffers.aux_inputs,
2428                        outputs: buffers.aux_outputs,
2429                    };
2430                    let mut context = wrapper.make_process_context(transport);
2431                    let result = plugin.process(buffers.main_buffer, &mut aux, &mut context);
2432                    wrapper.last_process_status.store(result);
2433                    result
2434                } else {
2435                    ProcessStatus::Normal
2436                };
2437
2438                let clap_result = match result {
2439                    ProcessStatus::Error(err) => {
2440                        crate::nice_debug_assert_failure!("Process error: {}", err);
2441
2442                        return CLAP_PROCESS_ERROR;
2443                    }
2444                    ProcessStatus::Normal => CLAP_PROCESS_CONTINUE_IF_NOT_QUIET,
2445                    ProcessStatus::Tail(_) => CLAP_PROCESS_CONTINUE,
2446                    ProcessStatus::KeepAlive => CLAP_PROCESS_CONTINUE,
2447                };
2448
2449                // After processing audio, send all spooled events to the host. This include note
2450                // events.
2451                if !process.out_events.is_null() {
2452                    unsafe {
2453                        wrapper.handle_out_events(
2454                            &*process.out_events,
2455                            block_start,
2456                            total_buffer_len,
2457                        )
2458                    };
2459                }
2460
2461                // If our block ends at the end of the buffer then that means there are no more
2462                // unprocessed (parameter) events. If there are more events, we'll just keep going
2463                // through this process until we've processed the entire buffer.
2464                if block_end == total_buffer_len {
2465                    break clap_result;
2466                } else {
2467                    block_start = block_end;
2468                }
2469            };
2470
2471            // After processing audio, we'll check if the editor has sent us updated plugin state.
2472            // We'll restore that here on the audio thread to prevent changing the values during the
2473            // process call and also to prevent inconsistent state when the host also wants to load
2474            // plugin state.
2475            // FIXME: Zero capacity channels allocate on receiving, find a better alternative that
2476            //        doesn't do that
2477            let updated_state = permit_alloc(|| wrapper.updated_state_receiver.try_recv());
2478            if let Ok(mut state) = updated_state {
2479                wrapper.set_state_inner(&mut state);
2480
2481                // We'll pass the state object back to the GUI thread so deallocation can happen
2482                // there without potentially blocking the audio thread
2483                if let Err(err) = wrapper.updated_state_sender.send(state) {
2484                    crate::nice_debug_assert_failure!(
2485                        "Failed to send state object back to GUI thread: {}",
2486                        err
2487                    );
2488                };
2489            }
2490
2491            result
2492        })
2493    }
2494
2495    unsafe extern "C" fn get_extension(
2496        plugin: *const clap_plugin,
2497        id: *const c_char,
2498    ) -> *const c_void {
2499        check_null_ptr!(
2500            std::ptr::null(),
2501            plugin,
2502            unsafe { (*plugin).plugin_data },
2503            id
2504        );
2505        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2506
2507        let id = unsafe { CStr::from_ptr(id) };
2508
2509        if id == CLAP_EXT_AUDIO_PORTS_CONFIG {
2510            &wrapper.clap_plugin_audio_ports_config as *const _ as *const c_void
2511        } else if id == CLAP_EXT_AUDIO_PORTS {
2512            &wrapper.clap_plugin_audio_ports as *const _ as *const c_void
2513        } else if id == CLAP_EXT_GUI && wrapper.editor.borrow().is_some() {
2514            // Only report that we support this extension if the plugin has an editor
2515            &wrapper.clap_plugin_gui as *const _ as *const c_void
2516        } else if id == CLAP_EXT_LATENCY {
2517            &wrapper.clap_plugin_latency as *const _ as *const c_void
2518        } else if id == CLAP_EXT_NOTE_PORTS
2519            && (P::MIDI_INPUT >= MidiConfig::Basic || P::MIDI_OUTPUT >= MidiConfig::Basic)
2520        {
2521            &wrapper.clap_plugin_note_ports as *const _ as *const c_void
2522        } else if id == CLAP_EXT_PARAMS {
2523            &wrapper.clap_plugin_params as *const _ as *const c_void
2524        } else if id == CLAP_EXT_REMOTE_CONTROLS {
2525            &wrapper.clap_plugin_remote_controls as *const _ as *const c_void
2526        } else if id == CLAP_EXT_RENDER {
2527            &wrapper.clap_plugin_render as *const _ as *const c_void
2528        } else if id == CLAP_EXT_STATE {
2529            &wrapper.clap_plugin_state as *const _ as *const c_void
2530        } else if id == CLAP_EXT_TAIL {
2531            &wrapper.clap_plugin_tail as *const _ as *const c_void
2532        } else if id == CLAP_EXT_TRACK_INFO {
2533            &wrapper.clap_plugin_track_info as *const _ as *const c_void
2534        } else if id == CLAP_EXT_VOICE_INFO && P::CLAP_POLY_MODULATION_CONFIG.is_some() {
2535            &wrapper.clap_plugin_voice_info as *const _ as *const c_void
2536        } else {
2537            crate::nice_trace!("Host tried to query unknown extension {:?}", id);
2538            std::ptr::null()
2539        }
2540    }
2541
2542    unsafe extern "C" fn on_main_thread(plugin: *const clap_plugin) {
2543        check_null_ptr!((), plugin, unsafe { (*plugin).plugin_data });
2544        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2545
2546        // [Self::schedule_gui] posts a task to the queue and asks the host to call this function
2547        // on the main thread, so once that's done we can just handle all requests here
2548        while let Some(task) = wrapper.tasks.pop() {
2549            wrapper.execute(task, true);
2550        }
2551    }
2552
2553    unsafe extern "C" fn ext_audio_ports_config_count(plugin: *const clap_plugin) -> u32 {
2554        check_null_ptr!(0, plugin, unsafe { (*plugin).plugin_data });
2555
2556        P::AUDIO_IO_LAYOUTS.len() as u32
2557    }
2558
2559    unsafe extern "C" fn ext_audio_ports_config_get(
2560        plugin: *const clap_plugin,
2561        index: u32,
2562        config: *mut clap_audio_ports_config,
2563    ) -> bool {
2564        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, config);
2565
2566        // This function directly maps to `P::AUDIO_IO_LAYOUTS`, and we thus also don't need to
2567        // access the `wrapper` instance
2568        match P::AUDIO_IO_LAYOUTS.get(index as usize) {
2569            Some(audio_io_layout) => {
2570                let name = audio_io_layout.name();
2571
2572                let main_input_channels = audio_io_layout.main_input_channels.map(NonZeroU32::get);
2573                let main_output_channels =
2574                    audio_io_layout.main_output_channels.map(NonZeroU32::get);
2575                let input_port_type = match main_input_channels {
2576                    Some(1) => CLAP_PORT_MONO.as_ptr(),
2577                    Some(2) => CLAP_PORT_STEREO.as_ptr(),
2578                    _ => std::ptr::null(),
2579                };
2580                let output_port_type = match main_output_channels {
2581                    Some(1) => CLAP_PORT_MONO.as_ptr(),
2582                    Some(2) => CLAP_PORT_STEREO.as_ptr(),
2583                    _ => std::ptr::null(),
2584                };
2585
2586                unsafe { *config = std::mem::zeroed() };
2587
2588                let config = unsafe { &mut *config };
2589                config.id = index;
2590                strlcpy(&mut config.name, &name);
2591                config.input_port_count = (if main_input_channels.is_some() { 1 } else { 0 }
2592                    + audio_io_layout.aux_input_ports.len())
2593                    as u32;
2594                config.output_port_count = (if main_output_channels.is_some() { 1 } else { 0 }
2595                    + audio_io_layout.aux_output_ports.len())
2596                    as u32;
2597                config.has_main_input = main_input_channels.is_some();
2598                config.main_input_channel_count = main_input_channels.unwrap_or_default();
2599                config.main_input_port_type = input_port_type;
2600                config.has_main_output = main_output_channels.is_some();
2601                config.main_output_channel_count = main_output_channels.unwrap_or_default();
2602                config.main_output_port_type = output_port_type;
2603
2604                true
2605            }
2606            None => {
2607                crate::nice_debug_assert_failure!(
2608                    "Host tried to query out of bounds audio port config {}",
2609                    index
2610                );
2611
2612                false
2613            }
2614        }
2615    }
2616
2617    unsafe extern "C" fn ext_audio_ports_config_select(
2618        plugin: *const clap_plugin,
2619        config_id: clap_id,
2620    ) -> bool {
2621        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
2622        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2623
2624        // We use the vector indices for the config ID
2625        match P::AUDIO_IO_LAYOUTS.get(config_id as usize) {
2626            Some(audio_io_layout) => {
2627                wrapper.current_audio_io_layout.store(*audio_io_layout);
2628
2629                true
2630            }
2631            None => {
2632                crate::nice_debug_assert_failure!(
2633                    "Host tried to select out of bounds audio port config {}",
2634                    config_id
2635                );
2636
2637                false
2638            }
2639        }
2640    }
2641
2642    unsafe extern "C" fn ext_audio_ports_count(plugin: *const clap_plugin, is_input: bool) -> u32 {
2643        check_null_ptr!(0, plugin, unsafe { (*plugin).plugin_data });
2644        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2645
2646        let audio_io_layout = wrapper.current_audio_io_layout.load();
2647        if is_input {
2648            let main_ports = if audio_io_layout.main_input_channels.is_some() {
2649                1
2650            } else {
2651                0
2652            };
2653            let aux_ports = audio_io_layout.aux_input_ports.len();
2654
2655            (main_ports + aux_ports) as u32
2656        } else {
2657            let main_ports = if audio_io_layout.main_output_channels.is_some() {
2658                1
2659            } else {
2660                0
2661            };
2662            let aux_ports = audio_io_layout.aux_output_ports.len();
2663
2664            (main_ports + aux_ports) as u32
2665        }
2666    }
2667
2668    unsafe extern "C" fn ext_audio_ports_get(
2669        plugin: *const clap_plugin,
2670        index: u32,
2671        is_input: bool,
2672        info: *mut clap_audio_port_info,
2673    ) -> bool {
2674        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, info);
2675        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2676
2677        let num_input_ports = unsafe { Self::ext_audio_ports_count(plugin, true) };
2678        let num_output_ports = unsafe { Self::ext_audio_ports_count(plugin, false) };
2679        if (is_input && index >= num_input_ports) || (!is_input && index >= num_output_ports) {
2680            crate::nice_debug_assert_failure!(
2681                "Host tried to query information for out of bounds audio port {} (input: {})",
2682                index,
2683                is_input
2684            );
2685
2686            return false;
2687        }
2688
2689        let current_audio_io_layout = wrapper.current_audio_io_layout.load();
2690        let has_main_input = current_audio_io_layout.main_input_channels.is_some();
2691        let has_main_output = current_audio_io_layout.main_output_channels.is_some();
2692
2693        // Whether this port is a main port or an auxiliary (sidechain) port
2694        let is_main_port =
2695            index == 0 && ((is_input && has_main_input) || (!is_input && has_main_output));
2696
2697        // We'll number the ports in a linear order from `0..num_input_ports` and
2698        // `num_input_ports..(num_input_ports + num_output_ports)`
2699        let stable_id = if is_input {
2700            index
2701        } else {
2702            index + num_input_ports
2703        };
2704        let pair_stable_id = match (is_input, is_main_port) {
2705            // Ports are named linearly with inputs coming before outputs, so this is the index of
2706            // the first output port
2707            (true, true) if has_main_output => num_input_ports,
2708            (false, true) if has_main_input => 0,
2709            _ => CLAP_INVALID_ID,
2710        };
2711
2712        let channel_count = match (index, is_input) {
2713            (0, true) if has_main_input => {
2714                current_audio_io_layout.main_input_channels.unwrap().get()
2715            }
2716            (0, false) if has_main_output => {
2717                current_audio_io_layout.main_output_channels.unwrap().get()
2718            }
2719            // `index` is off by one for the auxiliary ports if the plugin has a main port
2720            (n, true) if has_main_input => {
2721                current_audio_io_layout.aux_input_ports[n as usize - 1].get()
2722            }
2723            (n, false) if has_main_output => {
2724                current_audio_io_layout.aux_output_ports[n as usize - 1].get()
2725            }
2726            (n, true) => current_audio_io_layout.aux_input_ports[n as usize].get(),
2727            (n, false) => current_audio_io_layout.aux_output_ports[n as usize].get(),
2728        };
2729
2730        let port_type = match channel_count {
2731            1 => CLAP_PORT_MONO.as_ptr(),
2732            2 => CLAP_PORT_STEREO.as_ptr(),
2733            _ => std::ptr::null(),
2734        };
2735
2736        unsafe { *info = std::mem::zeroed() };
2737
2738        let info = unsafe { &mut *info };
2739        info.id = stable_id;
2740        match (is_input, is_main_port) {
2741            (true, true) => strlcpy(&mut info.name, &current_audio_io_layout.main_input_name()),
2742            (false, true) => strlcpy(&mut info.name, &current_audio_io_layout.main_output_name()),
2743            (true, false) => {
2744                let aux_input_idx = if has_main_input { index - 1 } else { index } as usize;
2745                strlcpy(
2746                    &mut info.name,
2747                    &current_audio_io_layout
2748                        .aux_input_name(aux_input_idx)
2749                        .expect("Out of bounds auxiliary input port"),
2750                );
2751            }
2752            (false, false) => {
2753                let aux_output_idx = if has_main_output { index - 1 } else { index } as usize;
2754                strlcpy(
2755                    &mut info.name,
2756                    &current_audio_io_layout
2757                        .aux_output_name(aux_output_idx)
2758                        .expect("Out of bounds auxiliary output port"),
2759                );
2760            }
2761        };
2762        info.flags = if is_main_port {
2763            CLAP_AUDIO_PORT_IS_MAIN
2764        } else {
2765            0
2766        };
2767        info.channel_count = channel_count;
2768        info.port_type = port_type;
2769        info.in_place_pair = pair_stable_id;
2770
2771        true
2772    }
2773
2774    unsafe extern "C" fn ext_gui_is_api_supported(
2775        _plugin: *const clap_plugin,
2776        api: *const c_char,
2777        is_floating: bool,
2778    ) -> bool {
2779        // We don't do standalone floating windows
2780        if is_floating {
2781            return false;
2782        }
2783
2784        unsafe {
2785            #[cfg(all(target_family = "unix", not(target_os = "macos")))]
2786            if CStr::from_ptr(api) == CLAP_WINDOW_API_X11 {
2787                return true;
2788            }
2789            #[cfg(target_os = "macos")]
2790            if CStr::from_ptr(api) == CLAP_WINDOW_API_COCOA {
2791                return true;
2792            }
2793            #[cfg(target_os = "windows")]
2794            if CStr::from_ptr(api) == CLAP_WINDOW_API_WIN32 {
2795                return true;
2796            }
2797        }
2798
2799        false
2800    }
2801
2802    unsafe extern "C" fn ext_gui_get_preferred_api(
2803        _plugin: *const clap_plugin,
2804        api: *mut *const c_char,
2805        is_floating: *mut bool,
2806    ) -> bool {
2807        check_null_ptr!(false, api, is_floating);
2808
2809        unsafe {
2810            #[cfg(all(target_family = "unix", not(target_os = "macos")))]
2811            {
2812                *api = CLAP_WINDOW_API_X11.as_ptr();
2813            }
2814            #[cfg(target_os = "macos")]
2815            {
2816                *api = CLAP_WINDOW_API_COCOA.as_ptr();
2817            }
2818            #[cfg(target_os = "windows")]
2819            {
2820                *api = CLAP_WINDOW_API_WIN32.as_ptr();
2821            }
2822
2823            // We don't do standalone floating windows yet
2824            *is_floating = false;
2825        }
2826
2827        true
2828    }
2829
2830    unsafe extern "C" fn ext_gui_create(
2831        plugin: *const clap_plugin,
2832        api: *const c_char,
2833        is_floating: bool,
2834    ) -> bool {
2835        // Double check this in case the host didn't
2836        if unsafe { !Self::ext_gui_is_api_supported(plugin, api, is_floating) } {
2837            return false;
2838        }
2839
2840        // In CLAP creating the editor window and embedding it in another window are separate, and
2841        // those things are one and the same in our framework. So we'll just pretend we did
2842        // something here.
2843        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
2844        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2845
2846        let editor_handle = wrapper.editor_handle.lock();
2847        if editor_handle.is_none() {
2848            true
2849        } else {
2850            crate::nice_debug_assert_failure!(
2851                "Tried creating editor while the editor was already active"
2852            );
2853            false
2854        }
2855    }
2856
2857    unsafe extern "C" fn ext_gui_destroy(plugin: *const clap_plugin) {
2858        check_null_ptr!((), plugin, unsafe { (*plugin).plugin_data });
2859        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2860
2861        let mut editor_handle = wrapper.editor_handle.lock();
2862        if editor_handle.is_some() {
2863            *editor_handle = None;
2864        } else {
2865            crate::nice_debug_assert_failure!(
2866                "Tried destroying editor while the editor was not active"
2867            );
2868        }
2869    }
2870
2871    unsafe extern "C" fn ext_gui_set_scale(plugin: *const clap_plugin, scale: f64) -> bool {
2872        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
2873        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2874
2875        // On macOS scaling is done by the OS, and all window sizes are in logical pixels
2876        if cfg!(target_os = "macos") {
2877            crate::nice_debug_assert_failure!(
2878                "Ignoring host request to set explicit DPI scaling factor"
2879            );
2880            return false;
2881        }
2882
2883        if wrapper
2884            .editor
2885            .borrow()
2886            .as_ref()
2887            .unwrap()
2888            .lock()
2889            .set_scale_factor(scale)
2890        {
2891            wrapper
2892                .scale_factor
2893                .store(scale, std::sync::atomic::Ordering::Relaxed);
2894            true
2895        } else {
2896            false
2897        }
2898    }
2899
2900    unsafe extern "C" fn ext_gui_get_size(
2901        plugin: *const clap_plugin,
2902        width: *mut u32,
2903        height: *mut u32,
2904    ) -> bool {
2905        check_null_ptr!(
2906            false,
2907            plugin,
2908            unsafe { (*plugin).plugin_data },
2909            width,
2910            height
2911        );
2912        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2913
2914        // For macOS the scaling factor is always 1
2915        let scale_factor = wrapper.scale_factor.load(Ordering::Relaxed);
2916        let size: PhysicalSize<u32> = wrapper
2917            .editor
2918            .borrow()
2919            .as_ref()
2920            .unwrap()
2921            .lock()
2922            .size()
2923            .to_physical(scale_factor);
2924
2925        unsafe {
2926            *width = size.width;
2927            *height = size.height;
2928        }
2929
2930        true
2931    }
2932
2933    unsafe extern "C" fn ext_gui_can_resize(plugin: *const clap_plugin) -> bool {
2934        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
2935        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2936
2937        // The editor decides whether it's resizable via `Editor::resize_hint()`.
2938        match wrapper.editor.borrow().as_ref() {
2939            Some(editor) => editor.lock().resize_hint().can_resize,
2940            None => false,
2941        }
2942    }
2943
2944    unsafe extern "C" fn ext_gui_get_resize_hints(
2945        plugin: *const clap_plugin,
2946        hints: *mut clap_gui_resize_hints,
2947    ) -> bool {
2948        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, hints);
2949        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2950
2951        let hint = match wrapper.editor.borrow().as_ref() {
2952            Some(editor) => editor.lock().resize_hint(),
2953            None => return false,
2954        };
2955        if !hint.can_resize {
2956            return false;
2957        }
2958
2959        let hints = unsafe { &mut *hints };
2960        hints.can_resize_horizontally = hint.can_resize_horizontally;
2961        hints.can_resize_vertically = hint.can_resize_vertically;
2962        hints.preserve_aspect_ratio = hint.preserve_aspect_ratio;
2963        hints.aspect_ratio_width = hint.aspect_ratio_width;
2964        hints.aspect_ratio_height = hint.aspect_ratio_height;
2965
2966        true
2967    }
2968
2969    unsafe extern "C" fn ext_gui_adjust_size(
2970        _plugin: *const clap_plugin,
2971        _width: *mut u32,
2972        _height: *mut u32,
2973    ) -> bool {
2974        // We accept whatever size the host proposes as-is (no snapping). The
2975        // width/height are left untouched, signalling that the requested size is
2976        // acceptable.
2977        true
2978    }
2979
2980    unsafe extern "C" fn ext_gui_set_size(
2981        plugin: *const clap_plugin,
2982        width: u32,
2983        height: u32,
2984    ) -> bool {
2985        // The host calls this after honoring an earlier `request_resize()`, when
2986        // the user drags a host-drawn resize handle, and (on Linux) if an
2987        // asynchronous resize request fails.
2988        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
2989        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2990
2991        // Hand the new size to the editor. If there is no editor open, or the
2992        // editor doesn't support being resized, this fails and we tell the host so.
2993        match wrapper.editor.borrow().as_ref() {
2994            Some(editor) => editor.lock().set_size(PhysicalSize { width, height }),
2995            None => false,
2996        }
2997    }
2998
2999    unsafe extern "C" fn ext_gui_set_parent(
3000        plugin: *const clap_plugin,
3001        window: *const clap_window,
3002    ) -> bool {
3003        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, window);
3004        // For this function we need the underlying Arc so we can pass it to the editor
3005        let wrapper = unsafe { Arc::from_raw((*plugin).plugin_data as *const Self) };
3006
3007        let window = unsafe { &*window };
3008
3009        let result = {
3010            let mut editor_handle = wrapper.editor_handle.lock();
3011            if editor_handle.is_none() {
3012                let api = unsafe { CStr::from_ptr(window.api) };
3013                let parent_handle = unsafe {
3014                    if api == CLAP_WINDOW_API_X11 {
3015                        #[allow(clippy::unnecessary_cast)]
3016                        let w = window.specific.x11 as c_ulong;
3017                        ParentWindowHandle::XlibWindow(w)
3018                    } else if api == CLAP_WINDOW_API_COCOA {
3019                        check_null_ptr!(false, window.specific.cocoa);
3020                        let w = NonNull::new(window.specific.cocoa).unwrap();
3021                        ParentWindowHandle::AppKitNsView(w)
3022                    } else if api == CLAP_WINDOW_API_WIN32 {
3023                        check_null_ptr!(false, window.specific.win32);
3024                        let w = NonZeroIsize::new(window.specific.win32 as isize).unwrap();
3025                        ParentWindowHandle::Win32Hwnd(w)
3026                    } else {
3027                        crate::nice_debug_assert_failure!("Host passed an invalid API");
3028                        return false;
3029                    }
3030                };
3031
3032                // This extension is only exposed when we have an editor
3033                *editor_handle = Some(Fragile::new(
3034                    wrapper
3035                        .editor
3036                        .borrow()
3037                        .as_ref()
3038                        .unwrap()
3039                        .lock()
3040                        .spawn(parent_handle, wrapper.clone().make_gui_context()),
3041                ));
3042
3043                true
3044            } else {
3045                crate::nice_debug_assert_failure!(
3046                    "Host tried to attach editor while the editor is already attached"
3047                );
3048
3049                false
3050            }
3051        };
3052
3053        // Leak the Arc again since we only needed a clone to pass to the GuiContext
3054        let _ = Arc::into_raw(wrapper);
3055
3056        result
3057    }
3058
3059    unsafe extern "C" fn ext_gui_set_transient(
3060        _plugin: *const clap_plugin,
3061        _window: *const clap_window,
3062    ) -> bool {
3063        // This is only relevant for floating windows
3064        false
3065    }
3066
3067    unsafe extern "C" fn ext_gui_suggest_title(_plugin: *const clap_plugin, _title: *const c_char) {
3068        // This is only relevant for floating windows
3069    }
3070
3071    unsafe extern "C" fn ext_gui_show(_plugin: *const clap_plugin) -> bool {
3072        // TODO: Does this get used? Is this only for the free-standing window extension? (which we
3073        //       don't implement) This wouldn't make any sense for embedded editors.
3074        false
3075    }
3076
3077    unsafe extern "C" fn ext_gui_hide(_plugin: *const clap_plugin) -> bool {
3078        // TODO: Same as the above
3079        false
3080    }
3081
3082    unsafe extern "C" fn ext_latency_get(plugin: *const clap_plugin) -> u32 {
3083        check_null_ptr!(0, plugin, unsafe { (*plugin).plugin_data });
3084        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3085
3086        wrapper.current_latency.load(Ordering::SeqCst)
3087    }
3088
3089    unsafe extern "C" fn ext_note_ports_count(_plugin: *const clap_plugin, is_input: bool) -> u32 {
3090        match is_input {
3091            true if P::MIDI_INPUT >= MidiConfig::Basic => 1,
3092            false if P::MIDI_OUTPUT >= MidiConfig::Basic => 1,
3093            _ => 0,
3094        }
3095    }
3096
3097    unsafe extern "C" fn ext_note_ports_get(
3098        _plugin: *const clap_plugin,
3099        index: u32,
3100        is_input: bool,
3101        info: *mut clap_note_port_info,
3102    ) -> bool {
3103        match (index, is_input) {
3104            (0, true) if P::MIDI_INPUT >= MidiConfig::Basic => {
3105                unsafe {
3106                    *info = std::mem::zeroed();
3107                }
3108
3109                let info = unsafe { &mut *info };
3110                info.id = 0;
3111                // NOTE: REAPER won't send us SysEx if we don't support the MIDI dialect
3112                // TODO: Implement MPE (would just be a toggle for the plugin to expose it) and MIDI2
3113                info.supported_dialects = CLAP_NOTE_DIALECT_CLAP | CLAP_NOTE_DIALECT_MIDI;
3114                info.preferred_dialect = CLAP_NOTE_DIALECT_CLAP;
3115                strlcpy(&mut info.name, "Note Input");
3116
3117                true
3118            }
3119            (0, false) if P::MIDI_OUTPUT >= MidiConfig::Basic => {
3120                unsafe { *info = std::mem::zeroed() };
3121
3122                let info = unsafe { &mut *info };
3123                info.id = 0;
3124                // If `P::MIDI_OUTPUT < MidiConfig::MidiCCs` we'll throw away MIDI CCs, pitch bend
3125                // messages, and other messages that are not basic note on, off and polyphonic
3126                // pressure messages. This way the behavior is the same as the VST3 wrapper.
3127                info.supported_dialects = CLAP_NOTE_DIALECT_CLAP | CLAP_NOTE_DIALECT_MIDI;
3128                info.preferred_dialect = CLAP_NOTE_DIALECT_CLAP;
3129                strlcpy(&mut info.name, "Note Output");
3130
3131                true
3132            }
3133            _ => false,
3134        }
3135    }
3136
3137    unsafe extern "C" fn ext_params_count(plugin: *const clap_plugin) -> u32 {
3138        check_null_ptr!(0, plugin, unsafe { (*plugin).plugin_data });
3139        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3140
3141        wrapper.param_hashes.len() as u32
3142    }
3143
3144    unsafe extern "C" fn ext_params_get_info(
3145        plugin: *const clap_plugin,
3146        param_index: u32,
3147        param_info: *mut clap_param_info,
3148    ) -> bool {
3149        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, param_info);
3150        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3151
3152        if param_index > unsafe { Self::ext_params_count(plugin) } {
3153            return false;
3154        }
3155
3156        let param_hash = &wrapper.param_hashes[param_index as usize];
3157        let param_group = &wrapper.param_group_by_hash[param_hash];
3158        let param_ptr = &wrapper.param_by_hash[param_hash];
3159        let default_value = unsafe { param_ptr.default_normalized_value() };
3160        let step_count = unsafe { param_ptr.step_count() };
3161        let flags = unsafe { param_ptr.flags() };
3162        let automatable = !flags.contains(ParamFlags::NON_AUTOMATABLE);
3163        let hidden = flags.contains(ParamFlags::HIDDEN);
3164        let is_bypass = flags.contains(ParamFlags::BYPASS);
3165
3166        unsafe {
3167            *param_info = std::mem::zeroed();
3168        }
3169
3170        // TODO: We don't use the cookies at this point. In theory this would be faster than the ID
3171        //       hashmap lookup, but for now we'll stay consistent with the VST3 implementation.
3172        let param_info = unsafe { &mut *param_info };
3173        param_info.id = *param_hash;
3174        // TODO: Somehow expose per note/channel/port modulation
3175        param_info.flags = 0;
3176        if automatable && !hidden {
3177            param_info.flags |= CLAP_PARAM_IS_AUTOMATABLE | CLAP_PARAM_IS_MODULATABLE;
3178            if wrapper.poly_mod_ids_by_hash.contains_key(param_hash) {
3179                param_info.flags |= CLAP_PARAM_IS_MODULATABLE_PER_NOTE_ID;
3180            }
3181        }
3182        if hidden {
3183            param_info.flags |= CLAP_PARAM_IS_HIDDEN | CLAP_PARAM_IS_READONLY;
3184        }
3185        if is_bypass {
3186            param_info.flags |= CLAP_PARAM_IS_BYPASS
3187        }
3188        if step_count.is_some() {
3189            param_info.flags |= CLAP_PARAM_IS_STEPPED
3190        }
3191        param_info.cookie = std::ptr::null_mut();
3192        strlcpy(&mut param_info.name, unsafe { param_ptr.name() });
3193        strlcpy(&mut param_info.module, param_group);
3194        // We don't use the actual minimum and maximum values here because that would not scale
3195        // with skewed integer ranges. Instead, just treat all parameters as `[0, 1]` normalized
3196        // parameters multiplied by the step size.
3197        param_info.min_value = 0.0;
3198        // Stepped parameters are unnormalized float parameters since there's no separate step
3199        // range option
3200        // TODO: This should probably be encapsulated in some way so we don't forget about this in one place
3201        param_info.max_value = step_count.unwrap_or(1) as f64;
3202        param_info.default_value = default_value as f64 * step_count.unwrap_or(1) as f64;
3203
3204        true
3205    }
3206
3207    unsafe extern "C" fn ext_params_get_value(
3208        plugin: *const clap_plugin,
3209        param_id: clap_id,
3210        value: *mut f64,
3211    ) -> bool {
3212        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, value);
3213        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3214
3215        match wrapper.param_by_hash.get(&param_id) {
3216            Some(param_ptr) => {
3217                unsafe {
3218                    *value = param_ptr.modulated_normalized_value() as f64
3219                        * param_ptr.step_count().unwrap_or(1) as f64;
3220                }
3221
3222                true
3223            }
3224            _ => false,
3225        }
3226    }
3227
3228    unsafe extern "C" fn ext_params_value_to_text(
3229        plugin: *const clap_plugin,
3230        param_id: clap_id,
3231        value: f64,
3232        display: *mut c_char,
3233        size: u32,
3234    ) -> bool {
3235        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, display);
3236        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3237
3238        let dest = unsafe { std::slice::from_raw_parts_mut(display, size as usize) };
3239
3240        match wrapper.param_by_hash.get(&param_id) {
3241            Some(param_ptr) => {
3242                unsafe {
3243                    strlcpy(
3244                        dest,
3245                        // CLAP does not have a separate unit, so we'll include the unit here
3246                        &param_ptr.normalized_value_to_string(
3247                            value as f32 / param_ptr.step_count().unwrap_or(1) as f32,
3248                            true,
3249                        ),
3250                    );
3251                }
3252
3253                true
3254            }
3255            _ => false,
3256        }
3257    }
3258
3259    unsafe extern "C" fn ext_params_text_to_value(
3260        plugin: *const clap_plugin,
3261        param_id: clap_id,
3262        display: *const c_char,
3263        value: *mut f64,
3264    ) -> bool {
3265        check_null_ptr!(
3266            false,
3267            plugin,
3268            unsafe { (*plugin).plugin_data },
3269            display,
3270            value
3271        );
3272        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3273
3274        let display = match unsafe { CStr::from_ptr(display).to_str() } {
3275            Ok(s) => s,
3276            Err(_) => return false,
3277        };
3278
3279        match wrapper.param_by_hash.get(&param_id) {
3280            Some(param_ptr) => {
3281                let normalized_value =
3282                    match unsafe { param_ptr.string_to_normalized_value(display) } {
3283                        Some(v) => v as f64,
3284                        None => return false,
3285                    };
3286                unsafe {
3287                    *value = normalized_value * param_ptr.step_count().unwrap_or(1) as f64;
3288                }
3289
3290                true
3291            }
3292            _ => false,
3293        }
3294    }
3295
3296    unsafe extern "C" fn ext_params_flush(
3297        plugin: *const clap_plugin,
3298        in_: *const clap_input_events,
3299        out: *const clap_output_events,
3300    ) {
3301        check_null_ptr!((), plugin, unsafe { (*plugin).plugin_data });
3302        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3303
3304        if !in_.is_null() {
3305            unsafe {
3306                wrapper.handle_in_events(&*in_, 0, 0);
3307            }
3308        }
3309
3310        if !out.is_null() {
3311            unsafe {
3312                wrapper.handle_out_events(&*out, 0, 0);
3313            }
3314        }
3315    }
3316
3317    unsafe extern "C" fn ext_remote_controls_count(plugin: *const clap_plugin) -> u32 {
3318        check_null_ptr!(0, plugin, unsafe { (*plugin).plugin_data });
3319        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3320
3321        wrapper.remote_control_pages.len() as u32
3322    }
3323
3324    unsafe extern "C" fn ext_remote_controls_get(
3325        plugin: *const clap_plugin,
3326        page_index: u32,
3327        page: *mut clap_remote_controls_page,
3328    ) -> bool {
3329        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, page);
3330        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3331
3332        crate::nice_debug_assert!(page_index as usize <= wrapper.remote_control_pages.len());
3333        match wrapper.remote_control_pages.get(page_index as usize) {
3334            Some(p) => {
3335                unsafe {
3336                    *page = *p;
3337                }
3338                true
3339            }
3340            None => false,
3341        }
3342    }
3343
3344    unsafe extern "C" fn ext_render_has_hard_realtime_requirement(
3345        _plugin: *const clap_plugin,
3346    ) -> bool {
3347        P::HARD_REALTIME_ONLY
3348    }
3349
3350    unsafe extern "C" fn ext_render_set(
3351        plugin: *const clap_plugin,
3352        mode: clap_plugin_render_mode,
3353    ) -> bool {
3354        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
3355        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3356
3357        let mode = match mode {
3358            CLAP_RENDER_REALTIME => ProcessMode::Realtime,
3359            // Even if the plugin has a hard realtime requirement, we'll still honor this
3360            CLAP_RENDER_OFFLINE => ProcessMode::Offline,
3361            n => {
3362                crate::nice_debug_assert_failure!(
3363                    "Unknown rendering mode '{}', defaulting to realtime",
3364                    n
3365                );
3366                ProcessMode::Realtime
3367            }
3368        };
3369
3370        if wrapper.current_process_mode.swap(mode) != mode
3371            && wrapper.is_activated.load(Ordering::SeqCst)
3372        {
3373            // We may change process mode while activated. In that case, restart the audio processor
3374            // so the plugin can react to the process mode change in `Plugin::initialize`.
3375            unsafe_clap_call! { &*wrapper.host_callback=>request_restart(&*wrapper.host_callback) };
3376        }
3377
3378        true
3379    }
3380
3381    unsafe extern "C" fn ext_state_save(
3382        plugin: *const clap_plugin,
3383        stream: *const clap_ostream,
3384    ) -> bool {
3385        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, stream);
3386        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3387
3388        let serialized = unsafe {
3389            state::serialize_json::<P>(
3390                wrapper.params.clone(),
3391                state::make_params_iter(&wrapper.param_by_hash, &wrapper.param_id_to_hash),
3392            )
3393        };
3394        match serialized {
3395            Ok(serialized) => {
3396                // CLAP does not provide a way to tell how much data there is left in a stream, so
3397                // we need to prepend it to our actual state data.
3398                let length_bytes = (serialized.len() as u64).to_le_bytes();
3399                if !write_stream(unsafe { &*stream }, &length_bytes) {
3400                    crate::nice_debug_assert_failure!(
3401                        "Error or end of stream while writing the state length to the stream."
3402                    );
3403                    return false;
3404                }
3405                if !write_stream(unsafe { &*stream }, &serialized) {
3406                    crate::nice_debug_assert_failure!(
3407                        "Error or end of stream while writing the state buffer to the stream."
3408                    );
3409                    return false;
3410                }
3411
3412                crate::nice_trace!("Saved state ({} bytes)", serialized.len());
3413
3414                true
3415            }
3416            Err(err) => {
3417                crate::nice_debug_assert_failure!("Could not save state: {:#}", err);
3418                false
3419            }
3420        }
3421    }
3422
3423    unsafe extern "C" fn ext_state_load(
3424        plugin: *const clap_plugin,
3425        stream: *const clap_istream,
3426    ) -> bool {
3427        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, stream);
3428        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3429
3430        // CLAP does not have a way to tell how much data there is left in a stream, so we've
3431        // prepended the size in front of our JSON state
3432        let mut length_bytes = [0u8; 8];
3433        if !read_stream(unsafe { &*stream }, length_bytes.as_mut_slice()) {
3434            crate::nice_debug_assert_failure!(
3435                "Error or end of stream while reading the state length from the stream."
3436            );
3437            return false;
3438        }
3439        let length = u64::from_le_bytes(length_bytes);
3440
3441        let mut read_buffer: Vec<u8> = Vec::with_capacity(length as usize);
3442        if !read_stream(unsafe { &*stream }, read_buffer.spare_capacity_mut()) {
3443            crate::nice_debug_assert_failure!(
3444                "Error or end of stream while reading the state buffer from the stream."
3445            );
3446            return false;
3447        }
3448        unsafe {
3449            read_buffer.set_len(length as usize);
3450        }
3451
3452        match unsafe { state::deserialize_json(&read_buffer) } {
3453            Some(mut state) => {
3454                let success = wrapper.set_state_inner(&mut state);
3455                if success {
3456                    crate::nice_trace!("Loaded state ({} bytes)", read_buffer.len());
3457                }
3458
3459                success
3460            }
3461            None => false,
3462        }
3463    }
3464
3465    unsafe extern "C" fn ext_track_info_changed(plugin: *const clap_plugin) {
3466        check_null_ptr!((), plugin, unsafe { (*plugin).plugin_data });
3467        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3468
3469        wrapper.update_track_info_from_host();
3470    }
3471
3472    unsafe extern "C" fn ext_tail_get(plugin: *const clap_plugin) -> u32 {
3473        check_null_ptr!(0, plugin, unsafe { (*plugin).plugin_data });
3474        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3475
3476        match wrapper.last_process_status.load() {
3477            ProcessStatus::Tail(samples) => samples,
3478            ProcessStatus::KeepAlive => u32::MAX,
3479            _ => 0,
3480        }
3481    }
3482
3483    unsafe extern "C" fn ext_voice_info_get(
3484        plugin: *const clap_plugin,
3485        info: *mut clap_voice_info,
3486    ) -> bool {
3487        check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, info);
3488        let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3489
3490        match P::CLAP_POLY_MODULATION_CONFIG {
3491            Some(config) => {
3492                unsafe {
3493                    *info = clap_voice_info {
3494                        voice_count: wrapper.current_voice_capacity.load(Ordering::Relaxed),
3495                        voice_capacity: config.max_voice_capacity,
3496                        flags: if config.supports_overlapping_voices {
3497                            CLAP_VOICE_INFO_SUPPORTS_OVERLAPPING_NOTES
3498                        } else {
3499                            0
3500                        },
3501                    };
3502                }
3503
3504                true
3505            }
3506            None => false,
3507        }
3508    }
3509}
3510
3511/// Convenience function to query an extension from the host.
3512///
3513/// # Safety
3514///
3515/// The extension type `T` must match the extension's name `name`.
3516unsafe fn query_host_extension<T>(
3517    host_callback: &ClapPtr<clap_host>,
3518    name: &CStr,
3519) -> Option<ClapPtr<T>> {
3520    let extension_ptr = unsafe {
3521        clap_call! { host_callback=>get_extension(&**host_callback, name.as_ptr()) }
3522    };
3523    if !extension_ptr.is_null() {
3524        unsafe { Some(ClapPtr::new(extension_ptr as *const T)) }
3525    } else {
3526        None
3527    }
3528}