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