1use crate::engine::audio::events::AudioEvent;
2use crate::language::syntax::ast::Value;
3use anyhow::Result;
4
5use super::AudioInterpreter;
6
7fn apply_automation_param(
11 interpreter: &AudioInterpreter,
12 target: &str,
13 param_names: &[&str], base_value: f32,
15 note_start_time: f32, ) -> f32 {
17 let mut result = base_value;
18
19 for name in param_names {
21 if let Some(v) =
22 interpreter
23 .automation_registry
24 .get_value(target, name, interpreter.cursor_time)
25 {
26 result = v;
27 break; }
29 }
30
31 if let Some(ctx) = interpreter.note_automation_templates.get(target) {
33 let note_progress = ctx.progress_at_time(note_start_time);
35
36 for tpl in ctx.templates.iter() {
37 for name in param_names {
38 if tpl.param_name == *name {
39 result =
41 crate::engine::audio::automation::evaluate_template_at(tpl, note_progress);
42 return result; }
44 }
45 }
46 }
47
48 result
49}
50
51fn extract_plugin_parameters(
56 effects: &Option<Value>,
57 synth_def: &mut crate::engine::audio::events::SynthDefinition,
58) -> Option<Value> {
59 if effects.is_none() {
60 return None;
61 }
62
63 let audio_effect_names = [
65 "reverb",
67 "delay",
68 "distortion",
69 "bitcrush",
70 "lowpass",
71 "lpf",
72 "highpass",
73 "hpf",
74 "bandpass",
75 "bpf",
76 "tremolo",
77 "vibrato",
78 "chorus",
79 "flanger",
80 "phaser",
81 "compressor",
82 "comp",
83 "drive",
84 "gate",
85 "lfo",
86 "reverse",
88 "speed",
89 "slice",
90 "stretch",
91 "roll",
92 "mono",
94 "monoizer",
95 "stereo",
96 "freeze",
98 "adsr",
99 "eq",
100 "equalizer",
101 "dist",
103 ];
104
105 if let Some(Value::Array(effects_arr)) = effects {
106 let mut remaining_effects = Vec::new();
107 let mut found_params = false;
108
109 for effect in effects_arr {
110 if let Value::Map(effect_map) = effect {
111 if let Some(Value::String(type_name)) = effect_map.get("type") {
113 let type_lower = type_name.to_lowercase();
114
115 if !audio_effect_names.contains(&type_lower.as_str()) {
116 found_params = true;
118
119 if let Some(Value::Number(val)) = effect_map.get("value") {
122 synth_def.options.insert(type_name.clone(), *val);
123 } else {
124 let mut found_numeric = false;
126 for (key, val) in effect_map.iter() {
127 if key != "type" {
128 if let Value::Number(n) = val {
129 synth_def.options.insert(key.clone(), *n);
130 found_numeric = true;
131 }
132 }
133 }
134 if !found_numeric {
136 continue;
137 }
138 }
139 continue; }
141 }
142 }
143 remaining_effects.push(effect.clone());
145 }
146
147 if found_params {
148 if remaining_effects.is_empty() {
149 return None; } else {
151 return Some(Value::Array(remaining_effects)); }
153 }
154 }
155
156 effects.clone() }
158
159pub fn extract_audio_event(
160 interpreter: &mut AudioInterpreter,
161 target: &str,
162 context: &crate::engine::functions::FunctionContext,
163) -> Result<()> {
164 if let Some(Value::String(note_name)) = context.get("note") {
167 let mut event_effects: Option<crate::language::syntax::ast::Value> = None;
169 let midi = crate::engine::functions::note::parse_note_to_midi(note_name)?;
170
171 let duration = if context.duration > 0.0 {
173 context.duration
174 } else if let Some(Value::Number(d)) = context.get("duration") {
175 d / 1000.0
176 } else {
177 0.5
178 };
179
180 let velocity = if let Some(Value::Number(v)) = context.get("velocity") {
182 if *v > 2.0 {
183 (*v) / 127.0
184 } else if *v > 1.0 {
185 (*v) / 100.0
186 } else {
187 *v
188 }
189 } else {
190 0.8
191 };
192
193 let base_pan = if let Some(Value::Number(p)) = context.get("pan") {
195 *p
196 } else {
197 0.0
198 };
199
200 let use_per_note_automation = interpreter.note_automation_templates.contains_key(target);
202
203 let pan = if use_per_note_automation {
205 base_pan
206 } else {
207 apply_automation_param(
208 interpreter,
209 target,
210 &["pan"],
211 base_pan,
212 interpreter.cursor_time,
213 )
214 };
215
216 let base_detune = if let Some(Value::Number(d)) = context.get("detune") {
218 *d
219 } else {
220 0.0
221 };
222 let detune = if use_per_note_automation {
223 base_detune
224 } else {
225 apply_automation_param(
226 interpreter,
227 target,
228 &["pitch", "detune"],
229 base_detune,
230 interpreter.cursor_time,
231 )
232 };
233
234 let base_gain = if let Some(Value::Number(g)) = context.get("gain") {
236 *g
237 } else {
238 1.0
239 };
240 let gain = if use_per_note_automation {
241 base_gain
242 } else {
243 apply_automation_param(
244 interpreter,
245 target,
246 &["volume", "gain"],
247 base_gain,
248 interpreter.cursor_time,
249 )
250 };
251
252 let synth_id = if target.is_empty() { "default" } else { target };
255
256 let mut synth_def = interpreter
259 .events
260 .get_synth(synth_id)
261 .cloned()
262 .unwrap_or_default();
263
264 for filter in &mut synth_def.filters {
267 let current_cutoff = filter.cutoff;
269 let automated_cutoff = apply_automation_param(
270 interpreter,
271 synth_id,
272 &["cutoff"],
273 current_cutoff,
274 interpreter.cursor_time,
275 );
276 if (automated_cutoff - current_cutoff).abs() > 0.0001 {
277 filter.cutoff = automated_cutoff;
278 }
279
280 let current_resonance = filter.resonance;
282 let automated_resonance = apply_automation_param(
283 interpreter,
284 synth_id,
285 &["resonance"],
286 current_resonance,
287 interpreter.cursor_time,
288 );
289 if (automated_resonance - current_resonance).abs() > 0.0001 {
290 filter.resonance = automated_resonance;
291 }
292 }
293
294 let synth_params = ["filter_type", "drive", "tone", "decay"];
296 for param in &synth_params {
297 let current_val = synth_def.options.get(*param).copied().unwrap_or(0.0);
298 let automated_val = apply_automation_param(
299 interpreter,
300 synth_id,
301 &[param],
302 current_val,
303 interpreter.cursor_time,
304 );
305 if (automated_val - current_val).abs() > 0.0001 {
306 synth_def.options.insert(param.to_string(), automated_val);
307 }
308 }
309
310 let mut synth_effects_vec: Vec<crate::language::syntax::ast::Value> = Vec::new();
311 if let Some(var_val) = interpreter.variables.get(synth_id) {
312 match var_val {
313 crate::language::syntax::ast::Value::Map(m) => {
314 if let Some(chain_v) = m.get("chain") {
315 if let crate::language::syntax::ast::Value::Array(arr) = chain_v {
316 synth_effects_vec.extend(arr.clone());
317 }
318 } else if let Some(effs) = m.get("effects") {
319 eprintln!(
321 "DEPRECATION: synth-level effect param map support is deprecated — use chained params instead."
322 );
323 let normalized =
324 crate::engine::audio::effects::normalize_effects(&Some(effs.clone()));
325 for (k, v) in normalized.into_iter() {
326 let mut map = std::collections::HashMap::new();
327 map.insert(
328 "type".to_string(),
329 crate::language::syntax::ast::Value::String(k),
330 );
331 for (pk, pv) in v.into_iter() {
332 map.insert(pk, pv);
333 }
334 synth_effects_vec.push(crate::language::syntax::ast::Value::Map(map));
335 }
336 }
337 }
338 crate::language::syntax::ast::Value::Statement(stmt_box) => {
339 if let crate::language::syntax::ast::StatementKind::ArrowCall { .. } =
340 &stmt_box.kind
341 {
342 if let crate::language::syntax::ast::Value::Map(m) = &stmt_box.value {
343 if let Some(crate::language::syntax::ast::Value::Array(arr)) =
344 m.get("chain")
345 {
346 synth_effects_vec.extend(arr.clone());
347 }
348 }
349 }
350 }
351 _ => {}
352 }
353 }
354
355 let mut note_effects_vec: Vec<crate::language::syntax::ast::Value> = Vec::new();
357 if let Some(crate::language::syntax::ast::Value::String(method_name)) =
358 context.get("method")
359 {
360 let m = method_name.as_str();
361 if m == "note" || m == "chord" || m == "sample" {
362 if let Some(eff_val) = context.get("effects") {
363 match eff_val {
364 crate::language::syntax::ast::Value::Array(arr) => {
365 note_effects_vec.extend(arr.clone());
366 }
367 crate::language::syntax::ast::Value::Map(map_v) => {
368 let normalized = crate::engine::audio::effects::normalize_effects(
370 &Some(crate::language::syntax::ast::Value::Map(map_v.clone())),
371 );
372 for (k, v) in normalized.into_iter() {
373 let mut map = std::collections::HashMap::new();
374 map.insert(
375 "type".to_string(),
376 crate::language::syntax::ast::Value::String(k),
377 );
378 for (pk, pv) in v.into_iter() {
379 map.insert(pk, pv);
380 }
381 note_effects_vec
382 .push(crate::language::syntax::ast::Value::Map(map));
383 }
384 }
385 _ => {}
386 }
387 }
388 }
389 }
390
391 if synth_effects_vec.is_empty() {
393 if let Some(eff_val) = context.get("effects") {
394 if let crate::language::syntax::ast::Value::Array(arr) = eff_val {
395 synth_effects_vec.extend(arr.clone());
396 } else if let crate::language::syntax::ast::Value::Map(map_v) = eff_val {
397 let normalized = crate::engine::audio::effects::normalize_effects(&Some(
398 crate::language::syntax::ast::Value::Map(map_v.clone()),
399 ));
400 for (k, v) in normalized.into_iter() {
401 let mut map = std::collections::HashMap::new();
402 map.insert(
403 "type".to_string(),
404 crate::language::syntax::ast::Value::String(k),
405 );
406 for (pk, pv) in v.into_iter() {
407 map.insert(pk, pv);
408 }
409 synth_effects_vec.push(crate::language::syntax::ast::Value::Map(map));
410 }
411 }
412 }
413 }
414
415 if !synth_effects_vec.is_empty() || !note_effects_vec.is_empty() {
417 let mut merged: Vec<crate::language::syntax::ast::Value> = Vec::new();
418 merged.extend(synth_effects_vec);
419 merged.extend(note_effects_vec);
420 event_effects = Some(crate::language::syntax::ast::Value::Array(merged));
421 }
422
423 event_effects = extract_plugin_parameters(&event_effects, &mut synth_def);
425
426 interpreter
430 .events
431 .synths
432 .insert(synth_id.to_string(), synth_def.clone());
433
434 interpreter.events.events.push(AudioEvent::Note {
435 midi,
436 start_time: interpreter.cursor_time,
437 duration,
438 velocity,
439 synth_id: synth_id.to_string(),
440 synth_def,
441 pan,
442 detune,
443 gain,
444 attack: None,
445 release: None,
446 delay_time: None,
447 delay_feedback: None,
448 delay_mix: None,
449 reverb_amount: None,
450 drive_amount: None,
451 drive_color: None,
452 effects: event_effects,
453 use_per_note_automation,
454 });
455 return Ok(());
456 }
457
458 if let Some(Value::Array(notes_arr)) = context.get("notes") {
460 let mut midis: Vec<u8> = Vec::new();
461 for n in notes_arr {
462 if let Value::String(s) = n {
463 if let Ok(m) = crate::engine::functions::note::parse_note_to_midi(s) {
464 midis.push(m);
465 }
466 }
467 }
468
469 if !midis.is_empty() {
470 let duration = if context.duration > 0.0 {
471 context.duration
472 } else if let Some(Value::Number(d)) = context.get("duration") {
473 d / 1000.0
474 } else {
475 0.5
476 };
477
478 let velocity = if let Some(Value::Number(v)) = context.get("velocity") {
479 if *v > 2.0 {
480 (*v) / 127.0
481 } else if *v > 1.0 {
482 (*v) / 100.0
483 } else {
484 *v
485 }
486 } else {
487 0.8
488 };
489
490 let pan = if let Some(Value::Number(p)) = context.get("pan") {
491 *p
492 } else {
493 0.0
494 };
495
496 let use_per_note_automation =
498 interpreter.note_automation_templates.contains_key(target);
499
500 let pan = if use_per_note_automation {
501 pan
502 } else {
503 apply_automation_param(interpreter, target, &["pan"], pan, interpreter.cursor_time)
504 };
505
506 let detune = if let Some(Value::Number(d)) = context.get("detune") {
507 *d
508 } else {
509 0.0
510 };
511 let detune = if use_per_note_automation {
512 detune
513 } else {
514 apply_automation_param(
515 interpreter,
516 target,
517 &["pitch", "detune"],
518 detune,
519 interpreter.cursor_time,
520 )
521 };
522
523 let spread = if let Some(Value::Number(s)) = context.get("spread") {
524 *s
525 } else {
526 0.0
527 };
528
529 let gain = if let Some(Value::Number(g)) = context.get("gain") {
530 *g
531 } else {
532 1.0
533 };
534 let gain = if use_per_note_automation {
535 gain
536 } else {
537 apply_automation_param(
538 interpreter,
539 target,
540 &["volume", "gain"],
541 gain,
542 interpreter.cursor_time,
543 )
544 };
545
546 let attack = context.get("attack").and_then(|v| {
548 if let Value::Number(n) = v {
549 Some(*n)
550 } else {
551 None
552 }
553 });
554 let release = context.get("release").and_then(|v| {
555 if let Value::Number(n) = v {
556 Some(*n)
557 } else {
558 None
559 }
560 });
561
562 let delay_time = context.get("delay_time").and_then(|v| {
564 if let Value::Number(n) = v {
565 Some(*n)
566 } else {
567 None
568 }
569 });
570 let delay_feedback = context.get("delay_feedback").and_then(|v| {
571 if let Value::Number(n) = v {
572 Some(*n)
573 } else {
574 None
575 }
576 });
577 let delay_mix = context.get("delay_mix").and_then(|v| {
578 if let Value::Number(n) = v {
579 Some(*n)
580 } else {
581 None
582 }
583 });
584 let reverb_amount = context.get("reverb_amount").and_then(|v| {
585 if let Value::Number(n) = v {
586 Some(*n)
587 } else {
588 None
589 }
590 });
591 let drive_amount = context.get("drive_amount").and_then(|v| {
592 if let Value::Number(n) = v {
593 Some(*n)
594 } else {
595 None
596 }
597 });
598 let drive_color = context.get("drive_color").and_then(|v| {
599 if let Value::Number(n) = v {
600 Some(*n)
601 } else {
602 None
603 }
604 });
605
606 let synth_id = if target.is_empty() { "default" } else { target };
607
608 let mut synth_def = interpreter
610 .events
611 .get_synth(synth_id)
612 .cloned()
613 .unwrap_or_default();
614 let mut event_effects: Option<crate::language::syntax::ast::Value> = None;
616
617 let mut synth_effects_vec: Vec<crate::language::syntax::ast::Value> = Vec::new();
619 if let Some(var_val) = interpreter.variables.get(synth_id) {
620 match var_val {
621 crate::language::syntax::ast::Value::Map(m) => {
622 if let Some(chain_v) = m.get("chain") {
623 if let crate::language::syntax::ast::Value::Array(arr) = chain_v {
624 synth_effects_vec.extend(arr.clone());
625 }
626 } else if let Some(effs) = m.get("effects") {
627 eprintln!(
628 "DEPRECATION: synth-level effect param map support is deprecated — use chained params instead."
629 );
630 let normalized = crate::engine::audio::effects::normalize_effects(
631 &Some(effs.clone()),
632 );
633 for (k, v) in normalized.into_iter() {
634 let mut map = std::collections::HashMap::new();
635 map.insert(
636 "type".to_string(),
637 crate::language::syntax::ast::Value::String(k),
638 );
639 for (pk, pv) in v.into_iter() {
640 map.insert(pk, pv);
641 }
642 synth_effects_vec
643 .push(crate::language::syntax::ast::Value::Map(map));
644 }
645 }
646 }
647 crate::language::syntax::ast::Value::Statement(stmt_box) => {
648 if let crate::language::syntax::ast::StatementKind::ArrowCall { .. } =
649 &stmt_box.kind
650 {
651 if let crate::language::syntax::ast::Value::Map(m) = &stmt_box.value {
652 if let Some(crate::language::syntax::ast::Value::Array(arr)) =
653 m.get("chain")
654 {
655 synth_effects_vec.extend(arr.clone());
656 }
657 }
658 }
659 }
660 _ => {}
661 }
662 }
663
664 let mut chord_effects_vec: Vec<crate::language::syntax::ast::Value> = Vec::new();
666 if let Some(crate::language::syntax::ast::Value::String(method_name)) =
667 context.get("method")
668 {
669 let m = method_name.as_str();
670 if m == "chord" {
671 if let Some(eff_val) = context.get("effects") {
672 match eff_val {
673 crate::language::syntax::ast::Value::Array(arr) => {
674 chord_effects_vec.extend(arr.clone());
675 }
676 crate::language::syntax::ast::Value::Map(map_v) => {
677 let normalized = crate::engine::audio::effects::normalize_effects(
678 &Some(crate::language::syntax::ast::Value::Map(map_v.clone())),
679 );
680 for (k, v) in normalized.into_iter() {
681 let mut map = std::collections::HashMap::new();
682 map.insert(
683 "type".to_string(),
684 crate::language::syntax::ast::Value::String(k),
685 );
686 for (pk, pv) in v.into_iter() {
687 map.insert(pk, pv);
688 }
689 chord_effects_vec
690 .push(crate::language::syntax::ast::Value::Map(map));
691 }
692 }
693 _ => {}
694 }
695 }
696 }
697 }
698
699 if !synth_effects_vec.is_empty() || !chord_effects_vec.is_empty() {
700 let mut merged: Vec<crate::language::syntax::ast::Value> = Vec::new();
701 merged.extend(synth_effects_vec);
702 merged.extend(chord_effects_vec);
703 event_effects = Some(crate::language::syntax::ast::Value::Array(merged));
704 }
705
706 event_effects = extract_plugin_parameters(&event_effects, &mut synth_def);
708
709 interpreter
712 .events
713 .synths
714 .insert(synth_id.to_string(), synth_def.clone());
715
716 interpreter.events.events.push(AudioEvent::Chord {
717 midis,
718 start_time: interpreter.cursor_time,
719 duration,
720 velocity,
721 synth_id: synth_id.to_string(),
722 synth_def,
723 pan,
724 detune,
725 spread,
726 gain,
727 attack,
728 release,
729 delay_time,
730 delay_feedback,
731 delay_mix,
732 reverb_amount,
733 drive_amount,
734 drive_color,
735 effects: event_effects,
736 use_per_note_automation: false,
737 });
738 let synth_params = [
741 "cutoff",
742 "resonance",
743 "filter_type",
744 "drive",
745 "tone",
746 "decay",
747 ];
748 let mut param_updates = Vec::new();
749
750 if let Some(synth_def) = interpreter.events.synths.get(synth_id) {
752 for param in &synth_params {
753 let current_val = synth_def.options.get(*param).copied().unwrap_or(0.0);
754 let automated_val = apply_automation_param(
755 interpreter,
756 synth_id,
757 &[param],
758 current_val,
759 interpreter.cursor_time,
760 );
761 if (automated_val - current_val).abs() > 0.0001 {
762 param_updates.push((param.to_string(), automated_val));
763 }
764 }
765 }
766
767 if let Some(synth_def) = interpreter.events.synths.get_mut(synth_id) {
769 for (param, val) in param_updates {
770 synth_def.options.insert(param, val);
771 }
772 }
773 }
774 }
775 Ok(())
776}