1use std::{
2 io::Write,
3 sync::{
4 Arc,
5 atomic::{AtomicU64, Ordering},
6 },
7};
8
9use clap_clap::prelude as clap;
10
11struct Gain {
14 gain: Arc<AtomicU64>,
15}
16
17impl Default for Gain {
18 fn default() -> Self {
19 Self {
20 gain: Arc::new(AtomicU64::new(1.0f64.to_bits())),
21 }
22 }
23}
24
25impl clap::Extensions<Self> for Gain {
26 fn audio_ports() -> Option<impl clap::AudioPorts<Self>> {
27 Some(clap::StereoPorts::<1, 1>)
28 }
29
30 fn params() -> Option<impl clap::Params<Self>> {
31 Some(GainParam)
32 }
33}
34
35struct GainParam;
36
37impl clap::Params<Gain> for GainParam {
38 fn count(_: &Gain) -> u32 {
39 1
40 }
41
42 fn get_info(_: &Gain, param_index: u32) -> Option<clap::ParamInfo> {
43 (param_index == 0).then(|| clap::ParamInfo {
44 id: 0.into(),
45 flags: clap::params::InfoFlags::RequiresProcess as u32
46 | clap::params::InfoFlags::Automatable as u32,
47 name: "Gain".to_string(),
48 module: "gain".to_string(),
49 min_value: 0.0,
50 max_value: 2.0,
51 default_value: 1.0,
52 })
53 }
54
55 fn get_value(plugin: &Gain, param_id: clap::ClapId) -> Option<f64> {
56 (param_id == 0.into()).then(|| f64::from_bits(plugin.gain.load(Ordering::Relaxed)))
57 }
58
59 fn value_to_text(
60 _: &Gain,
61 _: clap::ClapId,
62 value: f64,
63 mut out_buf: &mut [u8],
64 ) -> Result<(), clap::Error> {
65 Ok(write!(out_buf, "{value:.2}")?)
66 }
67
68 fn text_to_value(
69 _: &Gain,
70 _: clap::ClapId,
71 param_value_text: &str,
72 ) -> Result<f64, clap::Error> {
73 Ok(param_value_text.parse()?)
74 }
75
76 fn flush_inactive(_: &Gain, _: &clap::InputEvents, _: &clap::OutputEvents) {}
77
78 fn flush(
79 _: &<Gain as clap::Plugin>::AudioThread,
80 _: &clap::InputEvents,
81 _: &clap::OutputEvents,
82 ) {
83 }
84}
85
86impl clap::Plugin for Gain {
87 type AudioThread = AudioThread;
88
89 const ID: &'static str = "com.your-company.YourPlugin";
90 const NAME: &'static str = "Plugin Name";
91 const VENDOR: &'static str = "Vendor";
92 const URL: &'static str = "https://your-domain.com/your-plugin";
93 const MANUAL_URL: &'static str = "https://your-domain.com/your-plugin/manual";
94 const SUPPORT_URL: &'static str = "https://your-domain.com/support";
95 const VERSION: &'static str = "1.4.2";
96 const DESCRIPTION: &'static str = "The plugin description.";
97
98 fn features() -> impl Iterator<Item = &'static str> {
99 "fx stereo gain".split_whitespace()
100 }
101
102 fn init(&mut self, _: Arc<clap::Host>) -> Result<(), clap::Error> {
103 Ok(())
104 }
105
106 fn activate(&mut self, _: f64, _: u32, _: u32) -> Result<AudioThread, clap::Error> {
108 Ok(AudioThread {
109 gain: self.gain.clone(),
110 smoothed: Smooth::default(),
111 })
112 }
113}
114
115struct AudioThread {
116 gain: Arc<AtomicU64>,
117 smoothed: Smooth,
118}
119
120impl clap::AudioThread<Gain> for AudioThread {
121 fn process(&mut self, process: &mut clap::Process) -> Result<clap::Status, clap::Error> {
122 let mut gain = f64::from_bits(self.gain.load(Ordering::Relaxed));
123
124 let nframes = process.frames_count();
125 let nev = process.in_events().size();
126 let mut ev_index = 0;
127 let mut next_ev_frame = if nev > 0 { 0 } else { nframes };
128
129 let mut i = 0;
130 while i < nframes {
131 while ev_index < nev && next_ev_frame == i {
132 {
133 let in_events = process.in_events();
134 let header = in_events.get(ev_index);
135 if header.time() != i {
136 next_ev_frame = header.time();
137 break;
138 }
139
140 if let Ok(param_value) = header.param_value() {
141 gain = param_value.value();
142 self.gain.store(gain.to_bits(), Ordering::Release);
143 }
144 }
145
146 ev_index += 1;
147
148 if ev_index == nev {
149 next_ev_frame = nframes;
150 break;
151 }
152 }
153
154 {
155 let i = i as usize;
156 let gain = gain as f32;
157
158 let in_l = process.audio_inputs(0).data32(0)[i];
160 let in_r = process.audio_inputs(0).data32(1)[i];
161
162 let smoothed = self.smoothed.tick(gain);
163 let out_l = in_l * smoothed;
164 let out_r = in_r * smoothed;
165
166 process.audio_outputs(0).data32(0)[i] = out_l;
168 process.audio_outputs(0).data32(1)[i] = out_r;
169 }
170
171 i += 1;
172 }
173 Ok(clap::Continue)
174 }
175}
176
177clap::entry!(Gain);
179
180#[derive(Debug, Clone)]
182pub struct Smooth {
183 b0: f32,
184 a1: f32,
185 y1: f32,
186}
187
188impl Smooth {
189 fn tick(&mut self, sample: f32) -> f32 {
190 let y0 = sample * self.b0 - self.y1 * self.a1;
191 self.y1 = y0;
192 y0 * (1.0 + self.a1)
193 }
194}
195
196impl Default for Smooth {
197 fn default() -> Self {
198 Smooth {
199 b0: 1.0,
200 a1: -0.999,
201 y1: 0.0,
202 }
203 }
204}