devalang_wasm/engine/functions/
effects.rs1use super::{FunctionContext, FunctionExecutor};
3use crate::language::syntax::ast::nodes::Value;
4use anyhow::{Result, anyhow};
5
6pub struct VelocityFunction;
8
9impl FunctionExecutor for VelocityFunction {
10 fn name(&self) -> &str {
11 "velocity"
12 }
13
14 fn execute(&self, context: &mut FunctionContext, args: &[Value]) -> Result<()> {
15 if args.is_empty() {
16 return Err(anyhow!(
17 "velocity() requires 1 argument (velocity value 0-127)"
18 ));
19 }
20
21 let velocity = match &args[0] {
22 Value::Number(v) => *v,
23 _ => return Err(anyhow!("velocity() argument must be a number")),
24 };
25
26 context.set("velocity", Value::Number(velocity));
27 Ok(())
28 }
29}
30
31pub struct DurationFunction;
33
34impl FunctionExecutor for DurationFunction {
35 fn name(&self) -> &str {
36 "duration"
37 }
38
39 fn execute(&self, context: &mut FunctionContext, args: &[Value]) -> Result<()> {
40 if args.is_empty() {
41 return Err(anyhow!(
42 "duration() requires 1 argument (duration in ms or beats fraction like 1/4)"
43 ));
44 }
45
46 let duration_seconds = match &args[0] {
47 Value::Number(d) => *d / 1000.0,
49
50 Value::String(s) | Value::Identifier(s) => {
52 let frac_str = s.trim().trim_matches('"').trim_matches('\'');
53
54 use crate::language::syntax::parser::driver::duration::parse_duration_token;
56 match parse_duration_token(frac_str) {
57 Ok(crate::language::syntax::ast::DurationValue::Auto) => {
58 return Err(anyhow!("duration() cannot use 'auto'"));
59 }
60 Ok(duration) => crate::engine::audio::duration::duration_to_seconds(
61 &duration,
62 context.tempo,
63 )?,
64 Err(_) => {
65 return Err(anyhow!(
67 "Invalid duration format: '{}'. Use: number (ms), 'ms', fraction (1/4), or temporal (1 beat, 2 steps, 3 bar, etc.)",
68 s
69 ));
70 }
71 }
72 }
73
74 _ => {
75 return Err(anyhow!(
76 "duration() argument must be a number, string, or identifier"
77 ));
78 }
79 };
80
81 let duration_ms = duration_seconds * 1000.0;
83 context.set("duration", Value::Number(duration_ms));
84 context.duration = duration_seconds;
85 Ok(())
86 }
87}
88
89fn parse_fraction_beats(s: &str) -> Option<f32> {
91 let mut parts = s.split('/');
92 let numerator: f32 = parts.next()?.trim().parse().ok()?;
93 let denominator: f32 = parts.next()?.trim().parse().ok()?;
94 if denominator.abs() < f32::EPSILON {
95 return None;
96 }
97 Some(numerator / denominator)
98}
99
100pub struct PanFunction;
102
103impl FunctionExecutor for PanFunction {
104 fn name(&self) -> &str {
105 "pan"
106 }
107
108 fn execute(&self, context: &mut FunctionContext, args: &[Value]) -> Result<()> {
109 if args.is_empty() {
110 return Err(anyhow!("pan() requires 1 argument (pan value -1.0 to 1.0)"));
111 }
112
113 let pan = match &args[0] {
114 Value::Number(p) => *p,
115 _ => return Err(anyhow!("pan() argument must be a number")),
116 };
117
118 context.set("pan", Value::Number(pan));
119 Ok(())
120 }
121}
122
123pub struct DetuneFunction;
125
126impl FunctionExecutor for DetuneFunction {
127 fn name(&self) -> &str {
128 "detune"
129 }
130
131 fn execute(&self, context: &mut FunctionContext, args: &[Value]) -> Result<()> {
132 if args.is_empty() {
133 return Err(anyhow!("detune() requires 1 argument (cents)"));
134 }
135
136 let detune = match &args[0] {
137 Value::Number(d) => *d,
138 _ => return Err(anyhow!("detune() argument must be a number")),
139 };
140
141 context.set("detune", Value::Number(detune));
142 Ok(())
143 }
144}
145
146pub struct SpreadFunction;
148
149impl FunctionExecutor for SpreadFunction {
150 fn name(&self) -> &str {
151 "spread"
152 }
153
154 fn execute(&self, context: &mut FunctionContext, args: &[Value]) -> Result<()> {
155 if args.is_empty() {
156 return Err(anyhow!(
157 "spread() requires 1 argument (spread value 0.0 to 1.0)"
158 ));
159 }
160
161 let spread = match &args[0] {
162 Value::Number(s) => *s,
163 _ => return Err(anyhow!("spread() argument must be a number")),
164 };
165
166 context.set("spread", Value::Number(spread));
167 Ok(())
168 }
169}
170
171pub struct GainFunction;
173
174impl FunctionExecutor for GainFunction {
175 fn name(&self) -> &str {
176 "gain"
177 }
178
179 fn execute(&self, context: &mut FunctionContext, args: &[Value]) -> Result<()> {
180 if args.is_empty() {
181 return Err(anyhow!("gain() requires 1 argument (gain multiplier)"));
182 }
183
184 let gain = match &args[0] {
185 Value::Number(g) => *g,
186 _ => return Err(anyhow!("gain() argument must be a number")),
187 };
188
189 context.set("gain", Value::Number(gain));
190 Ok(())
191 }
192}
193
194pub struct AttackFunction;
196
197impl FunctionExecutor for AttackFunction {
198 fn name(&self) -> &str {
199 "attack"
200 }
201
202 fn execute(&self, context: &mut FunctionContext, args: &[Value]) -> Result<()> {
203 if args.is_empty() {
204 return Err(anyhow!("attack() requires 1 argument (attack time in ms)"));
205 }
206
207 let attack = match &args[0] {
208 Value::Number(a) => *a,
209 _ => return Err(anyhow!("attack() argument must be a number")),
210 };
211
212 context.set("attack", Value::Number(attack));
213 Ok(())
214 }
215}
216
217pub struct ReleaseFunction;
219
220impl FunctionExecutor for ReleaseFunction {
221 fn name(&self) -> &str {
222 "release"
223 }
224
225 fn execute(&self, context: &mut FunctionContext, args: &[Value]) -> Result<()> {
226 if args.is_empty() {
227 return Err(anyhow!(
228 "release() requires 1 argument (release time in ms)"
229 ));
230 }
231
232 let release = match &args[0] {
233 Value::Number(r) => *r,
234 _ => return Err(anyhow!("release() argument must be a number")),
235 };
236
237 context.set("release", Value::Number(release));
238 Ok(())
239 }
240}
241
242pub struct DelayFunction;
245
246impl FunctionExecutor for DelayFunction {
247 fn name(&self) -> &str {
248 "delay"
249 }
250
251 fn execute(&self, context: &mut FunctionContext, args: &[Value]) -> Result<()> {
252 if args.is_empty() {
253 return Err(anyhow!(
254 "delay() requires at least 1 argument (delay time in ms)"
255 ));
256 }
257 let mut time_val: Option<f32> = None;
260 let mut feedback: f32 = 0.3;
261 let mut mix: f32 = 0.5;
262
263 if let Some(Value::Map(params)) = args.first() {
264 if let Some(Value::Number(t)) = params.get("time") {
265 time_val = Some(*t);
266 }
267 if let Some(Value::Number(f)) = params.get("feedback") {
268 feedback = *f;
269 }
270 if let Some(Value::Number(m)) = params.get("mix") {
271 mix = *m;
272 }
273 } else {
274 time_val = match &args[0] {
277 Value::Number(t) => Some(*t),
278 _ => None,
279 };
280
281 if args.len() > 1 {
282 if let Value::Number(f) = &args[1] {
283 feedback = *f;
284 } else {
285 return Err(anyhow!(
286 "delay() second argument must be a number (feedback)"
287 ));
288 }
289 }
290
291 if args.len() > 2 {
292 if let Value::Number(m) = &args[2] {
293 mix = *m;
294 } else {
295 return Err(anyhow!("delay() third argument must be a number (mix)"));
296 }
297 }
298 }
299
300 let time = if let Some(t) = time_val {
301 t
302 } else {
303 return Err(anyhow!(
304 "delay() requires a time parameter either as first numeric arg or as {{ time: ... }}"
305 ));
306 };
307
308 context.set("delay_time", Value::Number(time));
309 context.set("delay_feedback", Value::Number(feedback));
310 context.set("delay_mix", Value::Number(mix));
311
312 Ok(())
313 }
314}
315
316pub struct ReverbFunction;
319
320impl FunctionExecutor for ReverbFunction {
321 fn name(&self) -> &str {
322 "reverb"
323 }
324
325 fn execute(&self, context: &mut FunctionContext, args: &[Value]) -> Result<()> {
326 let mut amount = 0.5; if let Some(Value::Map(params)) = args.first() {
330 if let Some(Value::Number(s)) = params.get("size") {
331 amount = *s;
332 } else if let Some(Value::Number(a)) = params.get("amount") {
333 amount = *a;
334 }
335 } else if let Some(Value::Number(a)) = args.get(0) {
336 amount = *a;
337 } else {
338 return Err(anyhow!(
339 "reverb() requires either a numeric argument or a parameter map {{ size: ... }}"
340 ));
341 }
342
343 context.set("reverb_amount", Value::Number(amount));
344 context.set("reverb_size", Value::Number(amount)); Ok(())
347 }
348}
349
350pub struct DriveFunction;
353
354impl FunctionExecutor for DriveFunction {
355 fn name(&self) -> &str {
356 "drive"
357 }
358
359 fn execute(&self, context: &mut FunctionContext, args: &[Value]) -> Result<()> {
360 if args.is_empty() {
361 return Err(anyhow!(
362 "drive() requires at least 1 argument (drive amount 0.0-1.0)"
363 ));
364 }
365 let mut amount = 0.5;
367 let mut color = 0.5;
368
369 if let Some(Value::Map(params)) = args.first() {
370 if let Some(Value::Number(g)) = params.get("gain") {
371 amount = *g;
372 } else if let Some(Value::Number(a)) = params.get("amount") {
373 amount = *a;
374 }
375
376 if let Some(Value::Number(c)) = params.get("color") {
377 color = *c;
378 }
379 } else {
380 amount = match &args[0] {
382 Value::Number(a) => *a,
383 _ => return Err(anyhow!("drive() first argument must be a number (amount)")),
384 };
385
386 if args.len() > 1 {
387 color = match &args[1] {
388 Value::Number(c) => *c,
389 _ => return Err(anyhow!("drive() second argument must be a number (color)")),
390 };
391 }
392 }
393
394 context.set("drive_amount", Value::Number(amount));
395 context.set("drive_amp", Value::Number(amount)); context.set("drive_color", Value::Number(color));
397
398 Ok(())
399 }
400}
401
402pub struct ChorusFunction;
404
405impl FunctionExecutor for ChorusFunction {
406 fn name(&self) -> &str {
407 "chorus"
408 }
409
410 fn execute(&self, context: &mut FunctionContext, args: &[Value]) -> Result<()> {
411 let mut depth = 0.5; let mut rate = 1.5; let mut mix = 0.5; if let Some(Value::Map(params)) = args.first() {
418 if let Some(Value::Number(d)) = params.get("depth") {
419 depth = *d;
420 }
421 if let Some(Value::Number(r)) = params.get("rate") {
422 rate = *r;
423 }
424 if let Some(Value::Number(m)) = params.get("mix") {
425 mix = *m;
426 }
427 }
428
429 context.set("chorus_depth", Value::Number(depth));
430 context.set("chorus_rate", Value::Number(rate));
431 context.set("chorus_mix", Value::Number(mix));
432
433 Ok(())
434 }
435}
436
437pub struct FlangerFunction;
439
440impl FunctionExecutor for FlangerFunction {
441 fn name(&self) -> &str {
442 "flanger"
443 }
444
445 fn execute(&self, context: &mut FunctionContext, args: &[Value]) -> Result<()> {
446 let mut depth = 0.7; let mut rate = 0.5; let mut feedback = 0.5; let mut mix = 0.5; if let Some(Value::Map(params)) = args.first() {
454 if let Some(Value::Number(d)) = params.get("depth") {
455 depth = *d;
456 }
457 if let Some(Value::Number(r)) = params.get("rate") {
458 rate = *r;
459 }
460 if let Some(Value::Number(f)) = params.get("feedback") {
461 feedback = *f;
462 }
463 if let Some(Value::Number(m)) = params.get("mix") {
464 mix = *m;
465 }
466 }
467
468 context.set("flanger_depth", Value::Number(depth));
469 context.set("flanger_rate", Value::Number(rate));
470 context.set("flanger_feedback", Value::Number(feedback));
471 context.set("flanger_mix", Value::Number(mix));
472
473 Ok(())
474 }
475}
476
477pub struct PhaserFunction;
479
480impl FunctionExecutor for PhaserFunction {
481 fn name(&self) -> &str {
482 "phaser"
483 }
484
485 fn execute(&self, context: &mut FunctionContext, args: &[Value]) -> Result<()> {
486 let mut stages = 4.0; let mut rate = 0.5; let mut depth = 0.7; let mut feedback = 0.5; let mut mix = 0.5; if let Some(Value::Map(params)) = args.first() {
495 if let Some(Value::Number(s)) = params.get("stages") {
496 stages = *s;
497 }
498 if let Some(Value::Number(r)) = params.get("rate") {
499 rate = *r;
500 }
501 if let Some(Value::Number(d)) = params.get("depth") {
502 depth = *d;
503 }
504 if let Some(Value::Number(f)) = params.get("feedback") {
505 feedback = *f;
506 }
507 if let Some(Value::Number(m)) = params.get("mix") {
508 mix = *m;
509 }
510 }
511
512 context.set("phaser_stages", Value::Number(stages));
513 context.set("phaser_rate", Value::Number(rate));
514 context.set("phaser_depth", Value::Number(depth));
515 context.set("phaser_feedback", Value::Number(feedback));
516 context.set("phaser_mix", Value::Number(mix));
517
518 Ok(())
519 }
520}
521
522pub struct CompressorFunction;
524
525impl FunctionExecutor for CompressorFunction {
526 fn name(&self) -> &str {
527 "compressor"
528 }
529
530 fn execute(&self, context: &mut FunctionContext, args: &[Value]) -> Result<()> {
531 let mut threshold = -20.0; let mut ratio = 4.0; let mut attack = 5.0; let mut release = 50.0; let mut makeup = 0.0; if let Some(Value::Map(params)) = args.first() {
540 if let Some(Value::Number(t)) = params.get("threshold") {
541 threshold = *t;
542 }
543 if let Some(Value::Number(r)) = params.get("ratio") {
544 ratio = *r;
545 }
546 if let Some(Value::Number(a)) = params.get("attack") {
547 attack = *a;
548 }
549 if let Some(Value::Number(r)) = params.get("release") {
550 release = *r;
551 }
552 if let Some(Value::Number(m)) = params.get("makeup") {
553 makeup = *m;
554 }
555 }
556
557 context.set("compressor_threshold", Value::Number(threshold));
558 context.set("compressor_ratio", Value::Number(ratio));
559 context.set("compressor_attack", Value::Number(attack));
560 context.set("compressor_release", Value::Number(release));
561 context.set("compressor_makeup", Value::Number(makeup));
562
563 Ok(())
564 }
565}
566
567pub struct DistortionFunction;
569
570impl FunctionExecutor for DistortionFunction {
571 fn name(&self) -> &str {
572 "distortion"
573 }
574
575 fn execute(&self, context: &mut FunctionContext, args: &[Value]) -> Result<()> {
576 let mut amount = 0.5; let mut tone = 0.5; let mut mix = 1.0; if let Some(Value::Map(params)) = args.first() {
583 if let Some(Value::Number(a)) = params.get("amount") {
584 amount = *a;
585 }
586 if let Some(Value::Number(t)) = params.get("tone") {
587 tone = *t;
588 }
589 if let Some(Value::Number(m)) = params.get("mix") {
590 mix = *m;
591 }
592 }
593
594 context.set("distortion_amount", Value::Number(amount));
595 context.set("distortion_tone", Value::Number(tone));
596 context.set("distortion_mix", Value::Number(mix));
597
598 Ok(())
599 }
600}