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