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 "speed" => {
198 let speed = get_f32_param(
200 ¶ms_map,
201 "speed",
202 get_f32_param(¶ms_map, "value", 1.0),
203 );
204
205 Some(Box::new(super::processors::SpeedProcessor::new(speed)))
206 }
207 "lfo" => {
208 let bpm = get_f32_param(¶ms_map, "bpm", 120.0);
211 let depth = get_f32_param(¶ms_map, "depth", 0.5).clamp(0.0, 1.0);
212 let phase = get_f32_param(¶ms_map, "phase", 0.0).fract();
213
214 let waveform = params_map
216 .get("waveform")
217 .and_then(|v| match v {
218 Value::String(s) | Value::Identifier(s) => Some(LfoWaveform::from_str(s)),
219 _ => None,
220 })
221 .unwrap_or(LfoWaveform::Sine);
222
223 let rate_value_opt = params_map.get("rate");
225 let rate = match rate_value_opt {
226 Some(Value::String(s)) | Some(Value::Identifier(s)) => LfoRate::from_value(s),
227 Some(Value::Number(n)) => LfoRate::Hz(*n),
228 _ => LfoRate::Hz(1.0),
229 };
230
231 let target = params_map
233 .get("target")
234 .and_then(|v| match v {
235 Value::String(s) | Value::Identifier(s) => LfoTarget::from_str(s),
236 _ => None,
237 })
238 .unwrap_or(LfoTarget::Volume);
239
240 let params = LfoParams {
241 rate,
242 depth,
243 waveform,
244 target,
245 phase,
246 };
247 let semitones = get_f32_param(¶ms_map, "semitones", 2.0);
249 let base_cutoff = get_f32_param(¶ms_map, "cutoff", 1000.0);
250 let cutoff_range = get_f32_param(¶ms_map, "cutoff_range", 1000.0);
251 Some(Box::new(super::processors::LfoProcessor::new(
252 params,
253 bpm,
254 semitones,
255 base_cutoff,
256 cutoff_range,
257 )))
258 }
259 "reverse" => {
260 let reverse = get_bool_param(
261 ¶ms_map,
262 "reverse",
263 get_bool_param(¶ms_map, "value", true),
264 );
265 Some(Box::new(super::processors::ReverseProcessor::new(reverse)))
266 }
267 _ => Some(base_processor),
268 }
269 } else {
270 Some(base_processor)
271 }
272}
273
274fn get_f32_param(map: &HashMap<String, Value>, key: &str, default: f32) -> f32 {
276 map.get(key)
277 .and_then(|v| match v {
278 Value::Number(n) => Some(*n),
279 Value::String(s) => s.parse::<f32>().ok(),
280 _ => None,
281 })
282 .unwrap_or(default)
283}
284
285fn get_bool_param(map: &HashMap<String, Value>, key: &str, default: bool) -> bool {
287 map.get(key)
288 .and_then(|v| match v {
289 Value::Boolean(b) => Some(*b),
290 Value::Number(n) => Some(*n != 0.0),
291 Value::String(s) => s.parse::<bool>().ok(),
292 _ => None,
293 })
294 .unwrap_or(default)
295}
296
297#[cfg(test)]
298mod tests {
299 use super::*;
300
301 #[test]
302 fn test_effect_chain_creation() {
303 let chain = EffectChain::new(true); assert_eq!(chain.len(), 0);
305 assert!(chain.is_empty());
306
307 let synth_effects = chain.available_effects();
309 assert!(synth_effects.contains(&"reverb"));
310 assert!(!synth_effects.contains(&"speed")); }
312
313 #[test]
314 fn test_effect_chain_trigger_context() {
315 let mut chain = EffectChain::new(false); assert!(chain.is_effect_available("speed"));
319 assert!(chain.is_effect_available("reverse"));
320
321 assert!(chain.add_effect("speed", None));
323 assert_eq!(chain.len(), 1);
324 }
325
326 #[test]
327 fn test_effect_parameter_parsing() {
328 let mut chain = EffectChain::new(true);
329
330 let mut params = HashMap::new();
332 params.insert("depth".to_string(), Value::Number(0.8));
333 params.insert("rate".to_string(), Value::Number(1.0));
334 params.insert("mix".to_string(), Value::Number(0.6));
335
336 assert!(chain.add_effect("chorus", Some(Value::Map(params))));
337 assert_eq!(chain.len(), 1);
338 }
339
340 #[test]
341 fn test_effect_context_restrictions() {
342 let mut synth_chain = EffectChain::new(true);
343 let mut trigger_chain = EffectChain::new(false);
344
345 assert!(!synth_chain.add_effect("speed", None));
347 assert!(trigger_chain.add_effect("speed", None));
348
349 assert!(synth_chain.add_effect("reverb", None));
351 assert!(trigger_chain.add_effect("reverb", None));
352 }
353}