1use crate::Result;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct Parameter {
9 pub id: u32,
11 pub name: String,
13 pub value: f64,
15 pub min: f64,
19 pub max: f64,
21 pub default: f64,
23 pub unit: String,
25 pub step_count: i32,
27 pub can_automate: bool,
29 pub is_read_only: bool,
31 pub is_bypass: bool,
33 pub flags: u32,
35}
36
37impl Parameter {
38 pub fn normalized_to_plain(&self, normalized: f64) -> f64 {
40 if self.step_count > 1 {
41 let steps = self.step_count as f64;
43 let step = (normalized * steps).round();
44 self.min + (step / steps) * (self.max - self.min)
45 } else {
46 self.min + normalized * (self.max - self.min)
48 }
49 }
50
51 pub fn plain_to_normalized(&self, plain: f64) -> f64 {
53 if (self.max - self.min).abs() < f64::EPSILON {
54 0.0
55 } else {
56 ((plain - self.min) / (self.max - self.min)).clamp(0.0, 1.0)
57 }
58 }
59
60 pub fn format_value(&self, normalized: f64) -> String {
67 let plain = self.normalized_to_plain(normalized);
68
69 if self.step_count == 2 {
70 if plain > 0.5 {
72 "On".to_string()
73 } else {
74 "Off".to_string()
75 }
76 } else if self.step_count > 2 {
77 format!("{:.0} {}", plain, self.unit)
79 } else {
80 if self.unit.is_empty() {
82 format!("{:.3}", plain)
83 } else {
84 format!("{:.3} {}", plain, self.unit)
85 }
86 }
87 }
88
89 pub fn is_discrete(&self) -> bool {
91 self.step_count > 1
92 }
93
94 pub fn is_boolean(&self) -> bool {
96 self.step_count == 2
97 }
98}
99
100#[derive(Debug, Clone)]
102pub struct ParameterChange {
103 pub id: u32,
105 pub value: f64,
107 pub sample_offset: i32,
109}
110
111pub struct ParameterUpdate<'a> {
113 updates: Vec<(u32, f64)>,
114 plugin: &'a mut crate::Plugin,
115}
116
117impl<'a> ParameterUpdate<'a> {
118 pub(crate) fn new(plugin: &'a mut crate::Plugin) -> Self {
119 Self {
120 updates: Vec::new(),
121 plugin,
122 }
123 }
124
125 pub fn set(&mut self, id: u32, value: f64) -> &mut Self {
127 self.updates.push((id, value));
128 self
129 }
130
131 pub fn apply(self) -> Result<()> {
133 for (id, value) in self.updates {
134 self.plugin.set_parameter(id, value)?;
135 }
136 Ok(())
137 }
138}
139
140#[derive(Debug, Clone, Copy, PartialEq)]
142pub enum AutomationCurve {
143 Linear,
145 Exponential,
147 Logarithmic,
149 Step,
151}
152
153#[derive(Debug, Clone)]
155pub struct AutomationPoint {
156 pub time: f64,
158 pub value: f64,
160 pub curve: AutomationCurve,
162}
163
164#[derive(Debug, Clone)]
166pub struct ParameterAutomation {
167 pub points: Vec<AutomationPoint>,
169 pub looping: bool,
171}
172
173impl ParameterAutomation {
174 pub fn new() -> Self {
176 Self {
177 points: Vec::new(),
178 looping: false,
179 }
180 }
181
182 pub fn add_point(mut self, time: f64, value: f64) -> Self {
184 self.points.push(AutomationPoint {
185 time,
186 value,
187 curve: AutomationCurve::Linear,
188 });
189 self.points.sort_by(|a, b| a.time.total_cmp(&b.time));
192 self
193 }
194
195 pub fn with_curve(mut self, curve: AutomationCurve) -> Self {
197 for point in &mut self.points {
198 point.curve = curve;
199 }
200 self
201 }
202
203 pub fn with_loop(mut self, looping: bool) -> Self {
205 self.looping = looping;
206 self
207 }
208
209 pub fn value_at_time(&self, time: f64) -> Option<f64> {
211 if self.points.is_empty() {
212 return None;
213 }
214
215 let time = if self.looping && !self.points.is_empty() {
217 let duration = self.points.last().unwrap().time;
218 if duration > 0.0 {
219 time % duration
220 } else {
221 time
222 }
223 } else {
224 time
225 };
226
227 let mut prev = None;
229 let mut next = None;
230
231 for (i, point) in self.points.iter().enumerate() {
232 if point.time <= time {
233 prev = Some(i);
234 } else {
235 next = Some(i);
236 break;
237 }
238 }
239
240 match (prev, next) {
241 (None, _) => Some(self.points[0].value),
242 (Some(i), None) => Some(self.points[i].value),
243 (Some(i), Some(j)) => {
244 let p1 = &self.points[i];
245 let p2 = &self.points[j];
246
247 let t = (time - p1.time) / (p2.time - p1.time);
248
249 let value = match p1.curve {
250 AutomationCurve::Linear => p1.value + (p2.value - p1.value) * t,
251 AutomationCurve::Exponential => p1.value + (p2.value - p1.value) * t * t,
252 AutomationCurve::Logarithmic => p1.value + (p2.value - p1.value) * t.sqrt(),
253 AutomationCurve::Step => p1.value,
254 };
255
256 Some(value.clamp(0.0, 1.0))
257 }
258 }
259 }
260
261 pub fn points_for_block(
271 &self,
272 block_start_secs: f64,
273 frames: usize,
274 sample_rate: f64,
275 points_per_block: usize,
276 ) -> Vec<(i32, f64)> {
277 if self.points.is_empty() || frames == 0 {
278 return Vec::new();
279 }
280 let n = points_per_block.clamp(1, frames);
281 let mut out = Vec::with_capacity(n);
282 for i in 0..n {
283 let offset = (i * frames) / n;
284 let time = block_start_secs + offset as f64 / sample_rate;
285 if let Some(value) = self.value_at_time(time) {
286 out.push((offset as i32, value));
287 }
288 }
289 out
290 }
291}
292
293impl Default for ParameterAutomation {
294 fn default() -> Self {
295 Self::new()
296 }
297}
298
299#[cfg(test)]
300mod tests {
301 use super::*;
302
303 #[test]
304 fn add_point_with_nan_time_does_not_panic() {
305 let auto = ParameterAutomation::new()
307 .add_point(0.0, 0.1)
308 .add_point(f64::NAN, 0.5)
309 .add_point(1.0, 0.9);
310 assert_eq!(auto.points.len(), 3);
311
312 let finite: Vec<f64> = auto
316 .points
317 .iter()
318 .map(|p| p.time)
319 .filter(|t| t.is_finite())
320 .collect();
321 assert_eq!(finite, vec![0.0, 1.0]);
322 assert!(auto.points.last().unwrap().time.is_nan());
323 }
324
325 #[test]
326 fn add_point_with_nan_value_is_not_used_in_ordering() {
327 let auto = ParameterAutomation::new()
330 .add_point(2.0, f64::NAN)
331 .add_point(1.0, 0.5)
332 .add_point(0.0, 0.25);
333 let times: Vec<f64> = auto.points.iter().map(|p| p.time).collect();
334 assert_eq!(times, vec![0.0, 1.0, 2.0]);
335 }
336}