1use crate::engine::audio::generator::FilterDef;
2use crate::language::syntax::ast::Value;
4use std::collections::HashMap;
5
6#[derive(Debug, Clone)]
7pub enum AudioEvent {
8 Note {
9 midi: u8,
10 start_time: f32,
11 duration: f32,
12 velocity: f32,
13 synth_id: String,
14 synth_def: SynthDefinition, pan: f32, detune: f32, gain: f32, attack: Option<f32>, release: Option<f32>, delay_time: Option<f32>, delay_feedback: Option<f32>, delay_mix: Option<f32>, reverb_amount: Option<f32>, drive_amount: Option<f32>, drive_color: Option<f32>, },
29 Chord {
30 midis: Vec<u8>,
31 start_time: f32,
32 duration: f32,
33 velocity: f32,
34 synth_id: String,
35 synth_def: SynthDefinition, pan: f32, detune: f32, spread: f32, gain: f32, attack: Option<f32>, release: Option<f32>, delay_time: Option<f32>, delay_feedback: Option<f32>, delay_mix: Option<f32>, reverb_amount: Option<f32>, drive_amount: Option<f32>, drive_color: Option<f32>, },
51 Sample {
52 uri: String,
53 start_time: f32,
54 velocity: f32,
55 },
56}
57
58#[derive(Debug, Default)]
60pub struct AudioEventList {
61 pub events: Vec<AudioEvent>,
62 pub synths: HashMap<String, SynthDefinition>,
63}
64
65#[derive(Debug, Clone)]
66pub struct SynthDefinition {
67 pub waveform: String,
68 pub attack: f32,
69 pub decay: f32,
70 pub sustain: f32,
71 pub release: f32,
72 pub synth_type: Option<String>,
73 pub filters: Vec<FilterDef>,
74 pub options: HashMap<String, f32>, pub plugin_author: Option<String>,
77 pub plugin_name: Option<String>,
78 pub plugin_export: Option<String>,
79}
80
81impl Default for SynthDefinition {
82 fn default() -> Self {
83 Self {
84 waveform: "sine".to_string(),
85 attack: 0.01,
86 decay: 0.1,
87 sustain: 0.7,
88 release: 0.2,
89 synth_type: None,
90 filters: Vec::new(),
91 options: HashMap::new(),
92 plugin_author: None,
93 plugin_name: None,
94 plugin_export: None,
95 }
96 }
97}
98
99impl AudioEventList {
100 pub fn new() -> Self {
101 Self {
102 events: Vec::new(),
103 synths: HashMap::new(),
104 }
105 }
106
107 pub fn add_synth(&mut self, name: String, definition: SynthDefinition) {
108 self.synths.insert(name, definition);
109 }
110
111 pub fn add_note_event(
112 &mut self,
113 synth_id: &str,
114 midi: u8,
115 start_time: f32,
116 duration: f32,
117 velocity: f32,
118 pan: f32,
119 detune: f32,
120 gain: f32,
121 attack: Option<f32>,
122 release: Option<f32>,
123 delay_time: Option<f32>,
124 delay_feedback: Option<f32>,
125 delay_mix: Option<f32>,
126 reverb_amount: Option<f32>,
127 drive_amount: Option<f32>,
128 drive_color: Option<f32>,
129 ) {
130 let synth_def = self.get_synth(synth_id).cloned().unwrap_or_else(|| {
132 println!("⚠️ Warning: Synth '{}' not found when creating note event. Available synths: {:?}",
133 synth_id, self.synths.keys().collect::<Vec<_>>());
134 SynthDefinition::default()
135 });
136
137 self.events.push(AudioEvent::Note {
138 midi,
139 start_time,
140 duration,
141 velocity,
142 synth_id: synth_id.to_string(),
143 synth_def,
144 pan,
145 detune,
146 gain,
147 attack,
148 release,
149 delay_time,
150 delay_feedback,
151 delay_mix,
152 reverb_amount,
153 drive_amount,
154 drive_color,
155 });
156 }
157
158 pub fn add_chord_event(
159 &mut self,
160 synth_id: &str,
161 midis: Vec<u8>,
162 start_time: f32,
163 duration: f32,
164 velocity: f32,
165 pan: f32,
166 detune: f32,
167 spread: f32,
168 gain: f32,
169 attack: Option<f32>,
170 release: Option<f32>,
171 delay_time: Option<f32>,
172 delay_feedback: Option<f32>,
173 delay_mix: Option<f32>,
174 reverb_amount: Option<f32>,
175 drive_amount: Option<f32>,
176 drive_color: Option<f32>,
177 ) {
178 let synth_def = self.get_synth(synth_id).cloned().unwrap_or_default();
180
181 self.events.push(AudioEvent::Chord {
182 midis,
183 start_time,
184 duration,
185 velocity,
186 synth_id: synth_id.to_string(),
187 synth_def,
188 pan,
189 detune,
190 spread,
191 gain,
192 attack,
193 release,
194 delay_time,
195 delay_feedback,
196 delay_mix,
197 reverb_amount,
198 drive_amount,
199 drive_color,
200 });
201 }
202
203 pub fn add_sample_event(&mut self, uri: &str, start_time: f32, velocity: f32) {
204 self.events.push(AudioEvent::Sample {
205 uri: uri.to_string(),
206 start_time,
207 velocity,
208 });
209 }
210
211 pub fn get_synth(&self, name: &str) -> Option<&SynthDefinition> {
212 self.synths.get(name)
213 }
214
215 pub fn total_duration(&self) -> f32 {
216 self.events
217 .iter()
218 .map(|event| match event {
219 AudioEvent::Note {
220 start_time,
221 duration,
222 ..
223 } => start_time + duration,
224 AudioEvent::Chord {
225 start_time,
226 duration,
227 ..
228 } => start_time + duration,
229 AudioEvent::Sample {
230 start_time, uri, ..
231 } => {
232 #[cfg(target_arch = "wasm32")]
234 {
235 use crate::web::registry::samples::get_sample;
236 if let Some(pcm) = get_sample(uri) {
237 let duration = pcm.len() as f32 / 44100.0;
239 start_time + duration
240 } else {
241 start_time + 2.0
243 }
244 }
245 #[cfg(not(target_arch = "wasm32"))]
246 {
247 let _ = uri; start_time + 2.0
250 }
251 }
252 })
253 .fold(0.0, f32::max)
254 }
255
256 pub fn merge(&mut self, other: AudioEventList) {
259 for (name, def) in other.synths {
261 if !self.synths.contains_key(&name) {
262 self.synths.insert(name, def);
263 }
264 }
265
266 for mut event in other.events {
268 match &mut event {
270 AudioEvent::Note {
271 synth_id,
272 synth_def,
273 ..
274 } => {
275 if let Some(updated_def) = self.synths.get(synth_id) {
277 *synth_def = updated_def.clone();
278 }
279 }
280 AudioEvent::Chord {
281 synth_id,
282 synth_def,
283 ..
284 } => {
285 if let Some(updated_def) = self.synths.get(synth_id) {
287 *synth_def = updated_def.clone();
288 }
289 }
290 _ => {}
291 }
292 self.events.push(event);
293 }
294 }
295}
296
297pub fn extract_number(map: &HashMap<String, Value>, key: &str, default: f32) -> f32 {
299 map.get(key)
300 .and_then(|v| {
301 if let Value::Number(n) = v {
302 Some(*n)
303 } else {
304 None
305 }
306 })
307 .unwrap_or(default)
308}
309
310pub fn extract_string(map: &HashMap<String, Value>, key: &str, default: &str) -> String {
311 map.get(key)
312 .and_then(|v| {
313 if let Value::String(s) = v {
314 Some(s.clone())
315 } else {
316 None
317 }
318 })
319 .unwrap_or_else(|| default.to_string())
320}
321
322pub fn extract_filters(filters_arr: &[Value]) -> Vec<FilterDef> {
323 filters_arr
324 .iter()
325 .filter_map(|v| {
326 if let Value::Map(filter_map) = v {
327 let filter_type = extract_string(filter_map, "type", "lowpass");
328 let cutoff = filter_map
329 .get("cutoff")
330 .and_then(|v| match v {
331 Value::Number(n) => Some(*n),
332 _ => None,
333 })
334 .unwrap_or(1000.0);
335 let resonance = filter_map
336 .get("resonance")
337 .and_then(|v| match v {
338 Value::Number(n) => Some(*n),
339 _ => None,
340 })
341 .unwrap_or(1.0);
342
343 Some(FilterDef {
344 filter_type,
345 cutoff,
346 resonance,
347 })
348 } else {
349 None
350 }
351 })
352 .collect()
353}