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
111const OUTPUT_EVENT_QUEUE_CAPACITY: usize = 2048;
114
115pub struct Wrapper<P: ClapPlugin> {
116 this: AtomicRefCell<Weak<Self>>,
118
119 plugin: Mutex<P>,
121 pub task_executor: Mutex<TaskExecutor<P>>,
123 params: Arc<dyn Params>,
127 #[cfg(feature = "editor")]
131 editor: AtomicRefCell<Option<Mutex<P::Editor>>>,
132 #[cfg(feature = "editor")]
135 #[allow(clippy::type_complexity)]
136 editor_window:
137 AtomicRefCell<Option<fragile::Fragile<SpawnedEditor<<P::Editor as Editor>::Handle>>>>,
138 #[cfg(feature = "editor")]
143 fallback_scale_factor: AtomicCell<Option<f64>>,
144 is_activated: AtomicBool,
145 is_processing: AtomicBool,
146 current_audio_io_layout: AtomicCell<AudioIOLayout>,
149 current_buffer_config: AtomicCell<Option<BufferConfig>>,
152 pub current_process_mode: AtomicCell<ProcessMode>,
154 input_events: AtomicRefCell<VecDeque<PluginNoteEvent<P>>>,
160 output_events: AtomicRefCell<VecDeque<PluginNoteEvent<P>>>,
163 last_process_status: AtomicCell<ProcessStatus>,
165 latency_changed: AtomicBool,
169 pub current_latency: AtomicU32,
173 buffer_manager: AtomicRefCell<BufferManager>,
176 updated_state_sender: channel::Sender<PluginState>,
185 updated_state_receiver: channel::Receiver<PluginState>,
187
188 host_callback: ClapPtr<clap_host>,
190
191 clap_plugin_audio_ports_config: clap_plugin_audio_ports_config,
192
193 pub clap_plugin: AtomicRefCell<clap_plugin>,
196 _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 param_hashes: Vec<u32>,
218 param_by_hash: HashMap<u32, ParamPtr>,
223 param_id_by_hash: HashMap<u32, String>,
226 param_group_by_hash: HashMap<u32, String>,
230 param_id_to_hash: HashMap<String, u32>,
233 pub param_ptr_to_hash: HashMap<ParamPtr, u32>,
238 poly_mod_ids_by_hash: HashMap<u32, u32>,
242 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 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 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 current_voice_capacity: AtomicU32,
274
275 tasks: ArrayQueue<Task<P>>,
281 main_thread_id: ThreadId,
286 background_thread: AtomicRefCell<Option<BackgroundThread<Task<P>, Self>>>,
289}
290
291#[allow(clippy::enum_variant_names)]
295pub enum Task<P: Plugin> {
296 PluginTask(P::BackgroundTask),
298 #[cfg(feature = "editor")]
301 ParameterValueChanged(u32, f32),
302 #[cfg(feature = "editor")]
305 ParameterModulationChanged(u32, f32),
306 #[cfg(feature = "editor")]
307 StateChanged,
308 LatencyChanged,
310 VoiceInfoChanged,
312 RescanParamValues,
314}
315
316pub enum ClapParamUpdate {
318 PlainValueSet(f64),
321 PlainValueMod(f64),
325}
326
327#[derive(Debug, Clone)]
330pub enum OutputParamEvent {
331 BeginGesture { param_hash: u32 },
333 SetValue {
336 param_hash: u32,
338 clap_plain_value: f64,
341 },
342 EndGesture { param_hash: u32 },
345}
346
347impl<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 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 match &*self.host_thread_check.borrow() {
382 Some(thread_check) => {
383 unsafe_clap_call! { thread_check=>is_main_thread(&*self.host_callback) }
384 }
385 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 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[¶m_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[¶m_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 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 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 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 assert!(!host_callback.is_null());
491 let host_callback = unsafe { ClapPtr::new(host_callback) };
492
493 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 ¶m_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 let mut remote_control_pages = Vec::new();
576 RemoteControlPages::define_remote_control_pages(
577 &plugin,
578 &mut remote_control_pages,
579 ¶m_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 #[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 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 desc: plugin_descriptor.clap_plugin_descriptor(),
625 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 background_thread: AtomicRefCell::new(None),
748 };
749
750 let wrapper = Arc::new(wrapper);
753 *wrapper.this.borrow_mut() = Arc::downgrade(&wrapper);
754
755 wrapper.clap_plugin.borrow_mut().plugin_data = Arc::as_ptr(&wrapper) as *mut _;
758
759 *wrapper.background_thread.borrow_mut() =
761 Some(BackgroundThread::get_or_create(Arc::downgrade(&wrapper)));
762
763 #[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 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 #[allow(unused)]
839 pub fn param_id_from_ptr(&self, param: ParamPtr) -> Option<&str> {
840 self.param_ptr_to_hash
841 .get(¶m)
842 .and_then(|hash| self.param_id_by_hash.get(hash))
843 .map(|s| s.as_str())
844 }
845
846 pub fn queue_parameter_event(&self, event: OutputParamEvent) -> bool {
854 let result = self.output_parameter_events.push(event).is_ok();
855
856 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 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 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 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 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 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 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 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 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 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 let mut output_events = self.output_events.borrow_mut();
1133 while let Some(event) = output_events.pop_front() {
1134 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 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 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 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 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 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 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 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 let Some(poly_modulation_id) = self.poly_mod_ids_by_hash.get(&event.param_id) {
1524 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 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 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 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 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 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 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 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 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 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 pub fn set_state_object_from_gui(&self, mut state: PluginState) {
1796 let mut did_set_state_inner = false;
1797
1798 loop {
1801 if self.is_processing.load(Ordering::SeqCst) {
1802 match self
1808 .updated_state_sender
1809 .send_timeout(state, Duration::from_secs(1))
1810 {
1811 Ok(_) => {
1812 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 self.set_state_inner(&mut state);
1831 did_set_state_inner = true;
1832 break;
1833 }
1834 }
1835
1836 if !did_set_state_inner {
1837 let task_posted = self.schedule_gui(Task::RescanParamValues);
1839 crate::nice_debug_assert!(task_posted, "The task queue is full, dropping task...");
1840 } }
1842
1843 pub fn set_latency_samples(&self, samples: u32) {
1844 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 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 } }
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 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 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 let Some(buffer_config) = buffer_config {
1965 let mut activate_context = self.make_activate_context();
1966 let mut plugin = self.plugin.lock();
1967
1968 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 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 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 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 for param in wrapper.param_by_hash.values() {
2060 unsafe { param._internal_update_smoother(buffer_config.sample_rate, true) };
2061 }
2062
2063 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 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 *wrapper.buffer_manager.borrow_mut() =
2081 BufferManager::for_audio_io_layout(max_frames_count as usize, audio_io_layout);
2082
2083 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 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
2107 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2108
2109 wrapper.last_process_status.store(ProcessStatus::Normal);
2111 wrapper.is_processing.store(true, Ordering::SeqCst);
2112
2113 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 process_wrapper(|| {
2149 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 let mut block_start = 0;
2164 let mut block_end = total_buffer_len;
2165 let mut event_start_idx = 0;
2166
2167 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 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 !(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 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 let block_len = block_end - block_start;
2228
2229 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 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 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 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 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 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 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 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 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 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 block_end == total_buffer_len {
2473 break clap_result;
2474 } else {
2475 block_start = block_end;
2476 }
2477 };
2478
2479 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 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 &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 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 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 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 let is_main_port =
2729 index == 0 && ((is_input && has_main_input) || (!is_input && has_main_output));
2730
2731 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 (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 (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, ¤t_audio_io_layout.main_input_name()),
2776 (false, true) => strlcpy(&mut info.name, ¤t_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 ¤t_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 ¤t_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 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 *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 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 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 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 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 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
3210 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3211
3212 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 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 }
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 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 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 let param_info = unsafe { &mut *param_info };
3378 param_info.id = *param_hash;
3379 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 param_info.min_value = 0.0;
3403 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(¶m_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(¶m_id) {
3446 Some(param_ptr) => {
3447 unsafe {
3448 strlcpy(
3449 dest,
3450 ¶m_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(¶m_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 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 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 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 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
3716unsafe 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}