cloudseedcore_rs/
params.rs

1use crate::bridge::cs_format_parameter;
2use num_enum::{IntoPrimitive, TryFromPrimitive};
3
4/// Converts a normalized parameter value to human-readable text.
5pub fn format_parameter_value(param_id: ParamId, value: f32) -> String {
6    let id: u8 = param_id.into();
7    cs_format_parameter(id as u32, value)
8}
9
10/// Parameter identifiers matching CloudSeedCore/Parameters.h
11#[repr(u8)]
12#[derive(Copy, Clone, Eq, PartialEq, Debug, IntoPrimitive, TryFromPrimitive)]
13pub enum ParamId {
14    Interpolation = 0,
15    LowCutEnabled = 1,
16    HighCutEnabled = 2,
17    InputMix = 3,
18    LowCut = 4,
19    HighCut = 5,
20    DryOut = 6,
21    EarlyOut = 7,
22    LateOut = 8,
23
24    TapEnabled = 9,
25    TapCount = 10,
26    TapDecay = 11,
27    TapPredelay = 12,
28    TapLength = 13,
29
30    EarlyDiffuseEnabled = 14,
31    EarlyDiffuseCount = 15,
32    EarlyDiffuseDelay = 16,
33    EarlyDiffuseModAmount = 17,
34    EarlyDiffuseFeedback = 18,
35    EarlyDiffuseModRate = 19,
36
37    LateMode = 20,
38    LateLineCount = 21,
39    LateDiffuseEnabled = 22,
40    LateDiffuseCount = 23,
41    LateLineSize = 24,
42    LateLineModAmount = 25,
43    LateDiffuseDelay = 26,
44    LateDiffuseModAmount = 27,
45    LateLineDecay = 28,
46    LateLineModRate = 29,
47    LateDiffuseFeedback = 30,
48    LateDiffuseModRate = 31,
49
50    EqLowShelfEnabled = 32,
51    EqHighShelfEnabled = 33,
52    EqLowpassEnabled = 34,
53    EqLowFreq = 35,
54    EqHighFreq = 36,
55    EqCutoff = 37,
56    EqLowGain = 38,
57    EqHighGain = 39,
58    EqCrossSeed = 40,
59
60    SeedTap = 41,
61    SeedDiffusion = 42,
62    SeedDelay = 43,
63    SeedPostDiffusion = 44,
64}
65
66#[derive(Clone, Copy, Debug, PartialEq)]
67pub enum LateMode {
68    Pre = 0,
69    Post = 1,
70}
71
72/// Contains the state of all CloudSeedCore parameters.
73/// All parameters are normalized to the range 0..1.
74#[derive(Clone, Copy, Debug, PartialEq)]
75pub struct Program {
76    // Mix
77    pub interpolation: bool,
78    pub low_cut_enabled: bool,
79    pub high_cut_enabled: bool,
80    pub input_mix: f32,
81    pub low_cut: f32,
82    pub high_cut: f32,
83    pub dry_out: f32,
84    pub early_out: f32,
85    pub late_out: f32,
86
87    // Tap
88    pub tap_enabled: bool,
89    pub tap_count: f32,
90    pub tap_decay: f32,
91    pub tap_predelay: f32,
92    pub tap_length: f32,
93
94    // Early
95    pub early_diffuse_enabled: bool,
96    pub early_diffuse_count: f32,
97    pub early_diffuse_delay: f32,
98    pub early_diffuse_mod_amount: f32,
99    pub early_diffuse_feedback: f32,
100    pub early_diffuse_mod_rate: f32,
101
102    // Late
103    pub late_mode: LateMode,
104    pub late_line_count: f32,
105    pub late_diffuse_enabled: bool,
106    pub late_diffuse_count: f32,
107    pub late_line_size: f32,
108    pub late_line_mod_amount: f32,
109    pub late_diffuse_delay: f32,
110    pub late_diffuse_mod_amount: f32,
111    pub late_line_decay: f32,
112    pub late_line_mod_rate: f32,
113    pub late_diffuse_feedback: f32,
114    pub late_diffuse_mod_rate: f32,
115
116    // EQ
117    pub eq_low_shelf_enabled: bool,
118    pub eq_high_shelf_enabled: bool,
119    pub eq_lowpass_enabled: bool,
120    pub eq_low_freq: f32,
121    pub eq_high_freq: f32,
122    pub eq_cutoff: f32,
123    pub eq_low_gain: f32,
124    pub eq_high_gain: f32,
125    pub eq_cross_seed: f32,
126
127    // Seeds
128    pub seed_tap: f32,
129    pub seed_diffusion: f32,
130    pub seed_delay: f32,
131    pub seed_post_diffusion: f32,
132}
133
134/// Conversions between Program and array of normalized parameter values.
135impl Program {
136    /// Converts this program to an array of normalized parameter values.
137    /// You can use this to serialize plugin state.
138    pub fn to_array(&self) -> [f32; 45] {
139        [
140            bool_to_param(self.interpolation),
141            bool_to_param(self.low_cut_enabled),
142            bool_to_param(self.high_cut_enabled),
143            self.input_mix,
144            self.low_cut,
145            self.high_cut,
146            self.dry_out,
147            self.early_out,
148            self.late_out,
149            bool_to_param(self.tap_enabled),
150            self.tap_count,
151            self.tap_decay,
152            self.tap_predelay,
153            self.tap_length,
154            bool_to_param(self.early_diffuse_enabled),
155            self.early_diffuse_count,
156            self.early_diffuse_delay,
157            self.early_diffuse_mod_amount,
158            self.early_diffuse_feedback,
159            self.early_diffuse_mod_rate,
160            match self.late_mode {
161                LateMode::Pre => 0.0,
162                LateMode::Post => 1.0,
163            },
164            self.late_line_count,
165            bool_to_param(self.late_diffuse_enabled),
166            self.late_diffuse_count,
167            self.late_line_size,
168            self.late_line_mod_amount,
169            self.late_diffuse_delay,
170            self.late_diffuse_mod_amount,
171            self.late_line_decay,
172            self.late_line_mod_rate,
173            self.late_diffuse_feedback,
174            self.late_diffuse_mod_rate,
175            bool_to_param(self.eq_low_shelf_enabled),
176            bool_to_param(self.eq_high_shelf_enabled),
177            bool_to_param(self.eq_lowpass_enabled),
178            self.eq_low_freq,
179            self.eq_high_freq,
180            self.eq_cutoff,
181            self.eq_low_gain,
182            self.eq_high_gain,
183            self.eq_cross_seed,
184            self.seed_tap,
185            self.seed_diffusion,
186            self.seed_delay,
187            self.seed_post_diffusion,
188        ]
189    }
190
191    /// Creates a `Program` from a full parameter array.
192    /// This can be used for deserialization purposes.
193    pub fn from_array(a: [f32; 45]) -> Self {
194        Self {
195            interpolation: param_to_bool(a[0]),
196            low_cut_enabled: param_to_bool(a[1]),
197            high_cut_enabled: param_to_bool(a[2]),
198            input_mix: a[3],
199            low_cut: a[4],
200            high_cut: a[5],
201            dry_out: a[6],
202            early_out: a[7],
203            late_out: a[8],
204            tap_enabled: param_to_bool(a[9]),
205            tap_count: a[10],
206            tap_decay: a[11],
207            tap_predelay: a[12],
208            tap_length: a[13],
209            early_diffuse_enabled: param_to_bool(a[14]),
210            early_diffuse_count: a[15],
211            early_diffuse_delay: a[16],
212            early_diffuse_mod_amount: a[17],
213            early_diffuse_feedback: a[18],
214            early_diffuse_mod_rate: a[19],
215            late_mode: if a[20] >= 0.5 {
216                LateMode::Post
217            } else {
218                LateMode::Pre
219            },
220            late_line_count: a[21],
221            late_diffuse_enabled: param_to_bool(a[22]),
222            late_diffuse_count: a[23],
223            late_line_size: a[24],
224            late_line_mod_amount: a[25],
225            late_diffuse_delay: a[26],
226            late_diffuse_mod_amount: a[27],
227            late_line_decay: a[28],
228            late_line_mod_rate: a[29],
229            late_diffuse_feedback: a[30],
230            late_diffuse_mod_rate: a[31],
231            eq_low_shelf_enabled: param_to_bool(a[32]),
232            eq_high_shelf_enabled: param_to_bool(a[33]),
233            eq_lowpass_enabled: param_to_bool(a[34]),
234            eq_low_freq: a[35],
235            eq_high_freq: a[36],
236            eq_cutoff: a[37],
237            eq_low_gain: a[38],
238            eq_high_gain: a[39],
239            eq_cross_seed: a[40],
240            seed_tap: a[41],
241            seed_diffusion: a[42],
242            seed_delay: a[43],
243            seed_post_diffusion: a[44],
244        }
245    }
246
247    /// Tries to create a `Program` from a slice of length 45.
248    /// Returns None if the slice doesn't have the expected length.
249    pub fn from_slice(slice: &[f32]) -> Option<Self> {
250        if slice.len() == 45 {
251            let mut a = [0.0f32; 45];
252            a.copy_from_slice(slice);
253            Some(Self::from_array(a))
254        } else {
255            None
256        }
257    }
258}
259
260/// Get and set parameters by id.
261impl Program {
262    /// Returns the normalized value of the parameter with the given id.
263    pub fn get(&self, id: ParamId) -> f32 {
264        match id {
265            ParamId::Interpolation => bool_to_param(self.interpolation),
266            ParamId::LowCutEnabled => bool_to_param(self.low_cut_enabled),
267            ParamId::HighCutEnabled => bool_to_param(self.high_cut_enabled),
268            ParamId::InputMix => self.input_mix,
269            ParamId::LowCut => self.low_cut,
270            ParamId::HighCut => self.high_cut,
271            ParamId::DryOut => self.dry_out,
272            ParamId::EarlyOut => self.early_out,
273            ParamId::LateOut => self.late_out,
274
275            ParamId::TapEnabled => bool_to_param(self.tap_enabled),
276            ParamId::TapCount => self.tap_count,
277            ParamId::TapDecay => self.tap_decay,
278            ParamId::TapPredelay => self.tap_predelay,
279            ParamId::TapLength => self.tap_length,
280
281            ParamId::EarlyDiffuseEnabled => bool_to_param(self.early_diffuse_enabled),
282            ParamId::EarlyDiffuseCount => self.early_diffuse_count,
283            ParamId::EarlyDiffuseDelay => self.early_diffuse_delay,
284            ParamId::EarlyDiffuseModAmount => self.early_diffuse_mod_amount,
285            ParamId::EarlyDiffuseFeedback => self.early_diffuse_feedback,
286            ParamId::EarlyDiffuseModRate => self.early_diffuse_mod_rate,
287
288            ParamId::LateMode => match self.late_mode {
289                LateMode::Pre => 0.0,
290                LateMode::Post => 1.0,
291            },
292            ParamId::LateLineCount => self.late_line_count,
293            ParamId::LateDiffuseEnabled => bool_to_param(self.late_diffuse_enabled),
294            ParamId::LateDiffuseCount => self.late_diffuse_count,
295            ParamId::LateLineSize => self.late_line_size,
296            ParamId::LateLineModAmount => self.late_line_mod_amount,
297            ParamId::LateDiffuseDelay => self.late_diffuse_delay,
298            ParamId::LateDiffuseModAmount => self.late_diffuse_mod_amount,
299            ParamId::LateLineDecay => self.late_line_decay,
300            ParamId::LateLineModRate => self.late_line_mod_rate,
301            ParamId::LateDiffuseFeedback => self.late_diffuse_feedback,
302            ParamId::LateDiffuseModRate => self.late_diffuse_mod_rate,
303
304            ParamId::EqLowShelfEnabled => bool_to_param(self.eq_low_shelf_enabled),
305            ParamId::EqHighShelfEnabled => bool_to_param(self.eq_high_shelf_enabled),
306            ParamId::EqLowpassEnabled => bool_to_param(self.eq_lowpass_enabled),
307
308            ParamId::EqLowFreq => self.eq_low_freq,
309            ParamId::EqHighFreq => self.eq_high_freq,
310            ParamId::EqCutoff => self.eq_cutoff,
311            ParamId::EqLowGain => self.eq_low_gain,
312            ParamId::EqHighGain => self.eq_high_gain,
313            ParamId::EqCrossSeed => self.eq_cross_seed,
314
315            ParamId::SeedTap => self.seed_tap,
316            ParamId::SeedDiffusion => self.seed_diffusion,
317            ParamId::SeedDelay => self.seed_delay,
318            ParamId::SeedPostDiffusion => self.seed_post_diffusion,
319        }
320    }
321
322    /// Set a parameter by id. Returns previous value on success.
323    pub fn set(&mut self, id: ParamId, value: f32) {
324        match id {
325            ParamId::Interpolation => {
326                self.interpolation = param_to_bool(value);
327            }
328            ParamId::LowCutEnabled => {
329                self.low_cut_enabled = param_to_bool(value);
330            }
331            ParamId::HighCutEnabled => {
332                self.high_cut_enabled = param_to_bool(value);
333            }
334            ParamId::InputMix => {
335                self.input_mix = value;
336            }
337            ParamId::LowCut => {
338                self.low_cut = value;
339            }
340            ParamId::HighCut => {
341                self.high_cut = value;
342            }
343            ParamId::DryOut => {
344                self.dry_out = value;
345            }
346            ParamId::EarlyOut => {
347                self.early_out = value;
348            }
349            ParamId::LateOut => {
350                self.late_out = value;
351            }
352            ParamId::TapEnabled => {
353                self.tap_enabled = param_to_bool(value);
354            }
355            ParamId::TapCount => {
356                self.tap_count = value;
357            }
358            ParamId::TapDecay => {
359                self.tap_decay = value;
360            }
361            ParamId::TapPredelay => {
362                self.tap_predelay = value;
363            }
364            ParamId::TapLength => {
365                self.tap_length = value;
366            }
367            ParamId::EarlyDiffuseEnabled => {
368                self.early_diffuse_enabled = param_to_bool(value);
369            }
370            ParamId::EarlyDiffuseCount => {
371                self.early_diffuse_count = value;
372            }
373            ParamId::EarlyDiffuseDelay => {
374                self.early_diffuse_delay = value;
375            }
376            ParamId::EarlyDiffuseModAmount => {
377                self.early_diffuse_mod_amount = value;
378            }
379            ParamId::EarlyDiffuseFeedback => {
380                self.early_diffuse_feedback = value;
381            }
382            ParamId::EarlyDiffuseModRate => {
383                self.early_diffuse_mod_rate = value;
384            }
385            ParamId::LateMode => {
386                self.late_mode = if value >= 0.5 {
387                    LateMode::Post
388                } else {
389                    LateMode::Pre
390                };
391            }
392            ParamId::LateLineCount => {
393                self.late_line_count = value;
394            }
395            ParamId::LateDiffuseEnabled => {
396                self.late_diffuse_enabled = param_to_bool(value);
397            }
398            ParamId::LateDiffuseCount => {
399                self.late_diffuse_count = value;
400            }
401            ParamId::LateLineSize => {
402                self.late_line_size = value;
403            }
404            ParamId::LateLineModAmount => {
405                self.late_line_mod_amount = value;
406            }
407            ParamId::LateDiffuseDelay => {
408                self.late_diffuse_delay = value;
409            }
410            ParamId::LateDiffuseModAmount => {
411                self.late_diffuse_mod_amount = value;
412            }
413            ParamId::LateLineDecay => {
414                self.late_line_decay = value;
415            }
416            ParamId::LateLineModRate => {
417                self.late_line_mod_rate = value;
418            }
419            ParamId::LateDiffuseFeedback => {
420                self.late_diffuse_feedback = value;
421            }
422            ParamId::LateDiffuseModRate => {
423                self.late_diffuse_mod_rate = value;
424            }
425            ParamId::EqLowShelfEnabled => {
426                self.eq_low_shelf_enabled = param_to_bool(value);
427            }
428            ParamId::EqHighShelfEnabled => {
429                self.eq_high_shelf_enabled = param_to_bool(value);
430            }
431            ParamId::EqLowpassEnabled => {
432                self.eq_lowpass_enabled = param_to_bool(value);
433            }
434            ParamId::EqLowFreq => {
435                self.eq_low_freq = value;
436            }
437            ParamId::EqHighFreq => {
438                self.eq_high_freq = value;
439            }
440            ParamId::EqCutoff => {
441                self.eq_cutoff = value;
442            }
443            ParamId::EqLowGain => {
444                self.eq_low_gain = value;
445            }
446            ParamId::EqHighGain => {
447                self.eq_high_gain = value;
448            }
449            ParamId::EqCrossSeed => {
450                self.eq_cross_seed = value;
451            }
452            ParamId::SeedTap => {
453                self.seed_tap = value;
454            }
455            ParamId::SeedDiffusion => {
456                self.seed_diffusion = value;
457            }
458            ParamId::SeedDelay => {
459                self.seed_delay = value;
460            }
461            ParamId::SeedPostDiffusion => {
462                self.seed_post_diffusion = value;
463            }
464        }
465    }
466}
467
468fn param_to_bool(value: f32) -> bool {
469    value >= 0.5
470}
471
472fn bool_to_param(value: bool) -> f32 {
473    if value {
474        1.0
475    } else {
476        0.0
477    }
478}
479
480/// "Dark Plate" preset as defined by CloudSeedCore
481pub static DARK_PLATE: Program = Program {
482    // Mix
483    interpolation: true,
484    low_cut_enabled: false,
485    high_cut_enabled: false,
486    input_mix: 0.23469999,
487    low_cut: 0.63999999,
488    high_cut: 0.29330000,
489    dry_out: 0.8706000,
490    early_out: 0.0,
491    late_out: 0.66139996,
492
493    // Tap
494    tap_enabled: false,
495    tap_count: 0.19599999,
496    tap_decay: 1.0,
497    tap_predelay: 0.0,
498    tap_length: 0.98670000,
499
500    // Early
501    early_diffuse_enabled: false,
502    early_diffuse_count: 0.29600000,
503    early_diffuse_delay: 0.30669999,
504    early_diffuse_mod_amount: 0.14389999,
505    early_diffuse_feedback: 0.77069998,
506    early_diffuse_mod_rate: 0.24669999,
507
508    // Late
509    late_mode: LateMode::Post,
510    late_line_count: 1.0,
511    late_diffuse_enabled: true,
512    late_diffuse_count: 0.48799998,
513    late_line_size: 0.46939999,
514    late_line_mod_amount: 0.27199998,
515    late_diffuse_delay: 0.23999999,
516    late_diffuse_mod_amount: 0.14680000,
517    late_line_decay: 0.63460000,
518    late_line_mod_rate: 0.22929999,
519    late_diffuse_feedback: 0.85069996,
520    late_diffuse_mod_rate: 0.16669999,
521
522    // EQ
523    eq_low_shelf_enabled: false,
524    eq_high_shelf_enabled: true,
525    eq_lowpass_enabled: false,
526    eq_low_freq: 0.38799998,
527    eq_high_freq: 0.51339996,
528    eq_cutoff: 0.97599995,
529    eq_low_gain: 0.55599999,
530    eq_high_gain: 0.76800001,
531    eq_cross_seed: 0.0,
532
533    // Seeds
534    seed_tap: 0.33399999,
535    seed_diffusion: 0.18500000,
536    seed_delay: 0.21810000,
537    seed_post_diffusion: 0.36530000,
538};