Skip to main content

nice_plug/wrapper/clap/
wrapper.rs

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