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 renderer;
131pub mod renderer_graph;
132
133pub struct AudioInterpreter {
134 pub sample_rate: u32,
135 pub bpm: f32,
136 pub function_registry: FunctionRegistry,
137 pub events: AudioEventList,
138 pub variables: HashMap<String, Value>,
139 pub groups: HashMap<String, Vec<Statement>>,
140 pub banks: BankRegistry,
141 pub automation_registry: crate::engine::audio::automation::AutomationRegistry,
143 pub note_automation_templates: std::collections::HashMap<String, NoteAutomationContext>,
145 pub cursor_time: f32,
146 pub special_vars: SpecialVarContext,
147 pub event_registry: EventRegistry,
148 #[cfg(feature = "cli")]
149 pub midi_manager: Option<std::sync::Arc<std::sync::Mutex<MidiManager>>>,
150 current_statement_location: Option<(usize, usize)>, pub suppress_beat_emit: bool,
154 pub suppress_print: bool,
156 pub break_flag: bool,
158 pub background_event_tx:
160 Option<std::sync::mpsc::Sender<crate::engine::audio::events::AudioEventList>>,
161 pub background_event_rx:
162 Option<std::sync::mpsc::Receiver<crate::engine::audio::events::AudioEventList>>,
163 pub background_workers: Vec<std::thread::JoinHandle<()>>,
165 pub realtime_print_tx: Option<std::sync::mpsc::Sender<(f32, String)>>,
167 pub function_call_depth: usize,
169 pub returning_flag: bool,
172 pub return_value: Option<Value>,
173 pub routing: RoutingSetup,
175 pub audio_graph: crate::engine::audio::interpreter::AudioGraph,
177 pub schedule_deadline: Option<f32>,
179}
180
181impl AudioInterpreter {
182 pub fn new(sample_rate: u32) -> Self {
183 Self {
184 sample_rate,
185 bpm: 120.0,
186 function_registry: FunctionRegistry::new(),
187 events: AudioEventList::new(),
188 variables: HashMap::new(),
189 groups: HashMap::new(),
190 banks: BankRegistry::new(),
191 automation_registry: crate::engine::audio::automation::AutomationRegistry::new(),
192 note_automation_templates: std::collections::HashMap::new(),
193 cursor_time: 0.0,
194 special_vars: SpecialVarContext::new(120.0, sample_rate),
195 event_registry: EventRegistry::new(),
196 #[cfg(feature = "cli")]
197 midi_manager: None,
198 current_statement_location: None,
199 suppress_beat_emit: false,
200 suppress_print: false,
201 break_flag: false,
202 background_event_tx: None,
203 background_event_rx: None,
204 background_workers: Vec::new(),
205 realtime_print_tx: None,
206 function_call_depth: 0,
207 returning_flag: false,
208 return_value: None,
209 routing: RoutingSetup::default(),
210 audio_graph: crate::engine::audio::interpreter::AudioGraph::new(),
211 schedule_deadline: None,
212 }
213 }
214
215 fn handle_trigger(&mut self, entity: &str) -> Result<()> {
217 handler::handle_trigger(self, entity, None)
219 }
220
221 fn debug_list_banks(&self) {
223 }
225
226 pub fn interpret(&mut self, statements: &[Statement]) -> Result<Vec<f32>> {
227 let total_duration = self.calculate_total_duration(statements)?;
229 self.special_vars.total_duration = total_duration;
230 self.special_vars.update_bpm(self.bpm);
231
232 self.collect_events(statements)?;
234
235 if self.background_event_rx.is_some() {
240 use std::sync::mpsc::RecvTimeoutError;
241 use std::time::{Duration, Instant};
242
243 let rx = self.background_event_rx.as_mut().unwrap();
244 let start = Instant::now();
245 let hard_limit = Duration::from_secs_f32(total_duration + 5.0);
247 loop {
248 match rx.recv_timeout(Duration::from_millis(200)) {
249 Ok(events) => {
250 self.events.merge(events);
251 }
252 Err(RecvTimeoutError::Timeout) => {
253 if self.events.total_duration() >= (total_duration - 0.001) {
255 break;
256 }
257 if start.elapsed() > hard_limit {
258 break;
260 }
261 }
263 Err(RecvTimeoutError::Disconnected) => {
264 break;
266 }
267 }
268 }
269 }
270
271 self.render_audio()
275 }
276
277 pub fn events(&self) -> &AudioEventList {
279 &self.events
280 }
281
282 pub fn current_statement_location(&self) -> Option<(usize, usize)> {
284 self.current_statement_location
285 }
286
287 pub fn calculate_total_duration(&self, _statements: &[Statement]) -> Result<f32> {
289 Ok(60.0) }
292
293 pub fn collect_events(&mut self, statements: &[Statement]) -> Result<()> {
294 collector::collect_events(self, statements)
296 }
297 pub fn handle_let(&mut self, name: &str, value: &Value) -> Result<()> {
298 handler::handle_let(self, name, value)
299 }
300
301 pub fn extract_audio_event(
302 &mut self,
303 target: &str,
304 context: &crate::engine::functions::FunctionContext,
305 ) -> Result<()> {
306 extractor::extract_audio_event(self, target, context)
308 }
309
310 pub fn render_audio(&self) -> Result<Vec<f32>> {
311 renderer::render_audio(self)
313 }
314
315 pub fn set_bpm(&mut self, bpm: f32) {
316 self.bpm = bpm.max(1.0).min(999.0);
317 self.special_vars.update_bpm(self.bpm);
319 }
320
321 pub fn samples_per_beat(&self) -> usize {
322 ((60.0 / self.bpm) * self.sample_rate as f32) as usize
323 }
324
325 pub fn beat_duration(&self) -> f32 {
327 60.0 / self.bpm
328 }
329
330 pub fn execute_print(&mut self, value: &Value) -> Result<()> {
333 handler::execute_print(self, value)
334 }
335
336 pub fn interpolate_string(&self, template: &str) -> String {
339 let mut result = template.to_string();
340
341 let re = regex::Regex::new(r"\{([a-zA-Z_][a-zA-Z0-9_]*)\}").unwrap();
343
344 for cap in re.captures_iter(template) {
345 let full_match = &cap[0]; let var_name = &cap[1]; if let Some(value) = self.variables.get(var_name) {
349 let replacement = self.value_to_string(value);
350 result = result.replace(full_match, &replacement);
351 } else {
352 result = result.replace(full_match, &format!("<undefined:{}>", var_name));
354 }
355 }
356
357 result
358 }
359
360 fn value_to_string(&self, value: &Value) -> String {
362 match value {
363 Value::String(s) => {
364 s.trim_matches('"').trim_matches('\'').to_string()
366 }
367 Value::Number(n) => {
368 if n.fract() == 0.0 {
370 format!("{:.0}", n)
371 } else {
372 format!("{}", n)
373 }
374 }
375 Value::Boolean(b) => b.to_string(),
376 Value::Array(arr) => {
377 let items: Vec<String> = arr.iter().map(|v| self.value_to_string(v)).collect();
378 format!("[{}]", items.join(", "))
379 }
380 Value::Identifier(id) => id.clone(),
381 _ => format!("{:?}", value),
382 }
383 }
384
385 pub fn execute_if(
387 &mut self,
388 condition: &Value,
389 body: &[Statement],
390 else_body: &Option<Vec<Statement>>,
391 ) -> Result<()> {
392 handler::execute_if(self, condition, body, else_body)
393 }
394
395 pub fn evaluate_condition(&mut self, condition: &Value) -> Result<bool> {
398 if let Value::Map(map) = condition {
400 let operator = map
401 .get("operator")
402 .and_then(|v| {
403 if let Value::String(s) = v {
404 Some(s.as_str())
405 } else {
406 None
407 }
408 })
409 .unwrap_or("==");
410
411 match operator {
413 "&&" => {
414 let left = map
415 .get("left")
416 .ok_or_else(|| anyhow::anyhow!("Missing left operand for &&"))?;
417 let right = map
418 .get("right")
419 .ok_or_else(|| anyhow::anyhow!("Missing right operand for &&"))?;
420
421 let left_result = self.evaluate_condition(left)?;
422 let right_result = self.evaluate_condition(right)?;
423 return Ok(left_result && right_result);
424 }
425 "||" => {
426 let left = map
427 .get("left")
428 .ok_or_else(|| anyhow::anyhow!("Missing left operand for ||"))?;
429 let right = map
430 .get("right")
431 .ok_or_else(|| anyhow::anyhow!("Missing right operand for ||"))?;
432
433 let left_result = self.evaluate_condition(left)?;
434 let right_result = self.evaluate_condition(right)?;
435 return Ok(left_result || right_result);
436 }
437 "!" => {
438 let operand = map
439 .get("operand")
440 .ok_or_else(|| anyhow::anyhow!("Missing operand for !"))?;
441
442 let operand_result = self.evaluate_condition(operand)?;
443 return Ok(!operand_result);
444 }
445 _ => {}
446 }
447
448 let left = map
450 .get("left")
451 .ok_or_else(|| anyhow::anyhow!("Missing left operand"))?;
452 let right = map
453 .get("right")
454 .ok_or_else(|| anyhow::anyhow!("Missing right operand"))?;
455
456 let left_val = self.resolve_value(left)?;
458 let right_val = self.resolve_value(right)?;
459
460 match operator {
462 "==" => Ok(self.values_equal(&left_val, &right_val)),
463 "!=" => Ok(!self.values_equal(&left_val, &right_val)),
464 "<" => self.compare_values(&left_val, &right_val, std::cmp::Ordering::Less),
465 ">" => self.compare_values(&left_val, &right_val, std::cmp::Ordering::Greater),
466 "<=" => {
467 let less =
468 self.compare_values(&left_val, &right_val, std::cmp::Ordering::Less)?;
469 let equal = self.values_equal(&left_val, &right_val);
470 Ok(less || equal)
471 }
472 ">=" => {
473 let greater =
474 self.compare_values(&left_val, &right_val, std::cmp::Ordering::Greater)?;
475 let equal = self.values_equal(&left_val, &right_val);
476 Ok(greater || equal)
477 }
478 _ => Err(anyhow::anyhow!("Unknown operator: {}", operator)),
479 }
480 } else {
481 let resolved = self.resolve_value(condition)?;
483 match resolved {
484 Value::Boolean(b) => Ok(b),
485 Value::Number(n) => Ok(n != 0.0),
486 Value::String(s) => Ok(!s.is_empty()),
487 Value::Identifier(id) => {
488 let further = self.resolve_value(&Value::Identifier(id))?;
490 match further {
491 Value::Boolean(b2) => Ok(b2),
492 Value::Number(n2) => Ok(n2 != 0.0),
493 Value::String(s2) => Ok(!s2.is_empty()),
494 _ => Ok(false),
495 }
496 }
497 _ => Ok(false),
498 }
499 }
500 }
501
502 pub fn resolve_value(&mut self, value: &Value) -> Result<Value> {
504 match value {
505 Value::Identifier(name) => {
506 if is_special_var(name) {
508 if let Some(special_val) = resolve_special_var(name, &self.special_vars) {
509 return Ok(special_val);
510 }
511 }
512
513 if name.contains('.') || name.contains('[') {
515 let mut chars = name.chars().peekable();
517 let mut ident = String::new();
519 while let Some(&c) = chars.peek() {
520 if c == '.' || c == '[' {
521 break;
522 }
523 ident.push(c);
524 chars.next();
525 }
526
527 let mut current: Option<Value> = if ident.is_empty() {
528 None
529 } else if is_special_var(&ident) {
530 resolve_special_var(&ident, &self.special_vars)
531 } else {
532 self.variables.get(&ident).cloned()
533 };
534
535 if current.is_none() {
536 return Ok(Value::Null);
537 }
538
539 while let Some(&c) = chars.peek() {
541 if c == '.' {
542 chars.next();
544 let mut prop = String::new();
546 while let Some(&nc) = chars.peek() {
547 if nc == '.' || nc == '[' {
548 break;
549 }
550 prop.push(nc);
551 chars.next();
552 }
553 match current {
555 Some(Value::Map(ref map)) => {
556 current = map.get(&prop).cloned();
557 }
558 _ => {
559 return Ok(Value::Null);
560 }
561 }
562 if current.is_none() {
563 return Ok(Value::Null);
564 }
565 } else if c == '[' {
566 chars.next();
568 let mut idx_tok = String::new();
570 while let Some(&nc) = chars.peek() {
571 if nc == ']' {
572 break;
573 }
574 idx_tok.push(nc);
575 chars.next();
576 }
577 if let Some(&nc) = chars.peek() {
579 if nc == ']' {
580 chars.next();
581 } else {
582 return Ok(Value::Null); }
584 } else {
585 return Ok(Value::Null); }
587
588 let idx_tok_trim = idx_tok.trim();
589
590 let index_value = if let Ok(n) = idx_tok_trim.parse::<usize>() {
592 Value::Number(n as f32)
593 } else {
594 Value::Identifier(idx_tok_trim.to_string())
595 };
596
597 match current {
599 Some(Value::Array(ref arr)) => {
600 let resolved_idx = if idx_tok_trim.ends_with("++") {
605 let varname = idx_tok_trim[..idx_tok_trim.len() - 2].trim();
607 let cur = match self.resolve_value(&Value::Identifier(
608 varname.to_string(),
609 ))? {
610 Value::Number(n2) => n2 as isize,
611 _ => return Ok(Value::Null),
612 };
613 self.variables.insert(
614 varname.to_string(),
615 Value::Number((cur + 1) as f32),
616 );
617 cur
618 } else if idx_tok_trim.ends_with("--") {
619 let varname = idx_tok_trim[..idx_tok_trim.len() - 2].trim();
621 let cur = match self.resolve_value(&Value::Identifier(
622 varname.to_string(),
623 ))? {
624 Value::Number(n2) => n2 as isize,
625 _ => return Ok(Value::Null),
626 };
627 self.variables.insert(
628 varname.to_string(),
629 Value::Number((cur - 1) as f32),
630 );
631 cur
632 } else if idx_tok_trim.contains('+') {
633 let parts: Vec<&str> =
634 idx_tok_trim.splitn(2, '+').collect();
635 let left = parts[0].trim();
636 let right = parts[1].trim();
637 let left_val = if let Ok(n) = left.parse::<isize>() {
638 n
639 } else {
640 match self.resolve_value(&Value::Identifier(
641 left.to_string(),
642 ))? {
643 Value::Number(n2) => n2 as isize,
644 _ => return Ok(Value::Null),
645 }
646 };
647 let right_val = if let Ok(n) = right.parse::<isize>() {
648 n
649 } else {
650 match self.resolve_value(&Value::Identifier(
651 right.to_string(),
652 ))? {
653 Value::Number(n2) => n2 as isize,
654 _ => return Ok(Value::Null),
655 }
656 };
657 left_val + right_val
658 } else if idx_tok_trim.contains('*') {
659 let parts: Vec<&str> =
660 idx_tok_trim.splitn(2, '*').collect();
661 let left = parts[0].trim();
662 let right = parts[1].trim();
663 let left_val = if let Ok(n) = left.parse::<isize>() {
664 n
665 } else {
666 match self.resolve_value(&Value::Identifier(
667 left.to_string(),
668 ))? {
669 Value::Number(n2) => n2 as isize,
670 _ => return Ok(Value::Null),
671 }
672 };
673 let right_val = if let Ok(n) = right.parse::<isize>() {
674 n
675 } else {
676 match self.resolve_value(&Value::Identifier(
677 right.to_string(),
678 ))? {
679 Value::Number(n2) => n2 as isize,
680 _ => return Ok(Value::Null),
681 }
682 };
683 left_val * right_val
684 } else if idx_tok_trim.contains('/') {
685 let parts: Vec<&str> =
686 idx_tok_trim.splitn(2, '/').collect();
687 let left = parts[0].trim();
688 let right = parts[1].trim();
689 let left_val = if let Ok(n) = left.parse::<isize>() {
690 n
691 } else {
692 match self.resolve_value(&Value::Identifier(
693 left.to_string(),
694 ))? {
695 Value::Number(n2) => n2 as isize,
696 _ => return Ok(Value::Null),
697 }
698 };
699 let right_val = if let Ok(n) = right.parse::<isize>() {
700 n
701 } else {
702 match self.resolve_value(&Value::Identifier(
703 right.to_string(),
704 ))? {
705 Value::Number(n2) => n2 as isize,
706 _ => return Ok(Value::Null),
707 }
708 };
709 if right_val == 0 {
710 return Err(anyhow::anyhow!(
711 "Division by zero in index expression"
712 ));
713 }
714 left_val / right_val
715 } else if idx_tok_trim.contains('%') {
716 let parts: Vec<&str> =
717 idx_tok_trim.splitn(2, '%').collect();
718 let left = parts[0].trim();
719 let right = parts[1].trim();
720 let left_val = if let Ok(n) = left.parse::<isize>() {
721 n
722 } else {
723 match self.resolve_value(&Value::Identifier(
724 left.to_string(),
725 ))? {
726 Value::Number(n2) => n2 as isize,
727 _ => return Ok(Value::Null),
728 }
729 };
730 let right_val = if let Ok(n) = right.parse::<isize>() {
731 n
732 } else {
733 match self.resolve_value(&Value::Identifier(
734 right.to_string(),
735 ))? {
736 Value::Number(n2) => n2 as isize,
737 _ => return Ok(Value::Null),
738 }
739 };
740 if right_val == 0 {
741 return Err(anyhow::anyhow!(
742 "Modulo by zero in index expression"
743 ));
744 }
745 left_val % right_val
746 } else if idx_tok_trim.contains('-') {
747 let parts: Vec<&str> =
748 idx_tok_trim.splitn(2, '-').collect();
749 let left = parts[0].trim();
750 let right = parts[1].trim();
751 let left_val = if let Ok(n) = left.parse::<isize>() {
752 n
753 } else {
754 match self.resolve_value(&Value::Identifier(
755 left.to_string(),
756 ))? {
757 Value::Number(n2) => n2 as isize,
758 _ => return Ok(Value::Null),
759 }
760 };
761 let right_val = if let Ok(n) = right.parse::<isize>() {
762 n
763 } else {
764 match self.resolve_value(&Value::Identifier(
765 right.to_string(),
766 ))? {
767 Value::Number(n2) => n2 as isize,
768 _ => return Ok(Value::Null),
769 }
770 };
771 left_val - right_val
772 } else {
773 match index_value {
774 Value::Number(n) => n as isize,
775 Value::Identifier(ref id) => match self
776 .resolve_value(&Value::Identifier(id.clone()))?
777 {
778 Value::Number(n2) => n2 as isize,
779 _ => return Ok(Value::Null),
780 },
781 _ => return Ok(Value::Null),
782 }
783 };
784
785 if resolved_idx < 0 {
786 return Ok(Value::Null);
787 }
788 let ui = resolved_idx as usize;
789 if ui < arr.len() {
790 let mut elem = arr[ui].clone();
791 if let Value::Identifier(ref id) = elem {
794 if !is_special_var(id)
795 && self.variables.get(id).is_none()
796 {
797 elem = Value::String(id.clone());
798 }
799 }
800
801 if let Value::Map(ref m) = elem {
806 if let Some(inner) = m.get("value") {
807 current = Some(inner.clone());
808 } else {
809 current = Some(elem);
810 }
811 } else {
812 current = Some(elem);
813 }
814 } else {
815 return Err(anyhow::anyhow!(
816 "Index out of range: {} (len={})",
817 ui,
818 arr.len()
819 ));
820 }
821 }
822 Some(Value::Map(ref map)) => {
823 let key = idx_tok_trim.trim_matches('"').trim_matches('\'');
825 current = map.get(key).cloned();
826 if current.is_none() {
827 return Ok(Value::Null);
828 }
829 }
830 _ => {
831 return Ok(Value::Null);
832 }
833 }
834 } else {
835 break;
837 }
838 }
839
840 return Ok(current.unwrap_or(Value::Null));
841 }
842
843 return Ok(self
845 .variables
846 .get(name)
847 .cloned()
848 .unwrap_or(Value::String(name.clone())));
849 }
850 Value::Call { name, args } => {
851 let mut resolved_args: Vec<Value> = Vec::new();
854 for a in args.iter() {
855 let rv = self.resolve_value(a)?;
856 resolved_args.push(rv);
857 }
858
859 let res = super::handler::call_function(self, name, &resolved_args)?;
861 return Ok(res);
862 }
863 _ => return Ok(value.clone()),
864 }
865 }
866
867 pub fn execute_event_handlers(&mut self, event_name: &str) -> Result<()> {
869 handler::execute_event_handlers(self, event_name)
870 }
871
872 pub fn values_equal(&self, left: &Value, right: &Value) -> bool {
874 match (left, right) {
875 (Value::Number(a), Value::Number(b)) => (a - b).abs() < 0.0001,
876 (Value::String(a), Value::String(b)) => a == b,
877 (Value::Boolean(a), Value::Boolean(b)) => a == b,
878 (Value::Null, Value::Null) => true,
879 _ => false,
880 }
881 }
882
883 pub fn compare_values(
885 &self,
886 left: &Value,
887 right: &Value,
888 ordering: std::cmp::Ordering,
889 ) -> Result<bool> {
890 match (left, right) {
891 (Value::Number(a), Value::Number(b)) => {
892 Ok(a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal) == ordering)
893 }
894 (Value::String(a), Value::String(b)) => Ok(a.cmp(b) == ordering),
895 _ => Err(anyhow::anyhow!("Cannot compare {:?} and {:?}", left, right)),
896 }
897 }
898
899 pub fn handle_assign(&mut self, target: &str, property: &str, value: &Value) -> Result<()> {
901 handler::handle_assign(self, target, property, value)
902 }
903
904 pub fn extract_synth_def_from_map(
906 &self,
907 map: &HashMap<String, Value>,
908 ) -> Result<SynthDefinition> {
909 handler::extract_synth_def_from_map(self, map)
910 }
911
912 pub fn handle_load(&mut self, source: &str, alias: &str) -> Result<()> {
914 handler::handle_load(self, source, alias)
915 }
916
917 pub fn handle_bind(&mut self, source: &str, target: &str, options: &Value) -> Result<()> {
919 handler::handle_bind(self, source, target, options)
920 }
921
922 pub fn extract_pattern_data(
924 &self,
925 value: &Value,
926 ) -> (Option<String>, Option<HashMap<String, f32>>) {
927 handler::extract_pattern_data(self, value)
928 }
929
930 pub fn execute_pattern(
932 &mut self,
933 target: &str,
934 pattern: &str,
935 options: Option<HashMap<String, f32>>,
936 ) -> Result<()> {
937 handler::execute_pattern(self, target, pattern, options)
938 }
939
940 pub fn execute_pattern_with_source(
942 &mut self,
943 target: &str,
944 pattern: &str,
945 options: Option<HashMap<String, f32>>,
946 source: Option<&str>,
947 ) -> Result<()> {
948 handler::execute_pattern_with_source(self, target, pattern, options, source)
949 }
950
951 pub fn resolve_sample_uri(&self, target: &str) -> String {
953 handler::resolve_sample_uri(self, target)
954 }
955
956 pub fn duration_to_seconds(
958 &self,
959 duration: &crate::language::syntax::ast::DurationValue,
960 ) -> Result<f32> {
961 use crate::language::syntax::ast::DurationValue;
962
963 match duration {
964 DurationValue::Milliseconds(ms) => Ok(ms / 1000.0),
965 DurationValue::Beats(b) => Ok(b * (60.0 / self.bpm)),
966 DurationValue::Beat(s) => {
967 let mut sp = s.split('/');
969 if let (Some(a), Some(b)) = (sp.next(), sp.next()) {
970 if let (Ok(an), Ok(bn)) = (a.trim().parse::<f32>(), b.trim().parse::<f32>()) {
971 if bn.abs() > f32::EPSILON {
972 return Ok((an / bn) * (60.0 / self.bpm));
973 }
974 }
975 }
976 anyhow::bail!("Invalid beat fraction: {}", s)
977 }
978 DurationValue::Number(n) => Ok(n / 1000.0),
979 DurationValue::Identifier(id) => {
980 if id.starts_with("__temporal__") {
983 let rest = &id[12..]; if let Some(last_underscore) = rest.rfind('_') {
985 if let Ok(beat_count) = rest[..last_underscore].parse::<f32>() {
986 return Ok(beat_count * (60.0 / self.bpm));
988 }
989 }
990 }
991 anyhow::bail!("Cannot resolve identifier duration: {}", id)
992 }
993 DurationValue::Auto => {
994 Ok(60.0 / self.bpm)
996 }
997 }
998 }
999}
1000
1001#[cfg(test)]
1002#[path = "test_arrays.rs"]
1003mod tests_arrays;
1004
1005#[cfg(test)]
1006#[path = "test_print.rs"]
1007mod tests_print;
1008
1009#[cfg(test)]
1010#[path = "test_functions.rs"]
1011mod tests_functions;
1012
1013#[cfg(test)]
1014#[path = "test_control_flow.rs"]
1015mod tests_control_flow;