devalang_wasm/engine/audio/interpreter/driver/
mod.rs

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// Provide a lightweight stub for BankRegistry when CLI feature is disabled (WASM/plugin builds)
13#[cfg(not(feature = "cli"))]
14#[allow(dead_code)]
15#[derive(Clone)]
16pub struct BankRegistry;
17
18#[cfg(not(feature = "cli"))]
19impl BankRegistry {
20    pub fn new() -> Self {
21        BankRegistry
22    }
23    pub fn list_banks(&self) -> Vec<(String, StubBank)> {
24        Vec::new()
25    }
26    pub fn resolve_trigger(&self, _var: &str, _prop: &str) -> Option<std::path::PathBuf> {
27        None
28    }
29    pub fn register_bank(
30        &self,
31        _alias: String,
32        _name: &str,
33        _cwd: &std::path::Path,
34        _cwd2: &std::path::Path,
35    ) -> Result<(), anyhow::Error> {
36        Ok(())
37    }
38}
39
40#[cfg(not(feature = "cli"))]
41#[allow(dead_code)]
42#[derive(Clone)]
43pub struct StubBank;
44
45#[cfg(not(feature = "cli"))]
46impl StubBank {
47    pub fn list_triggers(&self) -> Vec<String> {
48        Vec::new()
49    }
50}
51/// Audio interpreter driver - main execution loop
52use anyhow::Result;
53use std::collections::HashMap;
54
55pub mod collector;
56pub mod extractor;
57pub mod handler;
58pub mod renderer;
59
60pub struct AudioInterpreter {
61    pub sample_rate: u32,
62    pub bpm: f32,
63    pub function_registry: FunctionRegistry,
64    pub events: AudioEventList,
65    pub variables: HashMap<String, Value>,
66    pub groups: HashMap<String, Vec<Statement>>,
67    pub banks: BankRegistry,
68    pub cursor_time: f32,
69    pub special_vars: SpecialVarContext,
70    pub event_registry: EventRegistry,
71    #[cfg(feature = "cli")]
72    pub midi_manager: Option<std::sync::Arc<std::sync::Mutex<MidiManager>>>,
73    /// Track current statement location for better error reporting
74    current_statement_location: Option<(usize, usize)>, // (line, column)
75    /// Internal guard to avoid re-entrant beat emission during handler execution
76    pub suppress_beat_emit: bool,
77}
78
79impl AudioInterpreter {
80    pub fn new(sample_rate: u32) -> Self {
81        Self {
82            sample_rate,
83            bpm: 120.0,
84            function_registry: FunctionRegistry::new(),
85            events: AudioEventList::new(),
86            variables: HashMap::new(),
87            groups: HashMap::new(),
88            banks: BankRegistry::new(),
89            cursor_time: 0.0,
90            special_vars: SpecialVarContext::new(120.0, sample_rate),
91            event_registry: EventRegistry::new(),
92            #[cfg(feature = "cli")]
93            midi_manager: None,
94            current_statement_location: None,
95            suppress_beat_emit: false,
96        }
97    }
98
99    /// Handle a trigger statement (e.g., .kit.kick or kit.kick)
100    fn handle_trigger(&mut self, entity: &str) -> Result<()> {
101        // Delegate detailed trigger handling to the handler module
102        handler::handle_trigger(self, entity)
103    }
104
105    /// Helper to print banks and triggers for debugging
106    fn debug_list_banks(&self) {
107        println!("🔍 Available triggers in BankRegistry:");
108        for (bank_name, bank) in self.banks.list_banks() {
109            println!("   Bank: {}", bank_name);
110            for trigger in bank.list_triggers() {
111                println!("      Trigger: {}", trigger);
112            }
113        }
114    }
115
116    pub fn interpret(&mut self, statements: &[Statement]) -> Result<Vec<f32>> {
117        // Initialize special vars context
118        let total_duration = self.calculate_total_duration(statements)?;
119        self.special_vars.total_duration = total_duration;
120        self.special_vars.update_bpm(self.bpm);
121
122        // Phase 1: Collect events
123        self.collect_events(statements)?;
124
125        // Phase 2: Render audio
126
127        // Phase 2: Render audio
128        self.render_audio()
129    }
130
131    /// Get reference to collected audio events (for MIDI export)
132    pub fn events(&self) -> &AudioEventList {
133        &self.events
134    }
135
136    /// Get current statement location for error reporting
137    pub fn current_statement_location(&self) -> Option<(usize, usize)> {
138        self.current_statement_location
139    }
140
141    /// Calculate approximate total duration by scanning statements
142    pub fn calculate_total_duration(&self, _statements: &[Statement]) -> Result<f32> {
143        // For now, return a default duration (will be updated during collect_events)
144        Ok(60.0) // Default 60 seconds
145    }
146
147    pub fn collect_events(&mut self, statements: &[Statement]) -> Result<()> {
148        // Delegate to the collector child module
149        collector::collect_events(self, statements)
150    }
151    pub fn handle_let(&mut self, name: &str, value: &Value) -> Result<()> {
152        handler::handle_let(self, name, value)
153    }
154
155    pub fn extract_audio_event(
156        &mut self,
157        target: &str,
158        context: &crate::engine::functions::FunctionContext,
159    ) -> Result<()> {
160        // Delegate to extractor child module
161        extractor::extract_audio_event(self, target, context)
162    }
163
164    pub fn render_audio(&self) -> Result<Vec<f32>> {
165        // Delegate to renderer child module
166        renderer::render_audio(self)
167    }
168
169    pub fn set_bpm(&mut self, bpm: f32) {
170        self.bpm = bpm.max(1.0).min(999.0);
171    }
172
173    pub fn samples_per_beat(&self) -> usize {
174        ((60.0 / self.bpm) * self.sample_rate as f32) as usize
175    }
176
177    /// Get duration of one beat in seconds
178    pub fn beat_duration(&self) -> f32 {
179        60.0 / self.bpm
180    }
181
182    /// Execute print statement with variable interpolation
183    /// Supports {variable_name} syntax
184    pub fn execute_print(&self, value: &Value) -> Result<()> {
185        handler::execute_print(self, value)
186    }
187
188    /// Interpolate variables in a string
189    /// Replaces {variable_name} with the variable's value
190    pub fn interpolate_string(&self, template: &str) -> String {
191        let mut result = template.to_string();
192
193        // Find all {variable} patterns
194        let re = regex::Regex::new(r"\{([a-zA-Z_][a-zA-Z0-9_]*)\}").unwrap();
195
196        for cap in re.captures_iter(template) {
197            let full_match = &cap[0]; // {variable_name}
198            let var_name = &cap[1]; // variable_name
199
200            if let Some(value) = self.variables.get(var_name) {
201                let replacement = self.value_to_string(value);
202                result = result.replace(full_match, &replacement);
203            } else {
204                // Variable not found, leave placeholder or show error
205                result = result.replace(full_match, &format!("<undefined:{}>", var_name));
206            }
207        }
208
209        result
210    }
211
212    /// Convert a Value to a displayable string
213    fn value_to_string(&self, value: &Value) -> String {
214        match value {
215            Value::String(s) => {
216                // Remove surrounding quotes if present
217                s.trim_matches('"').trim_matches('\'').to_string()
218            }
219            Value::Number(n) => {
220                // Format nicely: remove trailing zeros
221                if n.fract() == 0.0 {
222                    format!("{:.0}", n)
223                } else {
224                    format!("{}", n)
225                }
226            }
227            Value::Boolean(b) => b.to_string(),
228            Value::Array(arr) => {
229                let items: Vec<String> = arr.iter().map(|v| self.value_to_string(v)).collect();
230                format!("[{}]", items.join(", "))
231            }
232            Value::Identifier(id) => id.clone(),
233            _ => format!("{:?}", value),
234        }
235    }
236
237    /// Execute if statement with condition evaluation
238    pub fn execute_if(
239        &mut self,
240        condition: &Value,
241        body: &[Statement],
242        else_body: &Option<Vec<Statement>>,
243    ) -> Result<()> {
244        handler::execute_if(self, condition, body, else_body)
245    }
246
247    /// Evaluate a condition to a boolean
248    /// Supports: ==, !=, <, >, <=, >=
249    pub fn evaluate_condition(&self, condition: &Value) -> Result<bool> {
250        // Condition is stored as a Map with operator and operands
251        if let Value::Map(map) = condition {
252            let operator = map
253                .get("operator")
254                .and_then(|v| {
255                    if let Value::String(s) = v {
256                        Some(s.as_str())
257                    } else {
258                        None
259                    }
260                })
261                .unwrap_or("==");
262
263            let left = map
264                .get("left")
265                .ok_or_else(|| anyhow::anyhow!("Missing left operand"))?;
266            let right = map
267                .get("right")
268                .ok_or_else(|| anyhow::anyhow!("Missing right operand"))?;
269
270            // Resolve values (replace identifiers with their values)
271            let left_val = self.resolve_value(left);
272            let right_val = self.resolve_value(right);
273
274            // Compare based on operator
275            match operator {
276                "==" => Ok(self.values_equal(&left_val, &right_val)),
277                "!=" => Ok(!self.values_equal(&left_val, &right_val)),
278                "<" => self.compare_values(&left_val, &right_val, std::cmp::Ordering::Less),
279                ">" => self.compare_values(&left_val, &right_val, std::cmp::Ordering::Greater),
280                "<=" => {
281                    let less =
282                        self.compare_values(&left_val, &right_val, std::cmp::Ordering::Less)?;
283                    let equal = self.values_equal(&left_val, &right_val);
284                    Ok(less || equal)
285                }
286                ">=" => {
287                    let greater =
288                        self.compare_values(&left_val, &right_val, std::cmp::Ordering::Greater)?;
289                    let equal = self.values_equal(&left_val, &right_val);
290                    Ok(greater || equal)
291                }
292                _ => Err(anyhow::anyhow!("Unknown operator: {}", operator)),
293            }
294        } else {
295            // Direct boolean value
296            match condition {
297                Value::Boolean(b) => Ok(*b),
298                Value::Number(n) => Ok(*n != 0.0),
299                Value::String(s) => Ok(!s.is_empty()),
300                _ => Ok(false),
301            }
302        }
303    }
304
305    /// Resolve a value (replace identifiers with their values from variables)
306    pub fn resolve_value(&self, value: &Value) -> Value {
307        match value {
308            Value::Identifier(name) => {
309                // Check if it's a special variable
310                if is_special_var(name) {
311                    if let Some(special_val) = resolve_special_var(name, &self.special_vars) {
312                        return special_val;
313                    }
314                }
315
316                // Otherwise, look in variables
317                self.variables.get(name).cloned().unwrap_or(Value::Null)
318            }
319            _ => value.clone(),
320        }
321    }
322
323    /// Execute event handlers matching the event name
324    pub fn execute_event_handlers(&mut self, event_name: &str) -> Result<()> {
325        handler::execute_event_handlers(self, event_name)
326    }
327
328    /// Check if two values are equal
329    pub fn values_equal(&self, left: &Value, right: &Value) -> bool {
330        match (left, right) {
331            (Value::Number(a), Value::Number(b)) => (a - b).abs() < 0.0001,
332            (Value::String(a), Value::String(b)) => a == b,
333            (Value::Boolean(a), Value::Boolean(b)) => a == b,
334            (Value::Null, Value::Null) => true,
335            _ => false,
336        }
337    }
338
339    /// Compare two values
340    pub fn compare_values(
341        &self,
342        left: &Value,
343        right: &Value,
344        ordering: std::cmp::Ordering,
345    ) -> Result<bool> {
346        match (left, right) {
347            (Value::Number(a), Value::Number(b)) => {
348                Ok(a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal) == ordering)
349            }
350            (Value::String(a), Value::String(b)) => Ok(a.cmp(b) == ordering),
351            _ => Err(anyhow::anyhow!("Cannot compare {:?} and {:?}", left, right)),
352        }
353    }
354
355    /// Handle property assignment: target.property = value
356    pub fn handle_assign(&mut self, target: &str, property: &str, value: &Value) -> Result<()> {
357        handler::handle_assign(self, target, property, value)
358    }
359
360    /// Extract synth definition from a map
361    pub fn extract_synth_def_from_map(
362        &self,
363        map: &HashMap<String, Value>,
364    ) -> Result<SynthDefinition> {
365        handler::extract_synth_def_from_map(self, map)
366    }
367
368    /// Handle MIDI file loading: @load "path.mid" as alias
369    pub fn handle_load(&mut self, source: &str, alias: &str) -> Result<()> {
370        handler::handle_load(self, source, alias)
371    }
372
373    /// Handle MIDI binding: bind source -> target { options }
374    pub fn handle_bind(&mut self, source: &str, target: &str, options: &Value) -> Result<()> {
375        handler::handle_bind(self, source, target, options)
376    }
377
378    /// Extract pattern string and options from pattern value
379    pub fn extract_pattern_data(
380        &self,
381        value: &Value,
382    ) -> (Option<String>, Option<HashMap<String, f32>>) {
383        handler::extract_pattern_data(self, value)
384    }
385
386    /// Execute a pattern with given target and pattern string
387    pub fn execute_pattern(
388        &mut self,
389        target: &str,
390        pattern: &str,
391        options: Option<HashMap<String, f32>>,
392    ) -> Result<()> {
393        handler::execute_pattern(self, target, pattern, options)
394    }
395
396    /// Resolve sample URI from bank.trigger notation (e.g., myBank.kick -> devalang://bank/devaloop.808/kick)
397    pub fn resolve_sample_uri(&self, target: &str) -> String {
398        handler::resolve_sample_uri(self, target)
399    }
400}