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