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