devalang_wasm/engine/audio/effects/
mod.rs1pub mod chain;
3pub mod processors;
4
5use crate::language::syntax::ast::Value;
6use std::collections::HashMap;
7
8#[derive(Debug, Clone)]
10pub struct EffectParams {
11 pub gain: f32,
12 pub pan: f32,
13 pub fade_in: f32,
14 pub fade_out: f32,
15 pub pitch: f32,
16 pub drive: f32,
17 pub reverb: f32,
18 pub delay: f32,
19}
20
21impl Default for EffectParams {
22 fn default() -> Self {
23 Self {
24 gain: 1.0,
25 pan: 0.0,
26 fade_in: 0.0,
27 fade_out: 0.0,
28 pitch: 1.0,
29 drive: 0.0,
30 reverb: 0.0,
31 delay: 0.0,
32 }
33 }
34}
35
36pub fn extract_effect_params(effects: &Option<Value>) -> EffectParams {
38 let mut params = EffectParams::default();
39
40 if let Some(Value::Map(map)) = effects {
41 params.gain = param_as_f32(map, &["gain", "volume", "vol"], 1.0);
42 params.pan = param_as_f32(map, &["pan", "panning"], 0.0);
43 params.fade_in = param_as_f32(map, &["fadeIn", "fadein", "fade_in"], 0.0);
44 params.fade_out = param_as_f32(map, &["fadeOut", "fadeout", "fade_out"], 0.0);
45 params.pitch = param_as_f32(map, &["pitch", "tune"], 1.0);
46 params.drive = param_as_f32(map, &["drive", "distortion"], 0.0);
47 params.reverb = param_as_f32(map, &["reverb", "verb"], 0.0);
48 params.delay = param_as_f32(map, &["delay", "echo"], 0.0);
49 }
50
51 params
52}
53
54pub fn param_as_f32(params: &HashMap<String, Value>, names: &[&str], default: f32) -> f32 {
56 for name in names.iter() {
57 if let Some(v) = params.get(*name) {
58 match v {
59 Value::Number(n) => return *n,
60 Value::String(s) | Value::Identifier(s) => {
61 if let Ok(parsed) = s.parse::<f32>() {
62 return parsed;
63 }
64 }
65 Value::Boolean(b) => return if *b { 1.0 } else { 0.0 },
66 _ => {}
67 }
68 }
69 }
70 default
71}
72
73pub fn normalize_effects(effects: &Option<Value>) -> HashMap<String, HashMap<String, Value>> {
75 let mut out: HashMap<String, HashMap<String, Value>> = HashMap::new();
76
77 if let Some(Value::Map(map)) = effects {
78 for (k, v) in map.iter() {
79 match v {
80 Value::Number(_) | Value::Boolean(_) | Value::String(_) | Value::Identifier(_) => {
81 let mut sub = HashMap::new();
82 sub.insert("value".to_string(), v.clone());
83 out.insert(k.clone(), sub);
84 }
85 Value::Map(sm) => {
86 out.insert(k.clone(), sm.clone());
87 }
88 _ => {}
89 }
90 }
91 }
92
93 out
94}
95
96pub fn merge_effect_entry(map: &mut HashMap<String, Value>, key: &str, val: &Value) {
98 match val {
99 Value::Number(_) | Value::Boolean(_) | Value::String(_) | Value::Identifier(_) => {
100 map.insert(key.to_string(), val.clone());
101 }
102 Value::Map(m) => {
103 map.insert(key.to_string(), Value::Map(m.clone()));
104 }
105 _ => {}
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 #[test]
114 fn test_extract_effect_params_defaults() {
115 let params = extract_effect_params(&None);
116 assert!((params.gain - 1.0).abs() < f32::EPSILON);
117 assert!((params.pan - 0.0).abs() < f32::EPSILON);
118 }
119
120 #[test]
121 fn test_extract_effect_params_with_values() {
122 let mut map = HashMap::new();
123 map.insert("gain".to_string(), Value::Number(0.5));
124 map.insert("pan".to_string(), Value::Number(-0.3));
125
126 let params = extract_effect_params(&Some(Value::Map(map)));
127 assert!((params.gain - 0.5).abs() < f32::EPSILON);
128 assert!((params.pan + 0.3).abs() < f32::EPSILON);
129 }
130
131 #[test]
132 fn test_param_as_f32_with_aliases() {
133 let mut map = HashMap::new();
134 map.insert("volume".to_string(), Value::Number(0.8));
135
136 let result = param_as_f32(&map, &["gain", "volume", "vol"], 1.0);
137 assert!((result - 0.8).abs() < f32::EPSILON);
138 }
139}