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