1use atomic_float::AtomicF64;
2use atomic_refcell::{AtomicRefCell, AtomicRefMut};
3use clap_sys::events::{
4 CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_IS_LIVE, CLAP_EVENT_MIDI, CLAP_EVENT_MIDI_SYSEX,
5 CLAP_EVENT_NOTE_CHOKE, CLAP_EVENT_NOTE_END, CLAP_EVENT_NOTE_EXPRESSION, CLAP_EVENT_NOTE_OFF,
6 CLAP_EVENT_NOTE_ON, CLAP_EVENT_PARAM_GESTURE_BEGIN, CLAP_EVENT_PARAM_GESTURE_END,
7 CLAP_EVENT_PARAM_MOD, CLAP_EVENT_PARAM_VALUE, CLAP_EVENT_TRANSPORT,
8 CLAP_NOTE_EXPRESSION_BRIGHTNESS, CLAP_NOTE_EXPRESSION_EXPRESSION, CLAP_NOTE_EXPRESSION_PAN,
9 CLAP_NOTE_EXPRESSION_PRESSURE, CLAP_NOTE_EXPRESSION_TUNING, CLAP_NOTE_EXPRESSION_VIBRATO,
10 CLAP_NOTE_EXPRESSION_VOLUME, CLAP_TRANSPORT_HAS_BEATS_TIMELINE,
11 CLAP_TRANSPORT_HAS_SECONDS_TIMELINE, CLAP_TRANSPORT_HAS_TEMPO,
12 CLAP_TRANSPORT_HAS_TIME_SIGNATURE, CLAP_TRANSPORT_IS_LOOP_ACTIVE, CLAP_TRANSPORT_IS_PLAYING,
13 CLAP_TRANSPORT_IS_RECORDING, CLAP_TRANSPORT_IS_WITHIN_PRE_ROLL, clap_event_header,
14 clap_event_midi, clap_event_midi_sysex, clap_event_note, clap_event_note_expression,
15 clap_event_param_gesture, clap_event_param_mod, clap_event_param_value, clap_event_transport,
16 clap_input_events, clap_output_events,
17};
18use clap_sys::ext::audio_ports::{
19 CLAP_AUDIO_PORT_IS_MAIN, CLAP_EXT_AUDIO_PORTS, CLAP_PORT_MONO, CLAP_PORT_STEREO,
20 clap_audio_port_info, clap_plugin_audio_ports,
21};
22use clap_sys::ext::audio_ports_config::{
23 CLAP_EXT_AUDIO_PORTS_CONFIG, clap_audio_ports_config, clap_plugin_audio_ports_config,
24};
25use clap_sys::ext::gui::{
26 CLAP_EXT_GUI, CLAP_WINDOW_API_COCOA, CLAP_WINDOW_API_WIN32, CLAP_WINDOW_API_X11,
27 clap_gui_resize_hints, clap_host_gui, clap_plugin_gui, clap_window,
28};
29use clap_sys::ext::latency::{CLAP_EXT_LATENCY, clap_host_latency, clap_plugin_latency};
30use clap_sys::ext::note_ports::{
31 CLAP_EXT_NOTE_PORTS, CLAP_NOTE_DIALECT_CLAP, CLAP_NOTE_DIALECT_MIDI, clap_note_port_info,
32 clap_plugin_note_ports,
33};
34use clap_sys::ext::params::{
35 CLAP_EXT_PARAMS, CLAP_PARAM_IS_AUTOMATABLE, CLAP_PARAM_IS_BYPASS, CLAP_PARAM_IS_HIDDEN,
36 CLAP_PARAM_IS_MODULATABLE, CLAP_PARAM_IS_MODULATABLE_PER_NOTE_ID, CLAP_PARAM_IS_READONLY,
37 CLAP_PARAM_IS_STEPPED, CLAP_PARAM_RESCAN_VALUES, clap_host_params, clap_param_info,
38 clap_plugin_params,
39};
40use clap_sys::ext::remote_controls::{
41 CLAP_EXT_REMOTE_CONTROLS, clap_plugin_remote_controls, clap_remote_controls_page,
42};
43use clap_sys::ext::render::{
44 CLAP_EXT_RENDER, CLAP_RENDER_OFFLINE, CLAP_RENDER_REALTIME, clap_plugin_render,
45 clap_plugin_render_mode,
46};
47use clap_sys::ext::state::{CLAP_EXT_STATE, clap_plugin_state};
48use clap_sys::ext::tail::{CLAP_EXT_TAIL, clap_plugin_tail};
49use clap_sys::ext::thread_check::{CLAP_EXT_THREAD_CHECK, clap_host_thread_check};
50use clap_sys::ext::track_info::{
51 CLAP_EXT_TRACK_INFO, CLAP_TRACK_INFO_HAS_TRACK_COLOR, CLAP_TRACK_INFO_HAS_TRACK_NAME,
52 clap_host_track_info, clap_plugin_track_info, clap_track_info,
53};
54use clap_sys::ext::voice_info::{
55 CLAP_EXT_VOICE_INFO, CLAP_VOICE_INFO_SUPPORTS_OVERLAPPING_NOTES, clap_host_voice_info,
56 clap_plugin_voice_info, clap_voice_info,
57};
58use clap_sys::fixedpoint::{CLAP_BEATTIME_FACTOR, CLAP_SECTIME_FACTOR};
59use clap_sys::host::clap_host;
60use clap_sys::id::{CLAP_INVALID_ID, clap_id};
61use clap_sys::plugin::clap_plugin;
62use clap_sys::process::{
63 CLAP_PROCESS_CONTINUE, CLAP_PROCESS_CONTINUE_IF_NOT_QUIET, CLAP_PROCESS_ERROR, clap_process,
64 clap_process_status,
65};
66use clap_sys::stream::{clap_istream, clap_ostream};
67use crossbeam::atomic::AtomicCell;
68use crossbeam::channel::{self, SendTimeoutError};
69use crossbeam::queue::ArrayQueue;
70use fragile::Fragile;
71use nice_plug_core::audio_setup::{AudioIOLayout, AuxiliaryBuffers, BufferConfig, ProcessMode};
72use nice_plug_core::context::gui::AsyncExecutor;
73use nice_plug_core::context::process::Transport;
74use nice_plug_core::editor::{Editor, ParentWindowHandle, dpi::PhysicalSize};
75use nice_plug_core::midi::sysex::SysExMessage;
76use nice_plug_core::midi::{MidiConfig, NoteEvent, PluginNoteEvent};
77use nice_plug_core::params::internals::ParamPtr;
78use nice_plug_core::params::{ParamFlags, Params};
79use nice_plug_core::plugin::{
80 Plugin, PluginState, ProcessStatus, TaskExecutor, TrackColor, TrackInfo,
81};
82use parking_lot::Mutex;
83use std::any::Any;
84use std::borrow::Borrow;
85use std::collections::{HashMap, HashSet, VecDeque};
86use std::ffi::{CStr, c_ulong, c_void};
87use std::mem;
88use std::num::{NonZeroIsize, NonZeroU32};
89use std::os::raw::c_char;
90use std::ptr::NonNull;
91use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
92use std::sync::{Arc, Weak};
93use std::thread::{self, ThreadId};
94use std::time::Duration;
95
96use super::context::{WrapperGuiContext, WrapperInitContext, WrapperProcessContext};
97use super::descriptor::PluginDescriptor;
98use super::util::ClapPtr;
99use crate::event_loop::{BackgroundThread, EventLoop, MainThreadExecutor, TASK_QUEUE_CAPACITY};
100use crate::midi::MidiResult;
101use crate::util::permit_alloc;
102use crate::wrapper::clap::ClapPlugin;
103use crate::wrapper::clap::context::RemoteControlPages;
104use crate::wrapper::clap::util::{read_stream, write_stream};
105use crate::wrapper::state::{self};
106use crate::wrapper::util::buffer_management::{BufferManager, ChannelPointers};
107use crate::wrapper::util::{
108 clamp_input_event_timing, clamp_output_event_timing, hash_param_id, process_wrapper, strlcpy,
109};
110
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 editor: AtomicRefCell<Option<Mutex<Box<dyn Editor>>>>,
131 editor_handle: Mutex<Option<Fragile<Box<dyn Any>>>>,
134 scale_factor: AtomicF64,
139
140 is_activated: AtomicBool,
141 is_processing: AtomicBool,
142 current_audio_io_layout: AtomicCell<AudioIOLayout>,
145 current_buffer_config: AtomicCell<Option<BufferConfig>>,
148 pub current_process_mode: AtomicCell<ProcessMode>,
150 input_events: AtomicRefCell<VecDeque<PluginNoteEvent<P>>>,
156 output_events: AtomicRefCell<VecDeque<PluginNoteEvent<P>>>,
159 last_process_status: AtomicCell<ProcessStatus>,
161 latency_changed: AtomicBool,
165 pub current_latency: AtomicU32,
169 buffer_manager: AtomicRefCell<BufferManager>,
172 updated_state_sender: channel::Sender<PluginState>,
181 updated_state_receiver: channel::Receiver<PluginState>,
183
184 host_callback: ClapPtr<clap_host>,
186
187 clap_plugin_audio_ports_config: clap_plugin_audio_ports_config,
188
189 pub clap_plugin: AtomicRefCell<clap_plugin>,
192 _plugin_descriptor: Box<PluginDescriptor>,
195
196 clap_plugin_audio_ports: clap_plugin_audio_ports,
197
198 clap_plugin_gui: clap_plugin_gui,
199 host_gui: AtomicRefCell<Option<ClapPtr<clap_host_gui>>>,
200
201 clap_plugin_latency: clap_plugin_latency,
202 host_latency: AtomicRefCell<Option<ClapPtr<clap_host_latency>>>,
203
204 clap_plugin_note_ports: clap_plugin_note_ports,
205
206 clap_plugin_params: clap_plugin_params,
207 host_params: AtomicRefCell<Option<ClapPtr<clap_host_params>>>,
208 param_hashes: Vec<u32>,
212 param_by_hash: HashMap<u32, ParamPtr>,
217 param_id_by_hash: HashMap<u32, String>,
220 param_group_by_hash: HashMap<u32, String>,
224 param_id_to_hash: HashMap<String, u32>,
227 pub param_ptr_to_hash: HashMap<ParamPtr, u32>,
232 poly_mod_ids_by_hash: HashMap<u32, u32>,
236 output_parameter_events: ArrayQueue<OutputParamEvent>,
243
244 host_thread_check: AtomicRefCell<Option<ClapPtr<clap_host_thread_check>>>,
245
246 clap_plugin_remote_controls: clap_plugin_remote_controls,
247 remote_control_pages: Vec<clap_remote_controls_page>,
249
250 clap_plugin_render: clap_plugin_render,
251
252 clap_plugin_state: clap_plugin_state,
253
254 clap_plugin_tail: clap_plugin_tail,
255
256 clap_plugin_track_info: clap_plugin_track_info,
257 host_track_info: AtomicRefCell<Option<ClapPtr<clap_host_track_info>>>,
258 current_track_info: AtomicRefCell<TrackInfo>,
261
262 clap_plugin_voice_info: clap_plugin_voice_info,
263 host_voice_info: AtomicRefCell<Option<ClapPtr<clap_host_voice_info>>>,
264 current_voice_capacity: AtomicU32,
268
269 tasks: ArrayQueue<Task<P>>,
275 main_thread_id: ThreadId,
280 background_thread: AtomicRefCell<Option<BackgroundThread<Task<P>, Self>>>,
283}
284
285#[allow(clippy::enum_variant_names)]
289pub enum Task<P: Plugin> {
290 PluginTask(P::BackgroundTask),
292 ParameterValuesChanged,
294 ParameterValueChanged(u32, f32),
297 ParameterModulationChanged(u32, f32),
300 LatencyChanged,
302 VoiceInfoChanged,
304 RescanParamValues,
306}
307
308pub enum ClapParamUpdate {
310 PlainValueSet(f64),
313 PlainValueMod(f64),
317}
318
319#[derive(Debug, Clone)]
322pub enum OutputParamEvent {
323 BeginGesture { param_hash: u32 },
325 SetValue {
328 param_hash: u32,
330 clap_plain_value: f64,
333 },
334 EndGesture { param_hash: u32 },
337}
338
339impl<P: ClapPlugin> EventLoop<Task<P>, Wrapper<P>> for Wrapper<P> {
342 fn new_and_spawn(_executor: Weak<Self>) -> Self {
343 panic!("What are you doing");
344 }
345
346 fn schedule_gui(&self, task: Task<P>) -> bool {
347 if self.is_main_thread() {
348 self.execute(task, true);
349 true
350 } else {
351 let success = self.tasks.push(task).is_ok();
352 if success {
353 let host = &self.host_callback;
355 unsafe_clap_call! { host=>request_callback(&**host) };
356 }
357
358 success
359 }
360 }
361
362 fn schedule_background(&self, task: Task<P>) -> bool {
363 self.background_thread
364 .borrow()
365 .as_ref()
366 .unwrap()
367 .schedule(task)
368 }
369
370 fn is_main_thread(&self) -> bool {
371 match &*self.host_thread_check.borrow() {
374 Some(thread_check) => {
375 unsafe_clap_call! { thread_check=>is_main_thread(&*self.host_callback) }
376 }
377 None => permit_alloc(|| thread::current().id() == self.main_thread_id),
380 }
381 }
382}
383
384impl<P: ClapPlugin> MainThreadExecutor<Task<P>> for Wrapper<P> {
385 fn execute(&self, task: Task<P>, is_gui_thread: bool) {
386 match task {
388 Task::PluginTask(task) => (self.task_executor.lock())(task),
389 Task::ParameterValuesChanged => {
390 if self.editor_handle.lock().is_some() {
391 if let Some(editor) = self.editor.borrow().as_ref() {
392 editor.lock().param_values_changed();
393 }
394 }
395 }
396 Task::ParameterValueChanged(param_hash, normalized_value) => {
397 if self.editor_handle.lock().is_some() {
398 if let Some(editor) = self.editor.borrow().as_ref() {
399 let param_id = &self.param_id_by_hash[¶m_hash];
400 editor
401 .lock()
402 .param_value_changed(param_id, normalized_value);
403 }
404 }
405 }
406 Task::ParameterModulationChanged(param_hash, modulation_offset) => {
407 if self.editor_handle.lock().is_some() {
408 if let Some(editor) = self.editor.borrow().as_ref() {
409 let param_id = &self.param_id_by_hash[¶m_hash];
410 editor
411 .lock()
412 .param_modulation_changed(param_id, modulation_offset);
413 }
414 }
415 }
416 Task::LatencyChanged => match &*self.host_latency.borrow() {
417 Some(host_latency) => {
418 crate::nice_debug_assert!(is_gui_thread);
419
420 if self.is_activated.load(Ordering::SeqCst) {
428 self.latency_changed.store(true, Ordering::SeqCst);
429 unsafe_clap_call! { &*self.host_callback=>request_restart(&*self.host_callback) };
430 } else {
431 unsafe_clap_call! { host_latency=>changed(&*self.host_callback) };
432 }
433 }
434 None => {
435 crate::nice_debug_assert_failure!("Host does not support the latency extension")
436 }
437 },
438 Task::VoiceInfoChanged => match &*self.host_voice_info.borrow() {
439 Some(host_voice_info) => {
440 crate::nice_debug_assert!(is_gui_thread);
441 unsafe_clap_call! { host_voice_info=>changed(&*self.host_callback) };
442 }
443 None => crate::nice_debug_assert_failure!(
444 "Host does not support the voice-info extension"
445 ),
446 },
447 Task::RescanParamValues => match &*self.host_params.borrow() {
448 Some(host_params) => {
449 crate::nice_debug_assert!(is_gui_thread);
450 unsafe_clap_call! { host_params=>rescan(&*self.host_callback, CLAP_PARAM_RESCAN_VALUES) };
451 }
452 None => {
453 crate::nice_debug_assert_failure!("The host does not support parameters? What?")
454 }
455 },
456 };
457 }
458}
459
460impl<P: ClapPlugin> Wrapper<P> {
461 pub unsafe fn new(host_callback: *const clap_host) -> Arc<Self> {
465 let mut plugin = P::default();
466 let task_executor = Mutex::new(plugin.task_executor());
467
468 let (updated_state_sender, updated_state_receiver) = channel::bounded(0);
471
472 let plugin_descriptor: Box<PluginDescriptor> =
473 Box::new(PluginDescriptor::for_plugin::<P>());
474
475 assert!(!host_callback.is_null());
478 let host_callback = unsafe { ClapPtr::new(host_callback) };
479
480 let params = plugin.params();
486 let param_id_hashes_ptrs_groups: Vec<_> = params
487 .param_map()
488 .into_iter()
489 .map(|(id, ptr, group)| {
490 let hash = hash_param_id(&id);
491 (id, hash, ptr, group)
492 })
493 .collect();
494 let param_hashes = param_id_hashes_ptrs_groups
495 .iter()
496 .map(|(_, hash, _, _)| *hash)
497 .collect();
498 let param_by_hash = param_id_hashes_ptrs_groups
499 .iter()
500 .map(|(_, hash, ptr, _)| (*hash, *ptr))
501 .collect();
502 let param_id_by_hash = param_id_hashes_ptrs_groups
503 .iter()
504 .map(|(id, hash, _, _)| (*hash, id.clone()))
505 .collect();
506 let param_group_by_hash = param_id_hashes_ptrs_groups
507 .iter()
508 .map(|(_, hash, _, group)| (*hash, group.clone()))
509 .collect();
510 let param_id_to_hash = param_id_hashes_ptrs_groups
511 .iter()
512 .map(|(id, hash, _, _)| (id.clone(), *hash))
513 .collect();
514 let param_ptr_to_hash = param_id_hashes_ptrs_groups
515 .iter()
516 .map(|(_, hash, ptr, _)| (*ptr, *hash))
517 .collect();
518 let poly_mod_ids_by_hash: HashMap<u32, u32> = param_id_hashes_ptrs_groups
519 .iter()
520 .filter_map(|(_, hash, ptr, _)| unsafe {
521 ptr.poly_modulation_id().map(|id| (*hash, id))
522 })
523 .collect();
524
525 if cfg!(debug_assertions) {
526 let param_map = params.param_map();
527 let param_ids: HashSet<_> = param_id_hashes_ptrs_groups
528 .iter()
529 .map(|(id, _, _, _)| id.clone())
530 .collect();
531 crate::nice_debug_assert_eq!(
532 param_map.len(),
533 param_ids.len(),
534 "The plugin has duplicate parameter IDs, weird things may happen. Consider using \
535 6 character parameter IDs to avoid collisions."
536 );
537
538 let poly_mod_ids: HashSet<u32> = poly_mod_ids_by_hash.values().copied().collect();
539 crate::nice_debug_assert_eq!(
540 poly_mod_ids_by_hash.len(),
541 poly_mod_ids.len(),
542 "The plugin has duplicate poly modulation IDs. Polyphonic modulation will not be \
543 routed to the correct parameter."
544 );
545
546 let mut bypass_param_exists = false;
547 for (_, _, ptr, _) in ¶m_id_hashes_ptrs_groups {
548 let flags = unsafe { ptr.flags() };
549 let is_bypass = flags.contains(ParamFlags::BYPASS);
550
551 if is_bypass && bypass_param_exists {
552 crate::nice_debug_assert_failure!(
553 "Duplicate bypass parameters found, the host will only use the first one"
554 );
555 }
556
557 bypass_param_exists |= is_bypass;
558 }
559 }
560
561 let mut remote_control_pages = Vec::new();
563 RemoteControlPages::define_remote_control_pages(
564 &plugin,
565 &mut remote_control_pages,
566 ¶m_ptr_to_hash,
567 );
568
569 let wrapper = Self {
570 this: AtomicRefCell::new(Weak::new()),
571
572 plugin: Mutex::new(plugin),
573 task_executor,
574 params,
575 editor: AtomicRefCell::new(None),
577 editor_handle: Mutex::new(None),
578 scale_factor: AtomicF64::new(1.0),
579
580 is_activated: AtomicBool::new(false),
581 is_processing: AtomicBool::new(false),
582 current_audio_io_layout: AtomicCell::new(
583 P::AUDIO_IO_LAYOUTS.first().copied().unwrap_or_default(),
584 ),
585 current_buffer_config: AtomicCell::new(None),
586 current_process_mode: AtomicCell::new(ProcessMode::Realtime),
587 input_events: AtomicRefCell::new(VecDeque::with_capacity(512)),
588 output_events: AtomicRefCell::new(VecDeque::with_capacity(512)),
589 last_process_status: AtomicCell::new(ProcessStatus::Normal),
590 latency_changed: AtomicBool::new(false),
591 current_latency: AtomicU32::new(0),
592 buffer_manager: AtomicRefCell::new(BufferManager::for_audio_io_layout(
595 0,
596 AudioIOLayout::default(),
597 )),
598 updated_state_sender,
599 updated_state_receiver,
600
601 host_callback,
602
603 clap_plugin: AtomicRefCell::new(clap_plugin {
604 desc: plugin_descriptor.clap_plugin_descriptor(),
609 plugin_data: std::ptr::null_mut(),
611 init: Some(Self::init),
612 destroy: Some(Self::destroy),
613 activate: Some(Self::activate),
614 deactivate: Some(Self::deactivate),
615 start_processing: Some(Self::start_processing),
616 stop_processing: Some(Self::stop_processing),
617 reset: Some(Self::reset),
618 process: Some(Self::process),
619 get_extension: Some(Self::get_extension),
620 on_main_thread: Some(Self::on_main_thread),
621 }),
622 _plugin_descriptor: plugin_descriptor,
623
624 clap_plugin_audio_ports_config: clap_plugin_audio_ports_config {
625 count: Some(Self::ext_audio_ports_config_count),
626 get: Some(Self::ext_audio_ports_config_get),
627 select: Some(Self::ext_audio_ports_config_select),
628 },
629
630 clap_plugin_audio_ports: clap_plugin_audio_ports {
631 count: Some(Self::ext_audio_ports_count),
632 get: Some(Self::ext_audio_ports_get),
633 },
634
635 clap_plugin_gui: clap_plugin_gui {
636 is_api_supported: Some(Self::ext_gui_is_api_supported),
637 get_preferred_api: Some(Self::ext_gui_get_preferred_api),
638 create: Some(Self::ext_gui_create),
639 destroy: Some(Self::ext_gui_destroy),
640 set_scale: Some(Self::ext_gui_set_scale),
641 get_size: Some(Self::ext_gui_get_size),
642 can_resize: Some(Self::ext_gui_can_resize),
643 get_resize_hints: Some(Self::ext_gui_get_resize_hints),
644 adjust_size: Some(Self::ext_gui_adjust_size),
645 set_size: Some(Self::ext_gui_set_size),
646 set_parent: Some(Self::ext_gui_set_parent),
647 set_transient: Some(Self::ext_gui_set_transient),
648 suggest_title: Some(Self::ext_gui_suggest_title),
649 show: Some(Self::ext_gui_show),
650 hide: Some(Self::ext_gui_hide),
651 },
652 host_gui: AtomicRefCell::new(None),
653
654 clap_plugin_latency: clap_plugin_latency {
655 get: Some(Self::ext_latency_get),
656 },
657 host_latency: AtomicRefCell::new(None),
658
659 clap_plugin_note_ports: clap_plugin_note_ports {
660 count: Some(Self::ext_note_ports_count),
661 get: Some(Self::ext_note_ports_get),
662 },
663
664 clap_plugin_params: clap_plugin_params {
665 count: Some(Self::ext_params_count),
666 get_info: Some(Self::ext_params_get_info),
667 get_value: Some(Self::ext_params_get_value),
668 value_to_text: Some(Self::ext_params_value_to_text),
669 text_to_value: Some(Self::ext_params_text_to_value),
670 flush: Some(Self::ext_params_flush),
671 },
672 host_params: AtomicRefCell::new(None),
673 param_hashes,
674 param_by_hash,
675 param_id_by_hash,
676 param_group_by_hash,
677 param_id_to_hash,
678 param_ptr_to_hash,
679 poly_mod_ids_by_hash,
680 output_parameter_events: ArrayQueue::new(OUTPUT_EVENT_QUEUE_CAPACITY),
681
682 host_thread_check: AtomicRefCell::new(None),
683
684 clap_plugin_remote_controls: clap_plugin_remote_controls {
685 count: Some(Self::ext_remote_controls_count),
686 get: Some(Self::ext_remote_controls_get),
687 },
688 remote_control_pages,
689
690 clap_plugin_render: clap_plugin_render {
691 has_hard_realtime_requirement: Some(Self::ext_render_has_hard_realtime_requirement),
692 set: Some(Self::ext_render_set),
693 },
694
695 clap_plugin_state: clap_plugin_state {
696 save: Some(Self::ext_state_save),
697 load: Some(Self::ext_state_load),
698 },
699
700 clap_plugin_tail: clap_plugin_tail {
701 get: Some(Self::ext_tail_get),
702 },
703
704 clap_plugin_track_info: clap_plugin_track_info {
705 changed: Some(Self::ext_track_info_changed),
706 },
707 host_track_info: AtomicRefCell::new(None),
708 current_track_info: AtomicRefCell::new(TrackInfo::default()),
709
710 clap_plugin_voice_info: clap_plugin_voice_info {
711 get: Some(Self::ext_voice_info_get),
712 },
713 host_voice_info: AtomicRefCell::new(None),
714 current_voice_capacity: AtomicU32::new(
715 P::CLAP_POLY_MODULATION_CONFIG
716 .map(|c| {
717 crate::nice_debug_assert!(
718 c.max_voice_capacity >= 1,
719 "The maximum voice capacity cannot be zero"
720 );
721 c.max_voice_capacity
722 })
723 .unwrap_or(1),
724 ),
725
726 tasks: ArrayQueue::new(TASK_QUEUE_CAPACITY),
727 main_thread_id: thread::current().id(),
728 background_thread: AtomicRefCell::new(None),
730 };
731
732 let wrapper = Arc::new(wrapper);
735 *wrapper.this.borrow_mut() = Arc::downgrade(&wrapper);
736
737 wrapper.clap_plugin.borrow_mut().plugin_data = Arc::as_ptr(&wrapper) as *mut _;
740
741 *wrapper.background_thread.borrow_mut() =
743 Some(BackgroundThread::get_or_create(Arc::downgrade(&wrapper)));
744
745 *wrapper.editor.borrow_mut() = wrapper
747 .plugin
748 .lock()
749 .editor(AsyncExecutor::new(
750 Arc::new({
751 let wrapper = Arc::downgrade(&wrapper);
752 move |task| {
753 let wrapper = match wrapper.upgrade() {
754 Some(wrapper) => wrapper,
755 None => return,
756 };
757
758 let task_posted = wrapper.schedule_background(Task::PluginTask(task));
759 crate::nice_debug_assert!(
760 task_posted,
761 "The task queue is full, dropping task..."
762 );
763 }
764 }),
765 Arc::new({
766 let wrapper = Arc::downgrade(&wrapper);
767 move |task| {
768 let wrapper = match wrapper.upgrade() {
769 Some(wrapper) => wrapper,
770 None => return,
771 };
772
773 let task_posted = wrapper.schedule_gui(Task::PluginTask(task));
774 crate::nice_debug_assert!(
775 task_posted,
776 "The task queue is full, dropping task..."
777 );
778 }
779 }),
780 ))
781 .map(Mutex::new);
782
783 wrapper
784 }
785
786 fn make_gui_context(self: Arc<Self>) -> Arc<WrapperGuiContext<P>> {
787 Arc::new(WrapperGuiContext {
788 wrapper: self,
789 #[cfg(debug_assertions)]
790 param_gesture_checker: Default::default(),
791 })
792 }
793
794 fn make_init_context(&self) -> WrapperInitContext<'_, P> {
799 WrapperInitContext {
800 wrapper: self,
801 pending_requests: Default::default(),
802 }
803 }
804
805 fn make_process_context(&self, transport: Transport) -> WrapperProcessContext<'_, P> {
806 WrapperProcessContext {
807 wrapper: self,
808 input_events_guard: self.input_events.borrow_mut(),
809 output_events_guard: self.output_events.borrow_mut(),
810 transport,
811 }
812 }
813
814 #[allow(unused)]
817 pub fn param_id_from_ptr(&self, param: ParamPtr) -> Option<&str> {
818 self.param_ptr_to_hash
819 .get(¶m)
820 .and_then(|hash| self.param_id_by_hash.get(hash))
821 .map(|s| s.as_str())
822 }
823
824 pub fn queue_parameter_event(&self, event: OutputParamEvent) -> bool {
832 let result = self.output_parameter_events.push(event).is_ok();
833
834 match &*self.host_params.borrow() {
836 Some(host_params) => {
837 unsafe_clap_call! { host_params=>request_flush(&*self.host_callback) }
838 }
839 None => {
840 crate::nice_debug_assert_failure!("The host does not support parameters? What?")
841 }
842 }
843
844 result
845 }
846
847 pub fn request_resize(&self) -> bool {
851 match (
852 self.host_gui.borrow().as_ref(),
853 self.editor.borrow().as_ref(),
854 ) {
855 (Some(host_gui), Some(editor)) => {
856 let scale_factor = self.scale_factor.load(Ordering::Relaxed);
857 let size: PhysicalSize<u32> = editor.lock().size().to_physical(scale_factor);
858
859 unsafe_clap_call! {
860 host_gui=>request_resize(
861 &*self.host_callback,
862 size.width,
863 size.height,
864 )
865 }
866 }
867 _ => false,
868 }
869 }
870
871 pub fn update_plain_value_by_hash(
879 &self,
880 hash: u32,
881 update_type: ClapParamUpdate,
882 sample_rate: Option<f32>,
883 ) -> bool {
884 match self.param_by_hash.get(&hash) {
885 Some(param_ptr) => {
886 match update_type {
887 ClapParamUpdate::PlainValueSet(clap_plain_value) => {
888 let normalized_value = clap_plain_value as f32
889 / unsafe { param_ptr.step_count() }.unwrap_or(1) as f32;
890
891 if unsafe { param_ptr._internal_set_normalized_value(normalized_value) } {
892 if let Some(sample_rate) = sample_rate {
893 unsafe { param_ptr._internal_update_smoother(sample_rate, false) };
894 }
895
896 let task_posted = self
899 .schedule_gui(Task::ParameterValueChanged(hash, normalized_value));
900 crate::nice_debug_assert!(
901 task_posted,
902 "The task queue is full, dropping task..."
903 );
904 }
905
906 true
907 }
908 ClapParamUpdate::PlainValueMod(clap_plain_delta) => {
909 let normalized_delta = clap_plain_delta as f32
910 / unsafe { param_ptr.step_count() }.unwrap_or(1) as f32;
911
912 if unsafe { param_ptr._internal_modulate_value(normalized_delta) } {
913 if let Some(sample_rate) = sample_rate {
914 unsafe { param_ptr._internal_update_smoother(sample_rate, false) };
915 }
916
917 let task_posted = self.schedule_gui(Task::ParameterModulationChanged(
918 hash,
919 normalized_delta,
920 ));
921 crate::nice_debug_assert!(
922 task_posted,
923 "The task queue is full, dropping task..."
924 );
925 }
926
927 true
928 }
929 }
930 }
931 _ => false,
932 }
933 }
934
935 pub unsafe fn handle_in_events(
942 &self,
943 in_: &clap_input_events,
944 current_sample_idx: usize,
945 total_buffer_len: usize,
946 ) {
947 let mut input_events = self.input_events.borrow_mut();
948 input_events.clear();
949
950 unsafe {
951 let num_events = clap_call! { in_=>size(in_) };
952 for event_idx in 0..num_events {
953 let event = clap_call! { in_=>get(in_, event_idx) };
954 self.handle_in_event(
955 event,
956 &mut input_events,
957 None,
958 current_sample_idx,
959 total_buffer_len,
960 );
961 }
962 }
963 }
964
965 pub unsafe fn handle_in_events_until(
980 &self,
981 in_: &clap_input_events,
982 transport_info: &mut *const clap_event_transport,
983 current_sample_idx: usize,
984 total_buffer_len: usize,
985 resume_from_event_idx: usize,
986 stop_predicate: impl Fn(*const clap_event_header) -> bool,
987 ) -> Option<(usize, usize)> {
988 let mut input_events = self.input_events.borrow_mut();
989 input_events.clear();
990
991 let num_events = unsafe {
993 clap_call! { in_=>size(in_) }
994 };
995 if num_events == 0 {
996 return None;
997 }
998
999 let start_idx = resume_from_event_idx as u32;
1000 let mut event: *const clap_event_header = unsafe {
1001 clap_call! { in_=>get(in_, start_idx) }
1002 };
1003 for next_event_idx in (start_idx + 1)..num_events {
1004 unsafe {
1005 self.handle_in_event(
1006 event,
1007 &mut input_events,
1008 Some(transport_info),
1009 current_sample_idx,
1010 total_buffer_len,
1011 );
1012 let next_event: *const clap_event_header =
1015 clap_call! { in_=>get(in_, next_event_idx) };
1016 if (*next_event).time > current_sample_idx as u32 && stop_predicate(next_event) {
1017 return Some(((*next_event).time as usize, next_event_idx as usize));
1018 }
1019 event = next_event;
1020 }
1021 }
1022
1023 unsafe {
1025 self.handle_in_event(
1026 event,
1027 &mut input_events,
1028 Some(transport_info),
1029 current_sample_idx,
1030 total_buffer_len,
1031 );
1032 }
1033
1034 None
1035 }
1036
1037 pub unsafe fn handle_out_events(
1048 &self,
1049 out: &clap_output_events,
1050 current_sample_idx: usize,
1051 total_buffer_len: usize,
1052 ) {
1053 let sample_rate = self.current_buffer_config.load().map(|c| c.sample_rate);
1056 while let Some(change) = self.output_parameter_events.pop() {
1057 let push_successful = match change {
1058 OutputParamEvent::BeginGesture { param_hash } => {
1059 let event = clap_event_param_gesture {
1060 header: clap_event_header {
1061 size: mem::size_of::<clap_event_param_gesture>() as u32,
1062 time: current_sample_idx as u32,
1063 space_id: CLAP_CORE_EVENT_SPACE_ID,
1064 type_: CLAP_EVENT_PARAM_GESTURE_BEGIN,
1065 flags: CLAP_EVENT_IS_LIVE,
1066 },
1067 param_id: param_hash,
1068 };
1069
1070 unsafe {
1071 clap_call! { out=>try_push(out, &event.header) }
1072 }
1073 }
1074 OutputParamEvent::SetValue {
1075 param_hash,
1076 clap_plain_value,
1077 } => {
1078 self.update_plain_value_by_hash(
1079 param_hash,
1080 ClapParamUpdate::PlainValueSet(clap_plain_value),
1081 sample_rate,
1082 );
1083
1084 let event = clap_event_param_value {
1085 header: clap_event_header {
1086 size: mem::size_of::<clap_event_param_value>() as u32,
1087 time: current_sample_idx as u32,
1088 space_id: CLAP_CORE_EVENT_SPACE_ID,
1089 type_: CLAP_EVENT_PARAM_VALUE,
1090 flags: CLAP_EVENT_IS_LIVE,
1091 },
1092 param_id: param_hash,
1093 cookie: std::ptr::null_mut(),
1094 port_index: -1,
1095 note_id: -1,
1096 channel: -1,
1097 key: -1,
1098 value: clap_plain_value,
1099 };
1100
1101 unsafe {
1102 clap_call! { out=>try_push(out, &event.header) }
1103 }
1104 }
1105 OutputParamEvent::EndGesture { param_hash } => {
1106 let event = clap_event_param_gesture {
1107 header: clap_event_header {
1108 size: mem::size_of::<clap_event_param_gesture>() as u32,
1109 time: current_sample_idx as u32,
1110 space_id: CLAP_CORE_EVENT_SPACE_ID,
1111 type_: CLAP_EVENT_PARAM_GESTURE_END,
1112 flags: CLAP_EVENT_IS_LIVE,
1113 },
1114 param_id: param_hash,
1115 };
1116
1117 unsafe {
1118 clap_call! { out=>try_push(out, &event.header) }
1119 }
1120 }
1121 };
1122
1123 crate::nice_debug_assert!(push_successful);
1124 }
1125
1126 let mut output_events = self.output_events.borrow_mut();
1128 while let Some(event) = output_events.pop_front() {
1129 let time = clamp_output_event_timing(
1131 event.timing() + current_sample_idx as u32,
1132 total_buffer_len as u32,
1133 );
1134
1135 let push_successful = match event {
1136 NoteEvent::NoteOn {
1137 timing: _,
1138 voice_id,
1139 channel,
1140 note,
1141 velocity,
1142 } if P::MIDI_OUTPUT >= MidiConfig::Basic => {
1143 let event = clap_event_note {
1144 header: clap_event_header {
1145 size: mem::size_of::<clap_event_note>() as u32,
1146 time,
1147 space_id: CLAP_CORE_EVENT_SPACE_ID,
1148 type_: CLAP_EVENT_NOTE_ON,
1149 flags: 0,
1151 },
1152 note_id: voice_id.unwrap_or(-1),
1153 port_index: 0,
1154 channel: channel as i16,
1155 key: note as i16,
1156 velocity: velocity as f64,
1157 };
1158
1159 unsafe {
1160 clap_call! { out=>try_push(out, &event.header) }
1161 }
1162 }
1163 NoteEvent::NoteOff {
1164 timing: _,
1165 voice_id,
1166 channel,
1167 note,
1168 velocity,
1169 } if P::MIDI_OUTPUT >= MidiConfig::Basic => {
1170 let event = clap_event_note {
1171 header: clap_event_header {
1172 size: mem::size_of::<clap_event_note>() as u32,
1173 time,
1174 space_id: CLAP_CORE_EVENT_SPACE_ID,
1175 type_: CLAP_EVENT_NOTE_OFF,
1176 flags: 0,
1177 },
1178 note_id: voice_id.unwrap_or(-1),
1179 port_index: 0,
1180 channel: channel as i16,
1181 key: note as i16,
1182 velocity: velocity as f64,
1183 };
1184
1185 unsafe {
1186 clap_call! { out=>try_push(out, &event.header) }
1187 }
1188 }
1189 NoteEvent::VoiceTerminated {
1192 timing: _,
1193 voice_id,
1194 channel,
1195 note,
1196 } if P::MIDI_INPUT >= MidiConfig::Basic => {
1197 let event = clap_event_note {
1198 header: clap_event_header {
1199 size: mem::size_of::<clap_event_note>() as u32,
1200 time,
1201 space_id: CLAP_CORE_EVENT_SPACE_ID,
1202 type_: CLAP_EVENT_NOTE_END,
1203 flags: 0,
1204 },
1205 note_id: voice_id.unwrap_or(-1),
1206 port_index: 0,
1207 channel: channel as i16,
1208 key: note as i16,
1209 velocity: 0.0,
1210 };
1211
1212 unsafe {
1213 clap_call! { out=>try_push(out, &event.header) }
1214 }
1215 }
1216 NoteEvent::PolyPressure {
1217 timing: _,
1218 voice_id,
1219 channel,
1220 note,
1221 pressure,
1222 } if P::MIDI_OUTPUT >= MidiConfig::Basic => {
1223 let event = clap_event_note_expression {
1224 header: clap_event_header {
1225 size: mem::size_of::<clap_event_note_expression>() as u32,
1226 time,
1227 space_id: CLAP_CORE_EVENT_SPACE_ID,
1228 type_: CLAP_EVENT_NOTE_EXPRESSION,
1229 flags: 0,
1230 },
1231 expression_id: CLAP_NOTE_EXPRESSION_PRESSURE,
1232 note_id: voice_id.unwrap_or(-1),
1233 port_index: 0,
1234 channel: channel as i16,
1235 key: note as i16,
1236 value: pressure as f64,
1237 };
1238
1239 unsafe {
1240 clap_call! { out=>try_push(out, &event.header) }
1241 }
1242 }
1243 NoteEvent::PolyVolume {
1244 timing: _,
1245 voice_id,
1246 channel,
1247 note,
1248 gain,
1249 } if P::MIDI_OUTPUT >= MidiConfig::Basic => {
1250 let event = clap_event_note_expression {
1251 header: clap_event_header {
1252 size: mem::size_of::<clap_event_note_expression>() as u32,
1253 time,
1254 space_id: CLAP_CORE_EVENT_SPACE_ID,
1255 type_: CLAP_EVENT_NOTE_EXPRESSION,
1256 flags: 0,
1257 },
1258 expression_id: CLAP_NOTE_EXPRESSION_VOLUME,
1259 note_id: voice_id.unwrap_or(-1),
1260 port_index: 0,
1261 channel: channel as i16,
1262 key: note as i16,
1263 value: gain as f64,
1264 };
1265
1266 unsafe {
1267 clap_call! { out=>try_push(out, &event.header) }
1268 }
1269 }
1270 NoteEvent::PolyPan {
1271 timing: _,
1272 voice_id,
1273 channel,
1274 note,
1275 pan,
1276 } if P::MIDI_OUTPUT >= MidiConfig::Basic => {
1277 let event = clap_event_note_expression {
1278 header: clap_event_header {
1279 size: mem::size_of::<clap_event_note_expression>() as u32,
1280 time,
1281 space_id: CLAP_CORE_EVENT_SPACE_ID,
1282 type_: CLAP_EVENT_NOTE_EXPRESSION,
1283 flags: 0,
1284 },
1285 expression_id: CLAP_NOTE_EXPRESSION_PAN,
1286 note_id: voice_id.unwrap_or(-1),
1287 port_index: 0,
1288 channel: channel as i16,
1289 key: note as i16,
1290 value: (pan as f64 + 1.0) / 2.0,
1291 };
1292
1293 unsafe {
1294 clap_call! { out=>try_push(out, &event.header) }
1295 }
1296 }
1297 NoteEvent::PolyTuning {
1298 timing: _,
1299 voice_id,
1300 channel,
1301 note,
1302 tuning,
1303 } if P::MIDI_OUTPUT >= MidiConfig::Basic => {
1304 let event = clap_event_note_expression {
1305 header: clap_event_header {
1306 size: mem::size_of::<clap_event_note_expression>() as u32,
1307 time,
1308 space_id: CLAP_CORE_EVENT_SPACE_ID,
1309 type_: CLAP_EVENT_NOTE_EXPRESSION,
1310 flags: 0,
1311 },
1312 expression_id: CLAP_NOTE_EXPRESSION_TUNING,
1313 note_id: voice_id.unwrap_or(-1),
1314 port_index: 0,
1315 channel: channel as i16,
1316 key: note as i16,
1317 value: tuning as f64,
1318 };
1319
1320 unsafe {
1321 clap_call! { out=>try_push(out, &event.header) }
1322 }
1323 }
1324 NoteEvent::PolyVibrato {
1325 timing: _,
1326 voice_id,
1327 channel,
1328 note,
1329 vibrato,
1330 } if P::MIDI_OUTPUT >= MidiConfig::Basic => {
1331 let event = clap_event_note_expression {
1332 header: clap_event_header {
1333 size: mem::size_of::<clap_event_note_expression>() as u32,
1334 time,
1335 space_id: CLAP_CORE_EVENT_SPACE_ID,
1336 type_: CLAP_EVENT_NOTE_EXPRESSION,
1337 flags: 0,
1338 },
1339 expression_id: CLAP_NOTE_EXPRESSION_VIBRATO,
1340 note_id: voice_id.unwrap_or(-1),
1341 port_index: 0,
1342 channel: channel as i16,
1343 key: note as i16,
1344 value: vibrato as f64,
1345 };
1346
1347 unsafe {
1348 clap_call! { out=>try_push(out, &event.header) }
1349 }
1350 }
1351 NoteEvent::PolyExpression {
1352 timing: _,
1353 voice_id,
1354 channel,
1355 note,
1356 expression,
1357 } if P::MIDI_OUTPUT >= MidiConfig::Basic => {
1358 let event = clap_event_note_expression {
1359 header: clap_event_header {
1360 size: mem::size_of::<clap_event_note_expression>() as u32,
1361 time,
1362 space_id: CLAP_CORE_EVENT_SPACE_ID,
1363 type_: CLAP_EVENT_NOTE_EXPRESSION,
1364 flags: 0,
1365 },
1366 expression_id: CLAP_NOTE_EXPRESSION_EXPRESSION,
1367 note_id: voice_id.unwrap_or(-1),
1368 port_index: 0,
1369 channel: channel as i16,
1370 key: note as i16,
1371 value: expression as f64,
1372 };
1373
1374 unsafe {
1375 clap_call! { out=>try_push(out, &event.header) }
1376 }
1377 }
1378 NoteEvent::PolyBrightness {
1379 timing: _,
1380 voice_id,
1381 channel,
1382 note,
1383 brightness,
1384 } if P::MIDI_OUTPUT >= MidiConfig::Basic => {
1385 let event = clap_event_note_expression {
1386 header: clap_event_header {
1387 size: mem::size_of::<clap_event_note_expression>() as u32,
1388 time,
1389 space_id: CLAP_CORE_EVENT_SPACE_ID,
1390 type_: CLAP_EVENT_NOTE_EXPRESSION,
1391 flags: 0,
1392 },
1393 expression_id: CLAP_NOTE_EXPRESSION_BRIGHTNESS,
1394 note_id: voice_id.unwrap_or(-1),
1395 port_index: 0,
1396 channel: channel as i16,
1397 key: note as i16,
1398 value: brightness as f64,
1399 };
1400
1401 unsafe {
1402 clap_call! { out=>try_push(out, &event.header) }
1403 }
1404 }
1405 midi_event @ (NoteEvent::MidiChannelPressure { .. }
1406 | NoteEvent::MidiPitchBend { .. }
1407 | NoteEvent::MidiCC { .. }
1408 | NoteEvent::MidiProgramChange { .. })
1409 if P::MIDI_OUTPUT >= MidiConfig::MidiCCs =>
1410 {
1411 let midi_data = match midi_event.as_midi() {
1414 Some(MidiResult::Basic(midi_data)) => midi_data,
1415 Some(MidiResult::SysEx(_, _)) => unreachable!(
1416 "Basic MIDI event read as SysEx, something's gone horribly wrong"
1417 ),
1418 None => unreachable!("Missing MIDI conversion for MIDI event"),
1419 };
1420
1421 let event = clap_event_midi {
1422 header: clap_event_header {
1423 size: mem::size_of::<clap_event_midi>() as u32,
1424 time,
1425 space_id: CLAP_CORE_EVENT_SPACE_ID,
1426 type_: CLAP_EVENT_MIDI,
1427 flags: 0,
1428 },
1429 port_index: 0,
1430 data: midi_data,
1431 };
1432
1433 unsafe {
1434 clap_call! { out=>try_push(out, &event.header) }
1435 }
1436 }
1437 NoteEvent::MidiSysEx { timing: _, message }
1438 if P::MIDI_OUTPUT >= MidiConfig::Basic =>
1439 {
1440 let (padded_sysex_buffer, length) = message.to_buffer();
1442 let padded_sysex_buffer = padded_sysex_buffer.borrow();
1443 crate::nice_debug_assert!(padded_sysex_buffer.len() >= length);
1444 let sysex_buffer = &padded_sysex_buffer[..length];
1445
1446 let event = clap_event_midi_sysex {
1447 header: clap_event_header {
1448 size: mem::size_of::<clap_event_midi_sysex>() as u32,
1449 time,
1450 space_id: CLAP_CORE_EVENT_SPACE_ID,
1451 type_: CLAP_EVENT_MIDI_SYSEX,
1452 flags: 0,
1453 },
1454 port_index: 0,
1455 buffer: sysex_buffer.as_ptr(),
1457 size: sysex_buffer.len() as u32,
1458 };
1459
1460 unsafe {
1461 clap_call! { out=>try_push(out, &event.header) }
1462 }
1463 }
1464 _ => {
1465 crate::nice_debug_assert_failure!(
1466 "Invalid output event for the current MIDI_OUTPUT setting"
1467 );
1468 continue;
1469 }
1470 };
1471
1472 crate::nice_debug_assert!(push_successful, "Could not send note event");
1473 }
1474 }
1475
1476 pub unsafe fn handle_in_event(
1490 &self,
1491 event: *const clap_event_header,
1492 input_events: &mut AtomicRefMut<VecDeque<PluginNoteEvent<P>>>,
1493 transport_info: Option<&mut *const clap_event_transport>,
1494 current_sample_idx: usize,
1495 total_buffer_len: usize,
1496 ) {
1497 let raw_event = unsafe { &*event };
1498
1499 let timing = clamp_input_event_timing(
1501 raw_event.time - current_sample_idx as u32,
1502 total_buffer_len as u32,
1503 );
1504
1505 match (raw_event.space_id, raw_event.type_) {
1506 (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_PARAM_VALUE) => {
1507 let event = unsafe { &*(event as *const clap_event_param_value) };
1508 self.update_plain_value_by_hash(
1509 event.param_id,
1510 ClapParamUpdate::PlainValueSet(event.value),
1511 self.current_buffer_config.load().map(|c| c.sample_rate),
1512 );
1513
1514 if let Some(poly_modulation_id) = self.poly_mod_ids_by_hash.get(&event.param_id) {
1519 let param_ptr = self.param_by_hash[&event.param_id];
1522 let normalized_value =
1523 event.value as f32 / unsafe { param_ptr.step_count().unwrap_or(1) as f32 };
1524
1525 input_events.push_back(NoteEvent::MonoAutomation {
1526 timing,
1527 poly_modulation_id: *poly_modulation_id,
1528 normalized_value,
1529 });
1530 }
1531 }
1532 (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_PARAM_MOD) => {
1533 let event = unsafe { &*(event as *const clap_event_param_mod) };
1534
1535 if event.note_id != -1 && P::MIDI_INPUT >= MidiConfig::Basic {
1536 match self.poly_mod_ids_by_hash.get(&event.param_id) {
1537 Some(poly_modulation_id) => {
1538 let param_ptr = self.param_by_hash[&event.param_id];
1541 let normalized_offset = event.amount as f32
1542 / unsafe { param_ptr.step_count().unwrap_or(1) as f32 };
1543
1544 input_events.push_back(NoteEvent::PolyModulation {
1548 timing,
1549 voice_id: event.note_id,
1550 poly_modulation_id: *poly_modulation_id,
1551 normalized_offset,
1552 });
1553
1554 return;
1555 }
1556 None => crate::nice_debug_assert_failure!(
1557 "Polyphonic modulation sent for a parameter without a poly modulation \
1558 ID"
1559 ),
1560 }
1561 }
1562
1563 self.update_plain_value_by_hash(
1564 event.param_id,
1565 ClapParamUpdate::PlainValueMod(event.amount),
1566 self.current_buffer_config.load().map(|c| c.sample_rate),
1567 );
1568 }
1569 (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_TRANSPORT) => {
1570 let event = unsafe { &*(event as *const clap_event_transport) };
1571 if let Some(transport_info) = transport_info {
1572 *transport_info = event;
1573 }
1574 }
1575 (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_NOTE_ON) => {
1576 if P::MIDI_INPUT >= MidiConfig::Basic {
1577 let event = unsafe { &*(event as *const clap_event_note) };
1578 input_events.push_back(NoteEvent::NoteOn {
1579 timing,
1582 voice_id: if event.note_id != -1 {
1583 Some(event.note_id)
1584 } else {
1585 None
1586 },
1587 channel: event.channel as u8,
1588 note: event.key as u8,
1589 velocity: event.velocity as f32,
1590 });
1591 }
1592 }
1593 (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_NOTE_OFF) => {
1594 if P::MIDI_INPUT >= MidiConfig::Basic {
1595 let event = unsafe { &*(event as *const clap_event_note) };
1596 input_events.push_back(NoteEvent::NoteOff {
1597 timing,
1598 voice_id: if event.note_id != -1 {
1599 Some(event.note_id)
1600 } else {
1601 None
1602 },
1603 channel: event.channel as u8,
1604 note: event.key as u8,
1605 velocity: event.velocity as f32,
1606 });
1607 }
1608 }
1609 (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_NOTE_CHOKE) => {
1610 if P::MIDI_INPUT >= MidiConfig::Basic {
1611 let event = unsafe { &*(event as *const clap_event_note) };
1612 input_events.push_back(NoteEvent::Choke {
1613 timing,
1614 voice_id: if event.note_id != -1 {
1615 Some(event.note_id)
1616 } else {
1617 None
1618 },
1619 channel: event.channel as u8,
1621 note: event.key as u8,
1622 });
1623 }
1624 }
1625 (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_NOTE_EXPRESSION) => {
1626 if P::MIDI_INPUT >= MidiConfig::Basic {
1627 let event = unsafe { &*(event as *const clap_event_note_expression) };
1629 match event.expression_id {
1630 CLAP_NOTE_EXPRESSION_PRESSURE => {
1631 input_events.push_back(NoteEvent::PolyPressure {
1632 timing,
1633 voice_id: if event.note_id != -1 {
1634 Some(event.note_id)
1635 } else {
1636 None
1637 },
1638 channel: event.channel as u8,
1639 note: event.key as u8,
1640 pressure: event.value as f32,
1641 });
1642 }
1643 CLAP_NOTE_EXPRESSION_VOLUME => {
1644 input_events.push_back(NoteEvent::PolyVolume {
1645 timing,
1646 voice_id: if event.note_id != -1 {
1647 Some(event.note_id)
1648 } else {
1649 None
1650 },
1651 channel: event.channel as u8,
1652 note: event.key as u8,
1653 gain: event.value as f32,
1654 });
1655 }
1656 CLAP_NOTE_EXPRESSION_PAN => {
1657 input_events.push_back(NoteEvent::PolyPan {
1658 timing,
1659 voice_id: if event.note_id != -1 {
1660 Some(event.note_id)
1661 } else {
1662 None
1663 },
1664 channel: event.channel as u8,
1665 note: event.key as u8,
1666 pan: (event.value as f32 * 2.0) - 1.0,
1668 });
1669 }
1670 CLAP_NOTE_EXPRESSION_TUNING => {
1671 input_events.push_back(NoteEvent::PolyTuning {
1672 timing,
1673 voice_id: if event.note_id != -1 {
1674 Some(event.note_id)
1675 } else {
1676 None
1677 },
1678 channel: event.channel as u8,
1679 note: event.key as u8,
1680 tuning: event.value as f32,
1681 });
1682 }
1683 CLAP_NOTE_EXPRESSION_VIBRATO => {
1684 input_events.push_back(NoteEvent::PolyVibrato {
1685 timing,
1686 voice_id: if event.note_id != -1 {
1687 Some(event.note_id)
1688 } else {
1689 None
1690 },
1691 channel: event.channel as u8,
1692 note: event.key as u8,
1693 vibrato: event.value as f32,
1694 });
1695 }
1696 CLAP_NOTE_EXPRESSION_EXPRESSION => {
1697 input_events.push_back(NoteEvent::PolyExpression {
1698 timing,
1699 voice_id: if event.note_id != -1 {
1700 Some(event.note_id)
1701 } else {
1702 None
1703 },
1704 channel: event.channel as u8,
1705 note: event.key as u8,
1706 expression: event.value as f32,
1707 });
1708 }
1709 CLAP_NOTE_EXPRESSION_BRIGHTNESS => {
1710 input_events.push_back(NoteEvent::PolyBrightness {
1711 timing,
1712 voice_id: if event.note_id != -1 {
1713 Some(event.note_id)
1714 } else {
1715 None
1716 },
1717 channel: event.channel as u8,
1718 note: event.key as u8,
1719 brightness: event.value as f32,
1720 });
1721 }
1722 n => {
1723 crate::nice_debug_assert_failure!("Unhandled note expression ID {}", n)
1724 }
1725 }
1726 }
1727 }
1728 (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_MIDI) => {
1729 let event = unsafe { &*(event as *const clap_event_midi) };
1733
1734 match NoteEvent::from_midi(timing, &event.data) {
1735 Ok(
1736 note_event @ (NoteEvent::NoteOn { .. }
1737 | NoteEvent::NoteOff { .. }
1738 | NoteEvent::PolyPressure { .. }),
1739 ) if P::MIDI_INPUT >= MidiConfig::Basic => {
1740 input_events.push_back(note_event);
1741 }
1742 Ok(note_event) if P::MIDI_INPUT >= MidiConfig::MidiCCs => {
1743 input_events.push_back(note_event);
1744 }
1745 Ok(_) => (),
1746 Err(n) => {
1747 crate::nice_debug_assert_failure!("Unhandled MIDI message type {}", n)
1748 }
1749 };
1750 }
1751 (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_MIDI_SYSEX)
1752 if P::MIDI_INPUT >= MidiConfig::Basic =>
1753 {
1754 let event = unsafe { &*(event as *const clap_event_midi_sysex) };
1755
1756 assert!(!event.buffer.is_null());
1759 let sysex_buffer =
1760 unsafe { std::slice::from_raw_parts(event.buffer, event.size as usize) };
1761 if let Ok(note_event) = NoteEvent::from_midi(timing, sysex_buffer) {
1762 input_events.push_back(note_event);
1763 };
1764 }
1765 _ => {
1766 crate::nice_trace!(
1767 "Unhandled CLAP event type {} for namespace {}",
1768 raw_event.type_,
1769 raw_event.space_id
1770 );
1771 }
1772 }
1773 }
1774
1775 pub fn get_state_object(&self) -> PluginState {
1779 unsafe {
1780 state::serialize_object::<P>(
1781 self.params.clone(),
1782 state::make_params_iter(&self.param_by_hash, &self.param_id_to_hash),
1783 )
1784 }
1785 }
1786
1787 pub fn set_state_object_from_gui(&self, mut state: PluginState) {
1791 loop {
1794 if self.is_processing.load(Ordering::SeqCst) {
1795 match self
1801 .updated_state_sender
1802 .send_timeout(state, Duration::from_secs(1))
1803 {
1804 Ok(_) => {
1805 let state = self.updated_state_receiver.recv();
1808 drop(state);
1809 break;
1810 }
1811 Err(SendTimeoutError::Timeout(value)) => {
1812 state = value;
1813 continue;
1814 }
1815 Err(SendTimeoutError::Disconnected(_)) => {
1816 crate::nice_debug_assert_failure!("State update channel got disconnected");
1817 return;
1818 }
1819 }
1820 } else {
1821 self.set_state_inner(&mut state);
1824 break;
1825 }
1826 }
1827
1828 let task_posted = self.schedule_gui(Task::RescanParamValues);
1830 crate::nice_debug_assert!(task_posted, "The task queue is full, dropping task...");
1831 }
1832
1833 pub fn set_latency_samples(&self, samples: u32) {
1834 let old_latency = self.current_latency.swap(samples, Ordering::SeqCst);
1838 if old_latency != samples {
1839 let task_posted = self.schedule_gui(Task::LatencyChanged);
1840 crate::nice_debug_assert!(task_posted, "The task queue is full, dropping task...");
1841 }
1842 }
1843
1844 pub fn set_current_voice_capacity(&self, capacity: u32) {
1845 match P::CLAP_POLY_MODULATION_CONFIG {
1846 Some(config) => {
1847 let clamped_capacity = capacity.clamp(1, config.max_voice_capacity);
1848 crate::nice_debug_assert_eq!(
1849 capacity,
1850 clamped_capacity,
1851 "The current voice capacity must be between 1 and the maximum capacity"
1852 );
1853
1854 if clamped_capacity != self.current_voice_capacity.load(Ordering::Relaxed) {
1855 self.current_voice_capacity
1856 .store(clamped_capacity, Ordering::Relaxed);
1857 let task_posted = self.schedule_gui(Task::VoiceInfoChanged);
1858 crate::nice_debug_assert!(
1859 task_posted,
1860 "The task queue is full, dropping task..."
1861 );
1862 }
1863 }
1864 None => crate::nice_debug_assert_failure!(
1865 "Configuring the current voice capacity is only possible when \
1866 'ClapPlugin::CLAP_POLY_MODULATION_CONFIG' is set"
1867 ),
1868 }
1869 }
1870
1871 fn update_track_info_from_host(&self) {
1873 let host_track_info = self.host_track_info.borrow();
1874 let Some(host_track_info) = host_track_info.as_ref() else {
1875 return;
1876 };
1877
1878 permit_alloc(|| {
1879 let mut clap_info: clap_track_info = unsafe { mem::zeroed() };
1880 let success = unsafe_clap_call! {
1881 host_track_info=>get(&*self.host_callback, &mut clap_info)
1882 };
1883 if !success {
1884 return;
1885 }
1886
1887 let mut current_track_info = self.current_track_info.borrow_mut();
1888 let mut name = current_track_info.name().to_owned();
1889 let mut color = current_track_info.color();
1890
1891 if clap_info.flags & CLAP_TRACK_INFO_HAS_TRACK_NAME != 0 {
1892 let name_bytes = unsafe {
1893 std::slice::from_raw_parts(
1894 clap_info.name.as_ptr().cast::<u8>(),
1895 clap_sys::string_sizes::CLAP_NAME_SIZE,
1896 )
1897 };
1898 if let Ok(cstr) = CStr::from_bytes_until_nul(name_bytes) {
1899 name = cstr.to_string_lossy().into_owned()
1900 } }
1902
1903 if clap_info.flags & CLAP_TRACK_INFO_HAS_TRACK_COLOR != 0 {
1904 color = Some(TrackColor::new(
1905 clap_info.color.red,
1906 clap_info.color.green,
1907 clap_info.color.blue,
1908 clap_info.color.alpha,
1909 ));
1910 }
1911
1912 let track_info = TrackInfo::new(name, color);
1913 *current_track_info = track_info.clone();
1914 self.plugin.lock().track_info_updated(track_info);
1915 });
1916 }
1917
1918 pub fn set_state_inner(&self, state: &mut PluginState) -> bool {
1929 let audio_io_layout = self.current_audio_io_layout.load();
1930 let buffer_config = self.current_buffer_config.load();
1931
1932 let mut success = permit_alloc(|| unsafe {
1939 state::deserialize_object::<P>(
1940 state,
1941 self.params.clone(),
1942 state::make_params_getter(&self.param_by_hash, &self.param_id_to_hash),
1943 self.current_buffer_config.load().as_ref(),
1944 )
1945 });
1946 if !success {
1947 crate::nice_debug_assert_failure!(
1948 "Deserializing plugin state from a state object failed"
1949 );
1950 return false;
1951 }
1952
1953 if let Some(buffer_config) = buffer_config {
1955 let mut init_context = self.make_init_context();
1957 let mut plugin = self.plugin.lock();
1958
1959 success = permit_alloc(|| {
1961 plugin.initialize(&audio_io_layout, &buffer_config, &mut init_context)
1962 });
1963 if success {
1964 process_wrapper(|| plugin.reset());
1965 }
1966 }
1967
1968 crate::nice_debug_assert!(
1969 success,
1970 "Plugin returned false when reinitializing after loading state"
1971 );
1972
1973 let task_posted = self.schedule_gui(Task::ParameterValuesChanged);
1975 crate::nice_debug_assert!(task_posted, "The task queue is full, dropping task...");
1976
1977 if self.editor_handle.lock().is_some() {
1981 self.request_resize();
1982 }
1983
1984 success
1985 }
1986
1987 unsafe extern "C" fn init(plugin: *const clap_plugin) -> bool {
1988 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
1989 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
1990
1991 unsafe {
1993 *wrapper.host_gui.borrow_mut() =
1994 query_host_extension::<clap_host_gui>(&wrapper.host_callback, CLAP_EXT_GUI);
1995 *wrapper.host_latency.borrow_mut() =
1996 query_host_extension::<clap_host_latency>(&wrapper.host_callback, CLAP_EXT_LATENCY);
1997 *wrapper.host_params.borrow_mut() =
1998 query_host_extension::<clap_host_params>(&wrapper.host_callback, CLAP_EXT_PARAMS);
1999 *wrapper.host_voice_info.borrow_mut() = query_host_extension::<clap_host_voice_info>(
2000 &wrapper.host_callback,
2001 CLAP_EXT_VOICE_INFO,
2002 );
2003 *wrapper.host_thread_check.borrow_mut() = query_host_extension::<clap_host_thread_check>(
2004 &wrapper.host_callback,
2005 CLAP_EXT_THREAD_CHECK,
2006 );
2007 *wrapper.host_track_info.borrow_mut() = query_host_extension::<clap_host_track_info>(
2008 &wrapper.host_callback,
2009 CLAP_EXT_TRACK_INFO,
2010 );
2011 }
2012
2013 wrapper.update_track_info_from_host();
2014
2015 true
2016 }
2017
2018 unsafe extern "C" fn destroy(plugin: *const clap_plugin) {
2019 assert!(!plugin.is_null() && unsafe { !(*plugin).plugin_data.is_null() });
2020 let this = unsafe { Arc::from_raw((*plugin).plugin_data as *mut Self) };
2021 crate::nice_debug_assert_eq!(Arc::strong_count(&this), 1);
2022
2023 drop(this);
2024 }
2025
2026 unsafe extern "C" fn activate(
2027 plugin: *const clap_plugin,
2028 sample_rate: f64,
2029 min_frames_count: u32,
2030 max_frames_count: u32,
2031 ) -> bool {
2032 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
2033 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2034
2035 let audio_io_layout = wrapper.current_audio_io_layout.load();
2036 let buffer_config = BufferConfig {
2037 sample_rate: sample_rate as f32,
2038 min_buffer_size: Some(min_frames_count),
2039 max_buffer_size: max_frames_count,
2040 process_mode: wrapper.current_process_mode.load(),
2041 };
2042
2043 for param in wrapper.param_by_hash.values() {
2045 unsafe { param._internal_update_smoother(buffer_config.sample_rate, true) };
2046 }
2047
2048 if wrapper.latency_changed.swap(false, Ordering::SeqCst) {
2051 if let Some(host_latency) = &*wrapper.host_latency.borrow() {
2052 unsafe_clap_call! { host_latency=>changed(&*wrapper.host_callback) };
2053 }
2054 }
2055
2056 let mut init_context = wrapper.make_init_context();
2058 let mut plugin = wrapper.plugin.lock();
2059 if plugin.initialize(&audio_io_layout, &buffer_config, &mut init_context) {
2060 *wrapper.buffer_manager.borrow_mut() =
2066 BufferManager::for_audio_io_layout(max_frames_count as usize, audio_io_layout);
2067
2068 wrapper.current_buffer_config.store(Some(buffer_config));
2070
2071 wrapper.is_activated.store(true, Ordering::SeqCst);
2072
2073 true
2074 } else {
2075 false
2076 }
2077 }
2078
2079 unsafe extern "C" fn deactivate(plugin: *const clap_plugin) {
2080 check_null_ptr!((), plugin, unsafe { (*plugin).plugin_data });
2081 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2082
2083 wrapper.plugin.lock().deactivate();
2084
2085 wrapper.is_activated.store(false, Ordering::SeqCst);
2086 }
2087
2088 unsafe extern "C" fn start_processing(plugin: *const clap_plugin) -> bool {
2089 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
2092 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2093
2094 wrapper.last_process_status.store(ProcessStatus::Normal);
2096 wrapper.is_processing.store(true, Ordering::SeqCst);
2097
2098 process_wrapper(|| wrapper.plugin.lock().reset());
2101
2102 true
2103 }
2104
2105 unsafe extern "C" fn stop_processing(plugin: *const clap_plugin) {
2106 check_null_ptr!((), plugin, unsafe { (*plugin).plugin_data });
2107 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2108
2109 wrapper.is_processing.store(false, Ordering::SeqCst);
2110 }
2111
2112 unsafe extern "C" fn reset(plugin: *const clap_plugin) {
2113 check_null_ptr!((), plugin, unsafe { (*plugin).plugin_data });
2114 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2115
2116 process_wrapper(|| wrapper.plugin.lock().reset());
2117 }
2118
2119 unsafe extern "C" fn process(
2120 plugin: *const clap_plugin,
2121 process: *const clap_process,
2122 ) -> clap_process_status {
2123 check_null_ptr!(
2124 CLAP_PROCESS_ERROR,
2125 plugin,
2126 unsafe { (*plugin).plugin_data },
2127 process
2128 );
2129 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2130
2131 process_wrapper(|| {
2134 let process = unsafe { &*process };
2138 let total_buffer_len = process.frames_count as usize;
2139
2140 let current_audio_io_layout = wrapper.current_audio_io_layout.load();
2141 let has_main_input = current_audio_io_layout.main_input_channels.is_some();
2142 let has_main_output = current_audio_io_layout.main_output_channels.is_some();
2143 let aux_input_start_idx = if has_main_input { 1 } else { 0 };
2144 let aux_output_start_idx = if has_main_output { 1 } else { 0 };
2145
2146 let mut block_start = 0;
2149 let mut block_end = total_buffer_len;
2150 let mut event_start_idx = 0;
2151
2152 let mut transport_info = process.transport;
2155
2156 let result = loop {
2157 if !process.in_events.is_null() {
2158 let split_result = unsafe {
2159 wrapper.handle_in_events_until(
2160 &*process.in_events,
2161 &mut transport_info,
2162 block_start,
2163 total_buffer_len,
2164 event_start_idx,
2165 |next_event| {
2166 if P::SAMPLE_ACCURATE_AUTOMATION {
2171 match ((*next_event).space_id, (*next_event).type_) {
2172 (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_PARAM_VALUE)
2173 | (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_TRANSPORT) => true,
2174 (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_PARAM_MOD) => {
2175 let next_event =
2176 &*(next_event as *const clap_event_param_mod);
2177
2178 !(next_event.note_id != -1
2181 && wrapper
2182 .poly_mod_ids_by_hash
2183 .contains_key(&next_event.param_id))
2184 }
2185 _ => false,
2186 }
2187 } else {
2188 matches!(
2189 ((*next_event).space_id, (*next_event).type_,),
2190 (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_TRANSPORT)
2191 )
2192 }
2193 },
2194 )
2195 };
2196
2197 match split_result {
2202 Some((next_param_change_sample_idx, next_param_change_event_idx)) => {
2203 block_end = next_param_change_sample_idx;
2204 event_start_idx = next_param_change_event_idx;
2205 }
2206 None => block_end = total_buffer_len,
2207 }
2208 }
2209
2210 let block_len = block_end - block_start;
2213
2214 let mut buffer_manager = wrapper.buffer_manager.borrow_mut();
2220 let buffers = unsafe {
2221 buffer_manager.create_buffers(block_start, block_len, |buffer_source| {
2222 if process.audio_outputs_count > 0
2226 && !process.audio_outputs.is_null()
2227 && !(*process.audio_outputs).data32.is_null()
2228 && has_main_output
2229 {
2230 let audio_output = &*process.audio_outputs;
2231 let ptrs = NonNull::new(audio_output.data32).unwrap();
2232 let num_channels = audio_output.channel_count as usize;
2233
2234 *buffer_source.main_output_channel_pointers =
2235 Some(ChannelPointers { ptrs, num_channels });
2236 }
2237
2238 if process.audio_inputs_count > 0
2239 && !process.audio_inputs.is_null()
2240 && !(*process.audio_inputs).data32.is_null()
2241 && has_main_input
2242 {
2243 let audio_input = &*process.audio_inputs;
2244 let ptrs = NonNull::new(audio_input.data32).unwrap();
2245 let num_channels = audio_input.channel_count as usize;
2246
2247 *buffer_source.main_input_channel_pointers =
2248 Some(ChannelPointers { ptrs, num_channels });
2249 }
2250
2251 if !process.audio_inputs.is_null() {
2252 for (aux_input_no, aux_input_channel_pointers) in buffer_source
2253 .aux_input_channel_pointers
2254 .iter_mut()
2255 .enumerate()
2256 {
2257 let aux_input_idx = aux_input_no + aux_input_start_idx;
2258 if aux_input_idx > process.audio_inputs_count as usize {
2259 break;
2260 }
2261
2262 let audio_input = &*process.audio_inputs.add(aux_input_idx);
2263 match NonNull::new(audio_input.data32) {
2264 Some(ptrs) => {
2265 let num_channels = audio_input.channel_count as usize;
2266
2267 *aux_input_channel_pointers =
2268 Some(ChannelPointers { ptrs, num_channels });
2269 }
2270 None => continue,
2271 }
2272 }
2273 }
2274
2275 if !process.audio_outputs.is_null() {
2276 for (aux_output_no, aux_output_channel_pointers) in buffer_source
2277 .aux_output_channel_pointers
2278 .iter_mut()
2279 .enumerate()
2280 {
2281 let aux_output_idx = aux_output_no + aux_output_start_idx;
2282 if aux_output_idx > process.audio_outputs_count as usize {
2283 break;
2284 }
2285
2286 let audio_output = &*process.audio_outputs.add(aux_output_idx);
2287 match NonNull::new(audio_output.data32) {
2288 Some(ptrs) => {
2289 let num_channels = audio_output.channel_count as usize;
2290
2291 *aux_output_channel_pointers =
2292 Some(ChannelPointers { ptrs, num_channels });
2293 }
2294 None => continue,
2295 }
2296 }
2297 }
2298 })
2299 };
2300
2301 let mut buffer_is_valid = true;
2308 for output_buffer_slice in buffers.main_buffer.as_slice_immutable().iter().chain(
2309 buffers
2310 .aux_outputs
2311 .iter()
2312 .flat_map(|buffer| buffer.as_slice_immutable().iter()),
2313 ) {
2314 if output_buffer_slice.is_empty() {
2315 buffer_is_valid = false;
2316 break;
2317 }
2318 }
2319
2320 crate::nice_debug_assert!(buffer_is_valid);
2321
2322 let sample_rate = wrapper
2326 .current_buffer_config
2327 .load()
2328 .expect("Process call without prior initialization call")
2329 .sample_rate;
2330 let mut transport = Transport::new(sample_rate);
2331 if !transport_info.is_null() {
2332 let context = unsafe { &*transport_info };
2333
2334 transport.playing = context.flags & CLAP_TRANSPORT_IS_PLAYING != 0;
2335 transport.recording = context.flags & CLAP_TRANSPORT_IS_RECORDING != 0;
2336 transport.preroll_active =
2337 Some(context.flags & CLAP_TRANSPORT_IS_WITHIN_PRE_ROLL != 0);
2338 if context.flags & CLAP_TRANSPORT_HAS_TEMPO != 0 {
2339 transport.tempo = Some(context.tempo);
2340 }
2341 if context.flags & CLAP_TRANSPORT_HAS_TIME_SIGNATURE != 0 {
2342 transport.time_sig_numerator = Some(context.tsig_num as i32);
2343 transport.time_sig_denominator = Some(context.tsig_denom as i32);
2344 }
2345 if context.flags & CLAP_TRANSPORT_HAS_BEATS_TIMELINE != 0 {
2346 let beats = context.song_pos_beats as f64 / CLAP_BEATTIME_FACTOR as f64;
2347
2348 if P::SAMPLE_ACCURATE_AUTOMATION
2352 && block_start > 0
2353 && (context.flags & CLAP_TRANSPORT_HAS_TEMPO != 0)
2354 {
2355 transport.pos_beats = Some(
2356 beats
2357 + (block_start as f64 / sample_rate as f64 / 60.0
2358 * context.tempo),
2359 );
2360 } else {
2361 transport.pos_beats = Some(beats);
2362 }
2363 }
2364 if context.flags & CLAP_TRANSPORT_HAS_SECONDS_TIMELINE != 0 {
2365 let seconds = context.song_pos_seconds as f64 / CLAP_SECTIME_FACTOR as f64;
2366
2367 if P::SAMPLE_ACCURATE_AUTOMATION
2369 && block_start > 0
2370 && (context.flags & CLAP_TRANSPORT_HAS_TEMPO != 0)
2371 {
2372 transport.pos_seconds =
2373 Some(seconds + (block_start as f64 / sample_rate as f64));
2374 } else {
2375 transport.pos_seconds = Some(seconds);
2376 }
2377 }
2378 if P::SAMPLE_ACCURATE_AUTOMATION && block_start > 0 {
2380 transport.bar_start_pos_beats = match transport.bar_start_pos_beats() {
2381 Some(updated) => Some(updated),
2382 None => Some(context.bar_start as f64 / CLAP_BEATTIME_FACTOR as f64),
2383 };
2384 transport.bar_number = match transport.bar_number() {
2385 Some(updated) => Some(updated),
2386 None => Some(context.bar_number),
2387 };
2388 } else {
2389 transport.bar_start_pos_beats =
2390 Some(context.bar_start as f64 / CLAP_BEATTIME_FACTOR as f64);
2391 transport.bar_number = Some(context.bar_number);
2392 }
2393 if context.flags & CLAP_TRANSPORT_IS_LOOP_ACTIVE != 0
2397 && context.flags & CLAP_TRANSPORT_HAS_BEATS_TIMELINE != 0
2398 {
2399 transport.loop_range_beats = Some((
2400 context.loop_start_beats as f64 / CLAP_BEATTIME_FACTOR as f64,
2401 context.loop_end_beats as f64 / CLAP_BEATTIME_FACTOR as f64,
2402 ));
2403 }
2404 if context.flags & CLAP_TRANSPORT_IS_LOOP_ACTIVE != 0
2405 && context.flags & CLAP_TRANSPORT_HAS_SECONDS_TIMELINE != 0
2406 {
2407 transport.loop_range_seconds = Some((
2408 context.loop_start_seconds as f64 / CLAP_SECTIME_FACTOR as f64,
2409 context.loop_end_seconds as f64 / CLAP_SECTIME_FACTOR as f64,
2410 ));
2411 }
2412 }
2413
2414 let result = if buffer_is_valid {
2415 let mut plugin = wrapper.plugin.lock();
2416 let mut aux = AuxiliaryBuffers {
2420 inputs: buffers.aux_inputs,
2421 outputs: buffers.aux_outputs,
2422 };
2423 let mut context = wrapper.make_process_context(transport);
2424 let result = plugin.process(buffers.main_buffer, &mut aux, &mut context);
2425 wrapper.last_process_status.store(result);
2426 result
2427 } else {
2428 ProcessStatus::Normal
2429 };
2430
2431 let clap_result = match result {
2432 ProcessStatus::Error(err) => {
2433 crate::nice_debug_assert_failure!("Process error: {}", err);
2434
2435 return CLAP_PROCESS_ERROR;
2436 }
2437 ProcessStatus::Normal => CLAP_PROCESS_CONTINUE_IF_NOT_QUIET,
2438 ProcessStatus::Tail(_) => CLAP_PROCESS_CONTINUE,
2439 ProcessStatus::KeepAlive => CLAP_PROCESS_CONTINUE,
2440 };
2441
2442 if !process.out_events.is_null() {
2445 unsafe {
2446 wrapper.handle_out_events(
2447 &*process.out_events,
2448 block_start,
2449 total_buffer_len,
2450 )
2451 };
2452 }
2453
2454 if block_end == total_buffer_len {
2458 break clap_result;
2459 } else {
2460 block_start = block_end;
2461 }
2462 };
2463
2464 let updated_state = permit_alloc(|| wrapper.updated_state_receiver.try_recv());
2471 if let Ok(mut state) = updated_state {
2472 wrapper.set_state_inner(&mut state);
2473
2474 if let Err(err) = wrapper.updated_state_sender.send(state) {
2477 crate::nice_debug_assert_failure!(
2478 "Failed to send state object back to GUI thread: {}",
2479 err
2480 );
2481 };
2482 }
2483
2484 result
2485 })
2486 }
2487
2488 unsafe extern "C" fn get_extension(
2489 plugin: *const clap_plugin,
2490 id: *const c_char,
2491 ) -> *const c_void {
2492 check_null_ptr!(
2493 std::ptr::null(),
2494 plugin,
2495 unsafe { (*plugin).plugin_data },
2496 id
2497 );
2498 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2499
2500 let id = unsafe { CStr::from_ptr(id) };
2501
2502 if id == CLAP_EXT_AUDIO_PORTS_CONFIG {
2503 &wrapper.clap_plugin_audio_ports_config as *const _ as *const c_void
2504 } else if id == CLAP_EXT_AUDIO_PORTS {
2505 &wrapper.clap_plugin_audio_ports as *const _ as *const c_void
2506 } else if id == CLAP_EXT_GUI && wrapper.editor.borrow().is_some() {
2507 &wrapper.clap_plugin_gui as *const _ as *const c_void
2509 } else if id == CLAP_EXT_LATENCY {
2510 &wrapper.clap_plugin_latency as *const _ as *const c_void
2511 } else if id == CLAP_EXT_NOTE_PORTS
2512 && (P::MIDI_INPUT >= MidiConfig::Basic || P::MIDI_OUTPUT >= MidiConfig::Basic)
2513 {
2514 &wrapper.clap_plugin_note_ports as *const _ as *const c_void
2515 } else if id == CLAP_EXT_PARAMS {
2516 &wrapper.clap_plugin_params as *const _ as *const c_void
2517 } else if id == CLAP_EXT_REMOTE_CONTROLS {
2518 &wrapper.clap_plugin_remote_controls as *const _ as *const c_void
2519 } else if id == CLAP_EXT_RENDER {
2520 &wrapper.clap_plugin_render as *const _ as *const c_void
2521 } else if id == CLAP_EXT_STATE {
2522 &wrapper.clap_plugin_state as *const _ as *const c_void
2523 } else if id == CLAP_EXT_TAIL {
2524 &wrapper.clap_plugin_tail as *const _ as *const c_void
2525 } else if id == CLAP_EXT_TRACK_INFO {
2526 &wrapper.clap_plugin_track_info as *const _ as *const c_void
2527 } else if id == CLAP_EXT_VOICE_INFO && P::CLAP_POLY_MODULATION_CONFIG.is_some() {
2528 &wrapper.clap_plugin_voice_info as *const _ as *const c_void
2529 } else {
2530 crate::nice_trace!("Host tried to query unknown extension {:?}", id);
2531 std::ptr::null()
2532 }
2533 }
2534
2535 unsafe extern "C" fn on_main_thread(plugin: *const clap_plugin) {
2536 check_null_ptr!((), plugin, unsafe { (*plugin).plugin_data });
2537 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2538
2539 while let Some(task) = wrapper.tasks.pop() {
2542 wrapper.execute(task, true);
2543 }
2544 }
2545
2546 unsafe extern "C" fn ext_audio_ports_config_count(plugin: *const clap_plugin) -> u32 {
2547 check_null_ptr!(0, plugin, unsafe { (*plugin).plugin_data });
2548
2549 P::AUDIO_IO_LAYOUTS.len() as u32
2550 }
2551
2552 unsafe extern "C" fn ext_audio_ports_config_get(
2553 plugin: *const clap_plugin,
2554 index: u32,
2555 config: *mut clap_audio_ports_config,
2556 ) -> bool {
2557 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, config);
2558
2559 match P::AUDIO_IO_LAYOUTS.get(index as usize) {
2562 Some(audio_io_layout) => {
2563 let name = audio_io_layout.name();
2564
2565 let main_input_channels = audio_io_layout.main_input_channels.map(NonZeroU32::get);
2566 let main_output_channels =
2567 audio_io_layout.main_output_channels.map(NonZeroU32::get);
2568 let input_port_type = match main_input_channels {
2569 Some(1) => CLAP_PORT_MONO.as_ptr(),
2570 Some(2) => CLAP_PORT_STEREO.as_ptr(),
2571 _ => std::ptr::null(),
2572 };
2573 let output_port_type = match main_output_channels {
2574 Some(1) => CLAP_PORT_MONO.as_ptr(),
2575 Some(2) => CLAP_PORT_STEREO.as_ptr(),
2576 _ => std::ptr::null(),
2577 };
2578
2579 unsafe { *config = std::mem::zeroed() };
2580
2581 let config = unsafe { &mut *config };
2582 config.id = index;
2583 strlcpy(&mut config.name, &name);
2584 config.input_port_count = (if main_input_channels.is_some() { 1 } else { 0 }
2585 + audio_io_layout.aux_input_ports.len())
2586 as u32;
2587 config.output_port_count = (if main_output_channels.is_some() { 1 } else { 0 }
2588 + audio_io_layout.aux_output_ports.len())
2589 as u32;
2590 config.has_main_input = main_input_channels.is_some();
2591 config.main_input_channel_count = main_input_channels.unwrap_or_default();
2592 config.main_input_port_type = input_port_type;
2593 config.has_main_output = main_output_channels.is_some();
2594 config.main_output_channel_count = main_output_channels.unwrap_or_default();
2595 config.main_output_port_type = output_port_type;
2596
2597 true
2598 }
2599 None => {
2600 crate::nice_debug_assert_failure!(
2601 "Host tried to query out of bounds audio port config {}",
2602 index
2603 );
2604
2605 false
2606 }
2607 }
2608 }
2609
2610 unsafe extern "C" fn ext_audio_ports_config_select(
2611 plugin: *const clap_plugin,
2612 config_id: clap_id,
2613 ) -> bool {
2614 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
2615 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2616
2617 match P::AUDIO_IO_LAYOUTS.get(config_id as usize) {
2619 Some(audio_io_layout) => {
2620 wrapper.current_audio_io_layout.store(*audio_io_layout);
2621
2622 true
2623 }
2624 None => {
2625 crate::nice_debug_assert_failure!(
2626 "Host tried to select out of bounds audio port config {}",
2627 config_id
2628 );
2629
2630 false
2631 }
2632 }
2633 }
2634
2635 unsafe extern "C" fn ext_audio_ports_count(plugin: *const clap_plugin, is_input: bool) -> u32 {
2636 check_null_ptr!(0, plugin, unsafe { (*plugin).plugin_data });
2637 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2638
2639 let audio_io_layout = wrapper.current_audio_io_layout.load();
2640 if is_input {
2641 let main_ports = if audio_io_layout.main_input_channels.is_some() {
2642 1
2643 } else {
2644 0
2645 };
2646 let aux_ports = audio_io_layout.aux_input_ports.len();
2647
2648 (main_ports + aux_ports) as u32
2649 } else {
2650 let main_ports = if audio_io_layout.main_output_channels.is_some() {
2651 1
2652 } else {
2653 0
2654 };
2655 let aux_ports = audio_io_layout.aux_output_ports.len();
2656
2657 (main_ports + aux_ports) as u32
2658 }
2659 }
2660
2661 unsafe extern "C" fn ext_audio_ports_get(
2662 plugin: *const clap_plugin,
2663 index: u32,
2664 is_input: bool,
2665 info: *mut clap_audio_port_info,
2666 ) -> bool {
2667 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, info);
2668 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2669
2670 let num_input_ports = unsafe { Self::ext_audio_ports_count(plugin, true) };
2671 let num_output_ports = unsafe { Self::ext_audio_ports_count(plugin, false) };
2672 if (is_input && index >= num_input_ports) || (!is_input && index >= num_output_ports) {
2673 crate::nice_debug_assert_failure!(
2674 "Host tried to query information for out of bounds audio port {} (input: {})",
2675 index,
2676 is_input
2677 );
2678
2679 return false;
2680 }
2681
2682 let current_audio_io_layout = wrapper.current_audio_io_layout.load();
2683 let has_main_input = current_audio_io_layout.main_input_channels.is_some();
2684 let has_main_output = current_audio_io_layout.main_output_channels.is_some();
2685
2686 let is_main_port =
2688 index == 0 && ((is_input && has_main_input) || (!is_input && has_main_output));
2689
2690 let stable_id = if is_input {
2693 index
2694 } else {
2695 index + num_input_ports
2696 };
2697 let pair_stable_id = match (is_input, is_main_port) {
2698 (true, true) if has_main_output => num_input_ports,
2701 (false, true) if has_main_input => 0,
2702 _ => CLAP_INVALID_ID,
2703 };
2704
2705 let channel_count = match (index, is_input) {
2706 (0, true) if has_main_input => {
2707 current_audio_io_layout.main_input_channels.unwrap().get()
2708 }
2709 (0, false) if has_main_output => {
2710 current_audio_io_layout.main_output_channels.unwrap().get()
2711 }
2712 (n, true) if has_main_input => {
2714 current_audio_io_layout.aux_input_ports[n as usize - 1].get()
2715 }
2716 (n, false) if has_main_output => {
2717 current_audio_io_layout.aux_output_ports[n as usize - 1].get()
2718 }
2719 (n, true) => current_audio_io_layout.aux_input_ports[n as usize].get(),
2720 (n, false) => current_audio_io_layout.aux_output_ports[n as usize].get(),
2721 };
2722
2723 let port_type = match channel_count {
2724 1 => CLAP_PORT_MONO.as_ptr(),
2725 2 => CLAP_PORT_STEREO.as_ptr(),
2726 _ => std::ptr::null(),
2727 };
2728
2729 unsafe { *info = std::mem::zeroed() };
2730
2731 let info = unsafe { &mut *info };
2732 info.id = stable_id;
2733 match (is_input, is_main_port) {
2734 (true, true) => strlcpy(&mut info.name, ¤t_audio_io_layout.main_input_name()),
2735 (false, true) => strlcpy(&mut info.name, ¤t_audio_io_layout.main_output_name()),
2736 (true, false) => {
2737 let aux_input_idx = if has_main_input { index - 1 } else { index } as usize;
2738 strlcpy(
2739 &mut info.name,
2740 ¤t_audio_io_layout
2741 .aux_input_name(aux_input_idx)
2742 .expect("Out of bounds auxiliary input port"),
2743 );
2744 }
2745 (false, false) => {
2746 let aux_output_idx = if has_main_output { index - 1 } else { index } as usize;
2747 strlcpy(
2748 &mut info.name,
2749 ¤t_audio_io_layout
2750 .aux_output_name(aux_output_idx)
2751 .expect("Out of bounds auxiliary output port"),
2752 );
2753 }
2754 };
2755 info.flags = if is_main_port {
2756 CLAP_AUDIO_PORT_IS_MAIN
2757 } else {
2758 0
2759 };
2760 info.channel_count = channel_count;
2761 info.port_type = port_type;
2762 info.in_place_pair = pair_stable_id;
2763
2764 true
2765 }
2766
2767 unsafe extern "C" fn ext_gui_is_api_supported(
2768 _plugin: *const clap_plugin,
2769 api: *const c_char,
2770 is_floating: bool,
2771 ) -> bool {
2772 if is_floating {
2774 return false;
2775 }
2776
2777 unsafe {
2778 #[cfg(all(target_family = "unix", not(target_os = "macos")))]
2779 if CStr::from_ptr(api) == CLAP_WINDOW_API_X11 {
2780 return true;
2781 }
2782 #[cfg(target_os = "macos")]
2783 if CStr::from_ptr(api) == CLAP_WINDOW_API_COCOA {
2784 return true;
2785 }
2786 #[cfg(target_os = "windows")]
2787 if CStr::from_ptr(api) == CLAP_WINDOW_API_WIN32 {
2788 return true;
2789 }
2790 }
2791
2792 false
2793 }
2794
2795 unsafe extern "C" fn ext_gui_get_preferred_api(
2796 _plugin: *const clap_plugin,
2797 api: *mut *const c_char,
2798 is_floating: *mut bool,
2799 ) -> bool {
2800 check_null_ptr!(false, api, is_floating);
2801
2802 unsafe {
2803 #[cfg(all(target_family = "unix", not(target_os = "macos")))]
2804 {
2805 *api = CLAP_WINDOW_API_X11.as_ptr();
2806 }
2807 #[cfg(target_os = "macos")]
2808 {
2809 *api = CLAP_WINDOW_API_COCOA.as_ptr();
2810 }
2811 #[cfg(target_os = "windows")]
2812 {
2813 *api = CLAP_WINDOW_API_WIN32.as_ptr();
2814 }
2815
2816 *is_floating = false;
2818 }
2819
2820 true
2821 }
2822
2823 unsafe extern "C" fn ext_gui_create(
2824 plugin: *const clap_plugin,
2825 api: *const c_char,
2826 is_floating: bool,
2827 ) -> bool {
2828 if unsafe { !Self::ext_gui_is_api_supported(plugin, api, is_floating) } {
2830 return false;
2831 }
2832
2833 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
2837 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2838
2839 let editor_handle = wrapper.editor_handle.lock();
2840 if editor_handle.is_none() {
2841 true
2842 } else {
2843 crate::nice_debug_assert_failure!(
2844 "Tried creating editor while the editor was already active"
2845 );
2846 false
2847 }
2848 }
2849
2850 unsafe extern "C" fn ext_gui_destroy(plugin: *const clap_plugin) {
2851 check_null_ptr!((), plugin, unsafe { (*plugin).plugin_data });
2852 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2853
2854 let mut editor_handle = wrapper.editor_handle.lock();
2855 if editor_handle.is_some() {
2856 *editor_handle = None;
2857 } else {
2858 crate::nice_debug_assert_failure!(
2859 "Tried destroying editor while the editor was not active"
2860 );
2861 }
2862 }
2863
2864 unsafe extern "C" fn ext_gui_set_scale(plugin: *const clap_plugin, scale: f64) -> bool {
2865 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
2866 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2867
2868 if cfg!(target_os = "macos") {
2870 crate::nice_debug_assert_failure!(
2871 "Ignoring host request to set explicit DPI scaling factor"
2872 );
2873 return false;
2874 }
2875
2876 if wrapper
2877 .editor
2878 .borrow()
2879 .as_ref()
2880 .unwrap()
2881 .lock()
2882 .set_scale_factor(scale)
2883 {
2884 wrapper
2885 .scale_factor
2886 .store(scale, std::sync::atomic::Ordering::Relaxed);
2887 true
2888 } else {
2889 false
2890 }
2891 }
2892
2893 unsafe extern "C" fn ext_gui_get_size(
2894 plugin: *const clap_plugin,
2895 width: *mut u32,
2896 height: *mut u32,
2897 ) -> bool {
2898 check_null_ptr!(
2899 false,
2900 plugin,
2901 unsafe { (*plugin).plugin_data },
2902 width,
2903 height
2904 );
2905 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2906
2907 let scale_factor = wrapper.scale_factor.load(Ordering::Relaxed);
2909 let size: PhysicalSize<u32> = wrapper
2910 .editor
2911 .borrow()
2912 .as_ref()
2913 .unwrap()
2914 .lock()
2915 .size()
2916 .to_physical(scale_factor);
2917
2918 unsafe {
2919 *width = size.width;
2920 *height = size.height;
2921 }
2922
2923 true
2924 }
2925
2926 unsafe extern "C" fn ext_gui_can_resize(plugin: *const clap_plugin) -> bool {
2927 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
2928 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2929
2930 match wrapper.editor.borrow().as_ref() {
2932 Some(editor) => editor.lock().resize_hint().can_resize,
2933 None => false,
2934 }
2935 }
2936
2937 unsafe extern "C" fn ext_gui_get_resize_hints(
2938 plugin: *const clap_plugin,
2939 hints: *mut clap_gui_resize_hints,
2940 ) -> bool {
2941 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, hints);
2942 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2943
2944 let hint = match wrapper.editor.borrow().as_ref() {
2945 Some(editor) => editor.lock().resize_hint(),
2946 None => return false,
2947 };
2948 if !hint.can_resize {
2949 return false;
2950 }
2951
2952 let hints = unsafe { &mut *hints };
2953 hints.can_resize_horizontally = hint.can_resize_horizontally;
2954 hints.can_resize_vertically = hint.can_resize_vertically;
2955 hints.preserve_aspect_ratio = hint.preserve_aspect_ratio;
2956 hints.aspect_ratio_width = hint.aspect_ratio_width;
2957 hints.aspect_ratio_height = hint.aspect_ratio_height;
2958
2959 true
2960 }
2961
2962 unsafe extern "C" fn ext_gui_adjust_size(
2963 _plugin: *const clap_plugin,
2964 _width: *mut u32,
2965 _height: *mut u32,
2966 ) -> bool {
2967 true
2971 }
2972
2973 unsafe extern "C" fn ext_gui_set_size(
2974 plugin: *const clap_plugin,
2975 width: u32,
2976 height: u32,
2977 ) -> bool {
2978 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
2982 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2983
2984 match wrapper.editor.borrow().as_ref() {
2987 Some(editor) => editor.lock().set_size(PhysicalSize { width, height }),
2988 None => false,
2989 }
2990 }
2991
2992 unsafe extern "C" fn ext_gui_set_parent(
2993 plugin: *const clap_plugin,
2994 window: *const clap_window,
2995 ) -> bool {
2996 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, window);
2997 let wrapper = unsafe { Arc::from_raw((*plugin).plugin_data as *const Self) };
2999
3000 let window = unsafe { &*window };
3001
3002 let result = {
3003 let mut editor_handle = wrapper.editor_handle.lock();
3004 if editor_handle.is_none() {
3005 let api = unsafe { CStr::from_ptr(window.api) };
3006 let parent_handle = unsafe {
3007 if api == CLAP_WINDOW_API_X11 {
3008 #[allow(clippy::unnecessary_cast)]
3009 let w = window.specific.x11 as c_ulong;
3010 ParentWindowHandle::XlibWindow(w)
3011 } else if api == CLAP_WINDOW_API_COCOA {
3012 check_null_ptr!(false, window.specific.cocoa);
3013 let w = NonNull::new(window.specific.cocoa).unwrap();
3014 ParentWindowHandle::AppKitNsView(w)
3015 } else if api == CLAP_WINDOW_API_WIN32 {
3016 check_null_ptr!(false, window.specific.win32);
3017 let w = NonZeroIsize::new(window.specific.win32 as isize).unwrap();
3018 ParentWindowHandle::Win32Hwnd(w)
3019 } else {
3020 crate::nice_debug_assert_failure!("Host passed an invalid API");
3021 return false;
3022 }
3023 };
3024
3025 *editor_handle = Some(Fragile::new(
3027 wrapper
3028 .editor
3029 .borrow()
3030 .as_ref()
3031 .unwrap()
3032 .lock()
3033 .spawn(parent_handle, wrapper.clone().make_gui_context()),
3034 ));
3035
3036 true
3037 } else {
3038 crate::nice_debug_assert_failure!(
3039 "Host tried to attach editor while the editor is already attached"
3040 );
3041
3042 false
3043 }
3044 };
3045
3046 let _ = Arc::into_raw(wrapper);
3048
3049 result
3050 }
3051
3052 unsafe extern "C" fn ext_gui_set_transient(
3053 _plugin: *const clap_plugin,
3054 _window: *const clap_window,
3055 ) -> bool {
3056 false
3058 }
3059
3060 unsafe extern "C" fn ext_gui_suggest_title(_plugin: *const clap_plugin, _title: *const c_char) {
3061 }
3063
3064 unsafe extern "C" fn ext_gui_show(_plugin: *const clap_plugin) -> bool {
3065 false
3068 }
3069
3070 unsafe extern "C" fn ext_gui_hide(_plugin: *const clap_plugin) -> bool {
3071 false
3073 }
3074
3075 unsafe extern "C" fn ext_latency_get(plugin: *const clap_plugin) -> u32 {
3076 check_null_ptr!(0, plugin, unsafe { (*plugin).plugin_data });
3077 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3078
3079 wrapper.current_latency.load(Ordering::SeqCst)
3080 }
3081
3082 unsafe extern "C" fn ext_note_ports_count(_plugin: *const clap_plugin, is_input: bool) -> u32 {
3083 match is_input {
3084 true if P::MIDI_INPUT >= MidiConfig::Basic => 1,
3085 false if P::MIDI_OUTPUT >= MidiConfig::Basic => 1,
3086 _ => 0,
3087 }
3088 }
3089
3090 unsafe extern "C" fn ext_note_ports_get(
3091 _plugin: *const clap_plugin,
3092 index: u32,
3093 is_input: bool,
3094 info: *mut clap_note_port_info,
3095 ) -> bool {
3096 match (index, is_input) {
3097 (0, true) if P::MIDI_INPUT >= MidiConfig::Basic => {
3098 unsafe {
3099 *info = std::mem::zeroed();
3100 }
3101
3102 let info = unsafe { &mut *info };
3103 info.id = 0;
3104 info.supported_dialects = CLAP_NOTE_DIALECT_CLAP | CLAP_NOTE_DIALECT_MIDI;
3107 info.preferred_dialect = CLAP_NOTE_DIALECT_CLAP;
3108 strlcpy(&mut info.name, "Note Input");
3109
3110 true
3111 }
3112 (0, false) if P::MIDI_OUTPUT >= MidiConfig::Basic => {
3113 unsafe { *info = std::mem::zeroed() };
3114
3115 let info = unsafe { &mut *info };
3116 info.id = 0;
3117 info.supported_dialects = CLAP_NOTE_DIALECT_CLAP | CLAP_NOTE_DIALECT_MIDI;
3121 info.preferred_dialect = CLAP_NOTE_DIALECT_CLAP;
3122 strlcpy(&mut info.name, "Note Output");
3123
3124 true
3125 }
3126 _ => false,
3127 }
3128 }
3129
3130 unsafe extern "C" fn ext_params_count(plugin: *const clap_plugin) -> u32 {
3131 check_null_ptr!(0, plugin, unsafe { (*plugin).plugin_data });
3132 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3133
3134 wrapper.param_hashes.len() as u32
3135 }
3136
3137 unsafe extern "C" fn ext_params_get_info(
3138 plugin: *const clap_plugin,
3139 param_index: u32,
3140 param_info: *mut clap_param_info,
3141 ) -> bool {
3142 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, param_info);
3143 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3144
3145 if param_index > unsafe { Self::ext_params_count(plugin) } {
3146 return false;
3147 }
3148
3149 let param_hash = &wrapper.param_hashes[param_index as usize];
3150 let param_group = &wrapper.param_group_by_hash[param_hash];
3151 let param_ptr = &wrapper.param_by_hash[param_hash];
3152 let default_value = unsafe { param_ptr.default_normalized_value() };
3153 let step_count = unsafe { param_ptr.step_count() };
3154 let flags = unsafe { param_ptr.flags() };
3155 let automatable = !flags.contains(ParamFlags::NON_AUTOMATABLE);
3156 let hidden = flags.contains(ParamFlags::HIDDEN);
3157 let is_bypass = flags.contains(ParamFlags::BYPASS);
3158
3159 unsafe {
3160 *param_info = std::mem::zeroed();
3161 }
3162
3163 let param_info = unsafe { &mut *param_info };
3166 param_info.id = *param_hash;
3167 param_info.flags = 0;
3169 if automatable && !hidden {
3170 param_info.flags |= CLAP_PARAM_IS_AUTOMATABLE | CLAP_PARAM_IS_MODULATABLE;
3171 if wrapper.poly_mod_ids_by_hash.contains_key(param_hash) {
3172 param_info.flags |= CLAP_PARAM_IS_MODULATABLE_PER_NOTE_ID;
3173 }
3174 }
3175 if hidden {
3176 param_info.flags |= CLAP_PARAM_IS_HIDDEN | CLAP_PARAM_IS_READONLY;
3177 }
3178 if is_bypass {
3179 param_info.flags |= CLAP_PARAM_IS_BYPASS
3180 }
3181 if step_count.is_some() {
3182 param_info.flags |= CLAP_PARAM_IS_STEPPED
3183 }
3184 param_info.cookie = std::ptr::null_mut();
3185 strlcpy(&mut param_info.name, unsafe { param_ptr.name() });
3186 strlcpy(&mut param_info.module, param_group);
3187 param_info.min_value = 0.0;
3191 param_info.max_value = step_count.unwrap_or(1) as f64;
3195 param_info.default_value = default_value as f64 * step_count.unwrap_or(1) as f64;
3196
3197 true
3198 }
3199
3200 unsafe extern "C" fn ext_params_get_value(
3201 plugin: *const clap_plugin,
3202 param_id: clap_id,
3203 value: *mut f64,
3204 ) -> bool {
3205 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, value);
3206 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3207
3208 match wrapper.param_by_hash.get(¶m_id) {
3209 Some(param_ptr) => {
3210 unsafe {
3211 *value = param_ptr.modulated_normalized_value() as f64
3212 * param_ptr.step_count().unwrap_or(1) as f64;
3213 }
3214
3215 true
3216 }
3217 _ => false,
3218 }
3219 }
3220
3221 unsafe extern "C" fn ext_params_value_to_text(
3222 plugin: *const clap_plugin,
3223 param_id: clap_id,
3224 value: f64,
3225 display: *mut c_char,
3226 size: u32,
3227 ) -> bool {
3228 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, display);
3229 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3230
3231 let dest = unsafe { std::slice::from_raw_parts_mut(display, size as usize) };
3232
3233 match wrapper.param_by_hash.get(¶m_id) {
3234 Some(param_ptr) => {
3235 unsafe {
3236 strlcpy(
3237 dest,
3238 ¶m_ptr.normalized_value_to_string(
3240 value as f32 / param_ptr.step_count().unwrap_or(1) as f32,
3241 true,
3242 ),
3243 );
3244 }
3245
3246 true
3247 }
3248 _ => false,
3249 }
3250 }
3251
3252 unsafe extern "C" fn ext_params_text_to_value(
3253 plugin: *const clap_plugin,
3254 param_id: clap_id,
3255 display: *const c_char,
3256 value: *mut f64,
3257 ) -> bool {
3258 check_null_ptr!(
3259 false,
3260 plugin,
3261 unsafe { (*plugin).plugin_data },
3262 display,
3263 value
3264 );
3265 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3266
3267 let display = match unsafe { CStr::from_ptr(display).to_str() } {
3268 Ok(s) => s,
3269 Err(_) => return false,
3270 };
3271
3272 match wrapper.param_by_hash.get(¶m_id) {
3273 Some(param_ptr) => {
3274 let normalized_value =
3275 match unsafe { param_ptr.string_to_normalized_value(display) } {
3276 Some(v) => v as f64,
3277 None => return false,
3278 };
3279 unsafe {
3280 *value = normalized_value * param_ptr.step_count().unwrap_or(1) as f64;
3281 }
3282
3283 true
3284 }
3285 _ => false,
3286 }
3287 }
3288
3289 unsafe extern "C" fn ext_params_flush(
3290 plugin: *const clap_plugin,
3291 in_: *const clap_input_events,
3292 out: *const clap_output_events,
3293 ) {
3294 check_null_ptr!((), plugin, unsafe { (*plugin).plugin_data });
3295 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3296
3297 if !in_.is_null() {
3298 unsafe {
3299 wrapper.handle_in_events(&*in_, 0, 0);
3300 }
3301 }
3302
3303 if !out.is_null() {
3304 unsafe {
3305 wrapper.handle_out_events(&*out, 0, 0);
3306 }
3307 }
3308 }
3309
3310 unsafe extern "C" fn ext_remote_controls_count(plugin: *const clap_plugin) -> u32 {
3311 check_null_ptr!(0, plugin, unsafe { (*plugin).plugin_data });
3312 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3313
3314 wrapper.remote_control_pages.len() as u32
3315 }
3316
3317 unsafe extern "C" fn ext_remote_controls_get(
3318 plugin: *const clap_plugin,
3319 page_index: u32,
3320 page: *mut clap_remote_controls_page,
3321 ) -> bool {
3322 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, page);
3323 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3324
3325 crate::nice_debug_assert!(page_index as usize <= wrapper.remote_control_pages.len());
3326 match wrapper.remote_control_pages.get(page_index as usize) {
3327 Some(p) => {
3328 unsafe {
3329 *page = *p;
3330 }
3331 true
3332 }
3333 None => false,
3334 }
3335 }
3336
3337 unsafe extern "C" fn ext_render_has_hard_realtime_requirement(
3338 _plugin: *const clap_plugin,
3339 ) -> bool {
3340 P::HARD_REALTIME_ONLY
3341 }
3342
3343 unsafe extern "C" fn ext_render_set(
3344 plugin: *const clap_plugin,
3345 mode: clap_plugin_render_mode,
3346 ) -> bool {
3347 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
3348 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3349
3350 let mode = match mode {
3351 CLAP_RENDER_REALTIME => ProcessMode::Realtime,
3352 CLAP_RENDER_OFFLINE => ProcessMode::Offline,
3354 n => {
3355 crate::nice_debug_assert_failure!(
3356 "Unknown rendering mode '{}', defaulting to realtime",
3357 n
3358 );
3359 ProcessMode::Realtime
3360 }
3361 };
3362
3363 if wrapper.current_process_mode.swap(mode) != mode
3364 && wrapper.is_activated.load(Ordering::SeqCst)
3365 {
3366 unsafe_clap_call! { &*wrapper.host_callback=>request_restart(&*wrapper.host_callback) };
3369 }
3370
3371 true
3372 }
3373
3374 unsafe extern "C" fn ext_state_save(
3375 plugin: *const clap_plugin,
3376 stream: *const clap_ostream,
3377 ) -> bool {
3378 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, stream);
3379 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3380
3381 let serialized = unsafe {
3382 state::serialize_json::<P>(
3383 wrapper.params.clone(),
3384 state::make_params_iter(&wrapper.param_by_hash, &wrapper.param_id_to_hash),
3385 )
3386 };
3387 match serialized {
3388 Ok(serialized) => {
3389 let length_bytes = (serialized.len() as u64).to_le_bytes();
3392 if !write_stream(unsafe { &*stream }, &length_bytes) {
3393 crate::nice_debug_assert_failure!(
3394 "Error or end of stream while writing the state length to the stream."
3395 );
3396 return false;
3397 }
3398 if !write_stream(unsafe { &*stream }, &serialized) {
3399 crate::nice_debug_assert_failure!(
3400 "Error or end of stream while writing the state buffer to the stream."
3401 );
3402 return false;
3403 }
3404
3405 crate::nice_trace!("Saved state ({} bytes)", serialized.len());
3406
3407 true
3408 }
3409 Err(err) => {
3410 crate::nice_debug_assert_failure!("Could not save state: {:#}", err);
3411 false
3412 }
3413 }
3414 }
3415
3416 unsafe extern "C" fn ext_state_load(
3417 plugin: *const clap_plugin,
3418 stream: *const clap_istream,
3419 ) -> bool {
3420 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, stream);
3421 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3422
3423 let mut length_bytes = [0u8; 8];
3426 if !read_stream(unsafe { &*stream }, length_bytes.as_mut_slice()) {
3427 crate::nice_debug_assert_failure!(
3428 "Error or end of stream while reading the state length from the stream."
3429 );
3430 return false;
3431 }
3432 let length = u64::from_le_bytes(length_bytes);
3433
3434 let mut read_buffer: Vec<u8> = Vec::with_capacity(length as usize);
3435 if !read_stream(unsafe { &*stream }, read_buffer.spare_capacity_mut()) {
3436 crate::nice_debug_assert_failure!(
3437 "Error or end of stream while reading the state buffer from the stream."
3438 );
3439 return false;
3440 }
3441 unsafe {
3442 read_buffer.set_len(length as usize);
3443 }
3444
3445 match unsafe { state::deserialize_json(&read_buffer) } {
3446 Some(mut state) => {
3447 let success = wrapper.set_state_inner(&mut state);
3448 if success {
3449 crate::nice_trace!("Loaded state ({} bytes)", read_buffer.len());
3450 }
3451
3452 success
3453 }
3454 None => false,
3455 }
3456 }
3457
3458 unsafe extern "C" fn ext_track_info_changed(plugin: *const clap_plugin) {
3459 check_null_ptr!((), plugin, unsafe { (*plugin).plugin_data });
3460 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3461
3462 wrapper.update_track_info_from_host();
3463 }
3464
3465 unsafe extern "C" fn ext_tail_get(plugin: *const clap_plugin) -> u32 {
3466 check_null_ptr!(0, plugin, unsafe { (*plugin).plugin_data });
3467 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3468
3469 match wrapper.last_process_status.load() {
3470 ProcessStatus::Tail(samples) => samples,
3471 ProcessStatus::KeepAlive => u32::MAX,
3472 _ => 0,
3473 }
3474 }
3475
3476 unsafe extern "C" fn ext_voice_info_get(
3477 plugin: *const clap_plugin,
3478 info: *mut clap_voice_info,
3479 ) -> bool {
3480 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, info);
3481 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3482
3483 match P::CLAP_POLY_MODULATION_CONFIG {
3484 Some(config) => {
3485 unsafe {
3486 *info = clap_voice_info {
3487 voice_count: wrapper.current_voice_capacity.load(Ordering::Relaxed),
3488 voice_capacity: config.max_voice_capacity,
3489 flags: if config.supports_overlapping_voices {
3490 CLAP_VOICE_INFO_SUPPORTS_OVERLAPPING_NOTES
3491 } else {
3492 0
3493 },
3494 };
3495 }
3496
3497 true
3498 }
3499 None => false,
3500 }
3501 }
3502}
3503
3504unsafe fn query_host_extension<T>(
3510 host_callback: &ClapPtr<clap_host>,
3511 name: &CStr,
3512) -> Option<ClapPtr<T>> {
3513 let extension_ptr = unsafe {
3514 clap_call! { host_callback=>get_extension(&**host_callback, name.as_ptr()) }
3515 };
3516 if !extension_ptr.is_null() {
3517 unsafe { Some(ClapPtr::new(extension_ptr as *const T)) }
3518 } else {
3519 None
3520 }
3521}