devalang_wasm/engine/audio/interpreter/statements/
routing.rs

1/// Audio routing statement handler
2use anyhow::Result;
3use std::collections::HashMap;
4
5/// Audio routing configuration
6#[derive(Debug, Clone)]
7pub struct AudioRoute {
8    pub source: String,
9    pub destination: String,
10    pub channels: Vec<usize>,
11    pub mix_level: f32,
12}
13
14impl Default for AudioRoute {
15    fn default() -> Self {
16        Self {
17            source: String::new(),
18            destination: "master".to_string(),
19            channels: vec![0, 1], // Stereo by default
20            mix_level: 1.0,
21        }
22    }
23}
24
25/// Execute audio routing statement
26///
27/// Routes audio signals from source to destination with optional processing.
28/// Supports multi-channel routing, level control, and send/return configurations.
29///
30/// # Arguments
31/// * `route` - Routing configuration
32/// * `routing_table` - Global routing table to update
33///
34/// # Examples
35/// ```ignore
36/// // Route synth to reverb
37/// route synth1 -> reverb
38///
39/// // Route with level control
40/// route drums -> master @ 0.8
41///
42/// // Multi-channel routing
43/// route synth -> [reverb, delay] @ [0.5, 0.3]
44/// ```
45pub fn execute_routing(
46    route: &AudioRoute,
47    routing_table: &mut HashMap<String, Vec<AudioRoute>>,
48) -> Result<()> {
49    // Validate routing configuration
50    if route.source.is_empty() {
51        anyhow::bail!("Routing source cannot be empty");
52    }
53
54    if route.destination.is_empty() {
55        anyhow::bail!("Routing destination cannot be empty");
56    }
57
58    if route.mix_level < 0.0 || route.mix_level > 2.0 {
59        anyhow::bail!(
60            "Mix level must be between 0.0 and 2.0, got: {}",
61            route.mix_level
62        );
63    }
64
65    if route.channels.is_empty() {
66        anyhow::bail!("Routing must specify at least one channel");
67    }
68
69    // Add route to routing table
70    routing_table
71        .entry(route.source.clone())
72        .or_insert_with(Vec::new)
73        .push(route.clone());
74
75    // Log routing setup
76    #[cfg(feature = "cli")]
77    {
78        eprintln!(
79            "Route: {} -> {} ({} ch @ {:.2})",
80            route.source,
81            route.destination,
82            route.channels.len(),
83            route.mix_level
84        );
85    }
86
87    Ok(())
88}
89
90/// Create a default routing from source to master
91pub fn create_default_route(source: String) -> AudioRoute {
92    AudioRoute {
93        source,
94        destination: "master".to_string(),
95        channels: vec![0, 1],
96        mix_level: 1.0,
97    }
98}
99
100/// Parse routing expression from statement
101pub fn parse_routing_expr(expr: &str) -> Result<AudioRoute> {
102    // Simple parser for routing expressions
103    // Format: "source -> destination @ level"
104
105    let parts: Vec<&str> = expr.split("->").collect();
106    if parts.len() != 2 {
107        anyhow::bail!("Invalid routing expression: {}", expr);
108    }
109
110    let source = parts[0].trim().to_string();
111    let dest_and_level: Vec<&str> = parts[1].split('@').collect();
112
113    let destination = dest_and_level[0].trim().to_string();
114    let mix_level = if dest_and_level.len() > 1 {
115        dest_and_level[1]
116            .trim()
117            .parse::<f32>()
118            .map_err(|_| anyhow::anyhow!("Invalid mix level: {}", dest_and_level[1]))?
119    } else {
120        1.0
121    };
122
123    Ok(AudioRoute {
124        source,
125        destination,
126        channels: vec![0, 1],
127        mix_level,
128    })
129}