state/
state.rs

1use std::{
2    io::{Read, Write},
3    mem,
4    sync::{
5        Arc,
6        atomic::{AtomicU64, Ordering},
7    },
8};
9
10use clap_clap::prelude as clap;
11
12const NUM_PARAMS: usize = 3;
13const NUM_BYTES: usize = NUM_PARAMS * 8; // Parameters have type: f64.
14
15// A plugin must implement `Default` trait.  The plugin instance will be created
16// by the host with the call to `State::default()`.
17struct Example {
18    // Three independent parameters to save and load as the plugin's state.
19    state: Arc<[AtomicU64; NUM_PARAMS]>,
20}
21
22impl Default for Example {
23    fn default() -> Self {
24        Self {
25            state: Arc::new([
26                AtomicU64::new(0.0f64.to_bits()),
27                AtomicU64::new(0.0f64.to_bits()),
28                AtomicU64::new(0.0f64.to_bits()),
29            ]),
30        }
31    }
32}
33
34impl clap::Extensions<Self> for Example {
35    fn params() -> Option<impl clap::Params<Self>> {
36        Some(ExampleParams)
37    }
38
39    fn state() -> Option<impl clap::State<Self>> {
40        Some(ExampleState)
41    }
42}
43
44struct ExampleParams;
45
46impl clap::Params<Example> for ExampleParams {
47    fn count(_: &Example) -> u32 {
48        NUM_PARAMS as u32
49    }
50
51    fn get_info(_: &Example, param_index: u32) -> Option<clap::ParamInfo> {
52        (param_index < NUM_PARAMS as u32).then(|| {
53            clap::ParamInfo {
54                id: clap::ClapId::from(param_index as u16),
55                flags: clap::params::InfoFlags::RequiresProcess as u32
56                    // Some DAWs, e.g. Bitwig, display only automatable parameters.
57                    | clap::params::InfoFlags::Automatable as u32,
58                name: format!("Param {param_index}"),
59                module: format!("{param_index}/param"),
60                min_value: 0.0,
61                max_value: 1.0,
62                default_value: 0.0,
63            }
64        })
65    }
66
67    fn get_value(plugin: &Example, param_id: clap::ClapId) -> Option<f64> {
68        let id: usize = param_id.into();
69        (id < NUM_PARAMS).then(|| f64::from_bits(plugin.state[id].load(Ordering::Relaxed)))
70    }
71
72    fn value_to_text(
73        _: &Example,
74        _: clap::ClapId,
75        value: f64,
76        mut out_buf: &mut [u8],
77    ) -> Result<(), clap::Error> {
78        Ok(write!(out_buf, "{value:.2}")?)
79    }
80
81    fn text_to_value(
82        _: &Example,
83        _: clap::ClapId,
84        param_value_text: &str,
85    ) -> Result<f64, clap::Error> {
86        Ok(param_value_text.parse()?)
87    }
88
89    fn flush_inactive(_: &Example, _: &clap::InputEvents, _: &clap::OutputEvents) {}
90
91    fn flush(
92        _: &<Example as clap::Plugin>::AudioThread,
93        _: &clap::InputEvents,
94        _: &clap::OutputEvents,
95    ) {
96    }
97}
98
99struct ExampleState;
100
101impl clap::State<Example> for ExampleState {
102    fn save(plugin: &Example, stream: &mut clap::OStream) -> Result<(), clap::Error> {
103        let buf: [u64; NUM_PARAMS] =
104            std::array::from_fn(|i| plugin.state[i].load(Ordering::Acquire));
105        let buf: [u8; NUM_BYTES] = unsafe { mem::transmute(buf) };
106        stream.write_all(&buf).map_err(Into::into)
107    }
108
109    fn load(plugin: &Example, stream: &mut clap::IStream) -> Result<(), clap::Error> {
110        let mut buf: [u8; NUM_BYTES] = [0; NUM_BYTES];
111        stream.read_exact(&mut buf)?;
112
113        let buf: [u64; NUM_PARAMS] = unsafe { mem::transmute(buf) };
114        for i in 0..NUM_PARAMS {
115            plugin.state[i].store(buf[i], Ordering::Release);
116        }
117
118        Ok(())
119    }
120}
121
122impl clap::Plugin for Example {
123    type AudioThread = AudioThread;
124
125    const ID: &'static str = "com.your-company.YourPlugin";
126    const NAME: &'static str = "Plugin Name";
127    const VENDOR: &'static str = "Vendor";
128    const URL: &'static str = "https://your-domain.com/your-plugin";
129    const MANUAL_URL: &'static str = "https://your-domain.com/your-plugin/manual";
130    const SUPPORT_URL: &'static str = "https://your-domain.com/support";
131    const VERSION: &'static str = "1.4.2";
132    const DESCRIPTION: &'static str = "The plugin description.";
133
134    fn features() -> impl Iterator<Item = &'static str> {
135        "example parameter state".split_whitespace()
136    }
137
138    fn init(&mut self, _: Arc<clap::Host>) -> Result<(), clap::Error> {
139        Ok(())
140    }
141
142    /// Start the audio thread.
143    fn activate(&mut self, _: f64, _: u32, _: u32) -> Result<AudioThread, clap::Error> {
144        Ok(AudioThread {
145            state: self.state.clone(),
146        })
147    }
148}
149
150struct AudioThread {
151    state: Arc<[AtomicU64; NUM_PARAMS]>,
152}
153
154impl clap::AudioThread<Example> for AudioThread {
155    fn process(&mut self, process: &mut clap::Process) -> Result<clap::Status, clap::Error> {
156        let in_events = process.in_events();
157
158        for i in 0..in_events.size() {
159            let header = in_events.get(i);
160
161            if let Ok(param) = header.param_value() {
162                let value = param.value();
163                let id: usize = param.param_id().into();
164
165                if id < NUM_PARAMS {
166                    self.state[id].store(value.to_bits(), Ordering::Release);
167                }
168            }
169        }
170        Ok(clap::Continue)
171    }
172}
173
174clap::entry!(Example);