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_default();
132 self.events.push(AudioEvent::Note {
133 midi,
134 start_time,
135 duration,
136 velocity,
137 synth_id: synth_id.to_string(),
138 synth_def,
139 pan,
140 detune,
141 gain,
142 attack,
143 release,
144 delay_time,
145 delay_feedback,
146 delay_mix,
147 reverb_amount,
148 drive_amount,
149 drive_color,
150 });
151 }
152
153 pub fn add_chord_event(
154 &mut self,
155 synth_id: &str,
156 midis: Vec<u8>,
157 start_time: f32,
158 duration: f32,
159 velocity: f32,
160 pan: f32,
161 detune: f32,
162 spread: f32,
163 gain: f32,
164 attack: Option<f32>,
165 release: Option<f32>,
166 delay_time: Option<f32>,
167 delay_feedback: Option<f32>,
168 delay_mix: Option<f32>,
169 reverb_amount: Option<f32>,
170 drive_amount: Option<f32>,
171 drive_color: Option<f32>,
172 ) {
173 let synth_def = self.get_synth(synth_id).cloned().unwrap_or_default();
175
176 self.events.push(AudioEvent::Chord {
177 midis,
178 start_time,
179 duration,
180 velocity,
181 synth_id: synth_id.to_string(),
182 synth_def,
183 pan,
184 detune,
185 spread,
186 gain,
187 attack,
188 release,
189 delay_time,
190 delay_feedback,
191 delay_mix,
192 reverb_amount,
193 drive_amount,
194 drive_color,
195 });
196 }
197
198 pub fn add_sample_event(&mut self, uri: &str, start_time: f32, velocity: f32) {
199 self.events.push(AudioEvent::Sample {
200 uri: uri.to_string(),
201 start_time,
202 velocity,
203 });
204 }
205
206 pub fn get_synth(&self, name: &str) -> Option<&SynthDefinition> {
207 self.synths.get(name)
208 }
209
210 pub fn total_duration(&self) -> f32 {
211 self.events
212 .iter()
213 .map(|event| match event {
214 AudioEvent::Note {
215 start_time,
216 duration,
217 ..
218 } => start_time + duration,
219 AudioEvent::Chord {
220 start_time,
221 duration,
222 ..
223 } => start_time + duration,
224 AudioEvent::Sample {
225 start_time, uri, ..
226 } => {
227 #[cfg(target_arch = "wasm32")]
229 {
230 use crate::web::registry::samples::get_sample;
231 if let Some(pcm) = get_sample(uri) {
232 let duration = pcm.len() as f32 / 44100.0;
234 start_time + duration
235 } else {
236 start_time + 2.0
238 }
239 }
240 #[cfg(not(target_arch = "wasm32"))]
241 {
242 let _ = uri; start_time + 2.0
245 }
246 }
247 })
248 .fold(0.0, f32::max)
249 }
250
251 pub fn merge(&mut self, other: AudioEventList) {
254 self.events.extend(other.events);
256
257 for (name, def) in other.synths {
259 if !self.synths.contains_key(&name) {
260 self.synths.insert(name, def);
261 }
262 }
263 }
264}
265
266pub fn extract_number(map: &HashMap<String, Value>, key: &str, default: f32) -> f32 {
268 map.get(key)
269 .and_then(|v| {
270 if let Value::Number(n) = v {
271 Some(*n)
272 } else {
273 None
274 }
275 })
276 .unwrap_or(default)
277}
278
279pub fn extract_string(map: &HashMap<String, Value>, key: &str, default: &str) -> String {
280 map.get(key)
281 .and_then(|v| {
282 if let Value::String(s) = v {
283 Some(s.clone())
284 } else {
285 None
286 }
287 })
288 .unwrap_or_else(|| default.to_string())
289}
290
291pub fn extract_filters(filters_arr: &[Value]) -> Vec<FilterDef> {
292 filters_arr
293 .iter()
294 .filter_map(|v| {
295 if let Value::Map(filter_map) = v {
296 let filter_type = extract_string(filter_map, "type", "lowpass");
297 let cutoff = filter_map
298 .get("cutoff")
299 .and_then(|v| match v {
300 Value::Number(n) => Some(*n),
301 _ => None,
302 })
303 .unwrap_or(1000.0);
304 let resonance = filter_map
305 .get("resonance")
306 .and_then(|v| match v {
307 Value::Number(n) => Some(*n),
308 _ => None,
309 })
310 .unwrap_or(1.0);
311
312 Some(FilterDef {
313 filter_type,
314 cutoff,
315 resonance,
316 })
317 } else {
318 None
319 }
320 })
321 .collect()
322}