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