Skip to main content

nice_plug/wrapper/clap/
wrapper.rs

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