clap_clap/ext/
note_ports.rs

1use std::fmt::{Display, Formatter};
2
3use crate::{
4    ffi::{
5        CLAP_NOTE_DIALECT_CLAP, CLAP_NOTE_DIALECT_MIDI, CLAP_NOTE_DIALECT_MIDI_MPE,
6        CLAP_NOTE_DIALECT_MIDI2, CLAP_NOTE_PORTS_RESCAN_ALL, CLAP_NOTE_PORTS_RESCAN_NAMES,
7        clap_host_note_ports, clap_note_port_info,
8    },
9    host::Host,
10    id::ClapId,
11    impl_flags_u32,
12    plugin::Plugin,
13};
14
15pub trait NotePorts<P>
16where
17    P: Plugin,
18{
19    fn count(plugin: &P, is_input: bool) -> u32;
20    fn get(plugin: &P, index: u32, is_input: bool) -> Option<NotePortInfo>;
21}
22
23impl<P: Plugin> NotePorts<P> for () {
24    fn count(_: &P, _: bool) -> u32 {
25        0
26    }
27
28    fn get(_: &P, _: u32, _: bool) -> Option<NotePortInfo> {
29        None
30    }
31}
32
33#[derive(Debug, Copy, Clone, PartialEq, Eq)]
34#[repr(u32)]
35pub enum NoteDialect {
36    /// Uses clap_event_note and clap_event_note_expression.
37    Clap = CLAP_NOTE_DIALECT_CLAP,
38    /// Uses clap_event_midi, no polyphonic expression
39    Midi = CLAP_NOTE_DIALECT_MIDI,
40    /// Uses clap_event_midi, with polyphonic expression (MPE)
41    MidiMPE = CLAP_NOTE_DIALECT_MIDI_MPE,
42    /// Uses clap_event_midi2
43    Midi2 = CLAP_NOTE_DIALECT_MIDI2,
44}
45
46impl_flags_u32!(NoteDialect);
47
48impl NoteDialect {
49    pub fn all() -> u32 {
50        !0
51    }
52}
53
54#[derive(Debug, Default, Clone, PartialEq)]
55pub struct NotePortInfo {
56    pub id: ClapId,
57    pub supported_dialects: u32,
58    pub preferred_dialect: u32,
59    pub name: String,
60}
61
62impl NotePortInfo {
63    pub(super) fn fill_clap_note_port_info(&self, info: &mut clap_note_port_info) {
64        info.id = self.id.into();
65
66        // info.name.len > 1 so no underflow
67        let n = self.name.len().min(info.name.len() - 1);
68        unsafe {
69            std::ptr::copy_nonoverlapping(self.name.as_ptr(), info.name.as_mut_ptr() as *mut _, n)
70        }
71        // n is within bounds
72        info.name[n] = b'\0' as _;
73
74        info.supported_dialects = self.supported_dialects;
75        info.preferred_dialect = self.preferred_dialect;
76    }
77}
78
79pub(crate) use ffi::PluginNotePorts;
80
81mod ffi {
82    use std::marker::PhantomData;
83
84    use crate::{
85        ext::note_ports::NotePorts,
86        ffi::{clap_note_port_info, clap_plugin, clap_plugin_note_ports},
87        plugin::{ClapPlugin, Plugin},
88    };
89
90    extern "C-unwind" fn count<E, P>(plugin: *const clap_plugin, is_input: bool) -> u32
91    where
92        P: Plugin,
93        E: NotePorts<P>,
94    {
95        if plugin.is_null() {
96            return 0;
97        }
98        // SAFETY: We just checked that the pointer is non-null and the plugin
99        // has been obtained from host and is tied to type P.
100        let mut clap_plugin = unsafe { ClapPlugin::<P>::new_unchecked(plugin) };
101
102        // SAFETY: This function is called on the main thread.
103        // It is guaranteed that we are the only function accessing the plugin now.
104        // So the mutable reference to plugin for the duration of this call is
105        // safe.
106        let plugin = unsafe { clap_plugin.plugin() };
107
108        E::count(plugin, is_input)
109    }
110
111    extern "C-unwind" fn get<E, P>(
112        plugin: *const clap_plugin,
113        index: u32,
114        is_input: bool,
115        info: *mut clap_note_port_info,
116    ) -> bool
117    where
118        P: Plugin,
119        E: NotePorts<P>,
120    {
121        if plugin.is_null() {
122            return false;
123        }
124        // SAFETY: We just checked that the pointer is non-null and the plugin
125        // has been obtained from host and is tied to type P.
126        let mut clap_plugin = unsafe { ClapPlugin::<P>::new_unchecked(plugin) };
127
128        // SAFETY: This function is called on the main thread.
129        // It is guaranteed that we are the only function accessing the plugin now.
130        // So the mutable reference to plugin for the duration of this call is
131        // safe.
132        let plugin = unsafe { clap_plugin.plugin() };
133
134        // SAFETY: The host guarantees we are the only function that can access info
135        // for the duration of the function call.  So obtaining a mutable reference
136        // is safe.
137        let info = unsafe { &mut *info };
138
139        E::get(plugin, index, is_input)
140            .map(|x| x.fill_clap_note_port_info(info))
141            .is_some()
142    }
143
144    pub struct PluginNotePorts<P> {
145        #[allow(unused)]
146        clap_plugin_note_ports: clap_plugin_note_ports,
147        _marker: PhantomData<P>,
148    }
149
150    impl<P: Plugin> PluginNotePorts<P> {
151        pub fn new<E: NotePorts<P>>(_: E) -> Self {
152            Self {
153                clap_plugin_note_ports: clap_plugin_note_ports {
154                    count: Some(count::<E, P>),
155                    get: Some(get::<E, P>),
156                },
157                _marker: PhantomData,
158            }
159        }
160    }
161}
162
163#[derive(Debug, Copy, Clone, PartialEq)]
164#[repr(u32)]
165pub enum RescanFlags {
166    /// The ports have changed, the host shall perform a full scan of the ports.
167    /// This flag can only be used if the plugin is not active.
168    /// If the plugin active, call host.request_restart() and then call rescan()
169    /// when the host calls deactivate()
170    All = CLAP_NOTE_PORTS_RESCAN_ALL,
171    /// The ports name did change, the host can scan them right away.
172    Names = CLAP_NOTE_PORTS_RESCAN_NAMES,
173}
174
175impl_flags_u32!(RescanFlags);
176
177#[derive(Debug)]
178pub struct HostNotePorts<'a> {
179    host: &'a Host,
180    clap_host_note_ports: &'a clap_host_note_ports,
181}
182
183impl<'a> HostNotePorts<'a> {
184    /// # Safety
185    ///
186    /// All extension interface function pointers must be non-null (Some), and
187    /// the functions must be thread-safe.
188    pub(crate) const unsafe fn new_unchecked(
189        host: &'a Host,
190        clap_host_note_ports: &'a clap_host_note_ports,
191    ) -> Self {
192        Self {
193            host,
194            clap_host_note_ports,
195        }
196    }
197
198    pub fn supported_dialects(&self) -> u32 {
199        // SAFETY: By construction, the callback must be a valid function pointer,
200        // and the call is thread-safe.
201        let callback = self.clap_host_note_ports.supported_dialects.unwrap();
202        unsafe { callback(self.host.clap_host()) }
203    }
204
205    pub fn rescan(&self, flags: u32) {
206        // SAFETY: By construction, the callback must be a valid function pointer,
207        // and the call is thread-safe.
208        let callback = self.clap_host_note_ports.rescan.unwrap();
209        unsafe { callback(self.host.clap_host(), flags) };
210    }
211}
212
213#[derive(Debug)]
214pub enum Error {}
215
216impl Display for Error {
217    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
218        write!(f, "note ports")
219    }
220}
221
222impl std::error::Error for Error {}
223
224impl From<Error> for crate::Error {
225    fn from(value: Error) -> Self {
226        crate::ext::Error::NotePorts(value).into()
227    }
228}