Skip to main content

devalang_wasm/engine/audio/nodes/
mod.rs

1// Audio Node Types (v0.2.0)
2// Defines the core building blocks of the audio graph
3
4use crate::language::syntax::ast::Value;
5use std::collections::HashMap;
6
7/// Type of audio node in the graph
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum NodeType {
10    /// Synthesizer source (generates audio)
11    Synth,
12    /// Sample playback source (loads and plays samples)
13    Sample,
14    /// Mix bus (collects routed audio before another destination)
15    Bus,
16    /// Group/container node (collects multiple nodes)
17    Group,
18    /// Master output node (final audio destination)
19    Output,
20}
21
22impl NodeType {
23    pub fn is_source(&self) -> bool {
24        matches!(self, NodeType::Synth | NodeType::Sample)
25    }
26
27    pub fn is_container(&self) -> bool {
28        matches!(self, NodeType::Bus | NodeType::Group | NodeType::Output)
29    }
30}
31
32/// Parameters for a node effect
33#[derive(Debug, Clone)]
34pub struct NodeParam {
35    pub name: String,
36    pub default: f32,
37    pub min: f32,
38    pub max: f32,
39    pub description: Option<String>,
40}
41
42/// Audio Node: source of audio or container of other nodes
43#[derive(Debug, Clone)]
44pub struct AudioNode {
45    /// Unique identifier
46    pub id: String,
47    /// Human-readable name (optional)
48    pub name: Option<String>,
49    /// Type of node
50    pub node_type: NodeType,
51    /// Effect chain: list of (effect_name, parameters)
52    pub effects: Vec<(String, Value)>,
53    /// Node parameters (e.g., volume, pan)
54    pub parameters: HashMap<String, f32>,
55}
56
57impl AudioNode {
58    /// Create new synth node
59    pub fn new_synth(id: impl Into<String>) -> Self {
60        let id = id.into();
61        let mut params = HashMap::new();
62        params.insert("volume".to_string(), 1.0);
63        params.insert("pan".to_string(), 0.0);
64
65        Self {
66            id,
67            name: None,
68            node_type: NodeType::Synth,
69            effects: Vec::new(),
70            parameters: params,
71        }
72    }
73
74    /// Create new sample node
75    pub fn new_sample(id: impl Into<String>) -> Self {
76        let id = id.into();
77        let mut params = HashMap::new();
78        params.insert("volume".to_string(), 1.0);
79        params.insert("pan".to_string(), 0.0);
80
81        Self {
82            id,
83            name: None,
84            node_type: NodeType::Sample,
85            effects: Vec::new(),
86            parameters: params,
87        }
88    }
89
90    /// Create new group node
91    pub fn new_group(id: impl Into<String>) -> Self {
92        let id = id.into();
93        let mut params = HashMap::new();
94        params.insert("volume".to_string(), 1.0);
95        params.insert("pan".to_string(), 0.0);
96
97        Self {
98            id,
99            name: None,
100            node_type: NodeType::Group,
101            effects: Vec::new(),
102            parameters: params,
103        }
104    }
105
106    /// Create new bus node
107    pub fn new_bus(id: impl Into<String>) -> Self {
108        let id = id.into();
109        let mut params = HashMap::new();
110        params.insert("volume".to_string(), 1.0);
111        params.insert("pan".to_string(), 0.0);
112
113        Self {
114            id,
115            name: None,
116            node_type: NodeType::Bus,
117            effects: Vec::new(),
118            parameters: params,
119        }
120    }
121
122    /// Create master output node
123    pub fn new_master() -> Self {
124        let mut params = HashMap::new();
125        params.insert("volume".to_string(), 1.0);
126
127        Self {
128            id: "master".to_string(),
129            name: Some("Master Output".to_string()),
130            node_type: NodeType::Output,
131            effects: Vec::new(),
132            parameters: params,
133        }
134    }
135
136    /// Add effect to effect chain
137    pub fn add_effect(&mut self, name: impl Into<String>, params: Value) {
138        self.effects.push((name.into(), params));
139    }
140
141    /// Set node parameter
142    pub fn set_parameter(&mut self, name: impl Into<String>, value: f32) {
143        self.parameters.insert(name.into(), value);
144    }
145
146    /// Get node parameter
147    pub fn get_parameter(&self, name: &str) -> Option<f32> {
148        self.parameters.get(name).copied()
149    }
150
151    /// Volume in dB (converted from linear)
152    pub fn volume(&self) -> f32 {
153        self.parameters.get("volume").copied().unwrap_or(1.0)
154    }
155
156    /// Pan (-1.0 to 1.0)
157    pub fn pan(&self) -> f32 {
158        self.parameters.get("pan").copied().unwrap_or(0.0)
159    }
160
161    /// Set volume (linear scale: 0.0 to 2.0+)
162    pub fn set_volume(&mut self, vol: f32) {
163        self.set_parameter("volume", vol);
164    }
165
166    /// Set pan (-1.0 to 1.0: left to right)
167    pub fn set_pan(&mut self, pan: f32) {
168        self.set_parameter("pan", pan.clamp(-1.0, 1.0));
169    }
170
171    /// Check if node is a source (generates audio)
172    pub fn is_source(&self) -> bool {
173        self.node_type.is_source()
174    }
175
176    /// Check if node is a container
177    pub fn is_container(&self) -> bool {
178        self.node_type.is_container()
179    }
180}
181
182#[cfg(test)]
183mod tests {
184    use super::*;
185
186    #[test]
187    fn test_new_synth_node() {
188        let node = AudioNode::new_synth("synth_1");
189        assert_eq!(node.id, "synth_1");
190        assert_eq!(node.node_type, NodeType::Synth);
191        assert_eq!(node.volume(), 1.0);
192        assert_eq!(node.pan(), 0.0);
193        assert!(node.is_source());
194    }
195
196    #[test]
197    fn test_new_group_node() {
198        let node = AudioNode::new_group("drums");
199        assert_eq!(node.id, "drums");
200        assert_eq!(node.node_type, NodeType::Group);
201        assert!(node.is_container());
202    }
203
204    #[test]
205    fn test_set_volume() {
206        let mut node = AudioNode::new_synth("syn");
207        node.set_volume(0.5);
208        assert_eq!(node.volume(), 0.5);
209    }
210
211    #[test]
212    fn test_add_effect() {
213        let mut node = AudioNode::new_synth("syn");
214        node.add_effect("reverb", Value::Number(0.8));
215        assert_eq!(node.effects.len(), 1);
216        assert_eq!(node.effects[0].0, "reverb");
217    }
218}