1use super::registry::{CloneableEffect, EffectRegistry};
3use crate::engine::audio::lfo::{LfoParams, LfoRate, LfoTarget, LfoWaveform};
4use crate::language::syntax::ast::Value;
5use std::collections::HashMap;
6
7#[derive(Debug)]
9pub struct EffectChain {
10 effects: Vec<Box<dyn CloneableEffect>>,
11 registry: EffectRegistry,
12 synth_context: bool,
13}
14
15impl EffectChain {
16 pub fn new(synth_context: bool) -> Self {
18 Self {
19 effects: Vec::new(),
20 registry: EffectRegistry::new(),
21 synth_context,
22 }
23 }
24
25 pub fn add_effect(&mut self, name: &str, params: Option<Value>) -> bool {
27 if !self.registry.is_effect_available(name, self.synth_context) {
28 return false;
29 }
30
31 if let Some(processor) =
32 build_effect_processor(&self.registry, name, params, self.synth_context)
33 {
34 self.effects.push(processor);
35 true
36 } else {
37 false
38 }
39 }
40
41 pub fn process(&mut self, samples: &mut [f32], sample_rate: u32) {
43 for effect in &mut self.effects {
44 effect.process(samples, sample_rate);
45 }
46 }
47
48 pub fn reset(&mut self) {
50 for effect in &mut self.effects {
51 effect.reset();
52 }
53 }
54
55 pub fn len(&self) -> usize {
57 self.effects.len()
58 }
59
60 pub fn is_empty(&self) -> bool {
62 self.effects.is_empty()
63 }
64
65 pub fn available_effects(&self) -> Vec<&'static str> {
67 self.registry.list_available_effects(self.synth_context)
68 }
69
70 pub fn is_effect_available(&self, name: &str) -> bool {
72 self.registry.is_effect_available(name, self.synth_context)
73 }
74}
75
76impl Default for EffectChain {
77 fn default() -> Self {
78 Self::new(true) }
80}
81
82pub fn build_effect_chain(effects_array: &[Value], synth_context: bool) -> EffectChain {
84 let mut chain = EffectChain::new(synth_context);
85
86 for effect_value in effects_array {
87 match effect_value {
88 Value::Map(map) => {
89 if let Some(effect_type) = map.get("type").or_else(|| map.get("effect")) {
91 if let Value::String(s) | Value::Identifier(s) = effect_type {
92 chain.add_effect(s, Some(Value::Map(map.clone())));
93 }
94 } else {
95 for (k, v) in map.iter() {
98 match v {
99 Value::Map(sm) => {
100 chain.add_effect(k, Some(Value::Map(sm.clone())));
101 }
102 _ => {
103 let mut sub = std::collections::HashMap::new();
105 sub.insert("value".to_string(), v.clone());
106 chain.add_effect(k, Some(Value::Map(sub.clone())));
107 }
108 }
109 }
110 }
111 }
112 Value::String(s) | Value::Identifier(s) => {
113 chain.add_effect(s, None);
114 }
115 _ => {}
116 }
117 }
118
119 chain
120}
121
122fn build_effect_processor(
124 registry: &EffectRegistry,
125 name: &str,
126 params: Option<Value>,
127 synth_context: bool,
128) -> Option<Box<dyn CloneableEffect>> {
129 let base_processor = registry.get_effect(name, synth_context)?;
130
131 if let Some(Value::Map(params_map)) = params {
132 match name {
133 "chorus" => {
134 let depth = get_f32_param(¶ms_map, "depth", 0.7);
135 let rate = get_f32_param(¶ms_map, "rate", 0.5);
136 let mix = get_f32_param(¶ms_map, "mix", 0.5);
137 Some(Box::new(super::processors::ChorusProcessor::new(
138 depth, rate, mix,
139 )))
140 }
141 "flanger" => {
142 let depth = get_f32_param(¶ms_map, "depth", 0.7);
143 let rate = get_f32_param(¶ms_map, "rate", 0.5);
144 let feedback = get_f32_param(¶ms_map, "feedback", 0.5);
145 let mix = get_f32_param(¶ms_map, "mix", 0.5);
146 Some(Box::new(super::processors::FlangerProcessor::new(
147 depth, rate, feedback, mix,
148 )))
149 }
150 "phaser" => {
151 let stages = get_f32_param(¶ms_map, "stages", 4.0) as usize;
152 let rate = get_f32_param(¶ms_map, "rate", 0.5);
153 let depth = get_f32_param(¶ms_map, "depth", 0.7);
154 let feedback = get_f32_param(¶ms_map, "feedback", 0.5);
155 let mix = get_f32_param(¶ms_map, "mix", 0.5);
156 Some(Box::new(super::processors::PhaserProcessor::new(
157 stages, rate, depth, feedback, mix,
158 )))
159 }
160 "compressor" => {
161 let threshold = get_f32_param(¶ms_map, "threshold", -20.0);
162 let ratio = get_f32_param(¶ms_map, "ratio", 4.0);
163 let attack = get_f32_param(¶ms_map, "attack", 0.005);
164 let release = get_f32_param(¶ms_map, "release", 0.1);
165 Some(Box::new(super::processors::CompressorProcessor::new(
166 threshold, ratio, attack, release,
167 )))
168 }
169 "drive" => {
170 let amount = get_f32_param(¶ms_map, "amount", 0.7);
171 let mix = get_f32_param(¶ms_map, "mix", 0.5);
172 let tone = get_f32_param(¶ms_map, "tone", 0.5);
173 let color = get_f32_param(¶ms_map, "color", 0.5);
174
175 Some(Box::new(super::processors::DriveProcessor::new(
176 amount, tone, color, mix,
177 )))
178 }
179 "reverb" => {
180 let size = get_f32_param(¶ms_map, "size", 0.5);
181 let damping = get_f32_param(¶ms_map, "damping", 0.5);
182 let decay = get_f32_param(¶ms_map, "decay", 0.5);
183 let mix = get_f32_param(¶ms_map, "mix", 0.3);
184
185 Some(Box::new(super::processors::ReverbProcessor::new(
186 size, damping, decay, mix,
187 )))
188 }
189 "delay" => {
190 let time = get_f32_param(¶ms_map, "time", 250.0);
191 let feedback = get_f32_param(¶ms_map, "feedback", 0.4);
192 let mix = get_f32_param(¶ms_map, "mix", 0.3);
193 Some(Box::new(super::processors::DelayProcessor::new(
194 time, feedback, mix,
195 )))
196 }
197 "eq" | "equalizer" => {
198 let low = get_f32_param(¶ms_map, "low", 1.0);
199 let mid = get_f32_param(¶ms_map, "mid", 1.0);
200 let high = get_f32_param(¶ms_map, "high", 1.0);
201 Some(Box::new(super::processors::EqProcessor::new(
202 low, mid, high,
203 )))
204 }
205 "lowpass" | "lpf" => {
206 let cutoff = get_f32_param(¶ms_map, "cutoff", 5000.0);
207 let resonance = get_f32_param(¶ms_map, "resonance", 0.1);
208 Some(Box::new(super::processors::LowpassProcessor::new(
209 cutoff, resonance,
210 )))
211 }
212 "highpass" | "hpf" => {
213 let cutoff = get_f32_param(¶ms_map, "cutoff", 200.0);
214 let resonance = get_f32_param(¶ms_map, "resonance", 0.1);
215 Some(Box::new(super::processors::HighpassProcessor::new(
216 cutoff, resonance,
217 )))
218 }
219 "bandpass" | "bpf" => {
220 let cutoff = get_f32_param(¶ms_map, "cutoff", 1000.0);
221 let resonance = get_f32_param(¶ms_map, "resonance", 0.2);
222 Some(Box::new(super::processors::BandpassProcessor::new(
223 cutoff, resonance,
224 )))
225 }
226 "distortion" | "dist" => {
227 let amount = get_f32_param(¶ms_map, "amount", 0.5);
228 let mix = get_f32_param(¶ms_map, "mix", 0.5);
229 Some(Box::new(super::processors::DistortionProcessor::new(
230 amount, mix,
231 )))
232 }
233 "speed" => {
234 let speed = get_f32_param(
236 ¶ms_map,
237 "speed",
238 get_f32_param(¶ms_map, "value", 1.0),
239 );
240
241 Some(Box::new(super::processors::SpeedProcessor::new(speed)))
242 }
243 "lfo" => {
244 let bpm = get_f32_param(¶ms_map, "bpm", 120.0);
247 let depth = get_f32_param(¶ms_map, "depth", 0.5).clamp(0.0, 1.0);
248 let phase = get_f32_param(¶ms_map, "phase", 0.0).fract();
249
250 let waveform = params_map
252 .get("waveform")
253 .and_then(|v| match v {
254 Value::String(s) | Value::Identifier(s) => Some(LfoWaveform::from_str(s)),
255 _ => None,
256 })
257 .unwrap_or(LfoWaveform::Sine);
258
259 let rate_value_opt = params_map.get("rate");
261 let rate = match rate_value_opt {
262 Some(Value::String(s)) | Some(Value::Identifier(s)) => LfoRate::from_value(s),
263 Some(Value::Number(n)) => LfoRate::Hz(*n),
264 _ => LfoRate::Hz(1.0),
265 };
266
267 let target = params_map
269 .get("target")
270 .and_then(|v| match v {
271 Value::String(s) | Value::Identifier(s) => LfoTarget::from_str(s),
272 _ => None,
273 })
274 .unwrap_or(LfoTarget::Volume);
275
276 let params = LfoParams {
277 rate,
278 depth,
279 waveform,
280 target,
281 phase,
282 };
283 let semitones = get_f32_param(¶ms_map, "semitones", 2.0);
285 let base_cutoff = get_f32_param(¶ms_map, "cutoff", 1000.0);
286 let cutoff_range = get_f32_param(¶ms_map, "cutoff_range", 1000.0);
287 Some(Box::new(super::processors::LfoProcessor::new(
288 params,
289 bpm,
290 semitones,
291 base_cutoff,
292 cutoff_range,
293 )))
294 }
295 "reverse" => {
296 let reverse = get_bool_param(
297 ¶ms_map,
298 "reverse",
299 get_bool_param(¶ms_map, "value", true),
300 );
301 Some(Box::new(super::processors::ReverseProcessor::new(reverse)))
302 }
303 _ => Some(base_processor),
304 }
305 } else {
306 Some(base_processor)
307 }
308}
309
310fn get_f32_param(map: &HashMap<String, Value>, key: &str, default: f32) -> f32 {
312 map.get(key)
313 .and_then(|v| match v {
314 Value::Number(n) => Some(*n),
315 Value::String(s) => s.parse::<f32>().ok(),
316 _ => None,
317 })
318 .unwrap_or(default)
319}
320
321fn get_bool_param(map: &HashMap<String, Value>, key: &str, default: bool) -> bool {
323 map.get(key)
324 .and_then(|v| match v {
325 Value::Boolean(b) => Some(*b),
326 Value::Number(n) => Some(*n != 0.0),
327 Value::String(s) => s.parse::<bool>().ok(),
328 _ => None,
329 })
330 .unwrap_or(default)
331}
332
333#[cfg(test)]
334mod tests {
335 use super::*;
336
337 #[test]
338 fn test_effect_chain_creation() {
339 let chain = EffectChain::new(true); assert_eq!(chain.len(), 0);
341 assert!(chain.is_empty());
342
343 let synth_effects = chain.available_effects();
345 assert!(synth_effects.contains(&"reverb"));
346 assert!(!synth_effects.contains(&"speed")); }
348
349 #[test]
350 fn test_effect_chain_trigger_context() {
351 let mut chain = EffectChain::new(false); assert!(chain.is_effect_available("speed"));
355 assert!(chain.is_effect_available("reverse"));
356
357 assert!(chain.add_effect("speed", None));
359 assert_eq!(chain.len(), 1);
360 }
361
362 #[test]
363 fn test_effect_parameter_parsing() {
364 let mut chain = EffectChain::new(true);
365
366 let mut params = HashMap::new();
368 params.insert("depth".to_string(), Value::Number(0.8));
369 params.insert("rate".to_string(), Value::Number(1.0));
370 params.insert("mix".to_string(), Value::Number(0.6));
371
372 assert!(chain.add_effect("chorus", Some(Value::Map(params))));
373 assert_eq!(chain.len(), 1);
374 }
375
376 #[test]
377 fn test_filter_effects_accept_parameter_maps() {
378 let mut params = HashMap::new();
379 params.insert("cutoff".to_string(), Value::Number(900.0));
380 params.insert("resonance".to_string(), Value::Number(0.2));
381
382 let effects = vec![Value::Map({
383 let mut effect = HashMap::new();
384 effect.insert("type".to_string(), Value::String("lowpass".to_string()));
385 effect.extend(params);
386 effect
387 })];
388
389 let chain = build_effect_chain(&effects, true);
390 assert_eq!(chain.len(), 1);
391 }
392
393 #[test]
394 fn test_eq_effect_accepts_three_band_map() {
395 let effects = vec![Value::Map({
396 let mut effect = HashMap::new();
397 effect.insert("type".to_string(), Value::String("eq".to_string()));
398 effect.insert("low".to_string(), Value::Number(1.2));
399 effect.insert("mid".to_string(), Value::Number(0.8));
400 effect.insert("high".to_string(), Value::Number(1.1));
401 effect
402 })];
403
404 let chain = build_effect_chain(&effects, true);
405 assert_eq!(chain.len(), 1);
406 }
407
408 #[test]
409 fn test_effect_context_restrictions() {
410 let mut synth_chain = EffectChain::new(true);
411 let mut trigger_chain = EffectChain::new(false);
412
413 assert!(!synth_chain.add_effect("speed", None));
415 assert!(trigger_chain.add_effect("speed", None));
416
417 assert!(synth_chain.add_effect("reverb", None));
419 assert!(trigger_chain.add_effect("reverb", None));
420 }
421}