1use std::fmt::{Display, Formatter};
2
3use crate::{
4 ffi::{
5 CLAP_AUDIO_PORTS_RESCAN_CHANNEL_COUNT, CLAP_AUDIO_PORTS_RESCAN_FLAGS,
6 CLAP_AUDIO_PORTS_RESCAN_IN_PLACE_PAIR, CLAP_AUDIO_PORTS_RESCAN_LIST,
7 CLAP_AUDIO_PORTS_RESCAN_NAMES, CLAP_AUDIO_PORTS_RESCAN_PORT_TYPE, clap_host_audio_ports,
8 },
9 impl_flags_u32,
10 plugin::Plugin,
11 prelude::Host,
12};
13
14pub trait AudioPorts<P>
15where
16 P: Plugin,
17{
18 fn count(plugin: &P, is_input: bool) -> u32;
19 fn get(plugin: &P, index: u32, is_input: bool) -> Option<AudioPortInfo>;
20}
21
22#[derive(Debug, Copy, Clone, PartialEq, Eq)]
34#[repr(u32)]
35pub enum RescanFlags {
36 Names = CLAP_AUDIO_PORTS_RESCAN_NAMES,
38 Flags = CLAP_AUDIO_PORTS_RESCAN_FLAGS,
40 ChannelCount = CLAP_AUDIO_PORTS_RESCAN_CHANNEL_COUNT,
42 PortType = CLAP_AUDIO_PORTS_RESCAN_PORT_TYPE,
44 InPlacePair = CLAP_AUDIO_PORTS_RESCAN_IN_PLACE_PAIR,
46 List = CLAP_AUDIO_PORTS_RESCAN_LIST,
48}
49
50impl_flags_u32!(RescanFlags);
51
52#[derive(Debug)]
53pub struct HostAudioPorts<'a> {
54 host: &'a Host,
55 clap_host_audio_ports: &'a clap_host_audio_ports,
56}
57
58impl<'a> HostAudioPorts<'a> {
59 pub(crate) const unsafe fn new_unchecked(
64 host: &'a Host,
65 clap_host_audio_ports: &'a clap_host_audio_ports,
66 ) -> Self {
67 Self {
68 host,
69 clap_host_audio_ports,
70 }
71 }
72
73 pub fn is_rescan_flag_supported(&self, flag: RescanFlags) -> bool {
74 let callback = self.clap_host_audio_ports.is_rescan_flag_supported.unwrap();
77 unsafe { callback(self.host.clap_host(), flag as u32) }
78 }
79
80 pub fn rescan(&self, flags: u32) {
81 let callback = self.clap_host_audio_ports.rescan.unwrap();
84 unsafe { callback(self.host.clap_host(), flags) };
85 }
86}
87
88pub(crate) use ffi::PluginAudioPorts;
89mod ffi {
90 use std::marker::PhantomData;
91
92 use crate::{
93 ext::audio_ports::AudioPorts,
94 ffi::{clap_audio_port_info, clap_plugin, clap_plugin_audio_ports},
95 plugin::{ClapPlugin, Plugin},
96 };
97
98 extern "C-unwind" fn count<A, P>(plugin: *const clap_plugin, is_input: bool) -> u32
99 where
100 P: Plugin,
101 A: AudioPorts<P>,
102 {
103 if plugin.is_null() {
104 return 0;
105 }
106 let mut clap_plugin = unsafe { ClapPlugin::<P>::new_unchecked(plugin) };
109
110 let plugin = unsafe { clap_plugin.plugin() };
115
116 A::count(plugin, is_input)
117 }
118
119 extern "C-unwind" fn get<A, P>(
120 plugin: *const clap_plugin,
121 index: u32,
122 is_input: bool,
123 info: *mut clap_audio_port_info,
124 ) -> bool
125 where
126 P: Plugin,
127 A: AudioPorts<P>,
128 {
129 if plugin.is_null() {
130 return false;
131 }
132 let mut clap_plugin = unsafe { ClapPlugin::<P>::new_unchecked(plugin) };
135
136 let plugin = unsafe { clap_plugin.plugin() };
141
142 let info = unsafe { &mut *info };
146
147 A::get(plugin, index, is_input)
148 .map(|x| x.fill_clap_audio_port_info(info))
149 .is_some()
150 }
151
152 pub struct PluginAudioPorts<P> {
153 #[allow(unused)]
154 clap_plugin_audio_ports: clap_plugin_audio_ports,
155 _marker: PhantomData<P>,
156 }
157
158 impl<P: Plugin> PluginAudioPorts<P> {
159 pub fn new<A: AudioPorts<P>>(_: A) -> Self {
160 Self {
161 clap_plugin_audio_ports: clap_plugin_audio_ports {
162 count: Some(count::<A, P>),
163 get: Some(get::<A, P>),
164 },
165 _marker: PhantomData,
166 }
167 }
168 }
169}
170
171pub use port_info::{AudioPortFlags, AudioPortInfo, AudioPortType};
172mod port_info {
173 use std::ptr::null;
174
175 use crate::{
176 ffi::{
177 CLAP_AUDIO_PORT_IS_MAIN, CLAP_AUDIO_PORT_PREFERS_64BITS,
178 CLAP_AUDIO_PORT_REQUIRES_COMMON_SAMPLE_SIZE, CLAP_AUDIO_PORT_SUPPORTS_64BITS,
179 CLAP_INVALID_ID, CLAP_PORT_AMBISONIC, CLAP_PORT_MONO, CLAP_PORT_STEREO,
180 CLAP_PORT_SURROUND, clap_audio_port_info,
181 },
182 id::ClapId,
183 impl_flags_u32,
184 plugin::Plugin,
185 prelude::AudioPorts,
186 };
187
188 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
189 #[repr(u32)]
190 pub enum AudioPortFlags {
191 IsMain = CLAP_AUDIO_PORT_IS_MAIN,
194 Supports64bits = CLAP_AUDIO_PORT_SUPPORTS_64BITS,
196 Prefers64bits = CLAP_AUDIO_PORT_PREFERS_64BITS,
198 RequiresCommonSampleSize = CLAP_AUDIO_PORT_REQUIRES_COMMON_SAMPLE_SIZE,
203 }
204
205 impl_flags_u32!(AudioPortFlags);
206
207 #[derive(Debug, Default, Clone, PartialEq)]
208 pub struct AudioPortInfo {
209 pub id: ClapId,
210 pub name: String,
211 pub flags: u32,
212 pub channel_count: u32,
213 pub port_type: Option<AudioPortType>,
214 pub in_place_pair: Option<ClapId>,
215 }
216
217 impl AudioPortInfo {
218 pub(super) fn fill_clap_audio_port_info(&self, info: &mut clap_audio_port_info) {
219 info.id = self.id.into();
220
221 let n = self.name.len().min(info.name.len() - 1);
223 unsafe {
224 std::ptr::copy_nonoverlapping(
225 self.name.as_ptr(),
226 info.name.as_mut_ptr() as *mut _,
227 n,
228 )
229 }
230 info.name[n] = b'\0' as _;
232
233 info.flags = self.flags;
234
235 info.channel_count = self.channel_count;
236
237 info.port_type = match self.port_type {
238 Some(AudioPortType::Mono) => CLAP_PORT_MONO.as_ptr(),
239 Some(AudioPortType::Stereo) => CLAP_PORT_STEREO.as_ptr(),
240 Some(AudioPortType::Surround) => CLAP_PORT_SURROUND.as_ptr(),
241 Some(AudioPortType::Ambisonic) => CLAP_PORT_AMBISONIC.as_ptr(),
242 None => null(),
243 };
244
245 info.in_place_pair = self
246 .in_place_pair
247 .map(Into::into)
248 .unwrap_or(CLAP_INVALID_ID);
249 }
250 }
251
252 #[derive(Debug, Copy, Clone, PartialEq)]
253 pub enum AudioPortType {
254 Mono,
255 Stereo,
256 Surround,
257 Ambisonic,
258 }
259
260 impl TryFrom<&str> for AudioPortType {
261 type Error = crate::ext::audio_ports::Error;
262
263 fn try_from(value: &str) -> Result<Self, Self::Error> {
264 if value == "mono" {
265 Ok(AudioPortType::Mono)
266 } else if value == "stereo" {
267 Ok(AudioPortType::Stereo)
268 } else if value == "ambisonic" {
269 Ok(AudioPortType::Ambisonic)
270 } else if value == "surround" {
271 Ok(AudioPortType::Surround)
272 } else {
273 Err(Self::Error::PortType)
274 }
275 }
276 }
277
278 impl<P: Plugin> AudioPorts<P> for () {
279 fn count(_: &P, _: bool) -> u32 {
280 0
281 }
282
283 fn get(_: &P, _: u32, _: bool) -> Option<AudioPortInfo> {
284 None
285 }
286 }
287}
288
289pub use static_ports::{MonoPorts, StereoPorts};
290mod static_ports {
291 use crate::{
292 ext::audio_ports::{AudioPortFlags, AudioPortInfo, AudioPortType, AudioPorts},
293 plugin::Plugin,
294 };
295
296 #[derive(Default, Debug, Copy, Clone)]
298 pub struct MonoPorts<const IN: u32, const OUT: u32>;
299
300 impl<const IN: u32, const OUT: u32> MonoPorts<IN, OUT> {
301 pub const fn new() -> Self {
302 Self {}
303 }
304 }
305
306 impl<P, const IN: u32, const OUT: u32> AudioPorts<P> for MonoPorts<IN, OUT>
307 where
308 P: Plugin,
309 {
310 fn count(_: &P, is_input: bool) -> u32 {
311 if is_input { IN } else { OUT }
312 }
313
314 fn get(_: &P, index: u32, is_input: bool) -> Option<AudioPortInfo> {
315 if is_input {
316 (index < IN).then_some(if index == 0 {
317 AudioPortInfo {
318 id: index.try_into().unwrap(),
319 name: "Main In".to_owned(),
320 flags: AudioPortFlags::IsMain as u32,
321 channel_count: 1,
322 port_type: Some(AudioPortType::Mono),
323 in_place_pair: None,
324 }
325 } else {
326 AudioPortInfo {
327 id: index.try_into().unwrap(),
328 name: format!("In {index}"),
329 flags: 0,
330 channel_count: 1,
331 port_type: Some(AudioPortType::Mono),
332 in_place_pair: None,
333 }
334 })
335 } else {
336 (index < OUT).then_some(if index == 0 {
337 AudioPortInfo {
338 id: (IN + index).try_into().unwrap(),
339 name: "Main Out".to_owned(),
340 flags: AudioPortFlags::IsMain as u32,
341 channel_count: 1,
342 port_type: Some(AudioPortType::Mono),
343 in_place_pair: None,
344 }
345 } else {
346 AudioPortInfo {
347 id: (IN + index).try_into().unwrap(),
348 name: format!("Out {index}"),
349 flags: 0,
350 channel_count: 1,
351 port_type: Some(AudioPortType::Mono),
352 in_place_pair: None,
353 }
354 })
355 }
356 }
357 }
358
359 #[derive(Debug, Copy, Clone)]
361 pub struct StereoPorts<const IN: u32, const OUT: u32>;
362
363 impl<P, const IN: u32, const OUT: u32> AudioPorts<P> for StereoPorts<IN, OUT>
364 where
365 P: Plugin,
366 {
367 fn count(_: &P, is_input: bool) -> u32 {
368 if is_input { IN } else { OUT }
369 }
370
371 fn get(_: &P, index: u32, is_input: bool) -> Option<AudioPortInfo> {
372 if is_input {
373 (index < IN).then_some(if index == 0 {
374 AudioPortInfo {
375 id: index.try_into().unwrap(),
376 name: "Main In".to_owned(),
377 flags: AudioPortFlags::IsMain as u32,
378 channel_count: 2,
379 port_type: Some(AudioPortType::Stereo),
380 in_place_pair: None,
381 }
382 } else {
383 AudioPortInfo {
384 id: index.try_into().unwrap(),
385 name: format!("In {index}"),
386 flags: 0,
387 channel_count: 2,
388 port_type: Some(AudioPortType::Stereo),
389 in_place_pair: None,
390 }
391 })
392 } else {
393 (index < OUT).then_some(if index == 0 {
394 AudioPortInfo {
395 id: (IN + index).try_into().unwrap(),
396 name: "Main Out".to_owned(),
397 flags: AudioPortFlags::IsMain as u32,
398 channel_count: 2,
399 port_type: Some(AudioPortType::Stereo),
400 in_place_pair: None,
401 }
402 } else {
403 AudioPortInfo {
404 id: (IN + index).try_into().unwrap(),
405 name: format!("Out {index}"),
406 flags: 0,
407 channel_count: 2,
408 port_type: Some(AudioPortType::Stereo),
409 in_place_pair: None,
410 }
411 })
412 }
413 }
414 }
415}
416
417#[derive(Debug)]
418pub enum Error {
419 PortType,
420}
421
422impl Display for Error {
423 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
424 match self {
425 Error::PortType => write!(f, "unknown port type"),
426 }
427 }
428}
429
430impl std::error::Error for Error {}
431
432impl From<Error> for crate::Error {
433 fn from(value: Error) -> Self {
434 match value {
435 Error::PortType => crate::ext::Error::AudioPorts(value).into(),
436 }
437 }
438}