devalang_wasm/engine/audio/effects/
chain.rs1use super::processors::{
3 ChorusProcessor, CompressorProcessor, DistortionProcessor, EffectProcessor, FlangerProcessor,
4 PhaserProcessor,
5};
6use crate::language::syntax::ast::Value;
7use std::collections::HashMap;
8
9#[derive(Debug)]
11pub struct EffectChain {
12 effects: Vec<Box<dyn EffectProcessor>>,
13}
14
15impl EffectChain {
16 pub fn new() -> Self {
17 Self {
18 effects: Vec::new(),
19 }
20 }
21
22 pub fn add_effect(&mut self, effect: Box<dyn EffectProcessor>) {
24 self.effects.push(effect);
25 }
26
27 pub fn process(&mut self, samples: &mut [f32], sample_rate: u32) {
29 for effect in &mut self.effects {
30 effect.process(samples, sample_rate);
31 }
32 }
33
34 pub fn reset(&mut self) {
36 for effect in &mut self.effects {
37 effect.reset();
38 }
39 }
40
41 pub fn len(&self) -> usize {
43 self.effects.len()
44 }
45
46 pub fn is_empty(&self) -> bool {
48 self.effects.is_empty()
49 }
50}
51
52impl Default for EffectChain {
53 fn default() -> Self {
54 Self::new()
55 }
56}
57
58pub fn build_effect_chain(effects_array: &[Value]) -> EffectChain {
60 let mut chain = EffectChain::new();
61
62 for effect_value in effects_array {
63 if let Some(processor) = build_effect_processor(effect_value) {
64 chain.add_effect(processor);
65 }
66 }
67
68 chain
69}
70
71fn build_effect_processor(value: &Value) -> Option<Box<dyn EffectProcessor>> {
73 match value {
74 Value::Map(map) => {
75 let effect_type =
77 map.get("type")
78 .or_else(|| map.get("effect"))
79 .and_then(|v| match v {
80 Value::String(s) | Value::Identifier(s) => Some(s.clone()),
81 _ => None,
82 })?;
83
84 match effect_type.to_lowercase().as_str() {
85 "chorus" => {
86 let depth = get_f32_param(map, "depth", 0.7);
87 let rate = get_f32_param(map, "rate", 0.5);
88 let mix = get_f32_param(map, "mix", 0.5);
89 Some(Box::new(ChorusProcessor::new(depth, rate, mix)))
90 }
91 "flanger" => {
92 let depth = get_f32_param(map, "depth", 0.7);
93 let rate = get_f32_param(map, "rate", 0.5);
94 let feedback = get_f32_param(map, "feedback", 0.5);
95 let mix = get_f32_param(map, "mix", 0.5);
96 Some(Box::new(FlangerProcessor::new(depth, rate, feedback, mix)))
97 }
98 "phaser" => {
99 let stages = get_f32_param(map, "stages", 4.0) as usize;
100 let rate = get_f32_param(map, "rate", 0.5);
101 let depth = get_f32_param(map, "depth", 0.7);
102 let feedback = get_f32_param(map, "feedback", 0.5);
103 let mix = get_f32_param(map, "mix", 0.5);
104 Some(Box::new(PhaserProcessor::new(
105 stages, rate, depth, feedback, mix,
106 )))
107 }
108 "compressor" => {
109 let threshold = get_f32_param(map, "threshold", -20.0);
110 let ratio = get_f32_param(map, "ratio", 4.0);
111 let attack = get_f32_param(map, "attack", 0.005);
112 let release = get_f32_param(map, "release", 0.1);
113 Some(Box::new(CompressorProcessor::new(
114 threshold, ratio, attack, release,
115 )))
116 }
117 "distortion" => {
118 let drive = get_f32_param(map, "drive", 10.0);
119 let mix = get_f32_param(map, "mix", 0.5);
120 Some(Box::new(DistortionProcessor::new(drive, mix)))
121 }
122 _ => None,
123 }
124 }
125 Value::String(s) | Value::Identifier(s) => {
126 match s.to_lowercase().as_str() {
128 "chorus" => Some(Box::new(ChorusProcessor::default())),
129 "flanger" => Some(Box::new(FlangerProcessor::default())),
130 "phaser" => Some(Box::new(PhaserProcessor::default())),
131 "compressor" => Some(Box::new(CompressorProcessor::default())),
132 "distortion" => Some(Box::new(DistortionProcessor::default())),
133 _ => None,
134 }
135 }
136 _ => None,
137 }
138}
139
140fn get_f32_param(map: &HashMap<String, Value>, key: &str, default: f32) -> f32 {
142 map.get(key)
143 .and_then(|v| match v {
144 Value::Number(n) => Some(*n),
145 Value::String(s) => s.parse::<f32>().ok(),
146 _ => None,
147 })
148 .unwrap_or(default)
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 #[test]
156 fn test_effect_chain_creation() {
157 let mut chain = EffectChain::new();
158 assert_eq!(chain.len(), 0);
159 assert!(chain.is_empty());
160
161 chain.add_effect(Box::new(ChorusProcessor::default()));
162 assert_eq!(chain.len(), 1);
163 assert!(!chain.is_empty());
164 }
165
166 #[test]
167 fn test_build_effect_from_string() {
168 let value = Value::String("chorus".to_string());
169 let processor = build_effect_processor(&value);
170 assert!(processor.is_some());
171 }
172
173 #[test]
174 fn test_build_effect_from_map() {
175 let mut map = HashMap::new();
176 map.insert("type".to_string(), Value::String("chorus".to_string()));
177 map.insert("depth".to_string(), Value::Number(0.8));
178 map.insert("rate".to_string(), Value::Number(1.0));
179
180 let value = Value::Map(map);
181 let processor = build_effect_processor(&value);
182 assert!(processor.is_some());
183 }
184}