1use crate::engine::audio::events::AudioEventList;
2use crate::engine::audio::events::SynthDefinition;
3#[cfg(feature = "cli")]
4use crate::engine::audio::midi_native::MidiManager;
5use crate::engine::events::EventRegistry;
6use crate::engine::functions::FunctionRegistry;
7use crate::engine::special_vars::{SpecialVarContext, is_special_var, resolve_special_var};
8#[cfg(feature = "cli")]
9use crate::language::addons::registry::BankRegistry;
10use crate::language::syntax::ast::{Statement, Value};
11
12#[derive(Debug, Clone)]
14pub struct RoutingNodeConfig {
15 pub name: String,
16 pub alias: Option<String>,
17 pub effects: Option<Value>, }
19
20#[derive(Debug, Clone)]
22pub struct RouteConfig {
23 pub source: String,
24 pub destination: String,
25 pub effects: Option<Value>,
26}
27
28#[derive(Debug, Clone)]
30pub struct DuckConfig {
31 pub source: String,
32 pub destination: String,
33 pub effect: Value,
34}
35
36#[derive(Debug, Clone)]
38pub struct SidechainConfig {
39 pub source: String,
40 pub destination: String,
41 pub effect: Value,
42}
43
44#[derive(Debug, Clone)]
46pub struct RoutingSetup {
47 pub nodes: HashMap<String, RoutingNodeConfig>,
48 pub routes: Vec<RouteConfig>,
49 pub ducks: Vec<DuckConfig>,
50 pub sidechains: Vec<SidechainConfig>,
51}
52
53impl Default for RoutingSetup {
54 fn default() -> Self {
55 Self {
56 nodes: HashMap::new(),
57 routes: Vec::new(),
58 ducks: Vec::new(),
59 sidechains: Vec::new(),
60 }
61 }
62}
63
64#[cfg(not(feature = "cli"))]
65#[allow(dead_code)]
66#[derive(Clone)]
67pub struct BankRegistry;
68
69#[cfg(not(feature = "cli"))]
70impl BankRegistry {
71 pub fn new() -> Self {
72 BankRegistry
73 }
74 pub fn list_banks(&self) -> Vec<(String, StubBank)> {
75 Vec::new()
76 }
77 pub fn resolve_trigger(&self, _var: &str, _prop: &str) -> Option<std::path::PathBuf> {
78 None
79 }
80 pub fn register_bank(
81 &self,
82 _alias: String,
83 _name: &str,
84 _cwd: &std::path::Path,
85 _cwd2: &std::path::Path,
86 ) -> Result<(), anyhow::Error> {
87 Ok(())
88 }
89}
90
91#[cfg(not(feature = "cli"))]
92#[allow(dead_code)]
93#[derive(Clone)]
94pub struct StubBank;
95
96#[cfg(not(feature = "cli"))]
97impl StubBank {
98 pub fn list_triggers(&self) -> Vec<String> {
99 Vec::new()
100 }
101}
102
103#[derive(Clone, Debug)]
105pub struct NoteAutomationContext {
106 pub templates: Vec<crate::engine::audio::automation::AutomationParamTemplate>,
107 pub start_time: f32,
108 pub end_time: f32,
109}
110
111impl NoteAutomationContext {
112 pub fn duration(&self) -> f32 {
114 (self.end_time - self.start_time).max(0.001) }
116
117 pub fn progress_at_time(&self, time: f32) -> f32 {
119 ((time - self.start_time) / self.duration()).clamp(0.0, 1.0)
120 }
121}
122
123use anyhow::Result;
125use std::collections::HashMap;
126
127pub mod collector;
128pub mod extractor;
129pub mod handler;
130pub mod patterns;
131pub mod renderer;
132pub mod renderer_graph;
133
134pub struct AudioInterpreter {
135 pub sample_rate: u32,
136 pub bpm: f32,
137 pub function_registry: FunctionRegistry,
138 pub events: AudioEventList,
139 pub variables: HashMap<String, Value>,
140 pub groups: HashMap<String, Vec<Statement>>,
141 pub banks: BankRegistry,
142 pub automation_registry: crate::engine::audio::automation::AutomationRegistry,
144 pub note_automation_templates: std::collections::HashMap<String, NoteAutomationContext>,
146 pub cursor_time: f32,
147 pub special_vars: SpecialVarContext,
148 pub event_registry: EventRegistry,
149 #[cfg(feature = "cli")]
150 pub midi_manager: Option<std::sync::Arc<std::sync::Mutex<MidiManager>>>,
151 current_statement_location: Option<(usize, usize)>, pub suppress_beat_emit: bool,
155 pub suppress_print: bool,
157 pub break_flag: bool,
159 pub background_event_tx:
161 Option<std::sync::mpsc::Sender<crate::engine::audio::events::AudioEventList>>,
162 pub background_event_rx:
163 Option<std::sync::mpsc::Receiver<crate::engine::audio::events::AudioEventList>>,
164 pub background_workers: Vec<std::thread::JoinHandle<()>>,
166 pub realtime_print_tx: Option<std::sync::mpsc::Sender<(f32, String)>>,
168 pub function_call_depth: usize,
170 pub returning_flag: bool,
173 pub return_value: Option<Value>,
174 pub routing: RoutingSetup,
176 pub audio_graph: crate::engine::audio::graph::AudioGraph,
178 pub current_event_node: Option<String>,
180 pub schedule_deadline: Option<f32>,
182}
183
184impl AudioInterpreter {
185 pub fn new(sample_rate: u32) -> Self {
186 Self {
187 sample_rate,
188 bpm: 120.0,
189 function_registry: FunctionRegistry::new(),
190 events: AudioEventList::new(),
191 variables: HashMap::new(),
192 groups: HashMap::new(),
193 banks: BankRegistry::new(),
194 automation_registry: crate::engine::audio::automation::AutomationRegistry::new(),
195 note_automation_templates: std::collections::HashMap::new(),
196 cursor_time: 0.0,
197 special_vars: SpecialVarContext::new(120.0, sample_rate),
198 event_registry: EventRegistry::new(),
199 #[cfg(feature = "cli")]
200 midi_manager: None,
201 current_statement_location: None,
202 suppress_beat_emit: false,
203 suppress_print: false,
204 break_flag: false,
205 background_event_tx: None,
206 background_event_rx: None,
207 background_workers: Vec::new(),
208 realtime_print_tx: None,
209 function_call_depth: 0,
210 returning_flag: false,
211 return_value: None,
212 routing: RoutingSetup::default(),
213 audio_graph: crate::engine::audio::graph::AudioGraph::new(),
214 current_event_node: None,
215 schedule_deadline: None,
216 }
217 }
218
219 fn handle_trigger(&mut self, entity: &str) -> Result<()> {
221 handler::handle_trigger(self, entity, None)
223 }
224
225 fn debug_list_banks(&self) {
227 }
229
230 pub fn interpret(&mut self, statements: &[Statement]) -> Result<Vec<f32>> {
231 let total_duration = self.calculate_total_duration(statements)?;
233 self.special_vars.total_duration = total_duration;
234 self.special_vars.update_bpm(self.bpm);
235
236 self.collect_events(statements)?;
238
239 if let Some(rx) = self.background_event_rx.as_mut() {
244 use std::sync::mpsc::RecvTimeoutError;
245 use std::time::{Duration, Instant};
246
247 let start = Instant::now();
248 let hard_limit = Duration::from_secs_f32(total_duration + 5.0);
250 loop {
251 match rx.recv_timeout(Duration::from_millis(200)) {
252 Ok(events) => {
253 self.events.merge(events);
254 }
255 Err(RecvTimeoutError::Timeout) => {
256 if self.events.total_duration() >= (total_duration - 0.001) {
258 break;
259 }
260 if start.elapsed() > hard_limit {
261 break;
263 }
264 }
266 Err(RecvTimeoutError::Disconnected) => {
267 break;
269 }
270 }
271 }
272 }
273
274 self.render_audio()
278 }
279
280 pub fn events(&self) -> &AudioEventList {
282 &self.events
283 }
284
285 pub fn current_statement_location(&self) -> Option<(usize, usize)> {
287 self.current_statement_location
288 }
289
290 pub fn calculate_total_duration(&self, _statements: &[Statement]) -> Result<f32> {
292 Ok(60.0) }
295
296 pub fn collect_events(&mut self, statements: &[Statement]) -> Result<()> {
297 collector::collect_events(self, statements)
299 }
300 pub fn handle_let(&mut self, name: &str, value: &Value) -> Result<()> {
301 handler::handle_let(self, name, value)
302 }
303
304 pub fn extract_audio_event(
305 &mut self,
306 target: &str,
307 context: &crate::engine::functions::FunctionContext,
308 ) -> Result<()> {
309 extractor::extract_audio_event(self, target, context)
311 }
312
313 pub fn render_audio(&self) -> Result<Vec<f32>> {
314 renderer::render_audio(self)
316 }
317
318 pub fn set_bpm(&mut self, bpm: f32) {
319 self.bpm = bpm.max(1.0).min(999.0);
320 self.special_vars.update_bpm(self.bpm);
322 }
323
324 pub fn samples_per_beat(&self) -> usize {
325 ((60.0 / self.bpm) * self.sample_rate as f32) as usize
326 }
327
328 pub fn beat_duration(&self) -> f32 {
330 60.0 / self.bpm
331 }
332
333 pub fn execute_print(&mut self, value: &Value) -> Result<()> {
336 handler::execute_print(self, value)
337 }
338
339 pub fn interpolate_string(&self, template: &str) -> String {
342 let mut result = template.to_string();
343
344 let Ok(re) = regex::Regex::new(r"\{([a-zA-Z_][a-zA-Z0-9_]*)\}") else {
346 return result;
347 };
348
349 for cap in re.captures_iter(template) {
350 let full_match = &cap[0]; let var_name = &cap[1]; if let Some(value) = self.variables.get(var_name) {
354 let replacement = self.value_to_string(value);
355 result = result.replace(full_match, &replacement);
356 } else {
357 result = result.replace(full_match, &format!("<undefined:{}>", var_name));
359 }
360 }
361
362 result
363 }
364
365 fn value_to_string(&self, value: &Value) -> String {
367 match value {
368 Value::String(s) => {
369 s.trim_matches('"').trim_matches('\'').to_string()
371 }
372 Value::Number(n) => {
373 if n.fract() == 0.0 {
375 format!("{:.0}", n)
376 } else {
377 format!("{}", n)
378 }
379 }
380 Value::Boolean(b) => b.to_string(),
381 Value::Array(arr) => {
382 let items: Vec<String> = arr.iter().map(|v| self.value_to_string(v)).collect();
383 format!("[{}]", items.join(", "))
384 }
385 Value::Identifier(id) => id.clone(),
386 _ => format!("{:?}", value),
387 }
388 }
389
390 pub fn execute_if(
392 &mut self,
393 condition: &Value,
394 body: &[Statement],
395 else_body: &Option<Vec<Statement>>,
396 ) -> Result<()> {
397 handler::execute_if(self, condition, body, else_body)
398 }
399
400 pub fn evaluate_condition(&mut self, condition: &Value) -> Result<bool> {
403 if let Value::Map(map) = condition {
405 let operator = map
406 .get("operator")
407 .and_then(|v| {
408 if let Value::String(s) = v {
409 Some(s.as_str())
410 } else {
411 None
412 }
413 })
414 .unwrap_or("==");
415
416 match operator {
418 "&&" => {
419 let left = map
420 .get("left")
421 .ok_or_else(|| anyhow::anyhow!("Missing left operand for &&"))?;
422 let right = map
423 .get("right")
424 .ok_or_else(|| anyhow::anyhow!("Missing right operand for &&"))?;
425
426 let left_result = self.evaluate_condition(left)?;
427 let right_result = self.evaluate_condition(right)?;
428 return Ok(left_result && right_result);
429 }
430 "||" => {
431 let left = map
432 .get("left")
433 .ok_or_else(|| anyhow::anyhow!("Missing left operand for ||"))?;
434 let right = map
435 .get("right")
436 .ok_or_else(|| anyhow::anyhow!("Missing right operand for ||"))?;
437
438 let left_result = self.evaluate_condition(left)?;
439 let right_result = self.evaluate_condition(right)?;
440 return Ok(left_result || right_result);
441 }
442 "!" => {
443 let operand = map
444 .get("operand")
445 .ok_or_else(|| anyhow::anyhow!("Missing operand for !"))?;
446
447 let operand_result = self.evaluate_condition(operand)?;
448 return Ok(!operand_result);
449 }
450 _ => {}
451 }
452
453 let left = map
455 .get("left")
456 .ok_or_else(|| anyhow::anyhow!("Missing left operand"))?;
457 let right = map
458 .get("right")
459 .ok_or_else(|| anyhow::anyhow!("Missing right operand"))?;
460
461 let left_val = self.resolve_value(left)?;
463 let right_val = self.resolve_value(right)?;
464
465 match operator {
467 "==" => Ok(self.values_equal(&left_val, &right_val)),
468 "!=" => Ok(!self.values_equal(&left_val, &right_val)),
469 "<" => self.compare_values(&left_val, &right_val, std::cmp::Ordering::Less),
470 ">" => self.compare_values(&left_val, &right_val, std::cmp::Ordering::Greater),
471 "<=" => {
472 let less =
473 self.compare_values(&left_val, &right_val, std::cmp::Ordering::Less)?;
474 let equal = self.values_equal(&left_val, &right_val);
475 Ok(less || equal)
476 }
477 ">=" => {
478 let greater =
479 self.compare_values(&left_val, &right_val, std::cmp::Ordering::Greater)?;
480 let equal = self.values_equal(&left_val, &right_val);
481 Ok(greater || equal)
482 }
483 _ => Err(anyhow::anyhow!("Unknown operator: {}", operator)),
484 }
485 } else {
486 let resolved = self.resolve_value(condition)?;
488 match resolved {
489 Value::Boolean(b) => Ok(b),
490 Value::Number(n) => Ok(n != 0.0),
491 Value::String(s) => Ok(!s.is_empty()),
492 Value::Identifier(id) => {
493 let further = self.resolve_value(&Value::Identifier(id))?;
495 match further {
496 Value::Boolean(b2) => Ok(b2),
497 Value::Number(n2) => Ok(n2 != 0.0),
498 Value::String(s2) => Ok(!s2.is_empty()),
499 _ => Ok(false),
500 }
501 }
502 _ => Ok(false),
503 }
504 }
505 }
506
507 pub fn resolve_value(&mut self, value: &Value) -> Result<Value> {
509 match value {
510 Value::Identifier(name) => {
511 if is_special_var(name) {
513 if let Some(special_val) = resolve_special_var(name, &self.special_vars) {
514 return Ok(special_val);
515 }
516 }
517
518 if name.contains('.') || name.contains('[') {
520 let mut chars = name.chars().peekable();
522 let mut ident = String::new();
524 while let Some(&c) = chars.peek() {
525 if c == '.' || c == '[' {
526 break;
527 }
528 ident.push(c);
529 chars.next();
530 }
531
532 let mut current: Option<Value> = if ident.is_empty() {
533 None
534 } else if is_special_var(&ident) {
535 resolve_special_var(&ident, &self.special_vars)
536 } else {
537 self.variables.get(&ident).cloned()
538 };
539
540 if current.is_none() {
541 return Ok(Value::Null);
542 }
543
544 while let Some(&c) = chars.peek() {
546 if c == '.' {
547 chars.next();
549 let mut prop = String::new();
551 while let Some(&nc) = chars.peek() {
552 if nc == '.' || nc == '[' {
553 break;
554 }
555 prop.push(nc);
556 chars.next();
557 }
558 match current {
560 Some(Value::Map(ref map)) => {
561 current = map.get(&prop).cloned();
562 }
563 _ => {
564 return Ok(Value::Null);
565 }
566 }
567 if current.is_none() {
568 return Ok(Value::Null);
569 }
570 } else if c == '[' {
571 chars.next();
573 let mut idx_tok = String::new();
575 while let Some(&nc) = chars.peek() {
576 if nc == ']' {
577 break;
578 }
579 idx_tok.push(nc);
580 chars.next();
581 }
582 if let Some(&nc) = chars.peek() {
584 if nc == ']' {
585 chars.next();
586 } else {
587 return Ok(Value::Null); }
589 } else {
590 return Ok(Value::Null); }
592
593 let idx_tok_trim = idx_tok.trim();
594
595 let index_value = if let Ok(n) = idx_tok_trim.parse::<usize>() {
597 Value::Number(n as f32)
598 } else {
599 Value::Identifier(idx_tok_trim.to_string())
600 };
601
602 match current {
604 Some(Value::Array(ref arr)) => {
605 let resolved_idx = if idx_tok_trim.ends_with("++") {
610 let varname = idx_tok_trim[..idx_tok_trim.len() - 2].trim();
612 let cur = match self.resolve_value(&Value::Identifier(
613 varname.to_string(),
614 ))? {
615 Value::Number(n2) => n2 as isize,
616 _ => return Ok(Value::Null),
617 };
618 self.variables.insert(
619 varname.to_string(),
620 Value::Number((cur + 1) as f32),
621 );
622 cur
623 } else if idx_tok_trim.ends_with("--") {
624 let varname = idx_tok_trim[..idx_tok_trim.len() - 2].trim();
626 let cur = match self.resolve_value(&Value::Identifier(
627 varname.to_string(),
628 ))? {
629 Value::Number(n2) => n2 as isize,
630 _ => return Ok(Value::Null),
631 };
632 self.variables.insert(
633 varname.to_string(),
634 Value::Number((cur - 1) as f32),
635 );
636 cur
637 } else if idx_tok_trim.contains('+') {
638 let parts: Vec<&str> =
639 idx_tok_trim.splitn(2, '+').collect();
640 let left = parts[0].trim();
641 let right = parts[1].trim();
642 let left_val = if let Ok(n) = left.parse::<isize>() {
643 n
644 } else {
645 match self.resolve_value(&Value::Identifier(
646 left.to_string(),
647 ))? {
648 Value::Number(n2) => n2 as isize,
649 _ => return Ok(Value::Null),
650 }
651 };
652 let right_val = if let Ok(n) = right.parse::<isize>() {
653 n
654 } else {
655 match self.resolve_value(&Value::Identifier(
656 right.to_string(),
657 ))? {
658 Value::Number(n2) => n2 as isize,
659 _ => return Ok(Value::Null),
660 }
661 };
662 left_val + right_val
663 } else if idx_tok_trim.contains('*') {
664 let parts: Vec<&str> =
665 idx_tok_trim.splitn(2, '*').collect();
666 let left = parts[0].trim();
667 let right = parts[1].trim();
668 let left_val = if let Ok(n) = left.parse::<isize>() {
669 n
670 } else {
671 match self.resolve_value(&Value::Identifier(
672 left.to_string(),
673 ))? {
674 Value::Number(n2) => n2 as isize,
675 _ => return Ok(Value::Null),
676 }
677 };
678 let right_val = if let Ok(n) = right.parse::<isize>() {
679 n
680 } else {
681 match self.resolve_value(&Value::Identifier(
682 right.to_string(),
683 ))? {
684 Value::Number(n2) => n2 as isize,
685 _ => return Ok(Value::Null),
686 }
687 };
688 left_val * right_val
689 } else if idx_tok_trim.contains('/') {
690 let parts: Vec<&str> =
691 idx_tok_trim.splitn(2, '/').collect();
692 let left = parts[0].trim();
693 let right = parts[1].trim();
694 let left_val = if let Ok(n) = left.parse::<isize>() {
695 n
696 } else {
697 match self.resolve_value(&Value::Identifier(
698 left.to_string(),
699 ))? {
700 Value::Number(n2) => n2 as isize,
701 _ => return Ok(Value::Null),
702 }
703 };
704 let right_val = if let Ok(n) = right.parse::<isize>() {
705 n
706 } else {
707 match self.resolve_value(&Value::Identifier(
708 right.to_string(),
709 ))? {
710 Value::Number(n2) => n2 as isize,
711 _ => return Ok(Value::Null),
712 }
713 };
714 if right_val == 0 {
715 return Err(anyhow::anyhow!(
716 "Division by zero in index expression"
717 ));
718 }
719 left_val / right_val
720 } else if idx_tok_trim.contains('%') {
721 let parts: Vec<&str> =
722 idx_tok_trim.splitn(2, '%').collect();
723 let left = parts[0].trim();
724 let right = parts[1].trim();
725 let left_val = if let Ok(n) = left.parse::<isize>() {
726 n
727 } else {
728 match self.resolve_value(&Value::Identifier(
729 left.to_string(),
730 ))? {
731 Value::Number(n2) => n2 as isize,
732 _ => return Ok(Value::Null),
733 }
734 };
735 let right_val = if let Ok(n) = right.parse::<isize>() {
736 n
737 } else {
738 match self.resolve_value(&Value::Identifier(
739 right.to_string(),
740 ))? {
741 Value::Number(n2) => n2 as isize,
742 _ => return Ok(Value::Null),
743 }
744 };
745 if right_val == 0 {
746 return Err(anyhow::anyhow!(
747 "Modulo by zero in index expression"
748 ));
749 }
750 left_val % right_val
751 } else if idx_tok_trim.contains('-') {
752 let parts: Vec<&str> =
753 idx_tok_trim.splitn(2, '-').collect();
754 let left = parts[0].trim();
755 let right = parts[1].trim();
756 let left_val = if let Ok(n) = left.parse::<isize>() {
757 n
758 } else {
759 match self.resolve_value(&Value::Identifier(
760 left.to_string(),
761 ))? {
762 Value::Number(n2) => n2 as isize,
763 _ => return Ok(Value::Null),
764 }
765 };
766 let right_val = if let Ok(n) = right.parse::<isize>() {
767 n
768 } else {
769 match self.resolve_value(&Value::Identifier(
770 right.to_string(),
771 ))? {
772 Value::Number(n2) => n2 as isize,
773 _ => return Ok(Value::Null),
774 }
775 };
776 left_val - right_val
777 } else {
778 match index_value {
779 Value::Number(n) => n as isize,
780 Value::Identifier(ref id) => match self
781 .resolve_value(&Value::Identifier(id.clone()))?
782 {
783 Value::Number(n2) => n2 as isize,
784 _ => return Ok(Value::Null),
785 },
786 _ => return Ok(Value::Null),
787 }
788 };
789
790 if resolved_idx < 0 {
791 return Ok(Value::Null);
792 }
793 let ui = resolved_idx as usize;
794 if ui < arr.len() {
795 let mut elem = arr[ui].clone();
796 if let Value::Identifier(ref id) = elem {
799 if !is_special_var(id)
800 && self.variables.get(id).is_none()
801 {
802 elem = Value::String(id.clone());
803 }
804 }
805
806 if let Value::Map(ref m) = elem {
811 if let Some(inner) = m.get("value") {
812 current = Some(inner.clone());
813 } else {
814 current = Some(elem);
815 }
816 } else {
817 current = Some(elem);
818 }
819 } else {
820 return Err(anyhow::anyhow!(
821 "Index out of range: {} (len={})",
822 ui,
823 arr.len()
824 ));
825 }
826 }
827 Some(Value::Map(ref map)) => {
828 let key = idx_tok_trim.trim_matches('"').trim_matches('\'');
830 current = map.get(key).cloned();
831 if current.is_none() {
832 return Ok(Value::Null);
833 }
834 }
835 _ => {
836 return Ok(Value::Null);
837 }
838 }
839 } else {
840 break;
842 }
843 }
844
845 return Ok(current.unwrap_or(Value::Null));
846 }
847
848 return Ok(self
850 .variables
851 .get(name)
852 .cloned()
853 .unwrap_or(Value::String(name.clone())));
854 }
855 Value::Call { name, args } => {
856 let mut resolved_args: Vec<Value> = Vec::new();
859 for a in args.iter() {
860 let rv = self.resolve_value(a)?;
861 resolved_args.push(rv);
862 }
863
864 let res = super::handler::call_function(self, name, &resolved_args)?;
866 return Ok(res);
867 }
868 _ => return Ok(value.clone()),
869 }
870 }
871
872 pub fn execute_event_handlers(&mut self, event_name: &str) -> Result<()> {
874 handler::execute_event_handlers(self, event_name)
875 }
876
877 pub fn values_equal(&self, left: &Value, right: &Value) -> bool {
879 match (left, right) {
880 (Value::Number(a), Value::Number(b)) => (a - b).abs() < 0.0001,
881 (Value::String(a), Value::String(b)) => a == b,
882 (Value::Boolean(a), Value::Boolean(b)) => a == b,
883 (Value::Null, Value::Null) => true,
884 _ => false,
885 }
886 }
887
888 pub fn compare_values(
890 &self,
891 left: &Value,
892 right: &Value,
893 ordering: std::cmp::Ordering,
894 ) -> Result<bool> {
895 match (left, right) {
896 (Value::Number(a), Value::Number(b)) => {
897 Ok(a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal) == ordering)
898 }
899 (Value::String(a), Value::String(b)) => Ok(a.cmp(b) == ordering),
900 _ => Err(anyhow::anyhow!("Cannot compare {:?} and {:?}", left, right)),
901 }
902 }
903
904 pub fn handle_assign(&mut self, target: &str, property: &str, value: &Value) -> Result<()> {
906 handler::handle_assign(self, target, property, value)
907 }
908
909 pub fn extract_synth_def_from_map(
911 &self,
912 map: &HashMap<String, Value>,
913 ) -> Result<SynthDefinition> {
914 handler::extract_synth_def_from_map(self, map)
915 }
916
917 pub fn handle_load(&mut self, source: &str, alias: &str) -> Result<()> {
919 handler::handle_load(self, source, alias)
920 }
921
922 pub fn handle_bind(&mut self, source: &str, target: &str, options: &Value) -> Result<()> {
924 handler::handle_bind(self, source, target, options)
925 }
926
927 pub fn extract_pattern_data(
929 &self,
930 value: &Value,
931 ) -> (Option<String>, Option<HashMap<String, f32>>) {
932 patterns::extract_pattern_data(self, value)
933 }
934
935 pub fn execute_pattern(
937 &mut self,
938 target: &str,
939 pattern: &str,
940 options: Option<HashMap<String, f32>>,
941 ) -> Result<()> {
942 patterns::execute_pattern(self, target, pattern, options)
943 }
944
945 pub fn execute_pattern_with_source(
947 &mut self,
948 target: &str,
949 pattern: &str,
950 options: Option<HashMap<String, f32>>,
951 source: Option<&str>,
952 ) -> Result<()> {
953 patterns::execute_pattern_with_source(self, target, pattern, options, source)
954 }
955
956 pub fn resolve_sample_uri(&self, target: &str) -> String {
958 patterns::resolve_sample_uri(self, target)
959 }
960
961 pub fn duration_to_seconds(
963 &self,
964 duration: &crate::language::syntax::ast::DurationValue,
965 ) -> Result<f32> {
966 crate::engine::audio::duration::duration_to_seconds(duration, self.bpm)
967 }
968
969 pub fn resolve_event_node_id(&self, source: &str) -> Option<String> {
971 if let Some(node_id) = &self.current_event_node {
972 return Some(node_id.clone());
973 }
974
975 let source = crate::engine::audio::graph::canonicalize_node_id(source);
976
977 for (node_name, config) in &self.routing.nodes {
978 let node_id = crate::engine::audio::graph::canonicalize_node_id(node_name);
979 if node_id == source
980 || config.name == source
981 || config
982 .alias
983 .as_ref()
984 .map(|alias| crate::engine::audio::graph::canonicalize_node_id(alias) == source)
985 .unwrap_or(false)
986 {
987 return Some(node_id);
988 }
989 }
990
991 Some(self.audio_graph.master.clone())
992 }
993
994 pub fn collect_with_event_node(
996 &mut self,
997 node_id: Option<String>,
998 body: &[Statement],
999 ) -> Result<()> {
1000 let previous = self.current_event_node.clone();
1001 self.current_event_node = node_id.or(previous.clone());
1002 let result = collector::collect_events(self, body);
1003 self.current_event_node = previous;
1004 result
1005 }
1006
1007 pub fn routed_node_for_alias(&self, alias: &str) -> Option<String> {
1009 self.routing.nodes.iter().find_map(|(node_name, config)| {
1010 if config.alias.as_deref() == Some(alias) || node_name == alias {
1011 Some(crate::engine::audio::graph::canonicalize_node_id(node_name))
1012 } else {
1013 None
1014 }
1015 })
1016 }
1017}
1018
1019#[cfg(test)]
1020#[path = "test_arrays.rs"]
1021mod tests_arrays;
1022
1023#[cfg(test)]
1024#[path = "test_print.rs"]
1025mod tests_print;
1026
1027#[cfg(test)]
1028#[path = "test_functions.rs"]
1029mod tests_functions;
1030
1031#[cfg(test)]
1032#[path = "test_control_flow.rs"]
1033mod tests_control_flow;
1034
1035#[cfg(test)]
1036mod phase_2_routing_tests {
1037 use super::*;
1038 use crate::engine::audio::events::{AudioEvent, SynthDefinition};
1039 use crate::engine::functions::FunctionContext;
1040 use crate::language::syntax::ast::Value;
1041
1042 fn route_pattern_to_bus(interpreter: &mut AudioInterpreter) {
1043 interpreter.routing.nodes.insert(
1044 "drums_bus".to_string(),
1045 RoutingNodeConfig {
1046 name: "drums_bus".to_string(),
1047 alias: Some("kickPattern".to_string()),
1048 effects: None,
1049 },
1050 );
1051 interpreter.audio_graph =
1052 crate::engine::audio::graph::AudioGraph::from_routing_setup(&interpreter.routing);
1053 }
1054
1055 #[test]
1056 fn note_event_node_id_is_resolved_during_collection() {
1057 let mut interpreter = AudioInterpreter::new(44_100);
1058 interpreter.routing.nodes.insert(
1059 "lead_bus".to_string(),
1060 RoutingNodeConfig {
1061 name: "lead_bus".to_string(),
1062 alias: Some("lead".to_string()),
1063 effects: None,
1064 },
1065 );
1066 interpreter.audio_graph =
1067 crate::engine::audio::graph::AudioGraph::from_routing_setup(&interpreter.routing);
1068 interpreter
1069 .events
1070 .add_synth("lead".to_string(), SynthDefinition::default());
1071
1072 let mut context = FunctionContext::new("lead".to_string(), 0.0, interpreter.bpm);
1073 context.set("note", Value::String("C4".to_string()));
1074 context.set("duration", Value::Number(250.0));
1075
1076 interpreter.extract_audio_event("lead", &context).unwrap();
1077
1078 match interpreter.events.events.last().unwrap() {
1079 AudioEvent::Note { node_id, .. } => {
1080 assert_eq!(node_id.as_deref(), Some("lead_bus"));
1081 }
1082 other => panic!("expected note event, got {:?}", other),
1083 }
1084 }
1085
1086 #[test]
1087 fn sample_event_node_id_is_resolved_from_pattern_source() {
1088 let mut interpreter = AudioInterpreter::new(44_100);
1089 route_pattern_to_bus(&mut interpreter);
1090
1091 interpreter
1092 .execute_pattern_with_source("kit.kick", "x---", None, Some("kickPattern"))
1093 .unwrap();
1094
1095 match interpreter.events.events.first().unwrap() {
1096 AudioEvent::Sample {
1097 node_id, source, ..
1098 } => {
1099 assert_eq!(source.as_deref(), Some("kickPattern"));
1100 assert_eq!(node_id.as_deref(), Some("drums_bus"));
1101 }
1102 other => panic!("expected sample event, got {:?}", other),
1103 }
1104 }
1105
1106 #[test]
1107 fn unrouted_event_falls_back_to_master_node() {
1108 let interpreter = AudioInterpreter::new(44_100);
1109 assert_eq!(
1110 interpreter.resolve_event_node_id("missing").as_deref(),
1111 Some("master")
1112 );
1113 }
1114}