1use crate::engine::audio::effects::processors::{
2 DelayProcessor, DriveProcessor, EffectProcessor, ReverbProcessor,
3};
4use crate::engine::audio::events::{
5 AudioEventList, SynthDefinition, extract_filters, extract_number, extract_string,
6};
7use crate::engine::audio::generator::{
8 SynthParams, generate_chord_with_options, generate_note_with_options,
9};
10use crate::engine::events::{EventHandler, EventRegistry};
11use crate::engine::functions::FunctionRegistry;
12use crate::engine::functions::note::parse_note_to_midi;
13use crate::engine::special_vars::{SpecialVarContext, is_special_var, resolve_special_var};
14use crate::language::syntax::ast::{Statement, StatementKind, Value};
15use anyhow::Result;
17use rayon::prelude::*;
18use std::collections::HashMap;
19
20pub struct AudioInterpreter {
21 pub sample_rate: u32,
22 pub bpm: f32,
23 function_registry: FunctionRegistry,
24 events: AudioEventList,
25 pub(crate) variables: HashMap<String, Value>,
26 groups: HashMap<String, Vec<Statement>>,
27 cursor_time: f32,
28 pub special_vars: SpecialVarContext,
29 pub event_registry: EventRegistry,
30 current_statement_location: Option<(usize, usize)>, }
33
34impl AudioInterpreter {
35 pub fn new(sample_rate: u32) -> Self {
36 Self {
37 sample_rate,
38 bpm: 120.0,
39 function_registry: FunctionRegistry::new(),
40 events: AudioEventList::new(),
41 variables: HashMap::new(),
42 groups: HashMap::new(),
43 cursor_time: 0.0,
44 special_vars: SpecialVarContext::new(120.0, sample_rate),
45 event_registry: EventRegistry::new(),
46 current_statement_location: None,
47 }
48 }
49
50 pub fn interpret(&mut self, statements: &[Statement]) -> Result<Vec<f32>> {
51 let total_duration = self.calculate_total_duration(statements)?;
53 self.special_vars.total_duration = total_duration;
54 self.special_vars.update_bpm(self.bpm);
55
56 self.collect_events(statements)?;
58
59 self.render_audio()
61 }
62
63 pub fn events(&self) -> &AudioEventList {
65 &self.events
66 }
67
68 pub fn current_statement_location(&self) -> Option<(usize, usize)> {
70 self.current_statement_location
71 }
72
73 fn calculate_total_duration(&self, _statements: &[Statement]) -> Result<f32> {
75 Ok(60.0) }
78
79 pub(crate) fn collect_events(&mut self, statements: &[Statement]) -> Result<()> {
80 let (spawns, others): (Vec<_>, Vec<_>) = statements
82 .iter()
83 .partition(|stmt| matches!(stmt.kind, StatementKind::Spawn { .. }));
84
85 for stmt in &others {
87 self.current_statement_location = Some((stmt.line, stmt.column));
89
90 self.special_vars.update_time(self.cursor_time);
92
93 match &stmt.kind {
94 StatementKind::Let { name, value } => {
95 if let Some(val) = value {
96 self.handle_let(name, val)?;
97 }
98 }
99
100 StatementKind::ArrowCall {
101 target,
102 method,
103 args,
104 } => {
105 let chain = if let Value::Map(map) = &stmt.value {
107 map.get("chain").and_then(|v| {
108 if let Value::Array(arr) = v {
109 Some(arr.as_slice())
110 } else {
111 None
112 }
113 })
114 } else {
115 None
116 };
117
118 let context = super::statements::arrow_call::execute_arrow_call(
120 &self.function_registry,
121 target,
122 method,
123 args,
124 chain,
125 self.cursor_time,
126 self.bpm,
127 )
128 .map_err(|e| {
129 #[cfg(target_arch = "wasm32")]
131 {
132 use crate::web::registry::debug;
133 if debug::is_debug_errors_enabled() {
134 debug::push_parse_error_from_parts(
135 format!("{}", e),
136 stmt.line,
137 stmt.column,
138 "RuntimeError".to_string(),
139 );
140 }
141 }
142 e
143 })?;
144
145 self.extract_audio_event(target, &context)?;
147
148 self.cursor_time += context.duration;
150 }
151
152 StatementKind::Tempo => {
153 if let Value::Number(bpm_value) = &stmt.value {
154 self.set_bpm(*bpm_value);
155 }
156 }
157
158 StatementKind::Sleep => {
159 if let Value::Number(duration) = &stmt.value {
161 self.cursor_time += duration / 1000.0; }
163 }
164
165 StatementKind::Group { name, body } => {
166 self.groups.insert(name.clone(), body.clone());
168 }
169
170 StatementKind::Call { name, args: _ } => {
171 if let Value::Map(map) = &stmt.value {
173 if let Some(Value::Boolean(true)) = map.get("inline_pattern") {
174 if let (Some(Value::String(target)), Some(Value::String(pattern))) =
176 (map.get("target"), map.get("pattern"))
177 {
178 println!("🎵 Call inline pattern: {} = \"{}\"", target, pattern);
179 let target = target.clone();
180 let pattern = pattern.clone();
181 self.execute_pattern(&target, &pattern, None)?;
182 continue;
183 }
184 }
185 }
186
187 let pattern_data: Option<(String, String, String, Option<HashMap<String, f32>>)> =
190 if let Some(pattern_value) = self.variables.get(name) {
191 if let Value::Statement(stmt_box) = pattern_value {
192 if let StatementKind::Pattern { target, .. } = &stmt_box.kind {
193 if let Some(tgt) = target {
194 let (pattern_str, options) = self.extract_pattern_data(&stmt_box.value);
195 if let Some(pat) = pattern_str {
196 Some((name.clone(), tgt.clone(), pat, options))
197 } else {
198 None
199 }
200 } else {
201 None
202 }
203 } else {
204 None
205 }
206 } else {
207 None
208 }
209 } else {
210 None
211 };
212
213 if let Some((pname, target, pattern, options)) = pattern_data {
214 println!("🎵 Call pattern: {} with {}", pname, target);
215 self.execute_pattern(&target, &pattern, options)?;
216 continue;
217 }
218
219 if let Some(body) = self.groups.get(name).cloned() {
221 println!("📞 Call group: {}", name);
222 self.collect_events(&body)?;
223 } else {
224 println!("⚠️ Warning: Group or pattern '{}' not found", name);
225 }
226 }
227
228 StatementKind::Spawn { .. } => {
229 unreachable!("Spawn statements should be handled in parallel section");
232 }
233
234 StatementKind::Loop { count, body } => {
235 self.execute_loop(count, body)?;
237 }
238
239 StatementKind::For {
240 variable,
241 iterable,
242 body,
243 } => {
244 self.execute_for(variable, iterable, body)?;
246 }
247
248 StatementKind::Print => {
249 self.execute_print(&stmt.value)?;
251 }
252
253 StatementKind::If {
254 condition,
255 body,
256 else_body,
257 } => {
258 self.execute_if(condition, body, else_body)?;
260 }
261
262 StatementKind::On { event, args, body } => {
263 let once = args
265 .as_ref()
266 .and_then(|a| a.first())
267 .and_then(|v| {
268 if let Value::String(s) = v {
269 Some(s == "once")
270 } else {
271 None
272 }
273 })
274 .unwrap_or(false);
275
276 let handler = EventHandler {
277 event_name: event.clone(),
278 body: body.clone(),
279 once,
280 };
281
282 self.event_registry.register_handler(handler);
283 println!("📡 Event handler registered: {} (once={})", event, once);
284 }
285
286 StatementKind::Emit { event, payload } => {
287 let data = if let Some(Value::Map(map)) = payload {
289 map.clone()
290 } else {
291 HashMap::new()
292 };
293
294 self.event_registry
295 .emit(event.clone(), data, self.cursor_time);
296
297 self.execute_event_handlers(event)?;
299
300 println!("📤 Event emitted: {}", event);
301 }
302
303 StatementKind::Assign { target, property } => {
304 self.handle_assign(target, property, &stmt.value)?;
306 }
307
308 StatementKind::Load { source, alias } => {
309 self.handle_load(source, alias)?;
311 }
312
313 StatementKind::Pattern { name, target } => {
314 let pattern_stmt = Statement {
317 kind: StatementKind::Pattern {
318 name: name.clone(),
319 target: target.clone(),
320 },
321 value: stmt.value.clone(),
322 indent: stmt.indent,
323 line: stmt.line,
324 column: stmt.column,
325 };
326
327 self.variables
328 .insert(name.clone(), Value::Statement(Box::new(pattern_stmt)));
329
330 println!("📝 Pattern defined: {}", name);
331 }
332
333 StatementKind::Bank { name, alias } => {
334 let target_alias = alias.clone().unwrap_or_else(|| {
339 name.split('.').last().unwrap_or(name).to_string()
341 });
342
343 let mut bank_found = false;
345
346 if let Some(existing_value) = self.variables.get(name) {
348 self.variables
350 .insert(target_alias.clone(), existing_value.clone());
351 bank_found = true;
352
353 #[cfg(target_arch = "wasm32")]
354 web_sys::console::log_1(
355 &format!("🏦 Bank alias created: {} -> {}", name, target_alias).into(),
356 );
357 #[cfg(not(target_arch = "wasm32"))]
358 println!("🏦 Bank alias created: {} -> {}", name, target_alias);
359 } else {
360 #[cfg(target_arch = "wasm32")]
362 {
363 use crate::web::registry::banks::REGISTERED_BANKS;
364
365 REGISTERED_BANKS.with(|banks| {
366 for bank in banks.borrow().iter() {
367 if bank.full_name == *name {
368 if let Some(Value::Map(bank_map)) =
370 self.variables.get(&bank.alias)
371 {
372 self.variables.insert(
373 target_alias.clone(),
374 Value::Map(bank_map.clone()),
375 );
376 bank_found = true;
377
378 web_sys::console::log_1(
379 &format!(
380 "🏦 Bank alias created: {} ({}) -> {}",
381 name, bank.alias, target_alias
382 )
383 .into(),
384 );
385 }
386 }
387 }
388 });
389 }
390
391 #[cfg(not(target_arch = "wasm32"))]
393 {
394 use std::collections::HashMap;
395
396 let mut bank_map = HashMap::new();
398 bank_map.insert("_name".to_string(), Value::String(name.clone()));
399 bank_map.insert("_alias".to_string(), Value::String(target_alias.clone()));
400
401 self.variables.insert(target_alias.clone(), Value::Map(bank_map));
403 bank_found = true;
404
405 println!("🏦 Bank alias created: {} -> {}", name, target_alias);
406 }
407 }
408
409 if !bank_found {
410 #[cfg(target_arch = "wasm32")]
411 web_sys::console::warn_1(&format!("⚠️ Bank not found: {}", name).into());
412 #[cfg(not(target_arch = "wasm32"))]
413 println!("⚠️ Bank not found: {}", name);
414 }
415 }
416
417 StatementKind::Bind { source, target } => {
418 self.handle_bind(source, target, &stmt.value)?;
420 }
421
422 StatementKind::Trigger {
423 entity,
424 duration: _,
425 effects: _,
426 } => {
427 let resolved_entity = if entity.starts_with('.') {
430 &entity[1..]
431 } else {
432 entity
433 };
434
435 if resolved_entity.contains('.') {
437 let parts: Vec<&str> = resolved_entity.split('.').collect();
438 if parts.len() == 2 {
439 let (var_name, property) = (parts[0], parts[1]);
440
441 if let Some(Value::Map(map)) = self.variables.get(var_name) {
443 if let Some(Value::String(sample_uri)) = map.get(property) {
444 let uri = sample_uri.trim_matches('"').trim_matches('\'');
445
446 #[cfg(target_arch = "wasm32")]
447 web_sys::console::log_1(
448 &format!(
449 "🎵 Trigger: {}.{} -> {} at {:.3}s",
450 var_name, property, uri, self.cursor_time
451 )
452 .into(),
453 );
454 #[cfg(not(target_arch = "wasm32"))]
455 println!(
456 "🎵 Trigger: {}.{} -> {} at {:.3}s",
457 var_name, property, uri, self.cursor_time
458 );
459
460 self.events.add_sample_event(
462 uri,
463 self.cursor_time,
464 1.0, );
466
467 let beat_duration = self.beat_duration();
469 self.cursor_time += beat_duration;
470
471 #[cfg(target_arch = "wasm32")]
472 web_sys::console::log_1(&format!(" Next trigger at {:.3}s (advanced by 1 beat = {:.3}s)",
473 self.cursor_time, beat_duration).into());
474 }
475 }
476 }
477 } else {
478 if let Some(Value::String(sample_uri)) = self.variables.get(resolved_entity)
480 {
481 let uri = sample_uri.trim_matches('"').trim_matches('\'');
482
483 #[cfg(target_arch = "wasm32")]
484 web_sys::console::log_1(
485 &format!(
486 "🎵 Trigger: {} -> {} at {:.3}s",
487 resolved_entity, uri, self.cursor_time
488 )
489 .into(),
490 );
491 #[cfg(not(target_arch = "wasm32"))]
492 println!(
493 "🎵 Trigger: {} -> {} at {:.3}s",
494 resolved_entity, uri, self.cursor_time
495 );
496
497 self.events.add_sample_event(
499 uri,
500 self.cursor_time,
501 1.0, );
503
504 let beat_duration = self.beat_duration();
506 self.cursor_time += beat_duration;
507
508 #[cfg(target_arch = "wasm32")]
509 web_sys::console::log_1(
510 &format!(
511 " Next trigger at {:.3}s (advanced by 1 beat = {:.3}s)",
512 self.cursor_time, beat_duration
513 )
514 .into(),
515 );
516 }
517 }
518 }
519
520 _ => {
521 }
523 }
524 }
525
526 if !spawns.is_empty() {
528 println!("🚀 Executing {} spawn(s) in parallel", spawns.len());
529
530 let current_time = self.cursor_time;
532 let current_bpm = self.bpm;
533 let groups_snapshot = self.groups.clone();
534 let variables_snapshot = self.variables.clone();
535 let special_vars_snapshot = self.special_vars.clone();
536
537 let spawn_results: Vec<Result<AudioEventList>> = spawns
539 .par_iter()
540 .map(|stmt| {
541 if let StatementKind::Spawn { name, args: _ } = &stmt.kind {
542 let resolved_name = if name.starts_with('.') {
544 &name[1..]
545 } else {
546 name
547 };
548 if resolved_name.contains('.') {
550 let parts: Vec<&str> = resolved_name.split('.').collect();
551 if parts.len() == 2 {
552 let (var_name, property) = (parts[0], parts[1]);
553 if let Some(Value::Map(map)) = variables_snapshot.get(var_name) {
555 if let Some(Value::String(sample_uri)) = map.get(property) {
556 println!("🎵 Spawn nested sample: {}.{} -> {}", var_name, property, sample_uri);
557 let mut event_list = AudioEventList::new();
558 event_list.add_sample_event(
559 sample_uri.trim_matches('"').trim_matches('\''),
560 current_time,
561 1.0, );
563 return Ok(event_list);
564 }
565 }
566 }
567 }
568 if let Some(sample_value) = variables_snapshot.get(resolved_name) {
570 if let Value::String(sample_uri) = sample_value {
571 println!("🎵 Spawn sample: {} -> {}", resolved_name, sample_uri);
572 let mut event_list = AudioEventList::new();
574 event_list.add_sample_event(
575 sample_uri.trim_matches('"').trim_matches('\''),
576 current_time,
577 1.0, );
579 return Ok(event_list);
580 }
581 }
582 let mut local_interpreter = AudioInterpreter {
584 sample_rate: self.sample_rate,
585 bpm: current_bpm,
586 function_registry: FunctionRegistry::new(), events: AudioEventList::new(),
588 variables: variables_snapshot.clone(),
589 groups: groups_snapshot.clone(),
590 cursor_time: current_time,
591 special_vars: special_vars_snapshot.clone(),
592 event_registry: EventRegistry::new(),
593 current_statement_location: None, };
595 if let Some(body) = groups_snapshot.get(resolved_name) {
597 println!("🎬 Spawn group: {} (parallel)", resolved_name);
598 local_interpreter.collect_events(body)?;
599 Ok(local_interpreter.events)
600 } else {
601 println!("⚠️ Warning: Spawn target '{}' not found (neither sample nor group)", resolved_name);
602 Ok(AudioEventList::new())
603 }
604 } else {
605 Ok(AudioEventList::new())
606 }
607 })
608 .collect();
609
610 for result in spawn_results {
612 match result {
613 Ok(spawn_events) => {
614 self.events.merge(spawn_events);
615 }
616 Err(e) => {
617 println!("⚠️ Error in spawn execution: {}", e);
618 }
620 }
621 }
622
623 println!("✅ Parallel spawn execution completed");
624 }
625
626 Ok(())
627 }
628
629 fn handle_let(&mut self, name: &str, value: &Value) -> Result<()> {
630 if let Value::Map(map) = value {
632 if map.contains_key("waveform") {
633 let waveform = extract_string(map, "waveform", "sine");
635 let attack = extract_number(map, "attack", 0.01);
636 let decay = extract_number(map, "decay", 0.1);
637 let sustain = extract_number(map, "sustain", 0.7);
638 let release = extract_number(map, "release", 0.2);
639
640 let synth_type = if let Some(Value::String(t)) = map.get("type") {
642 let clean = t.trim_matches('"').trim_matches('\'');
644 if clean.is_empty() || clean == "synth" {
645 None
646 } else {
647 Some(clean.to_string())
648 }
649 } else {
650 None
651 };
652
653 let filters = if let Some(Value::Array(filters_arr)) = map.get("filters") {
655 extract_filters(filters_arr)
656 } else {
657 Vec::new()
658 };
659
660 let mut options = std::collections::HashMap::new();
662 for (key, val) in map.iter() {
663 if ![
664 "waveform", "attack", "decay", "sustain", "release", "type", "filters",
665 ]
666 .contains(&key.as_str())
667 {
668 if let Value::Number(n) = val {
669 options.insert(key.clone(), *n);
670 }
671 }
672 }
673
674 let type_info = synth_type
675 .as_ref()
676 .map(|t| format!(" [{}]", t))
677 .unwrap_or_default();
678 let opts_info = if !options.is_empty() {
679 let opts: Vec<String> = options
680 .iter()
681 .map(|(k, v)| format!("{}={:.2}", k, v))
682 .collect();
683 format!(" (options: {})", opts.join(", "))
684 } else {
685 String::new()
686 };
687
688 let synth_def = SynthDefinition {
689 waveform,
690 attack,
691 decay,
692 sustain,
693 release,
694 synth_type,
695 filters,
696 options,
697 };
698
699 self.events.add_synth(name.to_string(), synth_def);
700
701 println!("🎹 Synth registered: {}{}{}", name, type_info, opts_info);
702 }
703 }
704
705 self.variables.insert(name.to_string(), value.clone());
707 Ok(())
708 }
709
710 fn extract_audio_event(
711 &mut self,
712 target: &str,
713 context: &crate::engine::functions::FunctionContext,
714 ) -> Result<()> {
715 if let Some(Value::String(note_name)) = context.get("note") {
717 let midi = parse_note_to_midi(note_name)?;
718 let duration = if let Some(Value::Number(d)) = context.get("duration") {
719 d / 1000.0 } else {
721 0.5
722 };
723 let velocity = if let Some(Value::Number(v)) = context.get("velocity") {
724 v / 100.0 } else {
726 0.8
727 };
728
729 let pan = if let Some(Value::Number(p)) = context.get("pan") {
731 *p
732 } else {
733 0.0
734 };
735
736 let detune = if let Some(Value::Number(d)) = context.get("detune") {
737 *d
738 } else {
739 0.0
740 };
741
742 let gain = if let Some(Value::Number(g)) = context.get("gain") {
743 *g
744 } else {
745 1.0
746 };
747
748 let attack = if let Some(Value::Number(a)) = context.get("attack") {
749 Some(*a)
750 } else {
751 None
752 };
753
754 let release = if let Some(Value::Number(r)) = context.get("release") {
755 Some(*r)
756 } else {
757 None
758 };
759
760 let delay_time = if let Some(Value::Number(t)) = context.get("delay_time") {
762 Some(*t)
763 } else {
764 None
765 };
766
767 let delay_feedback = if let Some(Value::Number(f)) = context.get("delay_feedback") {
768 Some(*f)
769 } else {
770 None
771 };
772
773 let delay_mix = if let Some(Value::Number(m)) = context.get("delay_mix") {
774 Some(*m)
775 } else {
776 None
777 };
778
779 let reverb_amount = if let Some(Value::Number(a)) = context.get("reverb_amount") {
780 Some(*a)
781 } else {
782 None
783 };
784
785 let drive_amount = if let Some(Value::Number(a)) = context.get("drive_amount") {
786 Some(*a)
787 } else {
788 None
789 };
790
791 let drive_color = if let Some(Value::Number(c)) = context.get("drive_color") {
792 Some(*c)
793 } else {
794 None
795 };
796
797 self.events.add_note_event(
798 target,
799 midi,
800 context.start_time,
801 duration,
802 velocity,
803 pan,
804 detune,
805 gain,
806 attack,
807 release,
808 delay_time,
809 delay_feedback,
810 delay_mix,
811 reverb_amount,
812 drive_amount,
813 drive_color,
814 );
815
816 #[cfg(target_arch = "wasm32")]
818 {
819 use crate::web::registry::playhead::{PlayheadEvent, push_event};
820
821 push_event(PlayheadEvent {
823 event_type: "note_on".to_string(),
824 midi: vec![midi],
825 time: context.start_time,
826 velocity,
827 synth_id: target.to_string(),
828 });
829
830 push_event(PlayheadEvent {
832 event_type: "note_off".to_string(),
833 midi: vec![midi],
834 time: context.start_time + duration,
835 velocity,
836 synth_id: target.to_string(),
837 });
838 }
839 }
840
841 if let Some(Value::Array(notes)) = context.get("notes") {
843 let mut midis = Vec::new();
844 for note_val in notes {
845 if let Value::String(note_name) = note_val {
846 midis.push(parse_note_to_midi(note_name)?);
847 }
848 }
849
850 let duration = if let Some(Value::Number(d)) = context.get("duration") {
851 d / 1000.0
852 } else {
853 0.5
854 };
855 let velocity = if let Some(Value::Number(v)) = context.get("velocity") {
856 v / 100.0
857 } else {
858 0.8
859 };
860
861 let pan = if let Some(Value::Number(p)) = context.get("pan") {
863 *p
864 } else {
865 0.0
866 };
867
868 let detune = if let Some(Value::Number(d)) = context.get("detune") {
869 *d
870 } else {
871 0.0
872 };
873
874 let spread = if let Some(Value::Number(s)) = context.get("spread") {
875 *s
876 } else {
877 0.0
878 };
879
880 let gain = if let Some(Value::Number(g)) = context.get("gain") {
881 *g
882 } else {
883 1.0
884 };
885
886 let attack = if let Some(Value::Number(a)) = context.get("attack") {
887 Some(*a)
888 } else {
889 None
890 };
891
892 let release = if let Some(Value::Number(r)) = context.get("release") {
893 Some(*r)
894 } else {
895 None
896 };
897
898 let delay_time = if let Some(Value::Number(t)) = context.get("delay_time") {
900 Some(*t)
901 } else {
902 None
903 };
904
905 let delay_feedback = if let Some(Value::Number(f)) = context.get("delay_feedback") {
906 Some(*f)
907 } else {
908 None
909 };
910
911 let delay_mix = if let Some(Value::Number(m)) = context.get("delay_mix") {
912 Some(*m)
913 } else {
914 None
915 };
916
917 let reverb_amount = if let Some(Value::Number(a)) = context.get("reverb_amount") {
918 Some(*a)
919 } else {
920 None
921 };
922
923 let drive_amount = if let Some(Value::Number(a)) = context.get("drive_amount") {
924 Some(*a)
925 } else {
926 None
927 };
928
929 let drive_color = if let Some(Value::Number(c)) = context.get("drive_color") {
930 Some(*c)
931 } else {
932 None
933 };
934
935 self.events.add_chord_event(
936 target,
937 midis.clone(),
938 context.start_time,
939 duration,
940 velocity,
941 pan,
942 detune,
943 spread,
944 gain,
945 attack,
946 release,
947 delay_time,
948 delay_feedback,
949 delay_mix,
950 reverb_amount,
951 drive_amount,
952 drive_color,
953 );
954
955 #[cfg(target_arch = "wasm32")]
957 {
958 use crate::web::registry::playhead::{PlayheadEvent, push_event};
959
960 push_event(PlayheadEvent {
962 event_type: "chord_on".to_string(),
963 midi: midis.clone(),
964 time: context.start_time,
965 velocity,
966 synth_id: target.to_string(),
967 });
968
969 push_event(PlayheadEvent {
971 event_type: "chord_off".to_string(),
972 midi: midis,
973 time: context.start_time + duration,
974 velocity,
975 synth_id: target.to_string(),
976 });
977 }
978 }
979
980 Ok(())
981 }
982
983 fn render_audio(&self) -> Result<Vec<f32>> {
984 let total_duration = self.events.total_duration();
985 if total_duration <= 0.0 {
986 return Ok(Vec::new());
987 }
988
989 let total_samples = (total_duration * self.sample_rate as f32).ceil() as usize;
990 let mut buffer = vec![0.0f32; total_samples * 2]; let mut _note_count = 0;
994 let mut _chord_count = 0;
995 let mut _sample_count = 0;
996 for event in &self.events.events {
997 match event {
998 crate::engine::audio::events::AudioEvent::Note { .. } => _note_count += 1,
999 crate::engine::audio::events::AudioEvent::Chord { .. } => _chord_count += 1,
1000 crate::engine::audio::events::AudioEvent::Sample { .. } => _sample_count += 1,
1001 }
1002 }
1003
1004 for event in &self.events.events {
1006 match event {
1007 crate::engine::audio::events::AudioEvent::Note {
1008 midi,
1009 start_time,
1010 duration,
1011 velocity,
1012 synth_id: _,
1013 synth_def,
1014 pan,
1015 detune,
1016 gain,
1017 attack,
1018 release,
1019 delay_time,
1020 delay_feedback,
1021 delay_mix,
1022 reverb_amount,
1023 drive_amount,
1024 drive_color,
1025 } => {
1026 let mut params = SynthParams {
1028 waveform: synth_def.waveform.clone(),
1029 attack: synth_def.attack,
1030 decay: synth_def.decay,
1031 sustain: synth_def.sustain,
1032 release: synth_def.release,
1033 synth_type: synth_def.synth_type.clone(),
1034 filters: synth_def.filters.clone(),
1035 options: synth_def.options.clone(),
1036 };
1037
1038 if let Some(a) = attack {
1040 params.attack = a / 1000.0; }
1042 if let Some(r) = release {
1043 params.release = r / 1000.0; }
1045
1046 let mut samples = generate_note_with_options(
1047 *midi,
1048 duration * 1000.0,
1049 velocity * gain, ¶ms,
1051 self.sample_rate,
1052 *pan,
1053 *detune,
1054 )?;
1055
1056 if let Some(amount) = drive_amount {
1060 let color = drive_color.unwrap_or(0.5);
1061 let mix = 0.7; let mut processor = DriveProcessor::new(*amount, color, mix);
1063 processor.process(&mut samples, self.sample_rate);
1064 }
1065
1066 if let Some(amount) = reverb_amount {
1068 let room_size = *amount; let damping = 0.5; let mix = *amount * 0.5; let mut processor = ReverbProcessor::new(room_size, damping, mix);
1072 processor.process(&mut samples, self.sample_rate);
1073 }
1074
1075 if let Some(time) = delay_time {
1077 let feedback = delay_feedback.unwrap_or(0.3);
1078 let mix = delay_mix.unwrap_or(0.5);
1079 let mut processor = DelayProcessor::new(*time, feedback, mix);
1080 processor.process(&mut samples, self.sample_rate);
1081 }
1082
1083 let start_sample = (*start_time * self.sample_rate as f32) as usize * 2;
1085 for (i, &sample) in samples.iter().enumerate() {
1086 let buf_idx = start_sample + i;
1087 if buf_idx < buffer.len() {
1088 buffer[buf_idx] += sample;
1089 }
1090 }
1091 }
1092
1093 crate::engine::audio::events::AudioEvent::Chord {
1094 midis,
1095 start_time,
1096 duration,
1097 velocity,
1098 synth_id: _,
1099 synth_def,
1100 pan,
1101 detune,
1102 spread,
1103 gain,
1104 attack,
1105 release,
1106 delay_time,
1107 delay_feedback,
1108 delay_mix,
1109 reverb_amount,
1110 drive_amount,
1111 drive_color,
1112 } => {
1113 let mut params = SynthParams {
1115 waveform: synth_def.waveform.clone(),
1116 attack: synth_def.attack,
1117 decay: synth_def.decay,
1118 sustain: synth_def.sustain,
1119 release: synth_def.release,
1120 synth_type: synth_def.synth_type.clone(),
1121 filters: synth_def.filters.clone(),
1122 options: synth_def.options.clone(),
1123 };
1124
1125 if let Some(a) = attack {
1127 params.attack = a / 1000.0; }
1129 if let Some(r) = release {
1130 params.release = r / 1000.0; }
1132
1133 let mut samples = generate_chord_with_options(
1134 midis,
1135 duration * 1000.0,
1136 velocity * gain, ¶ms,
1138 self.sample_rate,
1139 *pan,
1140 *detune,
1141 *spread,
1142 )?;
1143
1144 if let Some(amount) = drive_amount {
1148 let color = drive_color.unwrap_or(0.5);
1149 let mix = 0.7; let mut processor = DriveProcessor::new(*amount, color, mix);
1151 processor.process(&mut samples, self.sample_rate);
1152 }
1153
1154 if let Some(amount) = reverb_amount {
1156 let room_size = *amount; let damping = 0.5; let mix = *amount * 0.5; let mut processor = ReverbProcessor::new(room_size, damping, mix);
1160 processor.process(&mut samples, self.sample_rate);
1161 }
1162
1163 if let Some(time) = delay_time {
1165 let feedback = delay_feedback.unwrap_or(0.3);
1166 let mix = delay_mix.unwrap_or(0.5);
1167 let mut processor = DelayProcessor::new(*time, feedback, mix);
1168 processor.process(&mut samples, self.sample_rate);
1169 }
1170
1171 let start_sample = (*start_time * self.sample_rate as f32) as usize * 2;
1173 for (i, &sample) in samples.iter().enumerate() {
1174 let buf_idx = start_sample + i;
1175 if buf_idx < buffer.len() {
1176 buffer[buf_idx] += sample;
1177 }
1178 }
1179 }
1180
1181 crate::engine::audio::events::AudioEvent::Sample {
1182 uri,
1183 start_time,
1184 velocity,
1185 } => {
1186 #[cfg(target_arch = "wasm32")]
1188 {
1189 use crate::web::registry::samples::get_sample;
1190
1191 if let Some(pcm_data) = get_sample(uri) {
1192 let start_sample_idx = (*start_time * self.sample_rate as f32) as usize;
1194
1195 web_sys::console::log_1(
1196 &format!(
1197 "🔊 Rendering sample: {} at {:.3}s, {} frames, velocity {:.2}",
1198 uri,
1199 start_time,
1200 pcm_data.len(),
1201 velocity
1202 )
1203 .into(),
1204 );
1205 web_sys::console::log_1(
1206 &format!(
1207 " Start buffer index: {} (stereo pos: {})",
1208 start_sample_idx,
1209 start_sample_idx * 2
1210 )
1211 .into(),
1212 );
1213
1214 for (i, &pcm_value) in pcm_data.iter().enumerate() {
1216 let sample = (pcm_value as f32 / 32768.0) * velocity;
1218
1219 let stereo_pos = (start_sample_idx + i) * 2;
1221 let buf_idx_l = stereo_pos;
1222 let buf_idx_r = stereo_pos + 1;
1223
1224 if buf_idx_l < buffer.len() {
1226 buffer[buf_idx_l] += sample;
1227 }
1228 if buf_idx_r < buffer.len() {
1229 buffer[buf_idx_r] += sample;
1230 }
1231 }
1232 } else {
1233 println!("⚠️ Sample not found in registry: {}", uri);
1234 }
1235 }
1236
1237 #[cfg(not(target_arch = "wasm32"))]
1239 {
1240 use crate::engine::audio::samples;
1241
1242 if let Some(sample_data) = samples::get_sample(uri) {
1243 let start_sample_idx = (*start_time * self.sample_rate as f32) as usize;
1245 let velocity_scale = velocity / 100.0;
1246
1247 let resample_ratio = self.sample_rate as f32 / sample_data.sample_rate as f32;
1249
1250 for (i, &sample) in sample_data.samples.iter().enumerate() {
1251 let output_idx = start_sample_idx + (i as f32 * resample_ratio) as usize;
1253 let stereo_pos = output_idx * 2;
1254 let buf_idx_l = stereo_pos;
1255 let buf_idx_r = stereo_pos + 1;
1256
1257 let scaled_sample = sample * velocity_scale;
1258
1259 if buf_idx_l < buffer.len() {
1261 buffer[buf_idx_l] += scaled_sample;
1262 }
1263 if buf_idx_r < buffer.len() {
1264 buffer[buf_idx_r] += scaled_sample;
1265 }
1266 }
1267 } else {
1268 eprintln!("❌ Error: Bank sample not found: {}", uri);
1270 eprintln!(" Make sure the bank is loaded and the sample exists.");
1271 }
1273 }
1274 }
1275 }
1276 }
1277
1278 let max_amplitude = buffer.iter().map(|&s| s.abs()).fold(0.0f32, f32::max);
1280 if max_amplitude > 1.0 {
1281 for sample in buffer.iter_mut() {
1282 *sample /= max_amplitude;
1283 }
1284 }
1285
1286 Ok(buffer)
1287 }
1288
1289 pub fn set_bpm(&mut self, bpm: f32) {
1290 self.bpm = bpm.max(1.0).min(999.0);
1291 }
1292
1293 pub fn samples_per_beat(&self) -> usize {
1294 ((60.0 / self.bpm) * self.sample_rate as f32) as usize
1295 }
1296
1297 pub fn beat_duration(&self) -> f32 {
1299 60.0 / self.bpm
1300 }
1301
1302 fn execute_print(&self, value: &Value) -> Result<()> {
1305 let message = match value {
1306 Value::String(s) => {
1307 if s.contains('{') && s.contains('}') {
1309 self.interpolate_string(s)
1310 } else {
1311 s.clone()
1312 }
1313 }
1314 Value::Number(n) => n.to_string(),
1315 Value::Boolean(b) => b.to_string(),
1316 Value::Array(arr) => format!("{:?}", arr),
1317 Value::Map(map) => format!("{:?}", map),
1318 _ => format!("{:?}", value),
1319 };
1320
1321 println!("💬 {}", message);
1322 Ok(())
1323 }
1324
1325 fn interpolate_string(&self, template: &str) -> String {
1328 let mut result = template.to_string();
1329
1330 let re = regex::Regex::new(r"\{([a-zA-Z_][a-zA-Z0-9_]*)\}").unwrap();
1332
1333 for cap in re.captures_iter(template) {
1334 let full_match = &cap[0]; let var_name = &cap[1]; if let Some(value) = self.variables.get(var_name) {
1338 let replacement = self.value_to_string(value);
1339 result = result.replace(full_match, &replacement);
1340 } else {
1341 result = result.replace(full_match, &format!("<undefined:{}>", var_name));
1343 }
1344 }
1345
1346 result
1347 }
1348
1349 fn value_to_string(&self, value: &Value) -> String {
1351 match value {
1352 Value::String(s) => {
1353 s.trim_matches('"').trim_matches('\'').to_string()
1355 }
1356 Value::Number(n) => {
1357 if n.fract() == 0.0 {
1359 format!("{:.0}", n)
1360 } else {
1361 format!("{}", n)
1362 }
1363 }
1364 Value::Boolean(b) => b.to_string(),
1365 Value::Array(arr) => {
1366 let items: Vec<String> = arr.iter().map(|v| self.value_to_string(v)).collect();
1367 format!("[{}]", items.join(", "))
1368 }
1369 Value::Identifier(id) => id.clone(),
1370 _ => format!("{:?}", value),
1371 }
1372 }
1373
1374 fn execute_if(
1376 &mut self,
1377 condition: &Value,
1378 body: &[Statement],
1379 else_body: &Option<Vec<Statement>>,
1380 ) -> Result<()> {
1381 let condition_result = self.evaluate_condition(condition)?;
1382
1383 if condition_result {
1384 self.collect_events(body)?;
1386 } else if let Some(else_stmts) = else_body {
1387 self.collect_events(else_stmts)?;
1389 }
1390
1391 Ok(())
1392 }
1393
1394 fn evaluate_condition(&self, condition: &Value) -> Result<bool> {
1397 if let Value::Map(map) = condition {
1399 let operator = map
1400 .get("operator")
1401 .and_then(|v| {
1402 if let Value::String(s) = v {
1403 Some(s.as_str())
1404 } else {
1405 None
1406 }
1407 })
1408 .unwrap_or("==");
1409
1410 let left = map
1411 .get("left")
1412 .ok_or_else(|| anyhow::anyhow!("Missing left operand"))?;
1413 let right = map
1414 .get("right")
1415 .ok_or_else(|| anyhow::anyhow!("Missing right operand"))?;
1416
1417 let left_val = self.resolve_value(left);
1419 let right_val = self.resolve_value(right);
1420
1421 match operator {
1423 "==" => Ok(self.values_equal(&left_val, &right_val)),
1424 "!=" => Ok(!self.values_equal(&left_val, &right_val)),
1425 "<" => self.compare_values(&left_val, &right_val, std::cmp::Ordering::Less),
1426 ">" => self.compare_values(&left_val, &right_val, std::cmp::Ordering::Greater),
1427 "<=" => {
1428 let less =
1429 self.compare_values(&left_val, &right_val, std::cmp::Ordering::Less)?;
1430 let equal = self.values_equal(&left_val, &right_val);
1431 Ok(less || equal)
1432 }
1433 ">=" => {
1434 let greater =
1435 self.compare_values(&left_val, &right_val, std::cmp::Ordering::Greater)?;
1436 let equal = self.values_equal(&left_val, &right_val);
1437 Ok(greater || equal)
1438 }
1439 _ => Err(anyhow::anyhow!("Unknown operator: {}", operator)),
1440 }
1441 } else {
1442 match condition {
1444 Value::Boolean(b) => Ok(*b),
1445 Value::Number(n) => Ok(*n != 0.0),
1446 Value::String(s) => Ok(!s.is_empty()),
1447 _ => Ok(false),
1448 }
1449 }
1450 }
1451
1452 fn resolve_value(&self, value: &Value) -> Value {
1454 match value {
1455 Value::Identifier(name) => {
1456 if is_special_var(name) {
1458 if let Some(special_val) = resolve_special_var(name, &self.special_vars) {
1459 return special_val;
1460 }
1461 }
1462
1463 self.variables.get(name).cloned().unwrap_or(Value::Null)
1465 }
1466 _ => value.clone(),
1467 }
1468 }
1469
1470 fn execute_event_handlers(&mut self, event_name: &str) -> Result<()> {
1472 let handlers = self.event_registry.get_handlers_matching(event_name);
1473
1474 for (index, handler) in handlers.iter().enumerate() {
1475 if handler.once && !self.event_registry.should_execute_once(event_name, index) {
1477 continue;
1478 }
1479
1480 let body_clone = handler.body.clone();
1482 self.collect_events(&body_clone)?;
1483 }
1484
1485 Ok(())
1486 }
1487
1488 fn values_equal(&self, left: &Value, right: &Value) -> bool {
1490 match (left, right) {
1491 (Value::Number(a), Value::Number(b)) => (a - b).abs() < 0.0001,
1492 (Value::String(a), Value::String(b)) => a == b,
1493 (Value::Boolean(a), Value::Boolean(b)) => a == b,
1494 (Value::Null, Value::Null) => true,
1495 _ => false,
1496 }
1497 }
1498
1499 fn compare_values(
1501 &self,
1502 left: &Value,
1503 right: &Value,
1504 ordering: std::cmp::Ordering,
1505 ) -> Result<bool> {
1506 match (left, right) {
1507 (Value::Number(a), Value::Number(b)) => {
1508 Ok(a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal) == ordering)
1509 }
1510 (Value::String(a), Value::String(b)) => Ok(a.cmp(b) == ordering),
1511 _ => Err(anyhow::anyhow!("Cannot compare {:?} and {:?}", left, right)),
1512 }
1513 }
1514
1515 fn handle_assign(&mut self, target: &str, property: &str, value: &Value) -> Result<()> {
1517 if let Some(var) = self.variables.get_mut(target) {
1519 if let Value::Map(map) = var {
1520 map.insert(property.to_string(), value.clone());
1522
1523 if self.events.synths.contains_key(target) {
1525 let map_clone = map.clone();
1527 let updated_def = self.extract_synth_def_from_map(&map_clone)?;
1529 self.events.synths.insert(target.to_string(), updated_def);
1530 println!("🔧 Updated {}.{} = {:?}", target, property, value);
1531 }
1532 } else {
1533 return Err(anyhow::anyhow!(
1534 "Cannot assign property '{}' to non-map variable '{}'",
1535 property,
1536 target
1537 ));
1538 }
1539 } else {
1540 return Err(anyhow::anyhow!("Variable '{}' not found", target));
1541 }
1542
1543 Ok(())
1544 }
1545
1546 fn extract_synth_def_from_map(&self, map: &HashMap<String, Value>) -> Result<SynthDefinition> {
1548 use crate::engine::audio::events::extract_filters;
1549
1550 let waveform = extract_string(map, "waveform", "sine");
1551 let attack = extract_number(map, "attack", 0.01);
1552 let decay = extract_number(map, "decay", 0.1);
1553 let sustain = extract_number(map, "sustain", 0.7);
1554 let release = extract_number(map, "release", 0.2);
1555
1556 let synth_type = if let Some(Value::String(t)) = map.get("type") {
1557 let clean = t.trim_matches('"').trim_matches('\'');
1558 if clean.is_empty() || clean == "synth" {
1559 None
1560 } else {
1561 Some(clean.to_string())
1562 }
1563 } else {
1564 None
1565 };
1566
1567 let filters = if let Some(Value::Array(filters_arr)) = map.get("filters") {
1568 extract_filters(filters_arr)
1569 } else {
1570 Vec::new()
1571 };
1572
1573 let mut options = HashMap::new();
1574 for (key, val) in map.iter() {
1575 if ![
1576 "waveform", "attack", "decay", "sustain", "release", "type", "filters",
1577 ]
1578 .contains(&key.as_str())
1579 {
1580 if let Value::Number(n) = val {
1581 options.insert(key.clone(), *n);
1582 }
1583 }
1584 }
1585
1586 Ok(SynthDefinition {
1587 waveform,
1588 attack,
1589 decay,
1590 sustain,
1591 release,
1592 synth_type,
1593 filters,
1594 options,
1595 })
1596 }
1597
1598 fn handle_load(&mut self, source: &str, alias: &str) -> Result<()> {
1600 use crate::engine::audio::midi::load_midi_file;
1601 use std::path::Path;
1602
1603 let path = Path::new(source);
1605 let midi_data = load_midi_file(path)?;
1606
1607 self.variables.insert(alias.to_string(), midi_data);
1609
1610 println!("🎵 Loaded MIDI file: {} as {}", source, alias);
1611
1612 Ok(())
1613 }
1614
1615 fn handle_bind(&mut self, source: &str, target: &str, options: &Value) -> Result<()> {
1617 let midi_data = self
1619 .variables
1620 .get(source)
1621 .ok_or_else(|| anyhow::anyhow!("MIDI source '{}' not found", source))?
1622 .clone();
1623
1624 if let Value::Map(midi_map) = &midi_data {
1626 let notes = midi_map
1627 .get("notes")
1628 .ok_or_else(|| anyhow::anyhow!("MIDI data has no notes"))?;
1629
1630 if let Value::Array(notes_array) = notes {
1631 let _synth_def = self
1633 .events
1634 .synths
1635 .get(target)
1636 .ok_or_else(|| anyhow::anyhow!("Synth '{}' not found", target))?
1637 .clone();
1638
1639 let default_velocity = 100;
1641 let mut velocity = default_velocity;
1642
1643 if let Value::Map(opts) = options {
1644 if let Some(Value::Number(v)) = opts.get("velocity") {
1645 velocity = *v as u8;
1646 }
1647 }
1648
1649 for note_val in notes_array {
1651 if let Value::Map(note_map) = note_val {
1652 let time = extract_number(note_map, "time", 0.0);
1653 let note = extract_number(note_map, "note", 60.0) as u8;
1654 let note_velocity =
1655 extract_number(note_map, "velocity", velocity as f32) as u8;
1656
1657 use crate::engine::audio::events::AudioEvent;
1659 let synth_def = self.events.get_synth(target).cloned().unwrap_or_default();
1661
1662 let event = AudioEvent::Note {
1663 midi: note,
1664 start_time: time / 1000.0, duration: 0.5, velocity: note_velocity as f32,
1667 synth_id: target.to_string(),
1668 synth_def,
1669 pan: 0.0,
1670 detune: 0.0,
1671 gain: 1.0,
1672 attack: None,
1673 release: None,
1674 delay_time: None,
1675 delay_feedback: None,
1676 delay_mix: None,
1677 reverb_amount: None,
1678 drive_amount: None,
1679 drive_color: None,
1680 };
1681
1682 self.events.events.push(event);
1683 }
1684 }
1685
1686 println!(
1687 "🔗 Bound {} notes from {} to {}",
1688 notes_array.len(),
1689 source,
1690 target
1691 );
1692 }
1693 }
1694
1695 Ok(())
1696 }
1697
1698 fn extract_pattern_data(&self, value: &Value) -> (Option<String>, Option<HashMap<String, f32>>) {
1700 match value {
1701 Value::String(pattern) => (Some(pattern.clone()), None),
1702 Value::Map(map) => {
1703 let pattern = map.get("pattern").and_then(|v| {
1704 if let Value::String(s) = v {
1705 Some(s.clone())
1706 } else {
1707 None
1708 }
1709 });
1710
1711 let mut options = HashMap::new();
1713 for (key, val) in map.iter() {
1714 if key != "pattern" {
1715 if let Value::Number(num) = val {
1716 options.insert(key.clone(), *num);
1717 }
1718 }
1719 }
1720
1721 let opts = if options.is_empty() {
1722 None
1723 } else {
1724 Some(options)
1725 };
1726
1727 (pattern, opts)
1728 }
1729 _ => (None, None),
1730 }
1731 }
1732
1733 fn execute_pattern(
1735 &mut self,
1736 target: &str,
1737 pattern: &str,
1738 options: Option<HashMap<String, f32>>,
1739 ) -> Result<()> {
1740 use crate::engine::audio::events::AudioEvent;
1741
1742 let swing = options.as_ref().and_then(|o| o.get("swing").copied()).unwrap_or(0.0);
1744 let humanize = options.as_ref().and_then(|o| o.get("humanize").copied()).unwrap_or(0.0);
1745 let velocity_mult = options.as_ref().and_then(|o| o.get("velocity").copied()).unwrap_or(1.0);
1746 let tempo_override = options.as_ref().and_then(|o| o.get("tempo").copied());
1747
1748 let effective_bpm = tempo_override.unwrap_or(self.bpm);
1750
1751 let resolved_uri = self.resolve_sample_uri(target);
1753
1754 let pattern_chars: Vec<char> = pattern.chars().filter(|c| !c.is_whitespace()).collect();
1756 let step_count = pattern_chars.len() as f32;
1757
1758 if step_count == 0.0 {
1759 return Ok(());
1760 }
1761
1762 let bar_duration = (60.0 / effective_bpm) * 4.0;
1765 let step_duration = bar_duration / step_count;
1766
1767 for (i, &ch) in pattern_chars.iter().enumerate() {
1769 if ch == 'x' || ch == 'X' {
1770 let mut time = self.cursor_time + (i as f32 * step_duration);
1772
1773 if swing > 0.0 && i % 2 == 1 {
1775 time += step_duration * swing;
1776 }
1777
1778 if humanize > 0.0 {
1780 use rand::Rng;
1781 let mut rng = rand::thread_rng();
1782 let offset = rng.gen_range(-humanize..humanize);
1783 time += offset;
1784 }
1785
1786 let event = AudioEvent::Sample {
1788 uri: resolved_uri.clone(),
1789 start_time: time,
1790 velocity: 100.0 * velocity_mult,
1791 };
1792
1793 self.events.events.push(event);
1794 }
1795 }
1796
1797 self.cursor_time += bar_duration;
1799
1800 Ok(())
1801 }
1802
1803 fn resolve_sample_uri(&self, target: &str) -> String {
1805 if let Some(dot_pos) = target.find('.') {
1807 let bank_alias = &target[..dot_pos];
1808 let trigger_name = &target[dot_pos + 1..];
1809
1810 if let Some(Value::Map(bank_map)) = self.variables.get(bank_alias) {
1812 if let Some(Value::String(bank_name)) = bank_map.get("_name") {
1813 return format!("devalang://bank/{}/{}", bank_name, trigger_name);
1815 }
1816 }
1817 }
1818
1819 target.to_string()
1821 }
1822}
1823