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 let mut did_set_state_inner = false;
1792
1793 loop {
1796 if self.is_processing.load(Ordering::SeqCst) {
1797 match self
1803 .updated_state_sender
1804 .send_timeout(state, Duration::from_secs(1))
1805 {
1806 Ok(_) => {
1807 let state = self.updated_state_receiver.recv();
1810 drop(state);
1811 break;
1812 }
1813 Err(SendTimeoutError::Timeout(value)) => {
1814 state = value;
1815 continue;
1816 }
1817 Err(SendTimeoutError::Disconnected(_)) => {
1818 crate::nice_debug_assert_failure!("State update channel got disconnected");
1819 return;
1820 }
1821 }
1822 } else {
1823 self.set_state_inner(&mut state);
1826 did_set_state_inner = true;
1827 break;
1828 }
1829 }
1830
1831 if !did_set_state_inner {
1832 let task_posted = self.schedule_gui(Task::RescanParamValues);
1834 crate::nice_debug_assert!(task_posted, "The task queue is full, dropping task...");
1835 } }
1837
1838 pub fn set_latency_samples(&self, samples: u32) {
1839 let old_latency = self.current_latency.swap(samples, Ordering::SeqCst);
1843 if old_latency != samples {
1844 let task_posted = self.schedule_gui(Task::LatencyChanged);
1845 crate::nice_debug_assert!(task_posted, "The task queue is full, dropping task...");
1846 }
1847 }
1848
1849 pub fn set_current_voice_capacity(&self, capacity: u32) {
1850 match P::CLAP_POLY_MODULATION_CONFIG {
1851 Some(config) => {
1852 let clamped_capacity = capacity.clamp(1, config.max_voice_capacity);
1853 crate::nice_debug_assert_eq!(
1854 capacity,
1855 clamped_capacity,
1856 "The current voice capacity must be between 1 and the maximum capacity"
1857 );
1858
1859 if clamped_capacity != self.current_voice_capacity.load(Ordering::Relaxed) {
1860 self.current_voice_capacity
1861 .store(clamped_capacity, Ordering::Relaxed);
1862 let task_posted = self.schedule_gui(Task::VoiceInfoChanged);
1863 crate::nice_debug_assert!(
1864 task_posted,
1865 "The task queue is full, dropping task..."
1866 );
1867 }
1868 }
1869 None => crate::nice_debug_assert_failure!(
1870 "Configuring the current voice capacity is only possible when \
1871 'ClapPlugin::CLAP_POLY_MODULATION_CONFIG' is set"
1872 ),
1873 }
1874 }
1875
1876 fn update_track_info_from_host(&self) {
1878 let host_track_info = self.host_track_info.borrow();
1879 let Some(host_track_info) = host_track_info.as_ref() else {
1880 return;
1881 };
1882
1883 permit_alloc(|| {
1884 let mut clap_info: clap_track_info = unsafe { mem::zeroed() };
1885 let success = unsafe_clap_call! {
1886 host_track_info=>get(&*self.host_callback, &mut clap_info)
1887 };
1888 if !success {
1889 return;
1890 }
1891
1892 let mut current_track_info = self.current_track_info.borrow_mut();
1893 let mut name = current_track_info.name().to_owned();
1894 let mut color = current_track_info.color();
1895
1896 if clap_info.flags & CLAP_TRACK_INFO_HAS_TRACK_NAME != 0 {
1897 let name_bytes = unsafe {
1898 std::slice::from_raw_parts(
1899 clap_info.name.as_ptr().cast::<u8>(),
1900 clap_sys::string_sizes::CLAP_NAME_SIZE,
1901 )
1902 };
1903 if let Ok(cstr) = CStr::from_bytes_until_nul(name_bytes) {
1904 name = cstr.to_string_lossy().into_owned()
1905 } }
1907
1908 if clap_info.flags & CLAP_TRACK_INFO_HAS_TRACK_COLOR != 0 {
1909 color = Some(TrackColor::new(
1910 clap_info.color.red,
1911 clap_info.color.green,
1912 clap_info.color.blue,
1913 clap_info.color.alpha,
1914 ));
1915 }
1916
1917 let track_info = TrackInfo::new(name, color);
1918 *current_track_info = track_info.clone();
1919 self.plugin.lock().track_info_updated(track_info);
1920 });
1921 }
1922
1923 pub fn set_state_inner(&self, state: &mut PluginState) -> bool {
1934 let audio_io_layout = self.current_audio_io_layout.load();
1935 let buffer_config = self.current_buffer_config.load();
1936
1937 let mut success = permit_alloc(|| unsafe {
1944 state::deserialize_object::<P>(
1945 state,
1946 self.params.clone(),
1947 state::make_params_getter(&self.param_by_hash, &self.param_id_to_hash),
1948 self.current_buffer_config.load().as_ref(),
1949 )
1950 });
1951 if !success {
1952 crate::nice_debug_assert_failure!(
1953 "Deserializing plugin state from a state object failed"
1954 );
1955 return false;
1956 }
1957
1958 if let Some(buffer_config) = buffer_config {
1960 let mut init_context = self.make_init_context();
1962 let mut plugin = self.plugin.lock();
1963
1964 success = permit_alloc(|| {
1966 plugin.initialize(&audio_io_layout, &buffer_config, &mut init_context)
1967 });
1968 if success {
1969 process_wrapper(|| plugin.reset());
1970 }
1971 }
1972
1973 crate::nice_debug_assert!(
1974 success,
1975 "Plugin returned false when reinitializing after loading state"
1976 );
1977
1978 let task_posted = self.schedule_gui(Task::RescanParamValues);
1980 crate::nice_debug_assert!(task_posted, "The task queue is full, dropping task...");
1981 let task_posted = self.schedule_gui(Task::ParameterValuesChanged);
1982 crate::nice_debug_assert!(task_posted, "The task queue is full, dropping task...");
1983
1984 if self.editor_handle.lock().is_some() {
1988 self.request_resize();
1989 }
1990
1991 success
1992 }
1993
1994 unsafe extern "C" fn init(plugin: *const clap_plugin) -> bool {
1995 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
1996 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
1997
1998 unsafe {
2000 *wrapper.host_gui.borrow_mut() =
2001 query_host_extension::<clap_host_gui>(&wrapper.host_callback, CLAP_EXT_GUI);
2002 *wrapper.host_latency.borrow_mut() =
2003 query_host_extension::<clap_host_latency>(&wrapper.host_callback, CLAP_EXT_LATENCY);
2004 *wrapper.host_params.borrow_mut() =
2005 query_host_extension::<clap_host_params>(&wrapper.host_callback, CLAP_EXT_PARAMS);
2006 *wrapper.host_voice_info.borrow_mut() = query_host_extension::<clap_host_voice_info>(
2007 &wrapper.host_callback,
2008 CLAP_EXT_VOICE_INFO,
2009 );
2010 *wrapper.host_thread_check.borrow_mut() = query_host_extension::<clap_host_thread_check>(
2011 &wrapper.host_callback,
2012 CLAP_EXT_THREAD_CHECK,
2013 );
2014 *wrapper.host_track_info.borrow_mut() = query_host_extension::<clap_host_track_info>(
2015 &wrapper.host_callback,
2016 CLAP_EXT_TRACK_INFO,
2017 );
2018 }
2019
2020 wrapper.update_track_info_from_host();
2021
2022 true
2023 }
2024
2025 unsafe extern "C" fn destroy(plugin: *const clap_plugin) {
2026 assert!(!plugin.is_null() && unsafe { !(*plugin).plugin_data.is_null() });
2027 let this = unsafe { Arc::from_raw((*plugin).plugin_data as *mut Self) };
2028 crate::nice_debug_assert_eq!(Arc::strong_count(&this), 1);
2029
2030 drop(this);
2031 }
2032
2033 unsafe extern "C" fn activate(
2034 plugin: *const clap_plugin,
2035 sample_rate: f64,
2036 min_frames_count: u32,
2037 max_frames_count: u32,
2038 ) -> bool {
2039 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
2040 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2041
2042 let audio_io_layout = wrapper.current_audio_io_layout.load();
2043 let buffer_config = BufferConfig {
2044 sample_rate: sample_rate as f32,
2045 min_buffer_size: Some(min_frames_count),
2046 max_buffer_size: max_frames_count,
2047 process_mode: wrapper.current_process_mode.load(),
2048 };
2049
2050 for param in wrapper.param_by_hash.values() {
2052 unsafe { param._internal_update_smoother(buffer_config.sample_rate, true) };
2053 }
2054
2055 if wrapper.latency_changed.swap(false, Ordering::SeqCst) {
2058 if let Some(host_latency) = &*wrapper.host_latency.borrow() {
2059 unsafe_clap_call! { host_latency=>changed(&*wrapper.host_callback) };
2060 }
2061 }
2062
2063 let mut init_context = wrapper.make_init_context();
2065 let mut plugin = wrapper.plugin.lock();
2066 if plugin.initialize(&audio_io_layout, &buffer_config, &mut init_context) {
2067 *wrapper.buffer_manager.borrow_mut() =
2073 BufferManager::for_audio_io_layout(max_frames_count as usize, audio_io_layout);
2074
2075 wrapper.current_buffer_config.store(Some(buffer_config));
2077
2078 wrapper.is_activated.store(true, Ordering::SeqCst);
2079
2080 true
2081 } else {
2082 false
2083 }
2084 }
2085
2086 unsafe extern "C" fn deactivate(plugin: *const clap_plugin) {
2087 check_null_ptr!((), plugin, unsafe { (*plugin).plugin_data });
2088 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2089
2090 wrapper.plugin.lock().deactivate();
2091
2092 wrapper.is_activated.store(false, Ordering::SeqCst);
2093 }
2094
2095 unsafe extern "C" fn start_processing(plugin: *const clap_plugin) -> bool {
2096 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
2099 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2100
2101 wrapper.last_process_status.store(ProcessStatus::Normal);
2103 wrapper.is_processing.store(true, Ordering::SeqCst);
2104
2105 process_wrapper(|| wrapper.plugin.lock().reset());
2108
2109 true
2110 }
2111
2112 unsafe extern "C" fn stop_processing(plugin: *const clap_plugin) {
2113 check_null_ptr!((), plugin, unsafe { (*plugin).plugin_data });
2114 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2115
2116 wrapper.is_processing.store(false, Ordering::SeqCst);
2117 }
2118
2119 unsafe extern "C" fn reset(plugin: *const clap_plugin) {
2120 check_null_ptr!((), plugin, unsafe { (*plugin).plugin_data });
2121 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2122
2123 process_wrapper(|| wrapper.plugin.lock().reset());
2124 }
2125
2126 unsafe extern "C" fn process(
2127 plugin: *const clap_plugin,
2128 process: *const clap_process,
2129 ) -> clap_process_status {
2130 check_null_ptr!(
2131 CLAP_PROCESS_ERROR,
2132 plugin,
2133 unsafe { (*plugin).plugin_data },
2134 process
2135 );
2136 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2137
2138 process_wrapper(|| {
2141 let process = unsafe { &*process };
2145 let total_buffer_len = process.frames_count as usize;
2146
2147 let current_audio_io_layout = wrapper.current_audio_io_layout.load();
2148 let has_main_input = current_audio_io_layout.main_input_channels.is_some();
2149 let has_main_output = current_audio_io_layout.main_output_channels.is_some();
2150 let aux_input_start_idx = if has_main_input { 1 } else { 0 };
2151 let aux_output_start_idx = if has_main_output { 1 } else { 0 };
2152
2153 let mut block_start = 0;
2156 let mut block_end = total_buffer_len;
2157 let mut event_start_idx = 0;
2158
2159 let mut transport_info = process.transport;
2162
2163 let result = loop {
2164 if !process.in_events.is_null() {
2165 let split_result = unsafe {
2166 wrapper.handle_in_events_until(
2167 &*process.in_events,
2168 &mut transport_info,
2169 block_start,
2170 total_buffer_len,
2171 event_start_idx,
2172 |next_event| {
2173 if P::SAMPLE_ACCURATE_AUTOMATION {
2178 match ((*next_event).space_id, (*next_event).type_) {
2179 (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_PARAM_VALUE)
2180 | (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_TRANSPORT) => true,
2181 (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_PARAM_MOD) => {
2182 let next_event =
2183 &*(next_event as *const clap_event_param_mod);
2184
2185 !(next_event.note_id != -1
2188 && wrapper
2189 .poly_mod_ids_by_hash
2190 .contains_key(&next_event.param_id))
2191 }
2192 _ => false,
2193 }
2194 } else {
2195 matches!(
2196 ((*next_event).space_id, (*next_event).type_,),
2197 (CLAP_CORE_EVENT_SPACE_ID, CLAP_EVENT_TRANSPORT)
2198 )
2199 }
2200 },
2201 )
2202 };
2203
2204 match split_result {
2209 Some((next_param_change_sample_idx, next_param_change_event_idx)) => {
2210 block_end = next_param_change_sample_idx;
2211 event_start_idx = next_param_change_event_idx;
2212 }
2213 None => block_end = total_buffer_len,
2214 }
2215 }
2216
2217 let block_len = block_end - block_start;
2220
2221 let mut buffer_manager = wrapper.buffer_manager.borrow_mut();
2227 let buffers = unsafe {
2228 buffer_manager.create_buffers(block_start, block_len, |buffer_source| {
2229 if process.audio_outputs_count > 0
2233 && !process.audio_outputs.is_null()
2234 && !(*process.audio_outputs).data32.is_null()
2235 && has_main_output
2236 {
2237 let audio_output = &*process.audio_outputs;
2238 let ptrs = NonNull::new(audio_output.data32).unwrap();
2239 let num_channels = audio_output.channel_count as usize;
2240
2241 *buffer_source.main_output_channel_pointers =
2242 Some(ChannelPointers { ptrs, num_channels });
2243 }
2244
2245 if process.audio_inputs_count > 0
2246 && !process.audio_inputs.is_null()
2247 && !(*process.audio_inputs).data32.is_null()
2248 && has_main_input
2249 {
2250 let audio_input = &*process.audio_inputs;
2251 let ptrs = NonNull::new(audio_input.data32).unwrap();
2252 let num_channels = audio_input.channel_count as usize;
2253
2254 *buffer_source.main_input_channel_pointers =
2255 Some(ChannelPointers { ptrs, num_channels });
2256 }
2257
2258 if !process.audio_inputs.is_null() {
2259 for (aux_input_no, aux_input_channel_pointers) in buffer_source
2260 .aux_input_channel_pointers
2261 .iter_mut()
2262 .enumerate()
2263 {
2264 let aux_input_idx = aux_input_no + aux_input_start_idx;
2265 if aux_input_idx > process.audio_inputs_count as usize {
2266 break;
2267 }
2268
2269 let audio_input = &*process.audio_inputs.add(aux_input_idx);
2270 match NonNull::new(audio_input.data32) {
2271 Some(ptrs) => {
2272 let num_channels = audio_input.channel_count as usize;
2273
2274 *aux_input_channel_pointers =
2275 Some(ChannelPointers { ptrs, num_channels });
2276 }
2277 None => continue,
2278 }
2279 }
2280 }
2281
2282 if !process.audio_outputs.is_null() {
2283 for (aux_output_no, aux_output_channel_pointers) in buffer_source
2284 .aux_output_channel_pointers
2285 .iter_mut()
2286 .enumerate()
2287 {
2288 let aux_output_idx = aux_output_no + aux_output_start_idx;
2289 if aux_output_idx > process.audio_outputs_count as usize {
2290 break;
2291 }
2292
2293 let audio_output = &*process.audio_outputs.add(aux_output_idx);
2294 match NonNull::new(audio_output.data32) {
2295 Some(ptrs) => {
2296 let num_channels = audio_output.channel_count as usize;
2297
2298 *aux_output_channel_pointers =
2299 Some(ChannelPointers { ptrs, num_channels });
2300 }
2301 None => continue,
2302 }
2303 }
2304 }
2305 })
2306 };
2307
2308 let mut buffer_is_valid = true;
2315 for output_buffer_slice in buffers.main_buffer.as_slice_immutable().iter().chain(
2316 buffers
2317 .aux_outputs
2318 .iter()
2319 .flat_map(|buffer| buffer.as_slice_immutable().iter()),
2320 ) {
2321 if output_buffer_slice.is_empty() {
2322 buffer_is_valid = false;
2323 break;
2324 }
2325 }
2326
2327 crate::nice_debug_assert!(buffer_is_valid);
2328
2329 let sample_rate = wrapper
2333 .current_buffer_config
2334 .load()
2335 .expect("Process call without prior initialization call")
2336 .sample_rate;
2337 let mut transport = Transport::new(sample_rate);
2338 if !transport_info.is_null() {
2339 let context = unsafe { &*transport_info };
2340
2341 transport.playing = context.flags & CLAP_TRANSPORT_IS_PLAYING != 0;
2342 transport.recording = context.flags & CLAP_TRANSPORT_IS_RECORDING != 0;
2343 transport.preroll_active =
2344 Some(context.flags & CLAP_TRANSPORT_IS_WITHIN_PRE_ROLL != 0);
2345 if context.flags & CLAP_TRANSPORT_HAS_TEMPO != 0 {
2346 transport.tempo = Some(context.tempo);
2347 }
2348 if context.flags & CLAP_TRANSPORT_HAS_TIME_SIGNATURE != 0 {
2349 transport.time_sig_numerator = Some(context.tsig_num as i32);
2350 transport.time_sig_denominator = Some(context.tsig_denom as i32);
2351 }
2352 if context.flags & CLAP_TRANSPORT_HAS_BEATS_TIMELINE != 0 {
2353 let beats = context.song_pos_beats as f64 / CLAP_BEATTIME_FACTOR as f64;
2354
2355 if P::SAMPLE_ACCURATE_AUTOMATION
2359 && block_start > 0
2360 && (context.flags & CLAP_TRANSPORT_HAS_TEMPO != 0)
2361 {
2362 transport.pos_beats = Some(
2363 beats
2364 + (block_start as f64 / sample_rate as f64 / 60.0
2365 * context.tempo),
2366 );
2367 } else {
2368 transport.pos_beats = Some(beats);
2369 }
2370 }
2371 if context.flags & CLAP_TRANSPORT_HAS_SECONDS_TIMELINE != 0 {
2372 let seconds = context.song_pos_seconds as f64 / CLAP_SECTIME_FACTOR as f64;
2373
2374 if P::SAMPLE_ACCURATE_AUTOMATION
2376 && block_start > 0
2377 && (context.flags & CLAP_TRANSPORT_HAS_TEMPO != 0)
2378 {
2379 transport.pos_seconds =
2380 Some(seconds + (block_start as f64 / sample_rate as f64));
2381 } else {
2382 transport.pos_seconds = Some(seconds);
2383 }
2384 }
2385 if P::SAMPLE_ACCURATE_AUTOMATION && block_start > 0 {
2387 transport.bar_start_pos_beats = match transport.bar_start_pos_beats() {
2388 Some(updated) => Some(updated),
2389 None => Some(context.bar_start as f64 / CLAP_BEATTIME_FACTOR as f64),
2390 };
2391 transport.bar_number = match transport.bar_number() {
2392 Some(updated) => Some(updated),
2393 None => Some(context.bar_number),
2394 };
2395 } else {
2396 transport.bar_start_pos_beats =
2397 Some(context.bar_start as f64 / CLAP_BEATTIME_FACTOR as f64);
2398 transport.bar_number = Some(context.bar_number);
2399 }
2400 if context.flags & CLAP_TRANSPORT_IS_LOOP_ACTIVE != 0
2404 && context.flags & CLAP_TRANSPORT_HAS_BEATS_TIMELINE != 0
2405 {
2406 transport.loop_range_beats = Some((
2407 context.loop_start_beats as f64 / CLAP_BEATTIME_FACTOR as f64,
2408 context.loop_end_beats as f64 / CLAP_BEATTIME_FACTOR as f64,
2409 ));
2410 }
2411 if context.flags & CLAP_TRANSPORT_IS_LOOP_ACTIVE != 0
2412 && context.flags & CLAP_TRANSPORT_HAS_SECONDS_TIMELINE != 0
2413 {
2414 transport.loop_range_seconds = Some((
2415 context.loop_start_seconds as f64 / CLAP_SECTIME_FACTOR as f64,
2416 context.loop_end_seconds as f64 / CLAP_SECTIME_FACTOR as f64,
2417 ));
2418 }
2419 }
2420
2421 let result = if buffer_is_valid {
2422 let mut plugin = wrapper.plugin.lock();
2423 let mut aux = AuxiliaryBuffers {
2427 inputs: buffers.aux_inputs,
2428 outputs: buffers.aux_outputs,
2429 };
2430 let mut context = wrapper.make_process_context(transport);
2431 let result = plugin.process(buffers.main_buffer, &mut aux, &mut context);
2432 wrapper.last_process_status.store(result);
2433 result
2434 } else {
2435 ProcessStatus::Normal
2436 };
2437
2438 let clap_result = match result {
2439 ProcessStatus::Error(err) => {
2440 crate::nice_debug_assert_failure!("Process error: {}", err);
2441
2442 return CLAP_PROCESS_ERROR;
2443 }
2444 ProcessStatus::Normal => CLAP_PROCESS_CONTINUE_IF_NOT_QUIET,
2445 ProcessStatus::Tail(_) => CLAP_PROCESS_CONTINUE,
2446 ProcessStatus::KeepAlive => CLAP_PROCESS_CONTINUE,
2447 };
2448
2449 if !process.out_events.is_null() {
2452 unsafe {
2453 wrapper.handle_out_events(
2454 &*process.out_events,
2455 block_start,
2456 total_buffer_len,
2457 )
2458 };
2459 }
2460
2461 if block_end == total_buffer_len {
2465 break clap_result;
2466 } else {
2467 block_start = block_end;
2468 }
2469 };
2470
2471 let updated_state = permit_alloc(|| wrapper.updated_state_receiver.try_recv());
2478 if let Ok(mut state) = updated_state {
2479 wrapper.set_state_inner(&mut state);
2480
2481 if let Err(err) = wrapper.updated_state_sender.send(state) {
2484 crate::nice_debug_assert_failure!(
2485 "Failed to send state object back to GUI thread: {}",
2486 err
2487 );
2488 };
2489 }
2490
2491 result
2492 })
2493 }
2494
2495 unsafe extern "C" fn get_extension(
2496 plugin: *const clap_plugin,
2497 id: *const c_char,
2498 ) -> *const c_void {
2499 check_null_ptr!(
2500 std::ptr::null(),
2501 plugin,
2502 unsafe { (*plugin).plugin_data },
2503 id
2504 );
2505 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2506
2507 let id = unsafe { CStr::from_ptr(id) };
2508
2509 if id == CLAP_EXT_AUDIO_PORTS_CONFIG {
2510 &wrapper.clap_plugin_audio_ports_config as *const _ as *const c_void
2511 } else if id == CLAP_EXT_AUDIO_PORTS {
2512 &wrapper.clap_plugin_audio_ports as *const _ as *const c_void
2513 } else if id == CLAP_EXT_GUI && wrapper.editor.borrow().is_some() {
2514 &wrapper.clap_plugin_gui as *const _ as *const c_void
2516 } else if id == CLAP_EXT_LATENCY {
2517 &wrapper.clap_plugin_latency as *const _ as *const c_void
2518 } else if id == CLAP_EXT_NOTE_PORTS
2519 && (P::MIDI_INPUT >= MidiConfig::Basic || P::MIDI_OUTPUT >= MidiConfig::Basic)
2520 {
2521 &wrapper.clap_plugin_note_ports as *const _ as *const c_void
2522 } else if id == CLAP_EXT_PARAMS {
2523 &wrapper.clap_plugin_params as *const _ as *const c_void
2524 } else if id == CLAP_EXT_REMOTE_CONTROLS {
2525 &wrapper.clap_plugin_remote_controls as *const _ as *const c_void
2526 } else if id == CLAP_EXT_RENDER {
2527 &wrapper.clap_plugin_render as *const _ as *const c_void
2528 } else if id == CLAP_EXT_STATE {
2529 &wrapper.clap_plugin_state as *const _ as *const c_void
2530 } else if id == CLAP_EXT_TAIL {
2531 &wrapper.clap_plugin_tail as *const _ as *const c_void
2532 } else if id == CLAP_EXT_TRACK_INFO {
2533 &wrapper.clap_plugin_track_info as *const _ as *const c_void
2534 } else if id == CLAP_EXT_VOICE_INFO && P::CLAP_POLY_MODULATION_CONFIG.is_some() {
2535 &wrapper.clap_plugin_voice_info as *const _ as *const c_void
2536 } else {
2537 crate::nice_trace!("Host tried to query unknown extension {:?}", id);
2538 std::ptr::null()
2539 }
2540 }
2541
2542 unsafe extern "C" fn on_main_thread(plugin: *const clap_plugin) {
2543 check_null_ptr!((), plugin, unsafe { (*plugin).plugin_data });
2544 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2545
2546 while let Some(task) = wrapper.tasks.pop() {
2549 wrapper.execute(task, true);
2550 }
2551 }
2552
2553 unsafe extern "C" fn ext_audio_ports_config_count(plugin: *const clap_plugin) -> u32 {
2554 check_null_ptr!(0, plugin, unsafe { (*plugin).plugin_data });
2555
2556 P::AUDIO_IO_LAYOUTS.len() as u32
2557 }
2558
2559 unsafe extern "C" fn ext_audio_ports_config_get(
2560 plugin: *const clap_plugin,
2561 index: u32,
2562 config: *mut clap_audio_ports_config,
2563 ) -> bool {
2564 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, config);
2565
2566 match P::AUDIO_IO_LAYOUTS.get(index as usize) {
2569 Some(audio_io_layout) => {
2570 let name = audio_io_layout.name();
2571
2572 let main_input_channels = audio_io_layout.main_input_channels.map(NonZeroU32::get);
2573 let main_output_channels =
2574 audio_io_layout.main_output_channels.map(NonZeroU32::get);
2575 let input_port_type = match main_input_channels {
2576 Some(1) => CLAP_PORT_MONO.as_ptr(),
2577 Some(2) => CLAP_PORT_STEREO.as_ptr(),
2578 _ => std::ptr::null(),
2579 };
2580 let output_port_type = match main_output_channels {
2581 Some(1) => CLAP_PORT_MONO.as_ptr(),
2582 Some(2) => CLAP_PORT_STEREO.as_ptr(),
2583 _ => std::ptr::null(),
2584 };
2585
2586 unsafe { *config = std::mem::zeroed() };
2587
2588 let config = unsafe { &mut *config };
2589 config.id = index;
2590 strlcpy(&mut config.name, &name);
2591 config.input_port_count = (if main_input_channels.is_some() { 1 } else { 0 }
2592 + audio_io_layout.aux_input_ports.len())
2593 as u32;
2594 config.output_port_count = (if main_output_channels.is_some() { 1 } else { 0 }
2595 + audio_io_layout.aux_output_ports.len())
2596 as u32;
2597 config.has_main_input = main_input_channels.is_some();
2598 config.main_input_channel_count = main_input_channels.unwrap_or_default();
2599 config.main_input_port_type = input_port_type;
2600 config.has_main_output = main_output_channels.is_some();
2601 config.main_output_channel_count = main_output_channels.unwrap_or_default();
2602 config.main_output_port_type = output_port_type;
2603
2604 true
2605 }
2606 None => {
2607 crate::nice_debug_assert_failure!(
2608 "Host tried to query out of bounds audio port config {}",
2609 index
2610 );
2611
2612 false
2613 }
2614 }
2615 }
2616
2617 unsafe extern "C" fn ext_audio_ports_config_select(
2618 plugin: *const clap_plugin,
2619 config_id: clap_id,
2620 ) -> bool {
2621 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
2622 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2623
2624 match P::AUDIO_IO_LAYOUTS.get(config_id as usize) {
2626 Some(audio_io_layout) => {
2627 wrapper.current_audio_io_layout.store(*audio_io_layout);
2628
2629 true
2630 }
2631 None => {
2632 crate::nice_debug_assert_failure!(
2633 "Host tried to select out of bounds audio port config {}",
2634 config_id
2635 );
2636
2637 false
2638 }
2639 }
2640 }
2641
2642 unsafe extern "C" fn ext_audio_ports_count(plugin: *const clap_plugin, is_input: bool) -> u32 {
2643 check_null_ptr!(0, plugin, unsafe { (*plugin).plugin_data });
2644 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2645
2646 let audio_io_layout = wrapper.current_audio_io_layout.load();
2647 if is_input {
2648 let main_ports = if audio_io_layout.main_input_channels.is_some() {
2649 1
2650 } else {
2651 0
2652 };
2653 let aux_ports = audio_io_layout.aux_input_ports.len();
2654
2655 (main_ports + aux_ports) as u32
2656 } else {
2657 let main_ports = if audio_io_layout.main_output_channels.is_some() {
2658 1
2659 } else {
2660 0
2661 };
2662 let aux_ports = audio_io_layout.aux_output_ports.len();
2663
2664 (main_ports + aux_ports) as u32
2665 }
2666 }
2667
2668 unsafe extern "C" fn ext_audio_ports_get(
2669 plugin: *const clap_plugin,
2670 index: u32,
2671 is_input: bool,
2672 info: *mut clap_audio_port_info,
2673 ) -> bool {
2674 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, info);
2675 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2676
2677 let num_input_ports = unsafe { Self::ext_audio_ports_count(plugin, true) };
2678 let num_output_ports = unsafe { Self::ext_audio_ports_count(plugin, false) };
2679 if (is_input && index >= num_input_ports) || (!is_input && index >= num_output_ports) {
2680 crate::nice_debug_assert_failure!(
2681 "Host tried to query information for out of bounds audio port {} (input: {})",
2682 index,
2683 is_input
2684 );
2685
2686 return false;
2687 }
2688
2689 let current_audio_io_layout = wrapper.current_audio_io_layout.load();
2690 let has_main_input = current_audio_io_layout.main_input_channels.is_some();
2691 let has_main_output = current_audio_io_layout.main_output_channels.is_some();
2692
2693 let is_main_port =
2695 index == 0 && ((is_input && has_main_input) || (!is_input && has_main_output));
2696
2697 let stable_id = if is_input {
2700 index
2701 } else {
2702 index + num_input_ports
2703 };
2704 let pair_stable_id = match (is_input, is_main_port) {
2705 (true, true) if has_main_output => num_input_ports,
2708 (false, true) if has_main_input => 0,
2709 _ => CLAP_INVALID_ID,
2710 };
2711
2712 let channel_count = match (index, is_input) {
2713 (0, true) if has_main_input => {
2714 current_audio_io_layout.main_input_channels.unwrap().get()
2715 }
2716 (0, false) if has_main_output => {
2717 current_audio_io_layout.main_output_channels.unwrap().get()
2718 }
2719 (n, true) if has_main_input => {
2721 current_audio_io_layout.aux_input_ports[n as usize - 1].get()
2722 }
2723 (n, false) if has_main_output => {
2724 current_audio_io_layout.aux_output_ports[n as usize - 1].get()
2725 }
2726 (n, true) => current_audio_io_layout.aux_input_ports[n as usize].get(),
2727 (n, false) => current_audio_io_layout.aux_output_ports[n as usize].get(),
2728 };
2729
2730 let port_type = match channel_count {
2731 1 => CLAP_PORT_MONO.as_ptr(),
2732 2 => CLAP_PORT_STEREO.as_ptr(),
2733 _ => std::ptr::null(),
2734 };
2735
2736 unsafe { *info = std::mem::zeroed() };
2737
2738 let info = unsafe { &mut *info };
2739 info.id = stable_id;
2740 match (is_input, is_main_port) {
2741 (true, true) => strlcpy(&mut info.name, ¤t_audio_io_layout.main_input_name()),
2742 (false, true) => strlcpy(&mut info.name, ¤t_audio_io_layout.main_output_name()),
2743 (true, false) => {
2744 let aux_input_idx = if has_main_input { index - 1 } else { index } as usize;
2745 strlcpy(
2746 &mut info.name,
2747 ¤t_audio_io_layout
2748 .aux_input_name(aux_input_idx)
2749 .expect("Out of bounds auxiliary input port"),
2750 );
2751 }
2752 (false, false) => {
2753 let aux_output_idx = if has_main_output { index - 1 } else { index } as usize;
2754 strlcpy(
2755 &mut info.name,
2756 ¤t_audio_io_layout
2757 .aux_output_name(aux_output_idx)
2758 .expect("Out of bounds auxiliary output port"),
2759 );
2760 }
2761 };
2762 info.flags = if is_main_port {
2763 CLAP_AUDIO_PORT_IS_MAIN
2764 } else {
2765 0
2766 };
2767 info.channel_count = channel_count;
2768 info.port_type = port_type;
2769 info.in_place_pair = pair_stable_id;
2770
2771 true
2772 }
2773
2774 unsafe extern "C" fn ext_gui_is_api_supported(
2775 _plugin: *const clap_plugin,
2776 api: *const c_char,
2777 is_floating: bool,
2778 ) -> bool {
2779 if is_floating {
2781 return false;
2782 }
2783
2784 unsafe {
2785 #[cfg(all(target_family = "unix", not(target_os = "macos")))]
2786 if CStr::from_ptr(api) == CLAP_WINDOW_API_X11 {
2787 return true;
2788 }
2789 #[cfg(target_os = "macos")]
2790 if CStr::from_ptr(api) == CLAP_WINDOW_API_COCOA {
2791 return true;
2792 }
2793 #[cfg(target_os = "windows")]
2794 if CStr::from_ptr(api) == CLAP_WINDOW_API_WIN32 {
2795 return true;
2796 }
2797 }
2798
2799 false
2800 }
2801
2802 unsafe extern "C" fn ext_gui_get_preferred_api(
2803 _plugin: *const clap_plugin,
2804 api: *mut *const c_char,
2805 is_floating: *mut bool,
2806 ) -> bool {
2807 check_null_ptr!(false, api, is_floating);
2808
2809 unsafe {
2810 #[cfg(all(target_family = "unix", not(target_os = "macos")))]
2811 {
2812 *api = CLAP_WINDOW_API_X11.as_ptr();
2813 }
2814 #[cfg(target_os = "macos")]
2815 {
2816 *api = CLAP_WINDOW_API_COCOA.as_ptr();
2817 }
2818 #[cfg(target_os = "windows")]
2819 {
2820 *api = CLAP_WINDOW_API_WIN32.as_ptr();
2821 }
2822
2823 *is_floating = false;
2825 }
2826
2827 true
2828 }
2829
2830 unsafe extern "C" fn ext_gui_create(
2831 plugin: *const clap_plugin,
2832 api: *const c_char,
2833 is_floating: bool,
2834 ) -> bool {
2835 if unsafe { !Self::ext_gui_is_api_supported(plugin, api, is_floating) } {
2837 return false;
2838 }
2839
2840 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
2844 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2845
2846 let editor_handle = wrapper.editor_handle.lock();
2847 if editor_handle.is_none() {
2848 true
2849 } else {
2850 crate::nice_debug_assert_failure!(
2851 "Tried creating editor while the editor was already active"
2852 );
2853 false
2854 }
2855 }
2856
2857 unsafe extern "C" fn ext_gui_destroy(plugin: *const clap_plugin) {
2858 check_null_ptr!((), plugin, unsafe { (*plugin).plugin_data });
2859 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2860
2861 let mut editor_handle = wrapper.editor_handle.lock();
2862 if editor_handle.is_some() {
2863 *editor_handle = None;
2864 } else {
2865 crate::nice_debug_assert_failure!(
2866 "Tried destroying editor while the editor was not active"
2867 );
2868 }
2869 }
2870
2871 unsafe extern "C" fn ext_gui_set_scale(plugin: *const clap_plugin, scale: f64) -> bool {
2872 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
2873 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2874
2875 if cfg!(target_os = "macos") {
2877 crate::nice_debug_assert_failure!(
2878 "Ignoring host request to set explicit DPI scaling factor"
2879 );
2880 return false;
2881 }
2882
2883 if wrapper
2884 .editor
2885 .borrow()
2886 .as_ref()
2887 .unwrap()
2888 .lock()
2889 .set_scale_factor(scale)
2890 {
2891 wrapper
2892 .scale_factor
2893 .store(scale, std::sync::atomic::Ordering::Relaxed);
2894 true
2895 } else {
2896 false
2897 }
2898 }
2899
2900 unsafe extern "C" fn ext_gui_get_size(
2901 plugin: *const clap_plugin,
2902 width: *mut u32,
2903 height: *mut u32,
2904 ) -> bool {
2905 check_null_ptr!(
2906 false,
2907 plugin,
2908 unsafe { (*plugin).plugin_data },
2909 width,
2910 height
2911 );
2912 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2913
2914 let scale_factor = wrapper.scale_factor.load(Ordering::Relaxed);
2916 let size: PhysicalSize<u32> = wrapper
2917 .editor
2918 .borrow()
2919 .as_ref()
2920 .unwrap()
2921 .lock()
2922 .size()
2923 .to_physical(scale_factor);
2924
2925 unsafe {
2926 *width = size.width;
2927 *height = size.height;
2928 }
2929
2930 true
2931 }
2932
2933 unsafe extern "C" fn ext_gui_can_resize(plugin: *const clap_plugin) -> bool {
2934 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
2935 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2936
2937 match wrapper.editor.borrow().as_ref() {
2939 Some(editor) => editor.lock().resize_hint().can_resize,
2940 None => false,
2941 }
2942 }
2943
2944 unsafe extern "C" fn ext_gui_get_resize_hints(
2945 plugin: *const clap_plugin,
2946 hints: *mut clap_gui_resize_hints,
2947 ) -> bool {
2948 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, hints);
2949 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2950
2951 let hint = match wrapper.editor.borrow().as_ref() {
2952 Some(editor) => editor.lock().resize_hint(),
2953 None => return false,
2954 };
2955 if !hint.can_resize {
2956 return false;
2957 }
2958
2959 let hints = unsafe { &mut *hints };
2960 hints.can_resize_horizontally = hint.can_resize_horizontally;
2961 hints.can_resize_vertically = hint.can_resize_vertically;
2962 hints.preserve_aspect_ratio = hint.preserve_aspect_ratio;
2963 hints.aspect_ratio_width = hint.aspect_ratio_width;
2964 hints.aspect_ratio_height = hint.aspect_ratio_height;
2965
2966 true
2967 }
2968
2969 unsafe extern "C" fn ext_gui_adjust_size(
2970 _plugin: *const clap_plugin,
2971 _width: *mut u32,
2972 _height: *mut u32,
2973 ) -> bool {
2974 true
2978 }
2979
2980 unsafe extern "C" fn ext_gui_set_size(
2981 plugin: *const clap_plugin,
2982 width: u32,
2983 height: u32,
2984 ) -> bool {
2985 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
2989 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
2990
2991 match wrapper.editor.borrow().as_ref() {
2994 Some(editor) => editor.lock().set_size(PhysicalSize { width, height }),
2995 None => false,
2996 }
2997 }
2998
2999 unsafe extern "C" fn ext_gui_set_parent(
3000 plugin: *const clap_plugin,
3001 window: *const clap_window,
3002 ) -> bool {
3003 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, window);
3004 let wrapper = unsafe { Arc::from_raw((*plugin).plugin_data as *const Self) };
3006
3007 let window = unsafe { &*window };
3008
3009 let result = {
3010 let mut editor_handle = wrapper.editor_handle.lock();
3011 if editor_handle.is_none() {
3012 let api = unsafe { CStr::from_ptr(window.api) };
3013 let parent_handle = unsafe {
3014 if api == CLAP_WINDOW_API_X11 {
3015 #[allow(clippy::unnecessary_cast)]
3016 let w = window.specific.x11 as c_ulong;
3017 ParentWindowHandle::XlibWindow(w)
3018 } else if api == CLAP_WINDOW_API_COCOA {
3019 check_null_ptr!(false, window.specific.cocoa);
3020 let w = NonNull::new(window.specific.cocoa).unwrap();
3021 ParentWindowHandle::AppKitNsView(w)
3022 } else if api == CLAP_WINDOW_API_WIN32 {
3023 check_null_ptr!(false, window.specific.win32);
3024 let w = NonZeroIsize::new(window.specific.win32 as isize).unwrap();
3025 ParentWindowHandle::Win32Hwnd(w)
3026 } else {
3027 crate::nice_debug_assert_failure!("Host passed an invalid API");
3028 return false;
3029 }
3030 };
3031
3032 *editor_handle = Some(Fragile::new(
3034 wrapper
3035 .editor
3036 .borrow()
3037 .as_ref()
3038 .unwrap()
3039 .lock()
3040 .spawn(parent_handle, wrapper.clone().make_gui_context()),
3041 ));
3042
3043 true
3044 } else {
3045 crate::nice_debug_assert_failure!(
3046 "Host tried to attach editor while the editor is already attached"
3047 );
3048
3049 false
3050 }
3051 };
3052
3053 let _ = Arc::into_raw(wrapper);
3055
3056 result
3057 }
3058
3059 unsafe extern "C" fn ext_gui_set_transient(
3060 _plugin: *const clap_plugin,
3061 _window: *const clap_window,
3062 ) -> bool {
3063 false
3065 }
3066
3067 unsafe extern "C" fn ext_gui_suggest_title(_plugin: *const clap_plugin, _title: *const c_char) {
3068 }
3070
3071 unsafe extern "C" fn ext_gui_show(_plugin: *const clap_plugin) -> bool {
3072 false
3075 }
3076
3077 unsafe extern "C" fn ext_gui_hide(_plugin: *const clap_plugin) -> bool {
3078 false
3080 }
3081
3082 unsafe extern "C" fn ext_latency_get(plugin: *const clap_plugin) -> u32 {
3083 check_null_ptr!(0, plugin, unsafe { (*plugin).plugin_data });
3084 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3085
3086 wrapper.current_latency.load(Ordering::SeqCst)
3087 }
3088
3089 unsafe extern "C" fn ext_note_ports_count(_plugin: *const clap_plugin, is_input: bool) -> u32 {
3090 match is_input {
3091 true if P::MIDI_INPUT >= MidiConfig::Basic => 1,
3092 false if P::MIDI_OUTPUT >= MidiConfig::Basic => 1,
3093 _ => 0,
3094 }
3095 }
3096
3097 unsafe extern "C" fn ext_note_ports_get(
3098 _plugin: *const clap_plugin,
3099 index: u32,
3100 is_input: bool,
3101 info: *mut clap_note_port_info,
3102 ) -> bool {
3103 match (index, is_input) {
3104 (0, true) if P::MIDI_INPUT >= MidiConfig::Basic => {
3105 unsafe {
3106 *info = std::mem::zeroed();
3107 }
3108
3109 let info = unsafe { &mut *info };
3110 info.id = 0;
3111 info.supported_dialects = CLAP_NOTE_DIALECT_CLAP | CLAP_NOTE_DIALECT_MIDI;
3114 info.preferred_dialect = CLAP_NOTE_DIALECT_CLAP;
3115 strlcpy(&mut info.name, "Note Input");
3116
3117 true
3118 }
3119 (0, false) if P::MIDI_OUTPUT >= MidiConfig::Basic => {
3120 unsafe { *info = std::mem::zeroed() };
3121
3122 let info = unsafe { &mut *info };
3123 info.id = 0;
3124 info.supported_dialects = CLAP_NOTE_DIALECT_CLAP | CLAP_NOTE_DIALECT_MIDI;
3128 info.preferred_dialect = CLAP_NOTE_DIALECT_CLAP;
3129 strlcpy(&mut info.name, "Note Output");
3130
3131 true
3132 }
3133 _ => false,
3134 }
3135 }
3136
3137 unsafe extern "C" fn ext_params_count(plugin: *const clap_plugin) -> u32 {
3138 check_null_ptr!(0, plugin, unsafe { (*plugin).plugin_data });
3139 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3140
3141 wrapper.param_hashes.len() as u32
3142 }
3143
3144 unsafe extern "C" fn ext_params_get_info(
3145 plugin: *const clap_plugin,
3146 param_index: u32,
3147 param_info: *mut clap_param_info,
3148 ) -> bool {
3149 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, param_info);
3150 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3151
3152 if param_index > unsafe { Self::ext_params_count(plugin) } {
3153 return false;
3154 }
3155
3156 let param_hash = &wrapper.param_hashes[param_index as usize];
3157 let param_group = &wrapper.param_group_by_hash[param_hash];
3158 let param_ptr = &wrapper.param_by_hash[param_hash];
3159 let default_value = unsafe { param_ptr.default_normalized_value() };
3160 let step_count = unsafe { param_ptr.step_count() };
3161 let flags = unsafe { param_ptr.flags() };
3162 let automatable = !flags.contains(ParamFlags::NON_AUTOMATABLE);
3163 let hidden = flags.contains(ParamFlags::HIDDEN);
3164 let is_bypass = flags.contains(ParamFlags::BYPASS);
3165
3166 unsafe {
3167 *param_info = std::mem::zeroed();
3168 }
3169
3170 let param_info = unsafe { &mut *param_info };
3173 param_info.id = *param_hash;
3174 param_info.flags = 0;
3176 if automatable && !hidden {
3177 param_info.flags |= CLAP_PARAM_IS_AUTOMATABLE | CLAP_PARAM_IS_MODULATABLE;
3178 if wrapper.poly_mod_ids_by_hash.contains_key(param_hash) {
3179 param_info.flags |= CLAP_PARAM_IS_MODULATABLE_PER_NOTE_ID;
3180 }
3181 }
3182 if hidden {
3183 param_info.flags |= CLAP_PARAM_IS_HIDDEN | CLAP_PARAM_IS_READONLY;
3184 }
3185 if is_bypass {
3186 param_info.flags |= CLAP_PARAM_IS_BYPASS
3187 }
3188 if step_count.is_some() {
3189 param_info.flags |= CLAP_PARAM_IS_STEPPED
3190 }
3191 param_info.cookie = std::ptr::null_mut();
3192 strlcpy(&mut param_info.name, unsafe { param_ptr.name() });
3193 strlcpy(&mut param_info.module, param_group);
3194 param_info.min_value = 0.0;
3198 param_info.max_value = step_count.unwrap_or(1) as f64;
3202 param_info.default_value = default_value as f64 * step_count.unwrap_or(1) as f64;
3203
3204 true
3205 }
3206
3207 unsafe extern "C" fn ext_params_get_value(
3208 plugin: *const clap_plugin,
3209 param_id: clap_id,
3210 value: *mut f64,
3211 ) -> bool {
3212 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, value);
3213 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3214
3215 match wrapper.param_by_hash.get(¶m_id) {
3216 Some(param_ptr) => {
3217 unsafe {
3218 *value = param_ptr.modulated_normalized_value() as f64
3219 * param_ptr.step_count().unwrap_or(1) as f64;
3220 }
3221
3222 true
3223 }
3224 _ => false,
3225 }
3226 }
3227
3228 unsafe extern "C" fn ext_params_value_to_text(
3229 plugin: *const clap_plugin,
3230 param_id: clap_id,
3231 value: f64,
3232 display: *mut c_char,
3233 size: u32,
3234 ) -> bool {
3235 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, display);
3236 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3237
3238 let dest = unsafe { std::slice::from_raw_parts_mut(display, size as usize) };
3239
3240 match wrapper.param_by_hash.get(¶m_id) {
3241 Some(param_ptr) => {
3242 unsafe {
3243 strlcpy(
3244 dest,
3245 ¶m_ptr.normalized_value_to_string(
3247 value as f32 / param_ptr.step_count().unwrap_or(1) as f32,
3248 true,
3249 ),
3250 );
3251 }
3252
3253 true
3254 }
3255 _ => false,
3256 }
3257 }
3258
3259 unsafe extern "C" fn ext_params_text_to_value(
3260 plugin: *const clap_plugin,
3261 param_id: clap_id,
3262 display: *const c_char,
3263 value: *mut f64,
3264 ) -> bool {
3265 check_null_ptr!(
3266 false,
3267 plugin,
3268 unsafe { (*plugin).plugin_data },
3269 display,
3270 value
3271 );
3272 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3273
3274 let display = match unsafe { CStr::from_ptr(display).to_str() } {
3275 Ok(s) => s,
3276 Err(_) => return false,
3277 };
3278
3279 match wrapper.param_by_hash.get(¶m_id) {
3280 Some(param_ptr) => {
3281 let normalized_value =
3282 match unsafe { param_ptr.string_to_normalized_value(display) } {
3283 Some(v) => v as f64,
3284 None => return false,
3285 };
3286 unsafe {
3287 *value = normalized_value * param_ptr.step_count().unwrap_or(1) as f64;
3288 }
3289
3290 true
3291 }
3292 _ => false,
3293 }
3294 }
3295
3296 unsafe extern "C" fn ext_params_flush(
3297 plugin: *const clap_plugin,
3298 in_: *const clap_input_events,
3299 out: *const clap_output_events,
3300 ) {
3301 check_null_ptr!((), plugin, unsafe { (*plugin).plugin_data });
3302 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3303
3304 if !in_.is_null() {
3305 unsafe {
3306 wrapper.handle_in_events(&*in_, 0, 0);
3307 }
3308 }
3309
3310 if !out.is_null() {
3311 unsafe {
3312 wrapper.handle_out_events(&*out, 0, 0);
3313 }
3314 }
3315 }
3316
3317 unsafe extern "C" fn ext_remote_controls_count(plugin: *const clap_plugin) -> u32 {
3318 check_null_ptr!(0, plugin, unsafe { (*plugin).plugin_data });
3319 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3320
3321 wrapper.remote_control_pages.len() as u32
3322 }
3323
3324 unsafe extern "C" fn ext_remote_controls_get(
3325 plugin: *const clap_plugin,
3326 page_index: u32,
3327 page: *mut clap_remote_controls_page,
3328 ) -> bool {
3329 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, page);
3330 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3331
3332 crate::nice_debug_assert!(page_index as usize <= wrapper.remote_control_pages.len());
3333 match wrapper.remote_control_pages.get(page_index as usize) {
3334 Some(p) => {
3335 unsafe {
3336 *page = *p;
3337 }
3338 true
3339 }
3340 None => false,
3341 }
3342 }
3343
3344 unsafe extern "C" fn ext_render_has_hard_realtime_requirement(
3345 _plugin: *const clap_plugin,
3346 ) -> bool {
3347 P::HARD_REALTIME_ONLY
3348 }
3349
3350 unsafe extern "C" fn ext_render_set(
3351 plugin: *const clap_plugin,
3352 mode: clap_plugin_render_mode,
3353 ) -> bool {
3354 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data });
3355 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3356
3357 let mode = match mode {
3358 CLAP_RENDER_REALTIME => ProcessMode::Realtime,
3359 CLAP_RENDER_OFFLINE => ProcessMode::Offline,
3361 n => {
3362 crate::nice_debug_assert_failure!(
3363 "Unknown rendering mode '{}', defaulting to realtime",
3364 n
3365 );
3366 ProcessMode::Realtime
3367 }
3368 };
3369
3370 if wrapper.current_process_mode.swap(mode) != mode
3371 && wrapper.is_activated.load(Ordering::SeqCst)
3372 {
3373 unsafe_clap_call! { &*wrapper.host_callback=>request_restart(&*wrapper.host_callback) };
3376 }
3377
3378 true
3379 }
3380
3381 unsafe extern "C" fn ext_state_save(
3382 plugin: *const clap_plugin,
3383 stream: *const clap_ostream,
3384 ) -> bool {
3385 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, stream);
3386 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3387
3388 let serialized = unsafe {
3389 state::serialize_json::<P>(
3390 wrapper.params.clone(),
3391 state::make_params_iter(&wrapper.param_by_hash, &wrapper.param_id_to_hash),
3392 )
3393 };
3394 match serialized {
3395 Ok(serialized) => {
3396 let length_bytes = (serialized.len() as u64).to_le_bytes();
3399 if !write_stream(unsafe { &*stream }, &length_bytes) {
3400 crate::nice_debug_assert_failure!(
3401 "Error or end of stream while writing the state length to the stream."
3402 );
3403 return false;
3404 }
3405 if !write_stream(unsafe { &*stream }, &serialized) {
3406 crate::nice_debug_assert_failure!(
3407 "Error or end of stream while writing the state buffer to the stream."
3408 );
3409 return false;
3410 }
3411
3412 crate::nice_trace!("Saved state ({} bytes)", serialized.len());
3413
3414 true
3415 }
3416 Err(err) => {
3417 crate::nice_debug_assert_failure!("Could not save state: {:#}", err);
3418 false
3419 }
3420 }
3421 }
3422
3423 unsafe extern "C" fn ext_state_load(
3424 plugin: *const clap_plugin,
3425 stream: *const clap_istream,
3426 ) -> bool {
3427 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, stream);
3428 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3429
3430 let mut length_bytes = [0u8; 8];
3433 if !read_stream(unsafe { &*stream }, length_bytes.as_mut_slice()) {
3434 crate::nice_debug_assert_failure!(
3435 "Error or end of stream while reading the state length from the stream."
3436 );
3437 return false;
3438 }
3439 let length = u64::from_le_bytes(length_bytes);
3440
3441 let mut read_buffer: Vec<u8> = Vec::with_capacity(length as usize);
3442 if !read_stream(unsafe { &*stream }, read_buffer.spare_capacity_mut()) {
3443 crate::nice_debug_assert_failure!(
3444 "Error or end of stream while reading the state buffer from the stream."
3445 );
3446 return false;
3447 }
3448 unsafe {
3449 read_buffer.set_len(length as usize);
3450 }
3451
3452 match unsafe { state::deserialize_json(&read_buffer) } {
3453 Some(mut state) => {
3454 let success = wrapper.set_state_inner(&mut state);
3455 if success {
3456 crate::nice_trace!("Loaded state ({} bytes)", read_buffer.len());
3457 }
3458
3459 success
3460 }
3461 None => false,
3462 }
3463 }
3464
3465 unsafe extern "C" fn ext_track_info_changed(plugin: *const clap_plugin) {
3466 check_null_ptr!((), plugin, unsafe { (*plugin).plugin_data });
3467 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3468
3469 wrapper.update_track_info_from_host();
3470 }
3471
3472 unsafe extern "C" fn ext_tail_get(plugin: *const clap_plugin) -> u32 {
3473 check_null_ptr!(0, plugin, unsafe { (*plugin).plugin_data });
3474 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3475
3476 match wrapper.last_process_status.load() {
3477 ProcessStatus::Tail(samples) => samples,
3478 ProcessStatus::KeepAlive => u32::MAX,
3479 _ => 0,
3480 }
3481 }
3482
3483 unsafe extern "C" fn ext_voice_info_get(
3484 plugin: *const clap_plugin,
3485 info: *mut clap_voice_info,
3486 ) -> bool {
3487 check_null_ptr!(false, plugin, unsafe { (*plugin).plugin_data }, info);
3488 let wrapper = unsafe { &*((*plugin).plugin_data as *const Self) };
3489
3490 match P::CLAP_POLY_MODULATION_CONFIG {
3491 Some(config) => {
3492 unsafe {
3493 *info = clap_voice_info {
3494 voice_count: wrapper.current_voice_capacity.load(Ordering::Relaxed),
3495 voice_capacity: config.max_voice_capacity,
3496 flags: if config.supports_overlapping_voices {
3497 CLAP_VOICE_INFO_SUPPORTS_OVERLAPPING_NOTES
3498 } else {
3499 0
3500 },
3501 };
3502 }
3503
3504 true
3505 }
3506 None => false,
3507 }
3508 }
3509}
3510
3511unsafe fn query_host_extension<T>(
3517 host_callback: &ClapPtr<clap_host>,
3518 name: &CStr,
3519) -> Option<ClapPtr<T>> {
3520 let extension_ptr = unsafe {
3521 clap_call! { host_callback=>get_extension(&**host_callback, name.as_ptr()) }
3522 };
3523 if !extension_ptr.is_null() {
3524 unsafe { Some(ClapPtr::new(extension_ptr as *const T)) }
3525 } else {
3526 None
3527 }
3528}