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::Milliseconds(ms)) => {
58 ms / 1000.0
59 }
60 Ok(crate::language::syntax::ast::DurationValue::Beats(beats)) => {
61 beats * (60.0 / context.tempo)
62 }
63 Ok(crate::language::syntax::ast::DurationValue::Number(ms)) => ms / 1000.0,
64 Ok(crate::language::syntax::ast::DurationValue::Beat(beat_str)) => {
65 if let Ok(beats) = beat_str.parse::<f32>() {
67 beats * (60.0 / context.tempo)
68 } else {
69 return Err(anyhow!("Invalid beat value: '{}'", beat_str));
70 }
71 }
72 Ok(crate::language::syntax::ast::DurationValue::Identifier(id)) => {
73 if id.starts_with("__temporal__") {
75 if let Some(beats_str) = id.strip_prefix("__temporal__") {
76 if let Ok(beats) =
77 beats_str.split('_').next().unwrap_or("").parse::<f32>()
78 {
79 beats * (60.0 / context.tempo)
80 } else {
81 return Err(anyhow!(
82 "Invalid temporal duration format: '{}'",
83 s
84 ));
85 }
86 } else {
87 return Err(anyhow!("Invalid temporal duration format: '{}'", s));
88 }
89 } else {
90 return Err(anyhow!("Unknown duration: '{}'", s));
92 }
93 }
94 Ok(crate::language::syntax::ast::DurationValue::Auto) => {
95 return Err(anyhow!("duration() cannot use 'auto'"));
96 }
97 Err(_) => {
98 return Err(anyhow!(
100 "Invalid duration format: '{}'. Use: number (ms), 'ms', fraction (1/4), or temporal (1 beat, 2 steps, 3 bar, etc.)",
101 s
102 ));
103 }
104 }
105 }
106
107 _ => {
108 return Err(anyhow!(
109 "duration() argument must be a number, string, or identifier"
110 ));
111 }
112 };
113
114 let duration_ms = duration_seconds * 1000.0;
116 context.set("duration", Value::Number(duration_ms));
117 context.duration = duration_seconds;
118 Ok(())
119 }
120}
121
122fn parse_fraction_beats(s: &str) -> Option<f32> {
124 let mut parts = s.split('/');
125 let numerator: f32 = parts.next()?.trim().parse().ok()?;
126 let denominator: f32 = parts.next()?.trim().parse().ok()?;
127 if denominator.abs() < f32::EPSILON {
128 return None;
129 }
130 Some(numerator / denominator)
131}
132
133pub struct PanFunction;
135
136impl FunctionExecutor for PanFunction {
137 fn name(&self) -> &str {
138 "pan"
139 }
140
141 fn execute(&self, context: &mut FunctionContext, args: &[Value]) -> Result<()> {
142 if args.is_empty() {
143 return Err(anyhow!("pan() requires 1 argument (pan value -1.0 to 1.0)"));
144 }
145
146 let pan = match &args[0] {
147 Value::Number(p) => *p,
148 _ => return Err(anyhow!("pan() argument must be a number")),
149 };
150
151 context.set("pan", Value::Number(pan));
152 Ok(())
153 }
154}
155
156pub struct DetuneFunction;
158
159impl FunctionExecutor for DetuneFunction {
160 fn name(&self) -> &str {
161 "detune"
162 }
163
164 fn execute(&self, context: &mut FunctionContext, args: &[Value]) -> Result<()> {
165 if args.is_empty() {
166 return Err(anyhow!("detune() requires 1 argument (cents)"));
167 }
168
169 let detune = match &args[0] {
170 Value::Number(d) => *d,
171 _ => return Err(anyhow!("detune() argument must be a number")),
172 };
173
174 context.set("detune", Value::Number(detune));
175 Ok(())
176 }
177}
178
179pub struct SpreadFunction;
181
182impl FunctionExecutor for SpreadFunction {
183 fn name(&self) -> &str {
184 "spread"
185 }
186
187 fn execute(&self, context: &mut FunctionContext, args: &[Value]) -> Result<()> {
188 if args.is_empty() {
189 return Err(anyhow!(
190 "spread() requires 1 argument (spread value 0.0 to 1.0)"
191 ));
192 }
193
194 let spread = match &args[0] {
195 Value::Number(s) => *s,
196 _ => return Err(anyhow!("spread() argument must be a number")),
197 };
198
199 context.set("spread", Value::Number(spread));
200 Ok(())
201 }
202}
203
204pub struct GainFunction;
206
207impl FunctionExecutor for GainFunction {
208 fn name(&self) -> &str {
209 "gain"
210 }
211
212 fn execute(&self, context: &mut FunctionContext, args: &[Value]) -> Result<()> {
213 if args.is_empty() {
214 return Err(anyhow!("gain() requires 1 argument (gain multiplier)"));
215 }
216
217 let gain = match &args[0] {
218 Value::Number(g) => *g,
219 _ => return Err(anyhow!("gain() argument must be a number")),
220 };
221
222 context.set("gain", Value::Number(gain));
223 Ok(())
224 }
225}
226
227pub struct AttackFunction;
229
230impl FunctionExecutor for AttackFunction {
231 fn name(&self) -> &str {
232 "attack"
233 }
234
235 fn execute(&self, context: &mut FunctionContext, args: &[Value]) -> Result<()> {
236 if args.is_empty() {
237 return Err(anyhow!("attack() requires 1 argument (attack time in ms)"));
238 }
239
240 let attack = match &args[0] {
241 Value::Number(a) => *a,
242 _ => return Err(anyhow!("attack() argument must be a number")),
243 };
244
245 context.set("attack", Value::Number(attack));
246 Ok(())
247 }
248}
249
250pub struct ReleaseFunction;
252
253impl FunctionExecutor for ReleaseFunction {
254 fn name(&self) -> &str {
255 "release"
256 }
257
258 fn execute(&self, context: &mut FunctionContext, args: &[Value]) -> Result<()> {
259 if args.is_empty() {
260 return Err(anyhow!(
261 "release() requires 1 argument (release time in ms)"
262 ));
263 }
264
265 let release = match &args[0] {
266 Value::Number(r) => *r,
267 _ => return Err(anyhow!("release() argument must be a number")),
268 };
269
270 context.set("release", Value::Number(release));
271 Ok(())
272 }
273}
274
275pub struct DelayFunction;
278
279impl FunctionExecutor for DelayFunction {
280 fn name(&self) -> &str {
281 "delay"
282 }
283
284 fn execute(&self, context: &mut FunctionContext, args: &[Value]) -> Result<()> {
285 if args.is_empty() {
286 return Err(anyhow!(
287 "delay() requires at least 1 argument (delay time in ms)"
288 ));
289 }
290 let mut time_val: Option<f32> = None;
293 let mut feedback: f32 = 0.3;
294 let mut mix: f32 = 0.5;
295
296 if let Some(Value::Map(params)) = args.first() {
297 if let Some(Value::Number(t)) = params.get("time") {
298 time_val = Some(*t);
299 }
300 if let Some(Value::Number(f)) = params.get("feedback") {
301 feedback = *f;
302 }
303 if let Some(Value::Number(m)) = params.get("mix") {
304 mix = *m;
305 }
306 } else {
307 time_val = match &args[0] {
310 Value::Number(t) => Some(*t),
311 _ => None,
312 };
313
314 if args.len() > 1 {
315 if let Value::Number(f) = &args[1] {
316 feedback = *f;
317 } else {
318 return Err(anyhow!(
319 "delay() second argument must be a number (feedback)"
320 ));
321 }
322 }
323
324 if args.len() > 2 {
325 if let Value::Number(m) = &args[2] {
326 mix = *m;
327 } else {
328 return Err(anyhow!("delay() third argument must be a number (mix)"));
329 }
330 }
331 }
332
333 let time = if let Some(t) = time_val {
334 t
335 } else {
336 return Err(anyhow!(
337 "delay() requires a time parameter either as first numeric arg or as {{ time: ... }}"
338 ));
339 };
340
341 context.set("delay_time", Value::Number(time));
342 context.set("delay_feedback", Value::Number(feedback));
343 context.set("delay_mix", Value::Number(mix));
344
345 Ok(())
346 }
347}
348
349pub struct ReverbFunction;
352
353impl FunctionExecutor for ReverbFunction {
354 fn name(&self) -> &str {
355 "reverb"
356 }
357
358 fn execute(&self, context: &mut FunctionContext, args: &[Value]) -> Result<()> {
359 let mut amount = 0.5; if let Some(Value::Map(params)) = args.first() {
363 if let Some(Value::Number(s)) = params.get("size") {
364 amount = *s;
365 } else if let Some(Value::Number(a)) = params.get("amount") {
366 amount = *a;
367 }
368 } else if let Some(Value::Number(a)) = args.get(0) {
369 amount = *a;
370 } else {
371 return Err(anyhow!(
372 "reverb() requires either a numeric argument or a parameter map {{ size: ... }}"
373 ));
374 }
375
376 context.set("reverb_amount", Value::Number(amount));
377 context.set("reverb_size", Value::Number(amount)); Ok(())
380 }
381}
382
383pub struct DriveFunction;
386
387impl FunctionExecutor for DriveFunction {
388 fn name(&self) -> &str {
389 "drive"
390 }
391
392 fn execute(&self, context: &mut FunctionContext, args: &[Value]) -> Result<()> {
393 if args.is_empty() {
394 return Err(anyhow!(
395 "drive() requires at least 1 argument (drive amount 0.0-1.0)"
396 ));
397 }
398 let mut amount = 0.5;
400 let mut color = 0.5;
401
402 if let Some(Value::Map(params)) = args.first() {
403 if let Some(Value::Number(g)) = params.get("gain") {
404 amount = *g;
405 } else if let Some(Value::Number(a)) = params.get("amount") {
406 amount = *a;
407 }
408
409 if let Some(Value::Number(c)) = params.get("color") {
410 color = *c;
411 }
412 } else {
413 amount = match &args[0] {
415 Value::Number(a) => *a,
416 _ => return Err(anyhow!("drive() first argument must be a number (amount)")),
417 };
418
419 if args.len() > 1 {
420 color = match &args[1] {
421 Value::Number(c) => *c,
422 _ => return Err(anyhow!("drive() second argument must be a number (color)")),
423 };
424 }
425 }
426
427 context.set("drive_amount", Value::Number(amount));
428 context.set("drive_amp", Value::Number(amount)); context.set("drive_color", Value::Number(color));
430
431 Ok(())
432 }
433}
434
435pub struct ChorusFunction;
437
438impl FunctionExecutor for ChorusFunction {
439 fn name(&self) -> &str {
440 "chorus"
441 }
442
443 fn execute(&self, context: &mut FunctionContext, args: &[Value]) -> Result<()> {
444 let mut depth = 0.5; let mut rate = 1.5; let mut mix = 0.5; if let Some(Value::Map(params)) = args.first() {
451 if let Some(Value::Number(d)) = params.get("depth") {
452 depth = *d;
453 }
454 if let Some(Value::Number(r)) = params.get("rate") {
455 rate = *r;
456 }
457 if let Some(Value::Number(m)) = params.get("mix") {
458 mix = *m;
459 }
460 }
461
462 context.set("chorus_depth", Value::Number(depth));
463 context.set("chorus_rate", Value::Number(rate));
464 context.set("chorus_mix", Value::Number(mix));
465
466 Ok(())
467 }
468}
469
470pub struct FlangerFunction;
472
473impl FunctionExecutor for FlangerFunction {
474 fn name(&self) -> &str {
475 "flanger"
476 }
477
478 fn execute(&self, context: &mut FunctionContext, args: &[Value]) -> Result<()> {
479 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() {
487 if let Some(Value::Number(d)) = params.get("depth") {
488 depth = *d;
489 }
490 if let Some(Value::Number(r)) = params.get("rate") {
491 rate = *r;
492 }
493 if let Some(Value::Number(f)) = params.get("feedback") {
494 feedback = *f;
495 }
496 if let Some(Value::Number(m)) = params.get("mix") {
497 mix = *m;
498 }
499 }
500
501 context.set("flanger_depth", Value::Number(depth));
502 context.set("flanger_rate", Value::Number(rate));
503 context.set("flanger_feedback", Value::Number(feedback));
504 context.set("flanger_mix", Value::Number(mix));
505
506 Ok(())
507 }
508}
509
510pub struct PhaserFunction;
512
513impl FunctionExecutor for PhaserFunction {
514 fn name(&self) -> &str {
515 "phaser"
516 }
517
518 fn execute(&self, context: &mut FunctionContext, args: &[Value]) -> Result<()> {
519 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() {
528 if let Some(Value::Number(s)) = params.get("stages") {
529 stages = *s;
530 }
531 if let Some(Value::Number(r)) = params.get("rate") {
532 rate = *r;
533 }
534 if let Some(Value::Number(d)) = params.get("depth") {
535 depth = *d;
536 }
537 if let Some(Value::Number(f)) = params.get("feedback") {
538 feedback = *f;
539 }
540 if let Some(Value::Number(m)) = params.get("mix") {
541 mix = *m;
542 }
543 }
544
545 context.set("phaser_stages", Value::Number(stages));
546 context.set("phaser_rate", Value::Number(rate));
547 context.set("phaser_depth", Value::Number(depth));
548 context.set("phaser_feedback", Value::Number(feedback));
549 context.set("phaser_mix", Value::Number(mix));
550
551 Ok(())
552 }
553}
554
555pub struct CompressorFunction;
557
558impl FunctionExecutor for CompressorFunction {
559 fn name(&self) -> &str {
560 "compressor"
561 }
562
563 fn execute(&self, context: &mut FunctionContext, args: &[Value]) -> Result<()> {
564 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() {
573 if let Some(Value::Number(t)) = params.get("threshold") {
574 threshold = *t;
575 }
576 if let Some(Value::Number(r)) = params.get("ratio") {
577 ratio = *r;
578 }
579 if let Some(Value::Number(a)) = params.get("attack") {
580 attack = *a;
581 }
582 if let Some(Value::Number(r)) = params.get("release") {
583 release = *r;
584 }
585 if let Some(Value::Number(m)) = params.get("makeup") {
586 makeup = *m;
587 }
588 }
589
590 context.set("compressor_threshold", Value::Number(threshold));
591 context.set("compressor_ratio", Value::Number(ratio));
592 context.set("compressor_attack", Value::Number(attack));
593 context.set("compressor_release", Value::Number(release));
594 context.set("compressor_makeup", Value::Number(makeup));
595
596 Ok(())
597 }
598}
599
600pub struct DistortionFunction;
602
603impl FunctionExecutor for DistortionFunction {
604 fn name(&self) -> &str {
605 "distortion"
606 }
607
608 fn execute(&self, context: &mut FunctionContext, args: &[Value]) -> Result<()> {
609 let mut amount = 0.5; let mut tone = 0.5; let mut mix = 1.0; if let Some(Value::Map(params)) = args.first() {
616 if let Some(Value::Number(a)) = params.get("amount") {
617 amount = *a;
618 }
619 if let Some(Value::Number(t)) = params.get("tone") {
620 tone = *t;
621 }
622 if let Some(Value::Number(m)) = params.get("mix") {
623 mix = *m;
624 }
625 }
626
627 context.set("distortion_amount", Value::Number(amount));
628 context.set("distortion_tone", Value::Number(tone));
629 context.set("distortion_mix", Value::Number(mix));
630
631 Ok(())
632 }
633}