1#![allow(unused_macros)]
2use super::AudioInterpreter;
3use crate::engine::audio::effects::chain::{EffectChain, build_effect_chain};
4use crate::engine::audio::effects::normalize_effects;
5use crate::engine::audio::effects::processors::{
6 DelayProcessor, DriveProcessor, EffectProcessor, ReverbProcessor,
7};
8use crate::engine::audio::generator::{
9 SynthParams, generate_chord_with_options, generate_note_with_options,
10};
11use anyhow::Result;
12
13#[cfg(feature = "cli")]
15macro_rules! log_info {
16 ($logger:expr, $($arg:tt)*) => {
17 $logger.info(format!($($arg)*))
18 };
19}
20
21#[cfg(not(feature = "cli"))]
22macro_rules! log_info {
23 ($_logger:expr, $($arg:tt)*) => {
24 let _ = ($($arg)*);
25 };
26}
27
28#[cfg(feature = "cli")]
29macro_rules! log_warn {
30 ($logger:expr, $($arg:tt)*) => {
31 $logger.warn(format!($($arg)*))
32 };
33}
34
35#[cfg(not(feature = "cli"))]
36macro_rules! log_warn {
37 ($_logger:expr, $($arg:tt)*) => {
38 let _ = ($($arg)*);
39 };
40}
41
42#[cfg(feature = "cli")]
43macro_rules! log_error {
44 ($logger:expr, $($arg:tt)*) => {
45 $logger.error(format!($($arg)*))
46 };
47}
48
49#[cfg(not(feature = "cli"))]
50macro_rules! log_error {
51 ($_logger:expr, $($arg:tt)*) => {
52 let _ = ($($arg)*);
53 };
54}
55
56pub fn render_audio(interpreter: &AudioInterpreter) -> Result<Vec<f32>> {
57 let total_duration = interpreter.events.total_duration();
58 if total_duration <= 0.0 {
59 return Ok(Vec::new());
60 }
61
62 let total_samples = (total_duration * interpreter.sample_rate as f32).ceil() as usize;
63
64 #[cfg(feature = "cli")]
65 let logger = crate::tools::logger::Logger::new();
66 #[cfg(not(feature = "cli"))]
67 let _logger = ();
68
69 if !interpreter.audio_graph.node_names().is_empty()
71 && interpreter.audio_graph.node_names().len() > 1
72 {
73 log_info!(
74 logger,
75 "Using audio graph rendering with {} nodes",
76 interpreter.audio_graph.node_names().len()
77 );
78 return super::renderer_graph::render_audio_graph(interpreter, total_samples)
79 .map_err(|e| anyhow::anyhow!("Audio graph rendering failed: {}", e));
80 }
81
82 let mut buffer = vec![0.0f32; total_samples * 2]; log_info!(
86 logger,
87 "Starting audio rendering: {} events, {} synths, duration {:.2}s",
88 interpreter.events.events.len(),
89 interpreter.events.synths.len(),
90 total_duration
91 );
92
93 let mut note_count = 0;
97 let mut sample_count = 0;
98 for event in &interpreter.events.events {
99 match event {
100 crate::engine::audio::events::AudioEvent::Note {
101 midi,
102 start_time,
103 duration,
104 velocity,
105 synth_id,
106 synth_def,
107 pan,
108 detune,
109 gain,
110 attack,
111 release,
112 delay_time,
113 delay_feedback,
114 delay_mix,
115 reverb_amount,
116 drive_amount,
117 drive_color,
118 use_per_note_automation,
119 ..
120 } => {
121 note_count += 1;
122 let mut params = SynthParams {
125 waveform: synth_def.waveform.clone(),
126 attack: synth_def.attack,
127 decay: synth_def.decay,
128 sustain: synth_def.sustain,
129 release: synth_def.release,
130 synth_type: synth_def.synth_type.clone(),
131 filters: synth_def.filters.clone(),
132 options: synth_def.options.clone(),
133 lfo: synth_def.lfo.clone(),
134 plugin_author: synth_def.plugin_author.clone(),
135 plugin_name: synth_def.plugin_name.clone(),
136 plugin_export: synth_def.plugin_export.clone(),
137 };
138
139 if let Some(a) = attack {
140 params.attack = a / 1000.0;
141 }
142 if let Some(r) = release {
143 params.release = r / 1000.0;
144 }
145
146 if let Some(&decay_mode) = params.options.get("decay_mode") {
148 if decay_mode > 0.5 {
149 let attack_ms = params.attack * 1000.0;
152 let tail_ms = 50.0; let available_ms = (duration * 1000.0).max(attack_ms + tail_ms);
154 let auto_decay = available_ms - attack_ms - tail_ms;
155 params.decay = (auto_decay / 1000.0).max(0.01); }
157 }
158
159 let mut samples = if *use_per_note_automation {
160 if let Some(automation_ctx) =
162 interpreter.note_automation_templates.get(synth_id.as_str())
163 {
164 use crate::engine::audio::automation::evaluate_template_at;
165
166 let mut all_samples = Vec::new();
167 let num_segments = 8; let segment_duration = duration / num_segments as f32;
169
170 for segment_idx in 0..num_segments {
171 let segment_progress = (segment_idx as f32 + 0.5) / num_segments as f32;
173
174 let segment_pan = automation_ctx
176 .templates
177 .iter()
178 .find(|t| t.param_name == "pan")
179 .map(|t| evaluate_template_at(t, segment_progress))
180 .unwrap_or(*pan);
181
182 let segment_detune = automation_ctx
183 .templates
184 .iter()
185 .find(|t| t.param_name == "pitch" || t.param_name == "detune")
186 .map(|t| evaluate_template_at(t, segment_progress))
187 .unwrap_or(*detune);
188
189 let segment_gain = automation_ctx
190 .templates
191 .iter()
192 .find(|t| t.param_name == "volume" || t.param_name == "gain")
193 .map(|t| evaluate_template_at(t, segment_progress))
194 .unwrap_or(*gain);
195
196 let mut segment_params = params.clone();
198
199 for filter in &mut segment_params.filters {
201 let segment_cutoff = automation_ctx
202 .templates
203 .iter()
204 .find(|t| t.param_name == "cutoff")
205 .map(|t| evaluate_template_at(t, segment_progress))
206 .unwrap_or(filter.cutoff);
207 filter.cutoff = segment_cutoff;
208
209 let segment_resonance = automation_ctx
210 .templates
211 .iter()
212 .find(|t| t.param_name == "resonance")
213 .map(|t| evaluate_template_at(t, segment_progress))
214 .unwrap_or(filter.resonance);
215 filter.resonance = segment_resonance;
216 }
217
218 let segment_samples = generate_note_with_options(
220 *midi,
221 segment_duration * 1000.0,
222 (velocity * segment_gain).clamp(0.0, 1.0),
223 &segment_params,
224 interpreter.sample_rate,
225 segment_pan,
226 segment_detune,
227 )?;
228
229 all_samples.extend(segment_samples);
230 }
231
232 all_samples
233 } else {
234 generate_note_with_options(
236 *midi,
237 duration * 1000.0,
238 velocity * gain,
239 ¶ms,
240 interpreter.sample_rate,
241 *pan,
242 *detune,
243 )?
244 }
245 } else {
246 generate_note_with_options(
248 *midi,
249 duration * 1000.0,
250 velocity * gain,
251 ¶ms,
252 interpreter.sample_rate,
253 *pan,
254 *detune,
255 )?
256 };
257
258 let mut skip_drive = false;
261 let mut skip_reverb = false;
262 let mut skip_delay = false;
263 let mut effect_chain: Option<EffectChain> = None;
264
265 if let crate::engine::audio::events::AudioEvent::Note { effects, .. } = event {
267 if let Some(eff_val) = effects {
268 match eff_val {
269 crate::language::syntax::ast::Value::Array(arr) => {
270 let chain = build_effect_chain(arr, true);
271 if !chain.is_empty() {
272 effect_chain = Some(chain);
273 }
274 }
275 crate::language::syntax::ast::Value::Map(_) => {
276 let normalized = normalize_effects(&Some(eff_val.clone()));
277 if !normalized.is_empty() {
278 let mut chain = EffectChain::new(true);
279 for (k, v) in normalized.into_iter() {
280 chain.add_effect(
281 &k,
282 Some(crate::language::syntax::ast::Value::Map(v)),
283 );
284 match k.as_str() {
286 "drive" => skip_drive = true,
287 "reverb" => skip_reverb = true,
288 "delay" => skip_delay = true,
289 _ => {}
290 }
291 }
292 effect_chain = Some(chain);
293 }
294 }
295 _ => {}
296 }
297 }
298 }
299
300 if let Some(chain) = effect_chain.as_mut() {
301 chain.process(&mut samples, interpreter.sample_rate);
302 }
303
304 if let Some(amount) = drive_amount {
306 if !skip_drive {
307 let color = drive_color.unwrap_or(0.5);
308 let mix = 0.7;
309 let mut processor = DriveProcessor::new(*amount, 0.5, color, mix);
311 processor.process(&mut samples, interpreter.sample_rate);
312 }
313 }
314 if let Some(amount) = reverb_amount {
315 if !skip_reverb {
316 let room_size = *amount;
317 let damping = 0.5;
318 let decay = 0.5;
319 let mix = *amount * 0.5;
320 let mut processor = ReverbProcessor::new(room_size, damping, decay, mix);
321 processor.process(&mut samples, interpreter.sample_rate);
322 }
323 }
324 if let Some(time) = delay_time {
325 if !skip_delay {
326 let feedback = delay_feedback.unwrap_or(0.3);
327 let mix = delay_mix.unwrap_or(0.5);
328 let mut processor = DelayProcessor::new(*time, feedback, mix);
329 processor.process(&mut samples, interpreter.sample_rate);
330 }
331 }
332
333 let start_sample = (*start_time * interpreter.sample_rate as f32) as usize * 2;
334 for (i, &sample) in samples.iter().enumerate() {
335 let buf_idx = start_sample + i;
336 if buf_idx < buffer.len() {
337 buffer[buf_idx] += sample;
338 }
339 }
340 }
341
342 crate::engine::audio::events::AudioEvent::Chord {
343 midis,
344 start_time,
345 duration,
346 velocity,
347 synth_id: _,
348 synth_def,
349 pan,
350 detune,
351 spread,
352 gain,
353 attack,
354 release,
355 delay_time,
356 delay_feedback,
357 delay_mix,
358 reverb_amount,
359 drive_amount,
360 drive_color,
361 effects,
362 use_per_note_automation: _,
363 } => {
364 let mut params = SynthParams {
365 waveform: synth_def.waveform.clone(),
366 attack: synth_def.attack,
367 decay: synth_def.decay,
368 sustain: synth_def.sustain,
369 release: synth_def.release,
370 synth_type: synth_def.synth_type.clone(),
371 filters: synth_def.filters.clone(),
372 options: synth_def.options.clone(),
373 lfo: synth_def.lfo.clone(),
374 plugin_author: synth_def.plugin_author.clone(),
375 plugin_name: synth_def.plugin_name.clone(),
376 plugin_export: synth_def.plugin_export.clone(),
377 };
378 if let Some(a) = attack {
379 params.attack = a / 1000.0;
380 }
381 if let Some(r) = release {
382 params.release = r / 1000.0;
383 }
384
385 let mut samples = generate_chord_with_options(
386 midis,
387 duration * 1000.0,
388 velocity * gain,
389 ¶ms,
390 interpreter.sample_rate,
391 *pan,
392 *detune,
393 *spread,
394 )?;
395
396 let mut skip_drive = false;
398 let mut skip_reverb = false;
399 let mut skip_delay = false;
400 let mut effect_chain: Option<EffectChain> = None;
401 if let Some(eff_val) = effects {
402 match eff_val {
403 crate::language::syntax::ast::Value::Array(arr) => {
404 let chain = build_effect_chain(arr, true);
405 if !chain.is_empty() {
406 effect_chain = Some(chain);
407 }
408 }
409 crate::language::syntax::ast::Value::Map(_) => {
410 let normalized = normalize_effects(&Some(eff_val.clone()));
411 if !normalized.is_empty() {
412 let mut chain = EffectChain::new(true);
413 for (k, v) in normalized.into_iter() {
414 chain.add_effect(
415 &k,
416 Some(crate::language::syntax::ast::Value::Map(v)),
417 );
418 match k.as_str() {
419 "drive" => skip_drive = true,
420 "reverb" => skip_reverb = true,
421 "delay" => skip_delay = true,
422 _ => {}
423 }
424 }
425 effect_chain = Some(chain);
426 }
427 }
428 _ => {}
429 }
430 }
431
432 if let Some(chain) = effect_chain.as_mut() {
433 chain.process(&mut samples, interpreter.sample_rate);
434 }
435
436 if let Some(amount) = drive_amount {
437 if !skip_drive {
438 let color = drive_color.unwrap_or(0.5);
439 let mix = 0.7;
440 let mut processor = DriveProcessor::new(*amount, 0.5, color, mix);
441 processor.process(&mut samples, interpreter.sample_rate);
442 }
443 }
444 if let Some(amount) = reverb_amount {
445 if !skip_reverb {
446 let room_size = *amount;
447 let damping = 0.5;
448 let decay = 0.5;
449 let mix = *amount * 0.5;
450 let mut processor = ReverbProcessor::new(room_size, damping, decay, mix);
451 processor.process(&mut samples, interpreter.sample_rate);
452 }
453 }
454 if let Some(time) = delay_time {
455 if !skip_delay {
456 let feedback = delay_feedback.unwrap_or(0.3);
457 let mix = delay_mix.unwrap_or(0.5);
458 let mut processor = DelayProcessor::new(*time, feedback, mix);
459 processor.process(&mut samples, interpreter.sample_rate);
460 }
461 }
462
463 let start_sample = (*start_time * interpreter.sample_rate as f32) as usize * 2;
464 for (i, &sample) in samples.iter().enumerate() {
465 let buf_idx = start_sample + i;
466 if buf_idx < buffer.len() {
467 buffer[buf_idx] += sample;
468 }
469 }
470 }
471
472 crate::engine::audio::events::AudioEvent::Sample {
473 uri,
474 start_time,
475 velocity,
476 effects: _effects,
477 source: _,
478 } => {
479 sample_count += 1;
480 #[cfg(feature = "wasm")]
484 {
485 use crate::web::registry::samples::get_sample;
486 if let Some(pcm_data) = get_sample(uri) {
487 let start_sample_idx =
488 (*start_time * interpreter.sample_rate as f32) as usize;
489 for (i, &pcm_value) in pcm_data.iter().enumerate() {
490 let sample = (pcm_value as f32 / 32768.0) * velocity;
491 let stereo_pos = (start_sample_idx + i) * 2;
492 let buf_idx_l = stereo_pos;
493 let buf_idx_r = stereo_pos + 1;
494 if buf_idx_l < buffer.len() {
495 buffer[buf_idx_l] += sample;
496 }
497 if buf_idx_r < buffer.len() {
498 buffer[buf_idx_r] += sample;
499 }
500 }
501 } else {
502 log_warn!(logger, "Sample not found in registry: {}", uri);
503 }
504 }
505
506 #[cfg(feature = "cli")]
508 {
509 use crate::engine::audio::samples;
510 if let Some(sample_data) = samples::get_sample(uri) {
511 let start_sample_idx =
512 (*start_time * interpreter.sample_rate as f32) as usize;
513 let velocity_scale = velocity;
515 let resample_ratio =
516 interpreter.sample_rate as f32 / sample_data.sample_rate as f32;
517
518 let mut proc_samples = sample_data.samples.clone();
520
521 let mut sample_chain: Option<EffectChain> = None;
523 if let Some(eff_val) = _effects {
524 match eff_val {
525 crate::language::syntax::ast::Value::Array(arr) => {
526 let chain = build_effect_chain(arr, false);
527 if !chain.is_empty() {
528 sample_chain = Some(chain);
529 }
530 }
531 crate::language::syntax::ast::Value::Map(_) => {
532 let normalized = normalize_effects(&Some(eff_val.clone()));
533 if !normalized.is_empty() {
534 let mut chain = EffectChain::new(false);
535 for (k, v) in normalized.into_iter() {
536 chain.add_effect(
537 &k,
538 Some(crate::language::syntax::ast::Value::Map(v)),
539 );
540 }
541 sample_chain = Some(chain);
542 }
543 }
544 _ => {}
545 }
546 }
547
548 if let Some(chain) = sample_chain.as_mut() {
549 chain.process(&mut proc_samples, interpreter.sample_rate);
550 }
551
552 for (i, &sample) in proc_samples.iter().enumerate() {
553 let output_idx =
554 start_sample_idx + (i as f32 * resample_ratio) as usize;
555 let stereo_pos = output_idx * 2;
556 let buf_idx_l = stereo_pos;
557 let buf_idx_r = stereo_pos + 1;
558 let scaled_sample = sample * velocity_scale;
559 if buf_idx_l < buffer.len() {
560 buffer[buf_idx_l] += scaled_sample;
561 }
562 if buf_idx_r < buffer.len() {
563 buffer[buf_idx_r] += scaled_sample;
564 }
565 }
566 } else {
567 log_error!(logger, "Bank sample not found: {}", uri);
568 }
569 }
570 }
571 crate::engine::audio::events::AudioEvent::Stop { target, time: _ } => {
572 if let Some(entity) = target {
577 log_info!(logger, "Stop event for entity: {}", entity);
578 } else {
579 log_info!(logger, "Stop all event");
580 }
581 }
582 }
583 }
584
585 log_info!(
586 logger,
587 "Rendered {} notes + {} samples",
588 note_count,
589 sample_count
590 );
591 let max_amplitude = buffer.iter().map(|&s| s.abs()).fold(0.0f32, f32::max);
592 log_info!(
593 logger,
594 "Max amplitude before normalization: {:.4}",
595 max_amplitude
596 );
597
598 if max_amplitude > 1.0 {
599 for sample in buffer.iter_mut() {
600 *sample /= max_amplitude;
601 }
602 }
603
604 Ok(buffer)
605}
606
607pub fn render_audio_wrapper(interpreter: &mut AudioInterpreter) -> Result<Vec<f32>> {
608 render_audio(interpreter)
609}