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>, }
76
77impl Default for SynthDefinition {
78 fn default() -> Self {
79 Self {
80 waveform: "sine".to_string(),
81 attack: 0.01,
82 decay: 0.1,
83 sustain: 0.7,
84 release: 0.2,
85 synth_type: None,
86 filters: Vec::new(),
87 options: HashMap::new(),
88 }
89 }
90}
91
92impl AudioEventList {
93 pub fn new() -> Self {
94 Self {
95 events: Vec::new(),
96 synths: HashMap::new(),
97 }
98 }
99
100 pub fn add_synth(&mut self, name: String, definition: SynthDefinition) {
101 self.synths.insert(name, definition);
102 }
103
104 pub fn add_note_event(
105 &mut self,
106 synth_id: &str,
107 midi: u8,
108 start_time: f32,
109 duration: f32,
110 velocity: f32,
111 pan: f32,
112 detune: f32,
113 gain: f32,
114 attack: Option<f32>,
115 release: Option<f32>,
116 delay_time: Option<f32>,
117 delay_feedback: Option<f32>,
118 delay_mix: Option<f32>,
119 reverb_amount: Option<f32>,
120 drive_amount: Option<f32>,
121 drive_color: Option<f32>,
122 ) {
123 let synth_def = self.get_synth(synth_id).cloned().unwrap_or_default();
125
126 self.events.push(AudioEvent::Note {
127 midi,
128 start_time,
129 duration,
130 velocity,
131 synth_id: synth_id.to_string(),
132 synth_def,
133 pan,
134 detune,
135 gain,
136 attack,
137 release,
138 delay_time,
139 delay_feedback,
140 delay_mix,
141 reverb_amount,
142 drive_amount,
143 drive_color,
144 });
145 }
146
147 pub fn add_chord_event(
148 &mut self,
149 synth_id: &str,
150 midis: Vec<u8>,
151 start_time: f32,
152 duration: f32,
153 velocity: f32,
154 pan: f32,
155 detune: f32,
156 spread: f32,
157 gain: f32,
158 attack: Option<f32>,
159 release: Option<f32>,
160 delay_time: Option<f32>,
161 delay_feedback: Option<f32>,
162 delay_mix: Option<f32>,
163 reverb_amount: Option<f32>,
164 drive_amount: Option<f32>,
165 drive_color: Option<f32>,
166 ) {
167 let synth_def = self.get_synth(synth_id).cloned().unwrap_or_default();
169
170 self.events.push(AudioEvent::Chord {
171 midis,
172 start_time,
173 duration,
174 velocity,
175 synth_id: synth_id.to_string(),
176 synth_def,
177 pan,
178 detune,
179 spread,
180 gain,
181 attack,
182 release,
183 delay_time,
184 delay_feedback,
185 delay_mix,
186 reverb_amount,
187 drive_amount,
188 drive_color,
189 });
190 }
191
192 pub fn add_sample_event(&mut self, uri: &str, start_time: f32, velocity: f32) {
193 self.events.push(AudioEvent::Sample {
194 uri: uri.to_string(),
195 start_time,
196 velocity,
197 });
198 }
199
200 pub fn get_synth(&self, name: &str) -> Option<&SynthDefinition> {
201 self.synths.get(name)
202 }
203
204 pub fn total_duration(&self) -> f32 {
205 self.events
206 .iter()
207 .map(|event| match event {
208 AudioEvent::Note {
209 start_time,
210 duration,
211 ..
212 } => start_time + duration,
213 AudioEvent::Chord {
214 start_time,
215 duration,
216 ..
217 } => start_time + duration,
218 AudioEvent::Sample {
219 start_time, uri, ..
220 } => {
221 #[cfg(target_arch = "wasm32")]
223 {
224 use crate::web::registry::samples::get_sample;
225 if let Some(pcm) = get_sample(uri) {
226 let duration = pcm.len() as f32 / 44100.0;
228 start_time + duration
229 } else {
230 start_time + 2.0
232 }
233 }
234 #[cfg(not(target_arch = "wasm32"))]
235 {
236 let _ = uri; start_time + 2.0
239 }
240 }
241 })
242 .fold(0.0, f32::max)
243 }
244
245 pub fn merge(&mut self, other: AudioEventList) {
248 self.events.extend(other.events);
250
251 for (name, def) in other.synths {
253 if !self.synths.contains_key(&name) {
254 self.synths.insert(name, def);
255 }
256 }
257 }
258}
259
260pub fn extract_number(map: &HashMap<String, Value>, key: &str, default: f32) -> f32 {
262 map.get(key)
263 .and_then(|v| {
264 if let Value::Number(n) = v {
265 Some(*n)
266 } else {
267 None
268 }
269 })
270 .unwrap_or(default)
271}
272
273pub fn extract_string(map: &HashMap<String, Value>, key: &str, default: &str) -> String {
274 map.get(key)
275 .and_then(|v| {
276 if let Value::String(s) = v {
277 Some(s.clone())
278 } else {
279 None
280 }
281 })
282 .unwrap_or_else(|| default.to_string())
283}
284
285pub fn extract_filters(filters_arr: &[Value]) -> Vec<FilterDef> {
286 filters_arr
287 .iter()
288 .filter_map(|v| {
289 if let Value::Map(filter_map) = v {
290 let filter_type = extract_string(filter_map, "type", "lowpass");
291 let cutoff = filter_map
292 .get("cutoff")
293 .and_then(|v| match v {
294 Value::Number(n) => Some(*n),
295 _ => None,
296 })
297 .unwrap_or(1000.0);
298 let resonance = filter_map
299 .get("resonance")
300 .and_then(|v| match v {
301 Value::Number(n) => Some(*n),
302 _ => None,
303 })
304 .unwrap_or(1.0);
305
306 Some(FilterDef {
307 filter_type,
308 cutoff,
309 resonance,
310 })
311 } else {
312 None
313 }
314 })
315 .collect()
316}