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 node_id: Option<String>,
10 midi: u8,
11 start_time: f32,
12 duration: f32,
13 velocity: f32,
14 synth_id: String,
15 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>, effects: Option<crate::language::syntax::ast::Value>,
31 use_per_note_automation: bool, },
34 Chord {
35 node_id: Option<String>,
36 midis: Vec<u8>,
37 start_time: f32,
38 duration: f32,
39 velocity: f32,
40 synth_id: String,
41 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>, effects: Option<crate::language::syntax::ast::Value>,
58 use_per_note_automation: bool, },
61 Sample {
62 node_id: Option<String>,
63 uri: String,
64 start_time: f32,
65 velocity: f32,
66 effects: Option<crate::language::syntax::ast::Value>,
68 source: Option<String>,
70 },
71 Stop {
72 target: Option<String>, time: f32, },
75}
76
77#[derive(Debug, Default)]
79pub struct AudioEventList {
80 pub events: Vec<AudioEvent>,
81 pub logs: Vec<(f32, String)>,
84 pub synths: HashMap<String, SynthDefinition>,
85}
86
87#[derive(Debug, Clone)]
88pub struct SynthDefinition {
89 pub waveform: String,
90 pub attack: f32,
91 pub decay: f32,
92 pub sustain: f32,
93 pub release: f32,
94 pub synth_type: Option<String>,
95 pub filters: Vec<FilterDef>,
96 pub options: HashMap<String, f32>, pub lfo: Option<crate::engine::audio::lfo::LfoParams>, pub bpm: f32, pub plugin_author: Option<String>,
101 pub plugin_name: Option<String>,
102 pub plugin_export: Option<String>,
103}
104
105impl Default for SynthDefinition {
106 fn default() -> Self {
107 Self {
108 waveform: "sine".to_string(),
109 attack: 0.01,
110 decay: 0.1,
111 sustain: 0.7,
112 release: 0.2,
113 synth_type: None,
114 filters: Vec::new(),
115 options: HashMap::new(),
116 lfo: None,
117 bpm: 120.0,
118 plugin_author: None,
119 plugin_name: None,
120 plugin_export: None,
121 }
122 }
123}
124
125impl AudioEventList {
126 pub fn new() -> Self {
127 Self {
128 events: Vec::new(),
129 logs: Vec::new(),
130 synths: HashMap::new(),
131 }
132 }
133
134 pub fn add_synth(&mut self, name: String, definition: SynthDefinition) {
135 self.synths.insert(name, definition);
136 }
137
138 pub fn add_note_event(
139 &mut self,
140 synth_id: &str,
141 midi: u8,
142 start_time: f32,
143 duration: f32,
144 velocity: f32,
145 pan: f32,
146 detune: f32,
147 gain: f32,
148 attack: Option<f32>,
149 release: Option<f32>,
150 delay_time: Option<f32>,
151 delay_feedback: Option<f32>,
152 delay_mix: Option<f32>,
153 reverb_amount: Option<f32>,
154 drive_amount: Option<f32>,
155 drive_color: Option<f32>,
156 ) {
157 let synth_def = self.get_synth(synth_id).cloned().unwrap_or_else(|| {
159 println!("⚠️ Warning: Synth '{}' not found when creating note event. Available synths: {:?}",
160 synth_id, self.synths.keys().collect::<Vec<_>>());
161 SynthDefinition::default()
162 });
163
164 self.events.push(AudioEvent::Note {
165 node_id: Some("master".to_string()),
166 midi,
167 start_time,
168 duration,
169 velocity,
170 synth_id: synth_id.to_string(),
171 synth_def,
172 pan,
173 detune,
174 gain,
175 attack,
176 release,
177 delay_time,
178 delay_feedback,
179 delay_mix,
180 reverb_amount,
181 drive_amount,
182 drive_color,
183 effects: None,
184 use_per_note_automation: false,
185 });
186 }
187
188 pub fn add_chord_event(
189 &mut self,
190 synth_id: &str,
191 midis: Vec<u8>,
192 start_time: f32,
193 duration: f32,
194 velocity: f32,
195 pan: f32,
196 detune: f32,
197 spread: f32,
198 gain: f32,
199 attack: Option<f32>,
200 release: Option<f32>,
201 delay_time: Option<f32>,
202 delay_feedback: Option<f32>,
203 delay_mix: Option<f32>,
204 reverb_amount: Option<f32>,
205 drive_amount: Option<f32>,
206 drive_color: Option<f32>,
207 ) {
208 let synth_def = self.get_synth(synth_id).cloned().unwrap_or_default();
210
211 self.events.push(AudioEvent::Chord {
212 node_id: Some("master".to_string()),
213 midis,
214 start_time,
215 duration,
216 velocity,
217 synth_id: synth_id.to_string(),
218 synth_def,
219 pan,
220 detune,
221 spread,
222 gain,
223 attack,
224 release,
225 delay_time,
226 delay_feedback,
227 delay_mix,
228 reverb_amount,
229 drive_amount,
230 drive_color,
231 effects: None,
232 use_per_note_automation: false,
233 });
234 }
235
236 pub fn add_sample_event(&mut self, uri: &str, start_time: f32, velocity: f32) {
237 self.events.push(AudioEvent::Sample {
238 node_id: Some("master".to_string()),
239 uri: uri.to_string(),
240 start_time,
241 velocity,
242 effects: None,
243 source: None,
244 });
245 }
246
247 pub fn add_sample_event_with_effects(
249 &mut self,
250 uri: &str,
251 start_time: f32,
252 velocity: f32,
253 effects: Option<Value>,
254 ) {
255 self.events.push(AudioEvent::Sample {
256 node_id: Some("master".to_string()),
257 uri: uri.to_string(),
258 start_time,
259 velocity,
260 effects,
261 source: None,
262 });
263 }
264
265 pub fn add_log_event(&mut self, message: String, time: f32) {
268 self.logs.push((time, message));
269 }
270
271 pub fn get_synth(&self, name: &str) -> Option<&SynthDefinition> {
272 self.synths.get(name)
273 }
274
275 pub fn total_duration(&self) -> f32 {
276 let stop_time = self.events.iter().find_map(|event| match event {
278 AudioEvent::Stop { target: None, time } => Some(*time), _ => None,
280 });
281
282 let max_duration = self
284 .events
285 .iter()
286 .map(|event| match event {
287 AudioEvent::Note {
288 start_time,
289 duration,
290 ..
291 } => start_time + duration,
292 AudioEvent::Chord {
293 start_time,
294 duration,
295 ..
296 } => start_time + duration,
297 AudioEvent::Sample {
298 start_time, uri, ..
299 } => {
300 #[cfg(target_arch = "wasm32")]
302 {
303 use crate::web::registry::samples::get_sample;
304 if let Some(pcm) = get_sample(uri) {
305 let duration = pcm.len() as f32 / 44100.0;
307 start_time + duration
308 } else {
309 start_time + 2.0
311 }
312 }
313 #[cfg(not(target_arch = "wasm32"))]
314 {
315 let _ = uri; start_time + 2.0
318 }
319 }
320 AudioEvent::Stop { target: None, time } => *time, AudioEvent::Stop {
322 target: Some(_), ..
323 } => 0.0, })
325 .fold(0.0, f32::max);
326
327 if let Some(stop_t) = stop_time {
329 max_duration.min(stop_t)
330 } else {
331 max_duration
332 }
333 }
334
335 pub fn merge(&mut self, other: AudioEventList) {
338 for (name, def) in other.synths {
340 if !self.synths.contains_key(&name) {
341 self.synths.insert(name, def);
342 }
343 }
344
345 for mut event in other.events {
347 match &mut event {
349 AudioEvent::Note {
350 synth_id,
351 synth_def,
352 ..
353 } => {
354 if let Some(updated_def) = self.synths.get(synth_id) {
356 *synth_def = updated_def.clone();
357 }
358 }
359 AudioEvent::Chord {
360 synth_id,
361 synth_def,
362 ..
363 } => {
364 if let Some(updated_def) = self.synths.get(synth_id) {
366 *synth_def = updated_def.clone();
367 }
368 }
369 _ => {}
370 }
371 self.events.push(event);
372 }
373
374 for log in other.logs {
376 let mut duplicated = false;
381 for existing in &self.logs {
382 let time_diff = (existing.0 - log.0).abs();
383 if existing.1 == log.1 && time_diff <= 0.001 {
384 duplicated = true;
385 break;
386 }
387 }
388 if !duplicated {
389 self.logs.push(log);
390 }
391 }
392
393 self.logs
395 .sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal));
396 }
397}
398
399pub fn extract_number(map: &HashMap<String, Value>, key: &str, default: f32) -> f32 {
401 map.get(key)
402 .and_then(|v| {
403 if let Value::Number(n) = v {
404 Some(*n)
405 } else {
406 None
407 }
408 })
409 .unwrap_or(default)
410}
411
412pub fn extract_string(map: &HashMap<String, Value>, key: &str, default: &str) -> String {
413 map.get(key)
414 .and_then(|v| {
415 if let Value::String(s) = v {
416 Some(s.clone())
417 } else {
418 None
419 }
420 })
421 .unwrap_or_else(|| default.to_string())
422}
423
424pub fn extract_filters(filters_arr: &[Value]) -> Vec<FilterDef> {
425 filters_arr
426 .iter()
427 .filter_map(|v| {
428 if let Value::Map(filter_map) = v {
429 let filter_type = extract_string(filter_map, "type", "lowpass");
430 let cutoff = filter_map
431 .get("cutoff")
432 .and_then(|v| match v {
433 Value::Number(n) => Some(*n),
434 _ => None,
435 })
436 .unwrap_or(1000.0);
437 let resonance = filter_map
438 .get("resonance")
439 .and_then(|v| match v {
440 Value::Number(n) => Some(*n),
441 _ => None,
442 })
443 .unwrap_or(1.0);
444
445 Some(FilterDef {
446 filter_type,
447 cutoff,
448 resonance,
449 })
450 } else {
451 None
452 }
453 })
454 .collect()
455}