clap_clap/
process.rs

1//! CLAP Process interface.
2//!
3//! The facilities here are mostly const functions to access audio buffers
4//! and event lists in a safe way.
5use std::ptr::NonNull;
6
7use crate::{
8    audio_buffer::{AudioBuffer, AudioBufferMut},
9    events::{Header, InputEvents, OutputEvents, Transport},
10    ffi::{
11        CLAP_PROCESS_CONTINUE, CLAP_PROCESS_CONTINUE_IF_NOT_QUIET, CLAP_PROCESS_SLEEP,
12        CLAP_PROCESS_TAIL, clap_process, clap_process_status,
13    },
14};
15
16pub struct Process {
17    clap_process: NonNull<clap_process>,
18}
19
20impl Process {
21    /// # Safety
22    ///
23    /// 1. The pointer to clap_process must be obtained from CLAP host calling
24    ///    `clap_plugin.process()`.
25    /// 2. The Process lifetime must not exceed the duration of the call to
26    ///    `clap_plugin.process()`, as the pointer represents valid data only
27    ///    within that function scope.
28    /// 3. There must be only one Process that wraps around the given pointer.
29    /// 4. If 'clap_process.audio_input_count > 0', then
30    ///    'clap_process.audio_inputs' must be non-null.
31    /// 5. If 'clap_process.audio_outputs_count > 0', then
32    ///    'clap_process.audio_outputs' must be non-null.
33    /// 6. The pointers: `clap_process.in_events` and `clap_process.out_events`
34    ///    must be non-null.  These structures must be valid, in the sense that
35    ///    the function pointers that are their fields must be non-null (Some).
36    #[doc(hidden)]
37    pub const unsafe fn new_unchecked(clap_process: NonNull<clap_process>) -> Self {
38        #[cfg(debug_assertions)]
39        {
40            let clap_process = unsafe { clap_process.as_ref() };
41            assert!(clap_process.audio_inputs_count == 0 || !clap_process.audio_inputs.is_null());
42            assert!(clap_process.audio_outputs_count == 0 || !clap_process.audio_outputs.is_null());
43
44            assert!(!clap_process.in_events.is_null());
45            let in_events = unsafe { &*clap_process.in_events };
46            assert!(in_events.size.is_some() && in_events.get.is_some());
47
48            assert!(!clap_process.out_events.is_null());
49            let out_events = unsafe { &*clap_process.out_events };
50            assert!(out_events.try_push.is_some());
51        }
52
53        Self { clap_process }
54    }
55
56    const fn clap_process(&self) -> &clap_process {
57        // SAFETY: By the safety requirements of the constructor, we can obtain a shared
58        // reference to the underlying pointer.
59        unsafe { self.clap_process.as_ref() }
60    }
61
62    const fn clap_process_mut(&mut self) -> &mut clap_process {
63        // SAFETY: By the safety requirements of the constructor, we can obtain an
64        // exclusive reference to the underlying pointer.
65        unsafe { self.clap_process.as_mut() }
66    }
67
68    pub const fn steady_time(&self) -> i64 {
69        self.clap_process().steady_time
70    }
71
72    pub const fn frames_count(&self) -> u32 {
73        self.clap_process().frames_count
74    }
75
76    /// Transport info at sample 0.
77    ///
78    /// If None, then this is a free running host and no transport events will
79    /// be provided.
80    pub const fn transport(&self) -> Option<Transport<'_>> {
81        // SAFETY: The host ensures that transport, if non-null, points to a valid
82        // clap_event_transport.
83        let Some(transport) = (unsafe { self.clap_process().transport.as_ref() }) else {
84            return None;
85        };
86        // SAFETY: We just checked if transport is non-null. We know that
87        // clap_event_transport is constant and valid for the duration of self,
88        // so it's safe to create a shared reference to it for the lifetime of self.
89        let header = unsafe { Header::new_unchecked(&transport.header) };
90        // SAFETY: The host ensures that header is a header of a clap_event_transport.
91        Some(unsafe { Transport::new_unchecked(header) })
92    }
93
94    pub const fn audio_inputs_count(&self) -> u32 {
95        self.clap_process().audio_inputs_count
96    }
97
98    /// # Safety
99    ///
100    /// 1. The audio input number `n` must be less that
101    ///    `self.audio_inputs_count()`
102    /// 2. The audio input number `n` must fit into `usize` (cast).
103    const unsafe fn audio_inputs_unchecked(&self, n: u32) -> AudioBuffer<'_> {
104        debug_assert!(n < self.audio_inputs_count());
105        // SAFETY: `n` is less than `self.audio_inputs_count()`, so `clap_audio_buffer`
106        // is a valid pointer that belongs to `Process`.
107        let clap_audio_buffer = unsafe { self.clap_process().audio_inputs.add(n as usize) };
108        unsafe { AudioBuffer::new_unchecked(self, clap_audio_buffer) }
109    }
110
111    /// # Panic
112    ///
113    /// This function will panic if `n` is greater or equal
114    /// to `self.audio_input_counts()`.
115    pub const fn audio_inputs(&self, n: u32) -> AudioBuffer<'_> {
116        assert!(
117            n < self.audio_inputs_count(),
118            "audio input number must be less than the number of available input ports"
119        );
120
121        // SAFETY: we just checked if n is less then the limit.
122        unsafe { self.audio_inputs_unchecked(n) }
123    }
124
125    pub const fn audio_outputs_count(&self) -> u32 {
126        self.clap_process().audio_outputs_count
127    }
128
129    /// # Safety
130    ///
131    /// 1. The audio output number `n` must be less than
132    ///    self.audio_outputs_count()
133    /// 2. The audio output number `n` must fit into usize (cast).
134    const unsafe fn audio_outputs_unchecked(&mut self, n: u32) -> AudioBufferMut<'_> {
135        debug_assert!(n < self.audio_outputs_count());
136        // SAFETY: `n` is less that `self.audio_output_count()`, so `clap_audio_buffer`
137        // is a valid pointer that belongs to `Process`.
138        let clap_audio_buffer = unsafe { self.clap_process_mut().audio_outputs.add(n as usize) };
139        let clap_audio_buffer = unsafe { NonNull::new_unchecked(clap_audio_buffer) };
140        unsafe { AudioBufferMut::new_unchecked(self, clap_audio_buffer) }
141    }
142
143    /// # Panic
144    ///
145    /// This function will panic if `n` is larger or equal
146    /// `self.audio_output_counts()`.
147    pub const fn audio_outputs(&mut self, n: u32) -> AudioBufferMut<'_> {
148        assert!(
149            n < self.audio_outputs_count(),
150            "audio output number must be less than the number of available output ports"
151        );
152
153        // SAFETY: we just checked if n is less then the limit.
154        unsafe { self.audio_outputs_unchecked(n) }
155    }
156
157    pub const fn in_events(&self) -> InputEvents {
158        // SAFETY: By construction, the pointer is non-null.
159        let in_events = unsafe { &*self.clap_process().in_events };
160        // SAFETY: By construction, the pointers to `in_events` methods are Some.
161        unsafe { InputEvents::new_unchecked(in_events) }
162    }
163
164    pub fn out_events(&self) -> OutputEvents {
165        // SAFETY: By construction, the pointer is non-null.
166        let out_events = unsafe { &*self.clap_process().out_events };
167        // SAFETY: By construction, the pointer to `out_events` method is Some.
168        unsafe { OutputEvents::new_unchecked(out_events) }
169    }
170}
171
172#[derive(Debug, Copy, Clone)]
173pub enum Status {
174    Continue,
175    ContinueIfNotQuiet,
176    Tail,
177    Sleep,
178}
179
180impl From<Status> for clap_process_status {
181    fn from(value: Status) -> Self {
182        use Status::*;
183        match value {
184            Continue => CLAP_PROCESS_CONTINUE,
185            ContinueIfNotQuiet => CLAP_PROCESS_CONTINUE_IF_NOT_QUIET,
186            Tail => CLAP_PROCESS_TAIL,
187            Sleep => CLAP_PROCESS_SLEEP,
188        }
189    }
190}