note_transpose/
note_transpose.rs

1//! This example shows how to implement the NotePorts plugin extension.
2//!
3//! The plugin transposes incoming CLAP notes by a perfect fifth and passes all
4//! MIDI or MIDI2 data unchanged.
5
6use std::sync::Arc;
7
8use clap_clap::prelude as clap;
9
10#[derive(Default)]
11struct Transpose;
12
13impl clap::Extensions<Self> for Transpose {
14    fn note_ports() -> Option<impl clap::NotePorts<Self>> {
15        Some(Self {})
16    }
17}
18
19impl clap::NotePorts<Self> for Transpose {
20    fn count(_: &Self, _is_input: bool) -> u32 {
21        2 // two in, two out
22    }
23
24    fn get(_: &Self, index: u32, is_input: bool) -> Option<clap::NotePortInfo> {
25        (index < 2).then(|| clap::NotePortInfo {
26            id: if is_input {
27                clap::ClapId::from(index as u16)
28            } else {
29                clap::ClapId::from(index as u16 + 2)
30            },
31            supported_dialects: clap::NoteDialect::all(),
32            preferred_dialect: clap::NoteDialect::Clap as u32,
33            name: if is_input {
34                format!("In {index}")
35            } else {
36                format!("Out {index}")
37            },
38        })
39    }
40}
41
42impl clap::Plugin for Transpose {
43    type AudioThread = Self;
44
45    const ID: &'static str = "com.your-company.YourPlugin";
46    const NAME: &'static str = "Plugin Name";
47    const VENDOR: &'static str = "Vendor";
48    const URL: &'static str = "https://your-domain.com/your-plugin";
49    const MANUAL_URL: &'static str = "https://your-domain.com/your-plugin/manual";
50    const SUPPORT_URL: &'static str = "https://your-domain.com/support";
51    const VERSION: &'static str = "1.4.2";
52    const DESCRIPTION: &'static str = "The plugin description.";
53
54    fn features() -> impl Iterator<Item = &'static str> {
55        "fx transpose midi thru".split_whitespace()
56    }
57
58    fn init(&mut self, _: Arc<clap::Host>) -> Result<(), clap::Error> {
59        Ok(())
60    }
61
62    /// Start the audio thread.
63    fn activate(&mut self, _: f64, _: u32, _: u32) -> Result<Self, clap::Error> {
64        Ok(Self {})
65    }
66}
67
68impl clap::AudioThread<Self> for Transpose {
69    fn process(&mut self, process: &mut clap::Process) -> Result<clap::Status, clap::Error> {
70        let in_events = process.in_events();
71        let mut out_events = process.out_events();
72
73        for i in 0..in_events.size() {
74            let header = in_events.get(i);
75
76            if let Ok(note) = header.note() {
77                use clap::EventBuilder;
78                let n = note.update().key(note.key() + 7); // Transpose notes by a perfect fifth.
79                let _ = out_events.try_push(n.event());
80            }
81
82            if let Ok(note_expr) = header.note_expression() {
83                let _ = out_events.try_push(note_expr);
84            }
85
86            if let Ok(midi) = header.midi() {
87                let _ = out_events.try_push(midi);
88            }
89
90            if let Ok(midi2) = header.midi2() {
91                let _ = out_events.try_push(midi2);
92            }
93        }
94
95        Ok(clap::Continue)
96    }
97}
98
99// Export clap_entry symbols and build a plugin factory.
100clap::entry!(Transpose);