Skip to main content

maolan_plugin_host/
host.rs

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