1#[cfg(unix)]
2use crate::clap::{
3 CLAP_EXT_AUDIO_PORTS, ClapAudioBuffer, ClapEventHeader, ClapEventParamGesture,
4 ClapEventParamMod, ClapEventParamValue, ClapPluginParams, EventBuffer, PluginInstance,
5 host_timers,
6};
7#[cfg(unix)]
8use crate::clap::{
9 CLAP_EXT_PARAMS, CLAP_EXT_POSIX_FD_SUPPORT, CLAP_EXT_TIMER_SUPPORT, ClapProcess, EventCapture,
10 ThreadType, host_fds, set_thread_type,
11};
12use crate::events::EventPair;
13use crate::protocol::*;
14#[cfg(unix)]
15use crate::ringbuf::RingBuffer;
16use crate::shm::ShmMapping;
17#[cfg(unix)]
18use std::ptr;
19use std::sync::atomic::{AtomicBool, Ordering};
20use std::time::{Duration, Instant};
21
22static PARAMS_FLUSH_REQUESTED: AtomicBool = AtomicBool::new(false);
24
25pub fn request_params_flush() {
26 PARAMS_FLUSH_REQUESTED.store(true, Ordering::Release);
27}
28
29static AUDIO_PORTS_RESCAN_REQUESTED: AtomicBool = AtomicBool::new(false);
31
32pub fn request_audio_ports_rescan() {
33 AUDIO_PORTS_RESCAN_REQUESTED.store(true, Ordering::Release);
34}
35
36#[cfg(unix)]
38struct PortBuffers {
39 inputs: Vec<ClapAudioBuffer>,
40 outputs: Vec<ClapAudioBuffer>,
41 _input_ptrs: Vec<Vec<*mut f32>>,
42 _output_ptrs: Vec<Vec<*mut f32>>,
43}
44
45#[cfg(unix)]
46impl PortBuffers {
47 fn from_plugin(
50 plugin: *const crate::clap::ClapPlugin,
51 ptr: *mut u8,
52 num_in: usize,
53 num_out: usize,
54 ) -> Option<Self> {
55 let ext = unsafe {
56 (*plugin)
57 .get_extension
58 .map(|f| f(plugin, CLAP_EXT_AUDIO_PORTS.as_ptr()))
59 }?;
60 if ext.is_null() {
61 return None;
62 }
63 let ap = unsafe { &*(ext as *const crate::clap::ClapPluginAudioPorts) };
64 let in_count = ap.count.map(|f| unsafe { f(plugin, true) }).unwrap_or(0) as usize;
65 let out_count = ap.count.map(|f| unsafe { f(plugin, false) }).unwrap_or(0) as usize;
66
67 let mut inputs = Vec::with_capacity(in_count);
68 let mut input_ptrs = Vec::with_capacity(in_count);
69 let mut global_ch: usize = 0;
70 for i in 0..in_count {
71 let mut info = crate::clap::ClapAudioPortInfo {
72 id: 0,
73 name: [0; 256],
74 flags: 0,
75 channel_count: 1,
76 port_type: ptr::null(),
77 in_place_pair: 0,
78 };
79 let ch_count = if ap
80 .get
81 .map(|f| unsafe { f(plugin, i as u32, true, &mut info) })
82 .unwrap_or(false)
83 {
84 info.channel_count.max(1) as usize
85 } else {
86 1
87 };
88 let mut port_channels = Vec::with_capacity(ch_count);
89 for _ in 0..ch_count {
90 let shm_ptr = if global_ch < num_in {
91 unsafe { audio_channel_ptr(ptr, global_ch, 0) }
92 } else {
93 ptr::null_mut()
94 };
95 port_channels.push(shm_ptr);
96 global_ch += 1;
97 }
98 inputs.push(ClapAudioBuffer {
99 data32: port_channels.as_mut_ptr(),
100 data64: ptr::null_mut(),
101 channel_count: port_channels.len() as u32,
102 latency: 0,
103 constant_mask: 0,
104 });
105 input_ptrs.push(port_channels);
106 }
107
108 let mut outputs = Vec::with_capacity(out_count);
109 let mut output_ptrs = Vec::with_capacity(out_count);
110 global_ch = 0;
111 for i in 0..out_count {
112 let mut info = crate::clap::ClapAudioPortInfo {
113 id: 0,
114 name: [0; 256],
115 flags: 0,
116 channel_count: 1,
117 port_type: ptr::null(),
118 in_place_pair: 0,
119 };
120 let ch_count = if ap
121 .get
122 .map(|f| unsafe { f(plugin, i as u32, false, &mut info) })
123 .unwrap_or(false)
124 {
125 info.channel_count.max(1) as usize
126 } else {
127 1
128 };
129 let mut port_channels = Vec::with_capacity(ch_count);
130 for _ in 0..ch_count {
131 let shm_ptr = if global_ch < num_out {
132 unsafe { audio_channel_ptr(ptr, global_ch, 1) }
133 } else {
134 ptr::null_mut()
135 };
136 port_channels.push(shm_ptr);
137 global_ch += 1;
138 }
139 outputs.push(ClapAudioBuffer {
140 data32: port_channels.as_mut_ptr(),
141 data64: ptr::null_mut(),
142 channel_count: port_channels.len() as u32,
143 latency: 0,
144 constant_mask: 0,
145 });
146 output_ptrs.push(port_channels);
147 }
148
149 Some(Self {
150 inputs,
151 outputs,
152 _input_ptrs: input_ptrs,
153 _output_ptrs: output_ptrs,
154 })
155 }
156}
157
158pub struct HostRuntime {
160 pub mapping: ShmMapping,
161 pub events: EventPair,
162 pub format: String,
163 pub plugin_path: String,
164 pub instance_id: String,
165}
166
167impl HostRuntime {
168 pub fn attach(
170 shm_name: &str,
171 events: EventPair,
172 format: String,
173 plugin_path: String,
174 instance_id: String,
175 ) -> Result<Self, String> {
176 let mapping = ShmMapping::open_existing(shm_name, SHM_SIZE)?;
177 Ok(Self {
178 mapping,
179 events,
180 format,
181 plugin_path,
182 instance_id,
183 })
184 }
185
186 #[cfg(unix)]
189 fn plugin_id(&self) -> &str {
190 if let Some(pos) = self.plugin_path.rfind("::") {
191 &self.plugin_path[pos + 2..]
192 } else if let Some(pos) = self.plugin_path.rfind('#') {
193 &self.plugin_path[pos + 1..]
194 } else {
195 ""
196 }
197 }
198
199 #[cfg(unix)]
201 fn real_plugin_path(&self) -> &str {
202 if let Some(pos) = self.plugin_path.rfind("::") {
203 &self.plugin_path[..pos]
204 } else if let Some(pos) = self.plugin_path.rfind('#') {
205 &self.plugin_path[..pos]
206 } else {
207 &self.plugin_path
208 }
209 }
210
211 pub fn signal_ready(&self) {
213 let header = unsafe { header_mut(self.mapping.as_ptr()) };
214 header.ready.store(1, Ordering::Release);
215 tracing::info!(instance_id = %self.instance_id, "Plugin host ready");
216 }
217
218 pub fn write_test_magic(&self) {
220 let scratch = unsafe { scratch_ptr(self.mapping.as_ptr()) };
221 let magic: u32 = 0xDEADBEEF;
222 unsafe {
223 std::ptr::write_unaligned(scratch as *mut u32, magic);
224 }
225 }
226
227 pub fn run_until_shutdown(&self) {
230 let header = unsafe { header_ref(self.mapping.as_ptr()) };
231 let start = Instant::now();
232 loop {
233 if header.shutdown_request.load(Ordering::Acquire) != 0 {
234 tracing::info!(instance_id = %self.instance_id, "Shutdown requested");
235 break;
236 }
237 if start.elapsed() >= Duration::from_millis(100) {
238 header.heartbeat.fetch_add(1, Ordering::Relaxed);
239 }
240 match self.events.wait_daw(Duration::from_millis(100)) {
241 Ok(()) => continue,
242 Err(e) if e.kind() == std::io::ErrorKind::TimedOut => continue,
243 Err(e) => {
244 tracing::error!("Event pipe error: {e}");
245 break;
246 }
247 }
248 }
249 }
250
251 pub fn run_null_plugin(&self) {
254 let header = unsafe { header_ref(self.mapping.as_ptr()) };
255 let ptr = self.mapping.as_ptr();
256 tracing::info!(instance_id = %self.instance_id, "Null plugin running");
257
258 loop {
259 if header.shutdown_request.load(Ordering::Acquire) != 0 {
260 tracing::info!(instance_id = %self.instance_id, "Null plugin shutdown requested");
261 break;
262 }
263
264 match self.events.wait_daw(Duration::from_millis(100)) {
266 Ok(()) => {}
267 Err(e) if e.kind() == std::io::ErrorKind::TimedOut => continue,
268 Err(e) => {
269 tracing::error!("Null plugin event error: {e}");
270 break;
271 }
272 }
273
274 let block_size = header.block_size.load(Ordering::Acquire) as usize;
275 let num_in = header.num_input_channels.load(Ordering::Acquire) as usize;
276 let num_out = header.num_output_channels.load(Ordering::Acquire) as usize;
277
278 if block_size == 0 || block_size > MAX_BLOCK_SIZE {
279 tracing::warn!("Invalid block size {block_size}, skipping");
280 let _ = self.events.signal_daw();
281 continue;
282 }
283
284 let max_ch = num_in.min(num_out).min(MAX_CHANNELS);
287 for ch in 0..max_ch {
288 let in_ptr = unsafe { audio_channel_ptr(ptr, ch, 0) };
289 let out_ptr = unsafe { audio_channel_ptr(ptr, ch, 1) };
290 unsafe {
291 std::ptr::copy_nonoverlapping(in_ptr, out_ptr, block_size);
292 }
293 }
294
295 if let Err(e) = self.events.signal_daw() {
297 tracing::error!("Failed to signal DAW: {e}");
298 break;
299 }
300 }
301 }
302
303 #[cfg(unix)]
305 pub fn run_clap_plugin(&self) {
306 let mut plugin = match PluginInstance::new(self.real_plugin_path(), self.plugin_id()) {
307 Ok(p) => p,
308 Err(e) => {
309 tracing::error!(
310 "Failed to load CLAP plugin '{}': {e}",
311 self.real_plugin_path()
312 );
313 return;
314 }
315 };
316
317 tracing::info!(
318 instance_id = %self.instance_id,
319 name = %plugin.name(),
320 "CLAP plugin loaded"
321 );
322
323 let ptr = self.mapping.as_ptr();
324 let header = unsafe { header_ref(self.mapping.as_ptr()) };
325
326 unsafe {
327 maolan_plugin_protocol::protocol::write_plugin_name_to_scratch(ptr, &plugin.name());
328 }
329
330 let sample_rate = unsafe {
332 let ts = transport_ref(ptr);
333 if ts.sample_rate_hz > 0.0 {
334 ts.sample_rate_hz
335 } else {
336 48000.0
337 }
338 };
339
340 if let Err(e) = plugin.activate(sample_rate, 1, MAX_BLOCK_SIZE as u32) {
341 tracing::error!("Failed to activate plugin: {e}");
342 return;
343 }
344
345 let mut port_buffers = PortBuffers::from_plugin(plugin.plugin_ptr(), ptr, 0, 0);
347
348 let has_note_ports = unsafe {
349 (*plugin.plugin_ptr())
350 .get_extension
351 .map(|f| {
352 f(
353 plugin.plugin_ptr(),
354 crate::clap::CLAP_EXT_NOTE_PORTS.as_ptr(),
355 )
356 })
357 .filter(|p| !p.is_null())
358 .is_some()
359 };
360 let param_ring = unsafe {
363 let buf = param_ring_ptr(ptr);
364 let (w, r) = param_indices(ptr);
365 RingBuffer::new(buf, w, r, RING_CAPACITY)
366 };
367
368 let echo_ring = unsafe {
369 let buf = echo_ring_ptr(ptr);
370 let (w, r) = echo_indices(ptr);
371 RingBuffer::new(buf, w, r, RING_CAPACITY)
372 };
373
374 let midi_ring = unsafe {
375 let buf = midi_ring_ptr(ptr);
376 let (w, r) = midi_indices(ptr);
377 RingBuffer::new(buf, w, r, RING_CAPACITY)
378 };
379 let params_ext = unsafe {
382 (*plugin.plugin_ptr())
383 .get_extension
384 .map(|f| f(plugin.plugin_ptr(), CLAP_EXT_PARAMS.as_ptr()))
385 .filter(|p| !p.is_null())
386 .map(|p| p as *const ClapPluginParams)
387 };
388
389 let timer_ext = unsafe {
390 (*plugin.plugin_ptr())
391 .get_extension
392 .map(|f| f(plugin.plugin_ptr(), CLAP_EXT_TIMER_SUPPORT.as_ptr()))
393 .filter(|p| !p.is_null())
394 .map(|p| p as *const crate::clap::ClapPluginTimerSupport)
395 };
396
397 let fd_ext = unsafe {
398 (*plugin.plugin_ptr())
399 .get_extension
400 .map(|f| f(plugin.plugin_ptr(), CLAP_EXT_POSIX_FD_SUPPORT.as_ptr()))
401 .filter(|p| !p.is_null())
402 .map(|p| p as *const crate::clap::ClapPluginPosixFdSupport)
403 };
404 let mut steady_time: i64 = 0;
405 let daw_read_fd = self.events.host_read_fd();
406 let mut started_processing = false;
407
408 loop {
409 if header.shutdown_request.load(Ordering::Acquire) != 0 {
410 tracing::info!(instance_id = %self.instance_id, "CLAP plugin shutdown requested");
411 break;
412 }
413
414 let req = header.request_type.load(Ordering::Acquire);
416 if req != 0 {
417 let result = match req {
418 3 => {
419 tracing::info!(instance_id = %self.instance_id, "GUI show requested");
420 if !plugin.gui_is_supported() {
421 Err("Plugin does not support GUI".to_string())
422 } else {
423 let window_id = header.parent_window_usize() as u64;
424 let is_floating = window_id == 0;
425 if plugin.gui_created() {
427 plugin.gui_destroy();
428 }
429 let create_result = plugin.gui_create("x11", is_floating);
430 create_result
431 .and_then(|_| {
432 if window_id != 0 {
433 plugin.gui_set_parent(window_id)
434 } else {
435 Ok(())
436 }
437 })
438 .and_then(|_| plugin.gui_show())
439 }
440 }
441 4 => {
442 tracing::info!(instance_id = %self.instance_id, "GUI hide requested");
443 plugin.gui_hide()
444 }
445 _ => Err(format!("Unknown request type: {req}")),
446 };
447 header
448 .request_status
449 .store(if result.is_ok() { 1 } else { 2 }, Ordering::Release);
450 if req == 1 || req == 2 {
451 let _ = self.events.signal_daw();
452 }
453 header.request_type.store(0, Ordering::Release);
454 continue;
455 }
456
457 set_thread_type(ThreadType::MainThread);
459 self.handle_idle_work(&plugin, params_ext, timer_ext);
460
461 let timeout_ms = self.next_timer_ms().min(100);
463 let (daw_ready, ready_fds) = match timeout_ms {
464 0 => (true, Vec::new()), ms => self.poll_daw_and_fds(daw_read_fd, Duration::from_millis(ms)),
466 };
467
468 if let Some(ext) = fd_ext {
470 for (fd, flags) in ready_fds {
471 unsafe {
472 if let Some(cb) = (*ext).on_fd {
473 cb(plugin.plugin_ptr(), fd, flags);
474 }
475 }
476 }
477 }
478
479 if !daw_ready {
480 continue;
482 }
483
484 let block_size = header.block_size.load(Ordering::Acquire) as usize;
485 let num_in = header.num_input_channels.load(Ordering::Acquire) as usize;
486 let num_out = header.num_output_channels.load(Ordering::Acquire) as usize;
487
488 if block_size == 0 || block_size > MAX_BLOCK_SIZE {
489 tracing::warn!("Invalid block size {block_size}, skipping");
490 let _ = self.events.signal_daw();
491 continue;
492 }
493
494 if AUDIO_PORTS_RESCAN_REQUESTED.swap(false, Ordering::Acquire) {
496 tracing::info!(instance_id = %self.instance_id, "Rebuilding audio port buffers after rescan");
497 port_buffers = PortBuffers::from_plugin(plugin.plugin_ptr(), ptr, num_in, num_out);
498 }
499
500 if let Some(ref mut pb) = port_buffers {
502 let mut global_ch: usize = 0;
503 for port in &mut pb._input_ptrs {
504 for ch in port.iter_mut() {
505 *ch = if global_ch < num_in {
506 unsafe { audio_channel_ptr(ptr, global_ch, 0) }
507 } else {
508 ptr::null_mut()
509 };
510 global_ch += 1;
511 }
512 }
513 global_ch = 0;
514 for port in &mut pb._output_ptrs {
515 for ch in port.iter_mut() {
516 *ch = if global_ch < num_out {
517 unsafe { audio_channel_ptr(ptr, global_ch, 1) }
518 } else {
519 ptr::null_mut()
520 };
521 global_ch += 1;
522 }
523 }
524 } else {
525 let mut in_ptrs: [*mut f32; MAX_CHANNELS] = [ptr::null_mut(); MAX_CHANNELS];
527 let mut out_ptrs: [*mut f32; MAX_CHANNELS] = [ptr::null_mut(); MAX_CHANNELS];
528 for (ch, in_ptr) in in_ptrs
529 .iter_mut()
530 .enumerate()
531 .take(num_in.min(MAX_CHANNELS))
532 {
533 *in_ptr = unsafe { audio_channel_ptr(ptr, ch, 0) };
534 }
535 for (ch, out_ptr) in out_ptrs
536 .iter_mut()
537 .enumerate()
538 .take(num_out.min(MAX_CHANNELS))
539 {
540 *out_ptr = unsafe { audio_channel_ptr(ptr, ch, 1) };
541 }
542 }
543
544 let mut event_buf = EventBuffer::new();
546 while let Some(ev) = param_ring.pop() {
547 match ev.event_kind {
548 PARAM_EVENT_MOD => {
549 event_buf.push_param_mod(ev.param_index, ev.value as f64, ev.sample_offset);
550 }
551 PARAM_EVENT_GESTURE_BEGIN => {
552 event_buf.push_param_gesture_begin(ev.param_index, ev.sample_offset);
553 }
554 PARAM_EVENT_GESTURE_END => {
555 event_buf.push_param_gesture_end(ev.param_index, ev.sample_offset);
556 }
557 _ => {
558 event_buf.push_param_value(
559 ev.param_index,
560 ev.value as f64,
561 ev.sample_offset,
562 );
563 }
564 }
565 }
566 while let Some(ev) = midi_ring.pop() {
567 if has_note_ports {
568 self.push_midi_as_clap_events(
569 &mut event_buf,
570 ev.data,
571 ev.channel as u16,
572 ev.sample_offset,
573 );
574 } else {
575 event_buf.push_midi(ev.data, ev.channel as u16, ev.sample_offset);
576 }
577 }
578 let in_events = event_buf.as_input_events();
579
580 let mut event_capture = EventCapture::new();
582 let out_events = event_capture.as_output_events();
583
584 if PARAMS_FLUSH_REQUESTED.swap(false, Ordering::Acquire)
586 && let Some(params_ptr) = params_ext
587 {
588 unsafe {
589 let flush = (*params_ptr).flush;
590 if let Some(f) = flush {
591 let empty_in = crate::clap::empty_input_events();
592 let mut flush_capture = EventCapture::new();
593 let flush_out = flush_capture.as_output_events();
594 f(plugin.plugin_ptr(), &empty_in, &flush_out);
595 for bytes in flush_capture.drain() {
597 if bytes.len() >= std::mem::size_of::<ClapEventHeader>() {
598 let h = &*(bytes.as_ptr() as *const ClapEventHeader);
599 self.echo_event_to_daw(h, &bytes, &echo_ring);
600 }
601 }
602 }
603 }
604 }
605
606 let transport =
607 unsafe { transport_ref(ptr) as *const TransportState as *const std::ffi::c_void };
608
609 tracing::debug!(
610 instance_id = %self.instance_id,
611 block_size,
612 num_in,
613 num_out,
614 events_in = event_buf.len(),
615 "Processing block"
616 );
617
618 if !started_processing {
619 set_thread_type(ThreadType::AudioThread);
620 if let Err(e) = plugin.start_processing() {
621 tracing::error!("Failed to start processing: {e}");
622 break;
623 }
624 started_processing = true;
625 }
626
627 set_thread_type(ThreadType::AudioThread);
628
629 let process_result = if let Some(ref mut pb) = port_buffers {
630 let process = ClapProcess {
631 steady_time,
632 frames_count: block_size as u32,
633 transport,
634 audio_inputs: pb.inputs.as_ptr(),
635 audio_outputs: pb.outputs.as_mut_ptr(),
636 audio_inputs_count: pb.inputs.len() as u32,
637 audio_outputs_count: pb.outputs.len() as u32,
638 in_events: &in_events,
639 out_events: &out_events,
640 };
641 plugin.process(&process)
642 } else {
643 let mut fallback_in_ptrs: Vec<*mut f32> = Vec::new();
644 let mut fallback_out_ptrs: Vec<*mut f32> = Vec::new();
645 fallback_in_ptrs.resize(num_in.min(MAX_CHANNELS), ptr::null_mut());
646 fallback_out_ptrs.resize(num_out.min(MAX_CHANNELS), ptr::null_mut());
647 for (ch, in_ptr) in fallback_in_ptrs.iter_mut().enumerate() {
648 *in_ptr = unsafe { audio_channel_ptr(ptr, ch, 0) };
649 }
650 for (ch, out_ptr) in fallback_out_ptrs.iter_mut().enumerate() {
651 *out_ptr = unsafe { audio_channel_ptr(ptr, ch, 1) };
652 }
653 let fallback_audio_in = ClapAudioBuffer {
654 data32: fallback_in_ptrs.as_mut_ptr(),
655 data64: ptr::null_mut(),
656 channel_count: num_in as u32,
657 latency: 0,
658 constant_mask: 0,
659 };
660 let mut fallback_audio_out = ClapAudioBuffer {
661 data32: fallback_out_ptrs.as_mut_ptr(),
662 data64: ptr::null_mut(),
663 channel_count: num_out as u32,
664 latency: 0,
665 constant_mask: 0,
666 };
667 let process = ClapProcess {
668 steady_time,
669 frames_count: block_size as u32,
670 transport,
671 audio_inputs: &fallback_audio_in,
672 audio_outputs: &mut fallback_audio_out,
673 audio_inputs_count: 1,
674 audio_outputs_count: 1,
675 in_events: &in_events,
676 out_events: &out_events,
677 };
678 plugin.process(&process)
679 };
680
681 set_thread_type(ThreadType::MainThread);
682
683 if let Err(e) = process_result {
684 tracing::error!("Plugin process error: {e}");
685 break;
686 }
687
688 steady_time += block_size as i64;
689
690 for bytes in event_capture.drain() {
692 if bytes.len() >= std::mem::size_of::<ClapEventHeader>() {
693 let h = unsafe { &*(bytes.as_ptr() as *const ClapEventHeader) };
694 self.echo_event_to_daw(h, &bytes, &echo_ring);
695 }
696 }
697
698 tracing::debug!(instance_id = %self.instance_id, "Block processed, signalling DAW");
699
700 if let Err(e) = self.events.signal_daw() {
701 tracing::error!("Failed to signal DAW: {e}");
702 break;
703 }
704 }
705
706 if started_processing {
707 set_thread_type(ThreadType::AudioThread);
708 plugin.stop_processing();
709 set_thread_type(ThreadType::MainThread);
710 }
711 plugin.deactivate();
712 tracing::info!(instance_id = %self.instance_id, "CLAP plugin stopped");
713 }
714
715 pub fn shutdown(self) {
717 tracing::info!(instance_id = %self.instance_id, "Plugin host shutting down");
718 }
719
720 #[cfg(unix)]
726 fn push_midi_as_clap_events(
727 &self,
728 event_buf: &mut EventBuffer,
729 data: [u8; 3],
730 port_index: u16,
731 sample_offset: u32,
732 ) {
733 let status = data[0] & 0xF0;
734 let channel = (data[0] & 0x0F) as i16;
735 let note_id = -1i32;
736 match status {
737 0x90 => {
738 let velocity = data[2] as f64 / 127.0;
739 if velocity > 0.0 {
740 event_buf.push_note_on(
741 note_id,
742 port_index as i16,
743 channel,
744 data[1] as i16,
745 velocity,
746 sample_offset,
747 );
748 } else {
749 event_buf.push_note_off(
750 note_id,
751 port_index as i16,
752 channel,
753 data[1] as i16,
754 0.0,
755 sample_offset,
756 );
757 }
758 }
759 0x80 => {
760 let velocity = data[2] as f64 / 127.0;
761 event_buf.push_note_off(
762 note_id,
763 port_index as i16,
764 channel,
765 data[1] as i16,
766 velocity,
767 sample_offset,
768 );
769 }
770 _ => {}
771 }
772 event_buf.push_midi(data, port_index, sample_offset);
774 }
775
776 #[cfg(unix)]
778 fn echo_event_to_daw(
779 &self,
780 header: &ClapEventHeader,
781 bytes: &[u8],
782 echo_ring: &RingBuffer<ParameterEvent>,
783 ) {
784 match header.type_ {
785 crate::clap::CLAP_EVENT_PARAM_VALUE
786 if bytes.len() >= std::mem::size_of::<ClapEventParamValue>() =>
787 {
788 let ev = unsafe { &*(bytes.as_ptr() as *const ClapEventParamValue) };
789 let echo = ParameterEvent {
790 param_index: ev.param_id,
791 value: ev.value as f32,
792 sample_offset: ev.header.time,
793 event_kind: PARAM_EVENT_VALUE,
794 };
795 if !echo_ring.push(echo) {
796 tracing::warn!("Echo ring full, dropping parameter value event");
797 }
798 }
799 crate::clap::CLAP_EVENT_PARAM_MOD
800 if bytes.len() >= std::mem::size_of::<ClapEventParamMod>() =>
801 {
802 let ev = unsafe { &*(bytes.as_ptr() as *const ClapEventParamMod) };
803 let echo = ParameterEvent {
804 param_index: ev.param_id,
805 value: ev.amount as f32,
806 sample_offset: ev.header.time,
807 event_kind: PARAM_EVENT_MOD,
808 };
809 if !echo_ring.push(echo) {
810 tracing::warn!("Echo ring full, dropping parameter mod event");
811 }
812 }
813 crate::clap::CLAP_EVENT_PARAM_GESTURE_BEGIN
814 if bytes.len() >= std::mem::size_of::<ClapEventParamGesture>() =>
815 {
816 let ev = unsafe { &*(bytes.as_ptr() as *const ClapEventParamGesture) };
817 let echo = ParameterEvent {
818 param_index: ev.param_id,
819 value: 0.0,
820 sample_offset: ev.header.time,
821 event_kind: PARAM_EVENT_GESTURE_BEGIN,
822 };
823 if !echo_ring.push(echo) {
824 tracing::warn!("Echo ring full, dropping gesture begin event");
825 }
826 }
827 crate::clap::CLAP_EVENT_PARAM_GESTURE_END
828 if bytes.len() >= std::mem::size_of::<ClapEventParamGesture>() =>
829 {
830 let ev = unsafe { &*(bytes.as_ptr() as *const ClapEventParamGesture) };
831 let echo = ParameterEvent {
832 param_index: ev.param_id,
833 value: 0.0,
834 sample_offset: ev.header.time,
835 event_kind: PARAM_EVENT_GESTURE_END,
836 };
837 if !echo_ring.push(echo) {
838 tracing::warn!("Echo ring full, dropping gesture end event");
839 }
840 }
841 _ => {
842 }
844 }
845 }
846
847 #[cfg(unix)]
850 fn poll_daw_and_fds(&self, daw_fd: i32, timeout: Duration) -> (bool, Vec<(i32, u32)>) {
851 let fds = host_fds().lock().unwrap();
852 if fds.is_empty() {
853 return (self.events.wait_daw(timeout).is_ok(), Vec::new());
854 }
855 let mut poll_fds: Vec<libc::pollfd> = Vec::with_capacity(fds.len() + 1);
856 poll_fds.push(libc::pollfd {
857 fd: daw_fd,
858 events: libc::POLLIN,
859 revents: 0,
860 });
861 for f in fds.iter() {
862 let mut events = 0;
863 if f.flags & 1 != 0 {
864 events |= libc::POLLIN;
865 }
866 if f.flags & 2 != 0 {
867 events |= libc::POLLOUT;
868 }
869 if f.flags & 4 != 0 {
870 events |= libc::POLLERR;
871 }
872 poll_fds.push(libc::pollfd {
873 fd: f.fd,
874 events,
875 revents: 0,
876 });
877 }
878 let ms = timeout.as_millis().clamp(0, i32::MAX as u128) as i32;
879 let rc = unsafe { libc::poll(poll_fds.as_mut_ptr(), poll_fds.len() as libc::nfds_t, ms) };
880 if rc < 0 {
881 return (false, Vec::new());
882 }
883 let mut ready_fds = Vec::new();
884 for (i, f) in fds.iter().enumerate() {
885 let pfd = &poll_fds[i + 1];
886 if pfd.revents != 0 {
887 let mut flags = 0;
888 if pfd.revents & libc::POLLIN != 0 {
889 flags |= 1;
890 }
891 if pfd.revents & libc::POLLOUT != 0 {
892 flags |= 2;
893 }
894 if pfd.revents & libc::POLLERR != 0 {
895 flags |= 4;
896 }
897 tracing::debug!(fd = f.fd, flags, "FD event");
898 ready_fds.push((f.fd, flags));
899 }
900 }
901 (poll_fds[0].revents & libc::POLLIN != 0, ready_fds)
902 }
903
904 #[cfg(unix)]
906 fn next_timer_ms(&self) -> u64 {
907 let timers = host_timers().lock().unwrap();
908 let now = Instant::now();
909 timers
910 .iter()
911 .map(|t| {
912 if t.deadline <= now {
913 0
914 } else {
915 (t.deadline - now).as_millis() as u64
916 }
917 })
918 .min()
919 .unwrap_or(100)
920 }
921
922 #[cfg(unix)]
924 fn handle_idle_work(
925 &self,
926 plugin: &PluginInstance,
927 _params_ext: Option<*const ClapPluginParams>,
928 timer_ext: Option<*const crate::clap::ClapPluginTimerSupport>,
929 ) {
930 let now = Instant::now();
931 let mut fired_timers = Vec::new();
932 {
933 let mut timers = host_timers().lock().unwrap();
934 for t in timers.iter_mut() {
935 if t.deadline <= now {
936 fired_timers.push(t.id);
937 t.deadline = now + Duration::from_millis(t.period_ms as u64);
938 }
939 }
940 }
941 if let Some(ext) = timer_ext {
942 for id in fired_timers {
943 unsafe {
944 if let Some(f) = (*ext).on_timer {
945 f(plugin.plugin_ptr(), id);
946 }
947 }
948 }
949 }
950 }
951}