1use std::collections::HashMap;
12use std::sync::{Arc, Mutex};
13use std::collections::VecDeque;
14
15pub const SAMPLE_RATE: f32 = 44100.0;
18pub const BUFFER_SIZE: usize = 512;
19pub const MAX_VOICES: usize = 64;
20pub const MAX_CHANNELS: usize = 2; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
25pub struct NodeId(pub u32);
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
28pub struct EdgeId(pub u32);
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
31pub struct VoiceId(pub u32);
32
33#[derive(Debug, Clone)]
37pub struct AudioBuffer {
38 pub samples: [f32; BUFFER_SIZE],
39 pub len: usize,
40}
41
42impl AudioBuffer {
43 pub fn new() -> Self {
44 Self { samples: [0.0; BUFFER_SIZE], len: BUFFER_SIZE }
45 }
46
47 pub fn silence() -> Self { Self::new() }
48
49 pub fn fill(&mut self, v: f32) {
50 self.samples[..self.len].fill(v);
51 }
52
53 pub fn add_from(&mut self, other: &AudioBuffer) {
54 for i in 0..self.len.min(other.len) {
55 self.samples[i] += other.samples[i];
56 }
57 }
58
59 pub fn multiply_by(&mut self, other: &AudioBuffer) {
60 for i in 0..self.len.min(other.len) {
61 self.samples[i] *= other.samples[i];
62 }
63 }
64
65 pub fn scale(&mut self, gain: f32) {
66 for s in self.samples[..self.len].iter_mut() { *s *= gain; }
67 }
68
69 pub fn mix(&mut self, other: &AudioBuffer, weight: f32) {
70 for i in 0..self.len.min(other.len) {
71 self.samples[i] = self.samples[i] * (1.0 - weight) + other.samples[i] * weight;
72 }
73 }
74
75 pub fn peak(&self) -> f32 {
76 self.samples[..self.len].iter().map(|s| s.abs()).fold(0.0_f32, f32::max)
77 }
78
79 pub fn rms(&self) -> f32 {
80 if self.len == 0 { return 0.0; }
81 let sum: f32 = self.samples[..self.len].iter().map(|s| s * s).sum();
82 (sum / self.len as f32).sqrt()
83 }
84}
85
86impl Default for AudioBuffer { fn default() -> Self { Self::new() } }
87
88#[derive(Debug, Clone, Default)]
90pub struct StereoBuffer {
91 pub left: AudioBuffer,
92 pub right: AudioBuffer,
93}
94
95impl StereoBuffer {
96 pub fn new() -> Self { Self { left: AudioBuffer::new(), right: AudioBuffer::new() } }
97
98 pub fn silence() -> Self { Self::new() }
99
100 pub fn peak(&self) -> f32 { self.left.peak().max(self.right.peak()) }
101
102 pub fn scale(&mut self, gain: f32) {
103 self.left.scale(gain);
104 self.right.scale(gain);
105 }
106}
107
108#[derive(Debug, Clone, Copy, PartialEq)]
111pub enum Waveform {
112 Sine,
113 Square,
114 Triangle,
115 Sawtooth,
116 ReverseSawtooth,
117 Pulse(f32), Noise,
119 SineSquared,
120}
121
122impl Waveform {
123 pub fn sample(self, phase: f32) -> f32 {
124 let t = phase.fract();
125 match self {
126 Waveform::Sine => (t * std::f32::consts::TAU).sin(),
127 Waveform::Square => if t < 0.5 { 1.0 } else { -1.0 },
128 Waveform::Triangle => 1.0 - 4.0 * (t - 0.5).abs(),
129 Waveform::Sawtooth => 2.0 * t - 1.0,
130 Waveform::ReverseSawtooth => 1.0 - 2.0 * t,
131 Waveform::Pulse(pw) => if t < pw { 1.0 } else { -1.0 },
132 Waveform::Noise => pseudo_rand(phase) * 2.0 - 1.0,
133 Waveform::SineSquared => {
134 let s = (t * std::f32::consts::TAU).sin();
135 s * s.abs()
136 }
137 }
138 }
139}
140
141fn pseudo_rand(seed: f32) -> f32 {
142 let x = (seed * 127.1 + 311.7).sin() * 43758.547;
143 x - x.floor()
144}
145
146#[derive(Debug, Clone, Copy, PartialEq)]
149pub enum FilterType {
150 LowPass,
151 HighPass,
152 BandPass,
153 Notch,
154 AllPass,
155 LowShelf,
156 HighShelf,
157 Peaking,
158}
159
160#[derive(Debug, Clone, Default)]
162pub struct BiquadFilter {
163 b0: f32, b1: f32, b2: f32, a1: f32, a2: f32,
165 x1: f32, x2: f32, y1: f32, y2: f32,
167}
168
169impl BiquadFilter {
170 pub fn configure(&mut self, filter_type: FilterType, freq: f32, q: f32, gain_db: f32) {
171 let omega = std::f32::consts::TAU * freq / SAMPLE_RATE;
172 let sin_w = omega.sin();
173 let cos_w = omega.cos();
174 let alpha = sin_w / (2.0 * q.max(0.001));
175 let a_lin = 10.0_f32.powf(gain_db / 40.0);
176
177 let (b0, b1, b2, a0, a1, a2) = match filter_type {
178 FilterType::LowPass => {
179 ((1.0 - cos_w) / 2.0, 1.0 - cos_w, (1.0 - cos_w) / 2.0,
180 1.0 + alpha, -2.0 * cos_w, 1.0 - alpha)
181 }
182 FilterType::HighPass => {
183 ((1.0 + cos_w) / 2.0, -(1.0 + cos_w), (1.0 + cos_w) / 2.0,
184 1.0 + alpha, -2.0 * cos_w, 1.0 - alpha)
185 }
186 FilterType::BandPass => {
187 (alpha, 0.0, -alpha,
188 1.0 + alpha, -2.0 * cos_w, 1.0 - alpha)
189 }
190 FilterType::Notch => {
191 (1.0, -2.0 * cos_w, 1.0,
192 1.0 + alpha, -2.0 * cos_w, 1.0 - alpha)
193 }
194 FilterType::AllPass => {
195 (1.0 - alpha, -2.0 * cos_w, 1.0 + alpha,
196 1.0 + alpha, -2.0 * cos_w, 1.0 - alpha)
197 }
198 FilterType::LowShelf => {
199 let root_a = a_lin.sqrt();
200 let beta = (a_lin.sqrt()) * alpha;
201 (a_lin * ((a_lin + 1.0) - (a_lin - 1.0) * cos_w + 2.0 * beta),
202 2.0 * a_lin * ((a_lin - 1.0) - (a_lin + 1.0) * cos_w),
203 a_lin * ((a_lin + 1.0) - (a_lin - 1.0) * cos_w - 2.0 * beta),
204 (a_lin + 1.0) + (a_lin - 1.0) * cos_w + 2.0 * root_a * alpha,
205 -2.0 * ((a_lin - 1.0) + (a_lin + 1.0) * cos_w),
206 (a_lin + 1.0) + (a_lin - 1.0) * cos_w - 2.0 * root_a * alpha)
207 }
208 FilterType::HighShelf => {
209 let root_a = a_lin.sqrt();
210 let beta = root_a * alpha;
211 (a_lin * ((a_lin + 1.0) + (a_lin - 1.0) * cos_w + 2.0 * beta),
212 -2.0 * a_lin * ((a_lin - 1.0) + (a_lin + 1.0) * cos_w),
213 a_lin * ((a_lin + 1.0) + (a_lin - 1.0) * cos_w - 2.0 * beta),
214 (a_lin + 1.0) - (a_lin - 1.0) * cos_w + 2.0 * root_a * alpha,
215 2.0 * ((a_lin - 1.0) - (a_lin + 1.0) * cos_w),
216 (a_lin + 1.0) - (a_lin - 1.0) * cos_w - 2.0 * root_a * alpha)
217 }
218 FilterType::Peaking => {
219 (1.0 + alpha * a_lin, -2.0 * cos_w, 1.0 - alpha * a_lin,
220 1.0 + alpha / a_lin, -2.0 * cos_w, 1.0 - alpha / a_lin)
221 }
222 };
223
224 let inv_a0 = 1.0 / a0;
225 self.b0 = b0 * inv_a0;
226 self.b1 = b1 * inv_a0;
227 self.b2 = b2 * inv_a0;
228 self.a1 = a1 * inv_a0;
229 self.a2 = a2 * inv_a0;
230 }
231
232 pub fn process_sample(&mut self, x: f32) -> f32 {
233 let y = self.b0 * x + self.b1 * self.x1 + self.b2 * self.x2
234 - self.a1 * self.y1 - self.a2 * self.y2;
235 self.x2 = self.x1; self.x1 = x;
236 self.y2 = self.y1; self.y1 = y;
237 y
238 }
239
240 pub fn process_buffer(&mut self, buf: &mut AudioBuffer) {
241 for s in buf.samples[..buf.len].iter_mut() {
242 *s = self.process_sample(*s);
243 }
244 }
245
246 pub fn reset(&mut self) {
247 self.x1 = 0.0; self.x2 = 0.0; self.y1 = 0.0; self.y2 = 0.0;
248 }
249}
250
251#[derive(Debug, Clone, Copy, PartialEq)]
254pub enum EnvelopeStage { Idle, Attack, Decay, Sustain, Release }
255
256#[derive(Debug, Clone)]
257pub struct AdsrEnvelope {
258 pub attack_time: f32,
259 pub decay_time: f32,
260 pub sustain_level: f32,
261 pub release_time: f32,
262 pub attack_curve: f32, pub release_curve: f32,
264 stage: EnvelopeStage,
265 level: f32,
266 stage_t: f32,
267 sample_rate: f32,
268}
269
270impl AdsrEnvelope {
271 pub fn new(attack: f32, decay: f32, sustain: f32, release: f32) -> Self {
272 Self {
273 attack_time: attack,
274 decay_time: decay,
275 sustain_level: sustain,
276 release_time: release,
277 attack_curve: 1.0,
278 release_curve: 1.0,
279 stage: EnvelopeStage::Idle,
280 level: 0.0,
281 stage_t: 0.0,
282 sample_rate: SAMPLE_RATE,
283 }
284 }
285
286 pub fn note_on(&mut self) {
287 self.stage = EnvelopeStage::Attack;
288 self.stage_t = 0.0;
289 }
290
291 pub fn note_off(&mut self) {
292 if self.stage != EnvelopeStage::Idle {
293 self.stage = EnvelopeStage::Release;
294 self.stage_t = 0.0;
295 }
296 }
297
298 pub fn reset(&mut self) {
299 self.stage = EnvelopeStage::Idle;
300 self.level = 0.0;
301 self.stage_t = 0.0;
302 }
303
304 pub fn is_done(&self) -> bool { self.stage == EnvelopeStage::Idle }
305
306 pub fn next_sample(&mut self) -> f32 {
307 let dt = 1.0 / self.sample_rate;
308 self.stage_t += dt;
309 self.level = match self.stage {
310 EnvelopeStage::Idle => 0.0,
311 EnvelopeStage::Attack => {
312 let t = (self.stage_t / self.attack_time.max(1e-6)).clamp(0.0, 1.0);
313 let v = t.powf(self.attack_curve);
314 if t >= 1.0 { self.stage = EnvelopeStage::Decay; self.stage_t = 0.0; }
315 v
316 }
317 EnvelopeStage::Decay => {
318 let t = (self.stage_t / self.decay_time.max(1e-6)).clamp(0.0, 1.0);
319 let v = 1.0 - (1.0 - self.sustain_level) * t;
320 if t >= 1.0 { self.stage = EnvelopeStage::Sustain; }
321 v
322 }
323 EnvelopeStage::Sustain => self.sustain_level,
324 EnvelopeStage::Release => {
325 let t = (self.stage_t / self.release_time.max(1e-6)).clamp(0.0, 1.0);
326 let v = self.sustain_level * (1.0 - t.powf(self.release_curve));
327 if t >= 1.0 { self.stage = EnvelopeStage::Idle; self.level = 0.0; return 0.0; }
328 v
329 }
330 };
331 self.level
332 }
333
334 pub fn fill_buffer(&mut self, buf: &mut AudioBuffer) {
335 for s in buf.samples[..buf.len].iter_mut() {
336 *s = self.next_sample();
337 }
338 }
339}
340
341#[derive(Debug, Clone)]
344pub struct Lfo {
345 pub waveform: Waveform,
346 pub frequency: f32,
347 pub amplitude: f32,
348 pub offset: f32,
349 pub phase: f32,
350 pub sync_to_tempo: bool,
351 pub bpm: f32,
352 pub beat_div: f32, }
354
355impl Lfo {
356 pub fn new(waveform: Waveform, freq: f32) -> Self {
357 Self {
358 waveform, frequency: freq, amplitude: 1.0, offset: 0.0,
359 phase: 0.0, sync_to_tempo: false, bpm: 120.0, beat_div: 1.0,
360 }
361 }
362
363 pub fn next_sample(&mut self) -> f32 {
364 let freq = if self.sync_to_tempo {
365 self.bpm / 60.0 / self.beat_div
366 } else { self.frequency };
367
368 let v = self.waveform.sample(self.phase);
369 self.phase += freq / (SAMPLE_RATE / BUFFER_SIZE as f32);
370 if self.phase >= 1.0 { self.phase -= 1.0; }
371 self.offset + v * self.amplitude
372 }
373
374 pub fn fill_buffer(&mut self, buf: &mut AudioBuffer) {
375 for s in buf.samples[..buf.len].iter_mut() {
376 *s = self.next_sample();
377 }
378 }
379}
380
381#[derive(Debug, Clone)]
384pub struct DelayLine {
385 buffer: Vec<f32>,
386 write_pos: usize,
387 delay_samples: usize,
388}
389
390impl DelayLine {
391 pub fn new(max_delay_samples: usize) -> Self {
392 Self { buffer: vec![0.0; max_delay_samples], write_pos: 0, delay_samples: max_delay_samples / 2 }
393 }
394
395 pub fn set_delay_time(&mut self, secs: f32) {
396 self.delay_samples = ((secs * SAMPLE_RATE) as usize).min(self.buffer.len() - 1);
397 }
398
399 pub fn process(&mut self, input: f32) -> f32 {
400 let read_pos = (self.write_pos + self.buffer.len() - self.delay_samples) % self.buffer.len();
401 let out = self.buffer[read_pos];
402 self.buffer[self.write_pos] = input;
403 self.write_pos = (self.write_pos + 1) % self.buffer.len();
404 out
405 }
406
407 pub fn process_buffer(&mut self, input: &AudioBuffer, feedback: f32) -> AudioBuffer {
408 let mut out = AudioBuffer::new();
409 for i in 0..input.len {
410 let delayed = self.process(input.samples[i]);
411 out.samples[i] = delayed;
412 let fb_pos = (self.write_pos + self.buffer.len() - 1) % self.buffer.len();
414 self.buffer[fb_pos] += delayed * feedback;
415 }
416 out.len = input.len;
417 out
418 }
419}
420
421#[derive(Debug, Clone)]
425pub struct Reverb {
426 combs: [DelayLine; 4],
427 allpass: [DelayLine; 2],
428 pub room_size: f32,
429 pub damping: f32,
430 pub wet: f32,
431 pub pre_delay: f32,
432 pre_delay_line: DelayLine,
433 damp_filters: [f32; 4], }
435
436impl Reverb {
437 pub fn new() -> Self {
438 let sizes = [157, 161, 149, 142];
439 let combs: [DelayLine; 4] = [
440 DelayLine::new(sizes[0]), DelayLine::new(sizes[1]),
441 DelayLine::new(sizes[2]), DelayLine::new(sizes[3]),
442 ];
443 let allpass = [DelayLine::new(556), DelayLine::new(441)];
444 Self {
445 combs, allpass,
446 room_size: 0.5, damping: 0.5, wet: 0.3, pre_delay: 0.002,
447 pre_delay_line: DelayLine::new(4096),
448 damp_filters: [0.0; 4],
449 }
450 }
451
452 pub fn process_buffer(&mut self, input: &AudioBuffer) -> AudioBuffer {
453 let feedback = self.room_size * 0.28 + 0.7;
454 let damp = self.damping;
455
456 let mut out = AudioBuffer::new();
457 self.pre_delay_line.set_delay_time(self.pre_delay);
458
459 for i in 0..input.len {
460 let x = self.pre_delay_line.process(input.samples[i]);
461 let mut sum = 0.0_f32;
462
463 for (j, comb) in self.combs.iter_mut().enumerate() {
464 let delayed = comb.process(x + comb.buffer[comb.write_pos] * feedback);
465 self.damp_filters[j] = self.damp_filters[j] + (delayed - self.damp_filters[j]) * (1.0 - damp);
467 sum += self.damp_filters[j];
468 }
469
470 let mut ap_out = sum * 0.25;
472 for ap in self.allpass.iter_mut() {
473 ap_out = ap.process(ap_out + ap.buffer[ap.write_pos] * 0.5) - ap_out * 0.5;
474 }
475
476 out.samples[i] = input.samples[i] * (1.0 - self.wet) + ap_out * self.wet;
477 }
478 out.len = input.len;
479 out
480 }
481}
482
483impl Default for Reverb { fn default() -> Self { Self::new() } }
484
485#[derive(Debug, Clone)]
488pub struct Compressor {
489 pub threshold_db: f32,
490 pub ratio: f32,
491 pub attack_ms: f32,
492 pub release_ms: f32,
493 pub makeup_gain: f32,
494 pub knee_db: f32,
495 envelope: f32,
496}
497
498impl Compressor {
499 pub fn new(threshold_db: f32, ratio: f32) -> Self {
500 Self {
501 threshold_db, ratio,
502 attack_ms: 1.0, release_ms: 100.0, makeup_gain: 0.0, knee_db: 3.0,
503 envelope: 0.0,
504 }
505 }
506
507 pub fn process_sample(&mut self, x: f32) -> f32 {
508 let level_db = 20.0 * x.abs().max(1e-10).log10();
509 let over = level_db - self.threshold_db;
510 let gain_reduction = if over <= -self.knee_db / 2.0 {
511 0.0
512 } else if over <= self.knee_db / 2.0 {
513 let t = (over + self.knee_db / 2.0) / self.knee_db;
514 (1.0 / self.ratio - 1.0) * over * t * t / 2.0
515 } else {
516 (1.0 / self.ratio - 1.0) * over
517 };
518
519 let coeff = if gain_reduction < self.envelope {
521 1.0 - (-2.2 / (self.attack_ms * 0.001 * SAMPLE_RATE)).exp()
522 } else {
523 1.0 - (-2.2 / (self.release_ms * 0.001 * SAMPLE_RATE)).exp()
524 };
525 self.envelope += (gain_reduction - self.envelope) * coeff;
526
527 let gain_db = self.envelope + self.makeup_gain;
528 x * 10.0_f32.powf(gain_db / 20.0)
529 }
530
531 pub fn process_buffer(&mut self, buf: &mut AudioBuffer) {
532 for s in buf.samples[..buf.len].iter_mut() {
533 *s = self.process_sample(*s);
534 }
535 }
536}
537
538#[derive(Debug, Clone, Copy, PartialEq)]
541pub enum DistortionMode {
542 HardClip,
543 SoftClip,
544 Foldback,
545 Bitcrush(u32),
546 Waveshape,
547 Overdrive,
548}
549
550#[derive(Debug, Clone)]
551pub struct Distortion {
552 pub mode: DistortionMode,
553 pub drive: f32, pub mix: f32, }
556
557impl Distortion {
558 pub fn new(mode: DistortionMode, drive: f32) -> Self {
559 Self { mode, drive, mix: 1.0 }
560 }
561
562 pub fn process_sample(&self, x: f32) -> f32 {
563 let driven = x * self.drive;
564 let clipped = match self.mode {
565 DistortionMode::HardClip => driven.clamp(-1.0, 1.0),
566 DistortionMode::SoftClip => driven.tanh(),
567 DistortionMode::Foldback => {
568 let mut v = driven;
569 while v > 1.0 || v < -1.0 {
570 if v > 1.0 { v = 2.0 - v; }
571 if v < -1.0 { v = -2.0 - v; }
572 }
573 v
574 }
575 DistortionMode::Bitcrush(bits) => {
576 let steps = 2.0_f32.powi(bits as i32);
577 (driven * steps).round() / steps
578 }
579 DistortionMode::Waveshape => {
580 let k = 2.0;
581 driven * (k + 1.0) / (1.0 + k * driven.abs())
582 }
583 DistortionMode::Overdrive => {
584 let abs = driven.abs();
585 let sign = driven.signum();
586 sign * if abs < 1.0/3.0 { 2.0 * abs }
587 else if abs < 2.0/3.0 { (3.0 - (2.0 - 3.0*abs).powi(2)) / 3.0 }
588 else { 1.0 }
589 }
590 };
591 x * (1.0 - self.mix) + clipped * self.mix
592 }
593
594 pub fn process_buffer(&self, buf: &mut AudioBuffer) {
595 for s in buf.samples[..buf.len].iter_mut() {
596 *s = self.process_sample(*s);
597 }
598 }
599}
600
601#[derive(Debug, Clone)]
604pub struct Chorus {
605 delay_lines: Vec<DelayLine>,
606 lfos: Vec<Lfo>,
607 pub depth: f32,
608 pub rate: f32,
609 pub wet: f32,
610 pub voices: usize,
611}
612
613impl Chorus {
614 pub fn new(voices: usize) -> Self {
615 let delay_lines = (0..voices).map(|_| DelayLine::new(8192)).collect();
616 let lfos = (0..voices).map(|i| {
617 let mut lfo = Lfo::new(Waveform::Sine, 0.5 + i as f32 * 0.1);
618 lfo.phase = i as f32 / voices as f32;
619 lfo
620 }).collect();
621 Self { delay_lines, lfos, depth: 0.01, rate: 0.3, wet: 0.5, voices }
622 }
623
624 pub fn process_buffer(&mut self, input: &AudioBuffer) -> AudioBuffer {
625 let mut out = AudioBuffer::new();
626 for i in 0..input.len {
627 let mut sum = 0.0_f32;
628 for (dl, lfo) in self.delay_lines.iter_mut().zip(self.lfos.iter_mut()) {
629 let mod_t = lfo.next_sample() * self.depth + 0.01;
630 dl.set_delay_time(mod_t.max(0.0005));
631 sum += dl.process(input.samples[i]);
632 }
633 let wet = sum / self.voices as f32;
634 out.samples[i] = input.samples[i] * (1.0 - self.wet) + wet * self.wet;
635 }
636 out.len = input.len;
637 out
638 }
639}
640
641#[derive(Debug, Clone)]
645pub enum AudioNodeType {
646 Oscillator { waveform: Waveform, frequency: f32, detune_cents: f32 },
649 Noise { color: NoiseColor },
651 SamplePlayer { clip_id: u32, loop_start: f32, loop_end: f32, loop_mode: LoopMode },
653 Wavetable { table_id: u32, morph: f32, frequency: f32 },
655 KarplusStrong { frequency: f32, decay: f32, brightness: f32 },
657 FmOscillator { carrier_freq: f32, mod_freq: f32, mod_index: f32, waveform: Waveform },
659 Additive { fundamental: f32, partial_gains: Vec<f32> },
661
662 BiquadFilter { filter_type: FilterType, frequency: f32, q: f32, gain_db: f32 },
665 LadderFilter { cutoff: f32, resonance: f32, drive: f32 },
667 FormantFilter { vowel_a: f32, vowel_b: f32, blend: f32 },
669 CombFilter { delay_time: f32, feedback: f32, feedforward: f32 },
671
672 Adsr { attack: f32, decay: f32, sustain: f32, release: f32 },
675 MultiStageEnvelope { breakpoints: Vec<(f32, f32)>, loop_start: Option<usize> },
677 Lfo { waveform: Waveform, frequency: f32, amplitude: f32, phase: f32 },
679 SampleHold { rate: f32 },
681
682 Delay { delay_time: f32, feedback: f32, wet: f32 },
685 Reverb { room_size: f32, damping: f32, wet: f32 },
687 Chorus { voices: u32, rate: f32, depth: f32, wet: f32 },
689 Compressor { threshold_db: f32, ratio: f32, attack_ms: f32, release_ms: f32 },
691 Distortion { mode: DistortionMode, drive: f32 },
693 Panner { pan: f32 },
695 Gain { gain_db: f32 },
697 Limiter { ceiling_db: f32, release_ms: f32 },
699 StereoWidener { width: f32 },
701 BitCrusher { bits: u32, downsample: u32 },
703 PitchShift { semitones: f32 },
705 RingModulator { carrier_freq: f32, mix: f32 },
707 AutoWah { sensitivity: f32, speed: f32, min_freq: f32, max_freq: f32 },
709
710 Mixer { gains: Vec<f32> },
713 Crossfade { blend: f32 },
715 Constant { value: f32 },
717 Meter,
719 MathOp { op: AudioMathOp },
721 Output { volume: f32 },
723}
724
725#[derive(Debug, Clone, Copy, PartialEq)]
726pub enum NoiseColor { White, Pink, Brown }
727
728#[derive(Debug, Clone, Copy, PartialEq)]
729pub enum LoopMode { None, Forward, PingPong, BackwardLoop }
730
731#[derive(Debug, Clone, Copy, PartialEq)]
732pub enum AudioMathOp { Add, Subtract, Multiply, Abs, Invert, Clamp, Min, Max }
733
734#[derive(Debug, Clone)]
738pub struct AudioNode {
739 pub id: NodeId,
740 pub node_type: AudioNodeType,
741 pub label: String,
742 pub enabled: bool,
743 pub x: f32, pub y: f32,
745 pub phase: f32,
747 pub filter: BiquadFilter,
748 pub envelope: AdsrEnvelope,
749 pub lfo_state: Lfo,
750 pub delay_line: DelayLine,
751 pub reverb: Reverb,
752 pub chorus: Chorus,
753 pub compressor: Compressor,
754 ks_buffer: Vec<f32>,
756 ks_pos: usize,
757 lf_stages: [f32; 4],
759 lf_freq: f32,
760 ms_env_stage: usize,
762 ms_env_t: f32,
763 sh_value: f32,
765 sh_timer: f32,
766 formant_filters: [BiquadFilter; 3],
768 pub last_peak: f32,
770 pub last_rms: f32,
771}
772
773impl AudioNode {
774 pub fn new(id: NodeId, node_type: AudioNodeType) -> Self {
775 let label = format!("{:?}", node_type).split('{').next().unwrap_or("Node").trim().to_string();
776 Self {
777 id, node_type, label, enabled: true,
778 x: 0.0, y: 0.0,
779 phase: 0.0,
780 filter: BiquadFilter::default(),
781 envelope: AdsrEnvelope::new(0.01, 0.1, 0.8, 0.3),
782 lfo_state: Lfo::new(Waveform::Sine, 1.0),
783 delay_line: DelayLine::new(SAMPLE_RATE as usize * 2),
784 reverb: Reverb::new(),
785 chorus: Chorus::new(3),
786 compressor: Compressor::new(-12.0, 4.0),
787 ks_buffer: vec![0.0; 4096],
788 ks_pos: 0,
789 lf_stages: [0.0; 4],
790 lf_freq: 1000.0,
791 ms_env_stage: 0,
792 ms_env_t: 0.0,
793 sh_value: 0.0,
794 sh_timer: 0.0,
795 formant_filters: Default::default(),
796 last_peak: 0.0,
797 last_rms: 0.0,
798 }
799 }
800
801 pub fn initialize(&mut self) {
803 match &self.node_type.clone() {
804 AudioNodeType::BiquadFilter { filter_type, frequency, q, gain_db } => {
805 self.filter.configure(*filter_type, *frequency, *q, *gain_db);
806 }
807 AudioNodeType::Adsr { attack, decay, sustain, release } => {
808 self.envelope = AdsrEnvelope::new(*attack, *decay, *sustain, *release);
809 }
810 AudioNodeType::Lfo { waveform, frequency, amplitude, phase } => {
811 self.lfo_state = Lfo::new(*waveform, *frequency);
812 self.lfo_state.amplitude = *amplitude;
813 self.lfo_state.phase = *phase;
814 }
815 AudioNodeType::Delay { delay_time, .. } => {
816 self.delay_line.set_delay_time(*delay_time);
817 }
818 AudioNodeType::KarplusStrong { frequency, .. } => {
819 let period = (SAMPLE_RATE / frequency.max(1.0)) as usize;
820 let period = period.min(4095);
821 for i in 0..period {
823 self.ks_buffer[i] = pseudo_rand(i as f32) * 2.0 - 1.0;
824 }
825 self.ks_pos = 0;
826 }
827 AudioNodeType::Reverb { room_size, damping, wet } => {
828 self.reverb.room_size = *room_size;
829 self.reverb.damping = *damping;
830 self.reverb.wet = *wet;
831 }
832 AudioNodeType::Chorus { voices, rate, depth, wet } => {
833 self.chorus = Chorus::new(*voices as usize);
834 self.chorus.rate = *rate;
835 self.chorus.depth = *depth;
836 self.chorus.wet = *wet;
837 }
838 AudioNodeType::Compressor { threshold_db, ratio, attack_ms, release_ms } => {
839 self.compressor = Compressor::new(*threshold_db, *ratio);
840 self.compressor.attack_ms = *attack_ms;
841 self.compressor.release_ms = *release_ms;
842 }
843 _ => {}
844 }
845 }
846
847 pub fn process(&mut self, inputs: &[&AudioBuffer]) -> AudioBuffer {
849 if !self.enabled {
850 return inputs.first().copied().cloned().unwrap_or_default();
851 }
852
853 let input = inputs.first().copied().cloned().unwrap_or_else(AudioBuffer::silence);
854
855 let result = match &self.node_type.clone() {
856 AudioNodeType::Oscillator { waveform, frequency, detune_cents } => {
858 let freq = *frequency * 2.0_f32.powf(*detune_cents / 1200.0);
859 let phase_inc = freq / SAMPLE_RATE;
860 let mut out = AudioBuffer::new();
861 for s in out.samples[..out.len].iter_mut() {
862 *s = waveform.sample(self.phase);
863 self.phase += phase_inc;
864 if self.phase >= 1.0 { self.phase -= 1.0; }
865 }
866 out
867 }
868
869 AudioNodeType::Noise { color } => {
870 let mut out = AudioBuffer::new();
871 let mut b0 = 0.0_f32;
872 let mut b1 = 0.0_f32;
873 for (i, s) in out.samples[..out.len].iter_mut().enumerate() {
874 let white = pseudo_rand(self.phase + i as f32 * 0.1) * 2.0 - 1.0;
875 *s = match color {
876 NoiseColor::White => white,
877 NoiseColor::Pink => {
878 b0 = 0.99886 * b0 + white * 0.0555179;
879 b1 = 0.99332 * b1 + white * 0.0750759;
880 (b0 + b1 + white * 0.5) / 2.5
881 }
882 NoiseColor::Brown => {
883 b0 = (b0 + 0.02 * white) / 1.02;
884 b0 * 3.5
885 }
886 };
887 }
888 self.phase += 1.0;
889 out
890 }
891
892 AudioNodeType::KarplusStrong { frequency, decay, brightness } => {
893 let period = (SAMPLE_RATE / frequency.max(1.0)) as usize;
894 let period = period.clamp(2, self.ks_buffer.len());
895 let mut out = AudioBuffer::new();
896 for s in out.samples[..out.len].iter_mut() {
897 let i0 = self.ks_pos;
898 let i1 = (self.ks_pos + 1) % period;
899 let avg = (self.ks_buffer[i0] + self.ks_buffer[i1]) * 0.5 * decay;
901 let bright_mix = *brightness;
902 self.ks_buffer[i0] = avg * (1.0 - bright_mix) + self.ks_buffer[i0] * bright_mix;
903 *s = self.ks_buffer[i0];
904 self.ks_pos = (self.ks_pos + 1) % period;
905 }
906 out
907 }
908
909 AudioNodeType::FmOscillator { carrier_freq, mod_freq, mod_index, waveform } => {
910 let mut out = AudioBuffer::new();
911 let mut mod_phase = self.phase;
912 let mut car_phase = self.phase + 0.5;
913 for s in out.samples[..out.len].iter_mut() {
914 let modulator = waveform.sample(mod_phase);
915 let car_freq_mod = *carrier_freq + modulator * mod_index * mod_freq;
916 *s = Waveform::Sine.sample(car_phase);
917 mod_phase += mod_freq / SAMPLE_RATE;
918 car_phase += car_freq_mod / SAMPLE_RATE;
919 if mod_phase >= 1.0 { mod_phase -= 1.0; }
920 if car_phase >= 1.0 { car_phase -= 1.0; }
921 }
922 self.phase = mod_phase;
923 out
924 }
925
926 AudioNodeType::Additive { fundamental, partial_gains } => {
927 let mut out = AudioBuffer::new();
928 for (i, s) in out.samples[..out.len].iter_mut().enumerate() {
929 let mut v = 0.0_f32;
930 for (k, &gain) in partial_gains.iter().enumerate() {
931 let partial = (k + 1) as f32;
932 let phase = (self.phase * partial).fract();
933 v += Waveform::Sine.sample(phase) * gain;
934 }
935 *s = v;
936 let _ = i;
937 }
938 self.phase += fundamental / SAMPLE_RATE;
939 if self.phase >= 1.0 { self.phase -= 1.0; }
940 out
941 }
942
943 AudioNodeType::Constant { value } => {
944 let mut out = AudioBuffer::new();
945 out.fill(*value);
946 out
947 }
948
949 AudioNodeType::BiquadFilter { .. } => {
951 let mut out = input.clone();
952 self.filter.process_buffer(&mut out);
953 out
954 }
955
956 AudioNodeType::LadderFilter { cutoff, resonance, drive } => {
957 let mut out = input.clone();
958 let f = (std::f32::consts::PI * cutoff / SAMPLE_RATE).tan().clamp(0.0, 0.99);
959 let r = resonance * 4.0;
960 for s in out.samples[..out.len].iter_mut() {
961 let x = *s * drive - r * self.lf_stages[3];
962 let (mut a, mut b, mut c, mut d) = (self.lf_stages[0], self.lf_stages[1], self.lf_stages[2], self.lf_stages[3]);
963 a = a + f * (x.tanh() - a.tanh());
964 b = b + f * (a.tanh() - b.tanh());
965 c = c + f * (b.tanh() - c.tanh());
966 d = d + f * (c.tanh() - d.tanh());
967 self.lf_stages = [a, b, c, d];
968 *s = d;
969 }
970 out
971 }
972
973 AudioNodeType::CombFilter { delay_time, feedback, feedforward } => {
974 self.delay_line.set_delay_time(*delay_time);
975 let mut out = AudioBuffer::new();
976 for i in 0..input.len {
977 let delayed = self.delay_line.process(input.samples[i]);
978 out.samples[i] = input.samples[i] * feedforward + delayed * feedback;
979 }
980 out.len = input.len;
981 out
982 }
983
984 AudioNodeType::Adsr { .. } => {
986 let mut out = AudioBuffer::new();
987 self.envelope.fill_buffer(&mut out);
988 if !inputs.is_empty() {
990 out.multiply_by(&input);
991 }
992 out
993 }
994
995 AudioNodeType::Lfo { .. } => {
996 let mut out = AudioBuffer::new();
997 self.lfo_state.fill_buffer(&mut out);
998 out
999 }
1000
1001 AudioNodeType::SampleHold { rate } => {
1002 let trigger_period = 1.0 / rate.max(0.001);
1003 let mut out = AudioBuffer::new();
1004 for i in 0..input.len {
1005 self.sh_timer += 1.0 / SAMPLE_RATE;
1006 if self.sh_timer >= trigger_period {
1007 self.sh_value = input.samples[i];
1008 self.sh_timer = 0.0;
1009 }
1010 out.samples[i] = self.sh_value;
1011 }
1012 out.len = input.len;
1013 out
1014 }
1015
1016 AudioNodeType::Delay { feedback, wet, .. } => {
1018 let delayed = self.delay_line.process_buffer(&input, *feedback);
1019 let mut out = input.clone();
1020 out.mix(&delayed, *wet);
1021 out
1022 }
1023
1024 AudioNodeType::Reverb { .. } => {
1025 self.reverb.process_buffer(&input)
1026 }
1027
1028 AudioNodeType::Chorus { .. } => {
1029 self.chorus.process_buffer(&input)
1030 }
1031
1032 AudioNodeType::Compressor { .. } => {
1033 let mut out = input.clone();
1034 self.compressor.process_buffer(&mut out);
1035 out
1036 }
1037
1038 AudioNodeType::Distortion { mode, drive } => {
1039 let dist = Distortion::new(*mode, *drive);
1040 let mut out = input.clone();
1041 dist.process_buffer(&mut out);
1042 out
1043 }
1044
1045 AudioNodeType::Gain { gain_db } => {
1046 let linear = 10.0_f32.powf(*gain_db / 20.0);
1047 let mut out = input.clone();
1048 out.scale(linear);
1049 out
1050 }
1051
1052 AudioNodeType::Limiter { ceiling_db, release_ms } => {
1053 let ceiling = 10.0_f32.powf(*ceiling_db / 20.0);
1054 let release_coeff = 1.0 - (-2.2 / (release_ms * 0.001 * SAMPLE_RATE)).exp();
1055 let mut out = input.clone();
1056 let mut env = 0.0_f32;
1057 for s in out.samples[..out.len].iter_mut() {
1058 let abs = s.abs();
1059 if abs > env { env = abs; }
1060 else { env += (0.0 - env) * release_coeff; }
1061 if env > ceiling { *s *= ceiling / env; }
1062 }
1063 out
1064 }
1065
1066 AudioNodeType::RingModulator { carrier_freq, mix } => {
1067 let mut out = AudioBuffer::new();
1068 let phase_inc = carrier_freq / SAMPLE_RATE;
1069 for i in 0..input.len {
1070 let ring = Waveform::Sine.sample(self.phase);
1071 self.phase += phase_inc;
1072 if self.phase >= 1.0 { self.phase -= 1.0; }
1073 out.samples[i] = input.samples[i] * (1.0 - mix) + input.samples[i] * ring * mix;
1074 }
1075 out.len = input.len;
1076 out
1077 }
1078
1079 AudioNodeType::Panner { pan } => {
1080 let _ = pan;
1082 input.clone()
1083 }
1084
1085 AudioNodeType::Mixer { gains } => {
1087 let mut out = AudioBuffer::silence();
1088 for (i, &gain) in gains.iter().enumerate() {
1089 if i < inputs.len() {
1090 let mut buf = inputs[i].clone();
1091 buf.scale(gain);
1092 out.add_from(&buf);
1093 }
1094 }
1095 out
1096 }
1097
1098 AudioNodeType::Crossfade { blend } => {
1099 if inputs.len() >= 2 {
1100 let mut out = inputs[0].clone();
1101 out.mix(inputs[1], *blend);
1102 out
1103 } else { input.clone() }
1104 }
1105
1106 AudioNodeType::MathOp { op } => {
1107 let a = &input;
1108 let b = inputs.get(1).copied().cloned().unwrap_or_else(AudioBuffer::silence);
1109 let mut out = AudioBuffer::new();
1110 for i in 0..out.len {
1111 out.samples[i] = match op {
1112 AudioMathOp::Add => a.samples[i] + b.samples[i],
1113 AudioMathOp::Subtract => a.samples[i] - b.samples[i],
1114 AudioMathOp::Multiply => a.samples[i] * b.samples[i],
1115 AudioMathOp::Abs => a.samples[i].abs(),
1116 AudioMathOp::Invert => -a.samples[i],
1117 AudioMathOp::Clamp => a.samples[i].clamp(-1.0, 1.0),
1118 AudioMathOp::Min => a.samples[i].min(b.samples[i]),
1119 AudioMathOp::Max => a.samples[i].max(b.samples[i]),
1120 };
1121 }
1122 out
1123 }
1124
1125 AudioNodeType::Meter => {
1126 self.last_peak = input.peak();
1127 self.last_rms = input.rms();
1128 input.clone()
1129 }
1130
1131 AudioNodeType::Output { volume } => {
1132 let mut out = input.clone();
1133 out.scale(*volume);
1134 out
1135 }
1136
1137 _ => input.clone(),
1139 };
1140
1141 result
1142 }
1143
1144 pub fn note_on(&mut self, _freq: f32, _velocity: f32) {
1146 self.envelope.note_on();
1147 match &self.node_type {
1148 AudioNodeType::KarplusStrong { frequency, .. } => {
1149 let period = (SAMPLE_RATE / frequency.max(1.0)) as usize;
1150 let period = period.clamp(2, self.ks_buffer.len());
1151 for i in 0..period {
1152 self.ks_buffer[i] = pseudo_rand(self.phase + i as f32) * 2.0 - 1.0;
1153 }
1154 self.ks_pos = 0;
1155 }
1156 _ => {}
1157 }
1158 }
1159
1160 pub fn note_off(&mut self) {
1161 self.envelope.note_off();
1162 }
1163}
1164
1165#[derive(Debug, Clone)]
1168pub struct AudioEdge {
1169 pub id: EdgeId,
1170 pub from: NodeId,
1171 pub to: NodeId,
1172 pub from_slot: u8,
1173 pub to_slot: u8,
1174 pub gain: f32,
1175}
1176
1177#[derive(Debug, Clone, Copy)]
1180pub struct AutomationPoint {
1181 pub sample: usize,
1183 pub value: f32,
1184}
1185
1186#[derive(Debug, Clone)]
1188pub struct AutomationLane {
1189 pub node_id: NodeId,
1190 pub param_name: String,
1191 pub points: Vec<AutomationPoint>,
1192 pub sorted: bool,
1193}
1194
1195impl AutomationLane {
1196 pub fn new(node_id: NodeId, param_name: &str) -> Self {
1197 Self { node_id, param_name: param_name.to_string(), points: Vec::new(), sorted: true }
1198 }
1199
1200 pub fn add_point(&mut self, sample: usize, value: f32) {
1201 self.points.push(AutomationPoint { sample, value });
1202 self.sorted = false;
1203 }
1204
1205 pub fn sort(&mut self) {
1206 if !self.sorted {
1207 self.points.sort_by_key(|p| p.sample);
1208 self.sorted = true;
1209 }
1210 }
1211
1212 pub fn sample_at(&mut self, pos: usize) -> Option<f32> {
1214 self.sort();
1215 if self.points.is_empty() { return None; }
1216 let idx = self.points.partition_point(|p| p.sample <= pos);
1217 if idx == 0 { return Some(self.points[0].value); }
1218 if idx >= self.points.len() { return Some(self.points.last().unwrap().value); }
1219 let a = &self.points[idx - 1];
1220 let b = &self.points[idx];
1221 let t = if b.sample > a.sample {
1222 (pos - a.sample) as f32 / (b.sample - a.sample) as f32
1223 } else { 0.0 };
1224 Some(a.value * (1.0 - t) + b.value * t)
1225 }
1226}
1227
1228#[derive(Debug, Clone)]
1231pub struct Voice {
1232 pub id: VoiceId,
1233 pub note: u8, pub velocity: f32,
1235 pub frequency: f32,
1236 pub active: bool,
1237 pub steal_priority: f32, nodes: HashMap<NodeId, AudioNode>, age: u64,
1240}
1241
1242impl Voice {
1243 fn new(id: VoiceId, template: &HashMap<NodeId, AudioNode>) -> Self {
1244 let nodes = template.iter().map(|(&k, v)| (k, v.clone())).collect();
1245 Self {
1246 id, note: 69, velocity: 1.0, frequency: 440.0,
1247 active: false, steal_priority: 0.0, age: 0,
1248 nodes,
1249 }
1250 }
1251
1252 fn note_on(&mut self, note: u8, velocity: f32) {
1253 self.note = note;
1254 self.velocity = velocity;
1255 self.frequency = midi_to_freq(note);
1256 self.active = true;
1257 for n in self.nodes.values_mut() {
1258 n.note_on(self.frequency, velocity);
1259 }
1260 }
1261
1262 fn note_off(&mut self) {
1263 for n in self.nodes.values_mut() {
1264 n.note_off();
1265 }
1266 }
1267
1268 fn is_silent(&self) -> bool {
1269 self.nodes.values().all(|n| n.envelope.is_done())
1270 }
1271}
1272
1273fn midi_to_freq(note: u8) -> f32 {
1274 440.0 * 2.0_f32.powf((note as f32 - 69.0) / 12.0)
1275}
1276
1277#[derive(Debug, Clone)]
1280pub enum AudioCommand {
1281 NoteOn { note: u8, velocity: f32 },
1282 NoteOff { note: u8 },
1283 AllNotesOff,
1284 SetParam { node_id: NodeId, param: String, value: f32 },
1285 SetVolume { volume: f32 },
1286 SetBpm { bpm: f32 },
1287 Mute { node_id: NodeId },
1288 Unmute { node_id: NodeId },
1289}
1290
1291pub struct AudioGraph {
1295 pub nodes: HashMap<NodeId, AudioNode>,
1296 pub edges: Vec<AudioEdge>,
1297 pub output_node: Option<NodeId>,
1298 pub automation: Vec<AutomationLane>,
1299 topo_order: Vec<NodeId>,
1300 topo_dirty: bool,
1301 next_node_id: u32,
1302 next_edge_id: u32,
1303
1304 voices: Vec<Voice>,
1306 voice_counter: u64,
1307 pub polyphony: usize,
1308
1309 pub master_volume: f32,
1311 pub bpm: f32,
1312
1313 pub commands: Arc<Mutex<VecDeque<AudioCommand>>>,
1315
1316 pub output_buffer: StereoBuffer,
1318}
1319
1320impl AudioGraph {
1321 pub fn new() -> Self {
1322 Self {
1323 nodes: HashMap::new(),
1324 edges: Vec::new(),
1325 output_node: None,
1326 automation: Vec::new(),
1327 topo_order: Vec::new(),
1328 topo_dirty: true,
1329 next_node_id: 1,
1330 next_edge_id: 1,
1331 voices: Vec::new(),
1332 voice_counter: 0,
1333 polyphony: 16,
1334 master_volume: 1.0,
1335 bpm: 120.0,
1336 commands: Arc::new(Mutex::new(VecDeque::new())),
1337 output_buffer: StereoBuffer::new(),
1338 }
1339 }
1340
1341 pub fn add_node(&mut self, node_type: AudioNodeType) -> NodeId {
1342 let id = NodeId(self.next_node_id);
1343 self.next_node_id += 1;
1344 let mut node = AudioNode::new(id, node_type);
1345 node.initialize();
1346 self.nodes.insert(id, node);
1347 self.topo_dirty = true;
1348 id
1349 }
1350
1351 pub fn connect(&mut self, from: NodeId, from_slot: u8, to: NodeId, to_slot: u8) -> EdgeId {
1352 let id = EdgeId(self.next_edge_id);
1353 self.next_edge_id += 1;
1354 self.edges.push(AudioEdge { id, from, to, from_slot, to_slot, gain: 1.0 });
1355 self.topo_dirty = true;
1356 id
1357 }
1358
1359 pub fn disconnect(&mut self, edge_id: EdgeId) {
1360 self.edges.retain(|e| e.id != edge_id);
1361 self.topo_dirty = true;
1362 }
1363
1364 pub fn set_output(&mut self, node_id: NodeId) {
1365 self.output_node = Some(node_id);
1366 }
1367
1368 pub fn add_automation(&mut self, lane: AutomationLane) {
1369 self.automation.push(lane);
1370 }
1371
1372 pub fn send_command(&self, cmd: AudioCommand) {
1374 if let Ok(mut q) = self.commands.lock() {
1375 q.push_back(cmd);
1376 }
1377 }
1378
1379 pub fn note_on(&self, note: u8, velocity: f32) {
1380 self.send_command(AudioCommand::NoteOn { note, velocity });
1381 }
1382
1383 pub fn note_off(&self, note: u8) {
1384 self.send_command(AudioCommand::NoteOff { note });
1385 }
1386
1387 pub fn set_param(&self, node_id: NodeId, param: &str, value: f32) {
1388 self.send_command(AudioCommand::SetParam {
1389 node_id, param: param.to_string(), value,
1390 });
1391 }
1392
1393 pub fn sort_topologically(&mut self) -> Result<(), String> {
1395 use std::collections::VecDeque;
1396 let mut in_degree: HashMap<NodeId, usize> = self.nodes.keys().map(|&id| (id, 0)).collect();
1397 for edge in &self.edges {
1398 *in_degree.entry(edge.to).or_insert(0) += 1;
1399 }
1400 let mut queue: VecDeque<NodeId> = in_degree.iter()
1401 .filter(|(_, &d)| d == 0)
1402 .map(|(&id, _)| id)
1403 .collect();
1404 let mut order = Vec::with_capacity(self.nodes.len());
1405 while let Some(id) = queue.pop_front() {
1406 order.push(id);
1407 for edge in self.edges.iter().filter(|e| e.from == id) {
1408 let d = in_degree.entry(edge.to).or_insert(1);
1409 *d -= 1;
1410 if *d == 0 { queue.push_back(edge.to); }
1411 }
1412 }
1413 if order.len() != self.nodes.len() {
1414 return Err("Audio graph contains a cycle".to_string());
1415 }
1416 self.topo_order = order;
1417 self.topo_dirty = false;
1418 Ok(())
1419 }
1420
1421 pub fn process_block(&mut self) -> StereoBuffer {
1423 if self.topo_dirty {
1424 let _ = self.sort_topologically();
1425 }
1426
1427 self.process_commands();
1429
1430 let mut node_outputs: HashMap<NodeId, AudioBuffer> = HashMap::new();
1431
1432 for &node_id in &self.topo_order.clone() {
1433 let node = match self.nodes.get_mut(&node_id) {
1434 Some(n) => n,
1435 None => continue,
1436 };
1437
1438 let input_edges: Vec<(u8, NodeId, f32)> = self.edges.iter()
1440 .filter(|e| e.to == node_id)
1441 .map(|e| (e.to_slot, e.from, e.gain))
1442 .collect();
1443
1444 let mut inputs: Vec<AudioBuffer> = Vec::new();
1445 for (slot, from_id, gain) in &input_edges {
1446 let _ = slot;
1447 if let Some(buf) = node_outputs.get(from_id) {
1448 let mut b = buf.clone();
1449 b.scale(*gain);
1450 inputs.push(b);
1451 }
1452 }
1453
1454 let input_refs: Vec<&AudioBuffer> = inputs.iter().collect();
1455 let output = node.process(&input_refs);
1456 node_outputs.insert(node_id, output);
1457 }
1458
1459 let mono_out = if let Some(out_id) = self.output_node {
1461 node_outputs.get(&out_id).cloned().unwrap_or_default()
1462 } else {
1463 let has_outgoing: std::collections::HashSet<NodeId> = self.edges.iter().map(|e| e.from).collect();
1465 let mut sum = AudioBuffer::silence();
1466 for (&id, buf) in &node_outputs {
1467 if !has_outgoing.contains(&id) {
1468 sum.add_from(buf);
1469 }
1470 }
1471 sum
1472 };
1473
1474 let mut stereo = StereoBuffer::new();
1476 for i in 0..mono_out.len {
1477 let s = mono_out.samples[i] * self.master_volume;
1478 stereo.left.samples[i] = s;
1479 stereo.right.samples[i] = s;
1480 }
1481 stereo.left.len = mono_out.len;
1482 stereo.right.len = mono_out.len;
1483
1484 self.output_buffer = stereo.clone();
1485 stereo
1486 }
1487
1488 fn process_commands(&mut self) {
1489 let cmds: Vec<AudioCommand> = if let Ok(mut q) = self.commands.lock() {
1490 q.drain(..).collect()
1491 } else { Vec::new() };
1492
1493 for cmd in cmds {
1494 match cmd {
1495 AudioCommand::NoteOn { note, velocity } => {
1496 self.trigger_note_on(note, velocity);
1497 }
1498 AudioCommand::NoteOff { note } => {
1499 self.trigger_note_off(note);
1500 }
1501 AudioCommand::AllNotesOff => {
1502 for node in self.nodes.values_mut() {
1503 node.note_off();
1504 }
1505 }
1506 AudioCommand::SetParam { node_id, param, value } => {
1507 self.apply_param(node_id, ¶m, value);
1508 }
1509 AudioCommand::SetVolume { volume } => {
1510 self.master_volume = volume;
1511 }
1512 AudioCommand::SetBpm { bpm } => {
1513 self.bpm = bpm;
1514 }
1515 AudioCommand::Mute { node_id } => {
1516 if let Some(n) = self.nodes.get_mut(&node_id) { n.enabled = false; }
1517 }
1518 AudioCommand::Unmute { node_id } => {
1519 if let Some(n) = self.nodes.get_mut(&node_id) { n.enabled = true; }
1520 }
1521 }
1522 }
1523 }
1524
1525 fn trigger_note_on(&mut self, note: u8, velocity: f32) {
1526 for node in self.nodes.values_mut() {
1527 let freq = midi_to_freq(note);
1528 node.note_on(freq, velocity);
1529 }
1530 }
1531
1532 fn trigger_note_off(&mut self, note: u8) {
1533 let _ = note;
1534 for node in self.nodes.values_mut() {
1535 node.note_off();
1536 }
1537 }
1538
1539 fn apply_param(&mut self, node_id: NodeId, param: &str, value: f32) {
1540 if let Some(node) = self.nodes.get_mut(&node_id) {
1541 match param {
1542 "gain_db" => {
1543 if let AudioNodeType::Gain { gain_db } = &mut node.node_type {
1544 *gain_db = value;
1545 }
1546 }
1547 "frequency" => {
1548 match &mut node.node_type {
1549 AudioNodeType::Oscillator { frequency, .. } => *frequency = value,
1550 AudioNodeType::BiquadFilter { frequency, .. } => {
1551 *frequency = value;
1552 node.initialize();
1553 }
1554 AudioNodeType::Lfo { frequency, .. } => *frequency = value,
1555 _ => {}
1556 }
1557 }
1558 "cutoff" => {
1559 if let AudioNodeType::LadderFilter { cutoff, .. } = &mut node.node_type {
1560 *cutoff = value;
1561 }
1562 }
1563 "resonance" => {
1564 if let AudioNodeType::LadderFilter { resonance, .. } = &mut node.node_type {
1565 *resonance = value;
1566 }
1567 }
1568 "room_size" => {
1569 if let AudioNodeType::Reverb { room_size, .. } = &mut node.node_type {
1570 *room_size = value;
1571 node.reverb.room_size = value;
1572 }
1573 }
1574 "delay_time" => {
1575 if let AudioNodeType::Delay { delay_time, .. } = &mut node.node_type {
1576 *delay_time = value;
1577 node.delay_line.set_delay_time(value);
1578 }
1579 }
1580 "volume" => {
1581 if let AudioNodeType::Output { volume } = &mut node.node_type {
1582 *volume = value;
1583 }
1584 }
1585 "pan" => {
1586 if let AudioNodeType::Panner { pan } = &mut node.node_type {
1587 *pan = value;
1588 }
1589 }
1590 _ => {}
1591 }
1592 }
1593 }
1594
1595 pub fn basic_synth_chain(&mut self, wave: Waveform, freq: f32) -> (NodeId, NodeId, NodeId, NodeId) {
1597 let osc = self.add_node(AudioNodeType::Oscillator { waveform: wave, frequency: freq, detune_cents: 0.0 });
1598 let filt = self.add_node(AudioNodeType::BiquadFilter { filter_type: FilterType::LowPass, frequency: 2000.0, q: 0.7, gain_db: 0.0 });
1599 let env = self.add_node(AudioNodeType::Adsr { attack: 0.01, decay: 0.1, sustain: 0.8, release: 0.3 });
1600 let out = self.add_node(AudioNodeType::Output { volume: 1.0 });
1601
1602 self.connect(osc, 0, filt, 0);
1603 self.connect(filt, 0, env, 0);
1604 self.connect(env, 0, out, 0);
1605 self.set_output(out);
1606
1607 (osc, filt, env, out)
1608 }
1609
1610 pub fn fm_synth_chain(&mut self, carrier: f32, ratio: f32, index: f32) -> (NodeId, NodeId) {
1612 let fm = self.add_node(AudioNodeType::FmOscillator {
1613 carrier_freq: carrier,
1614 mod_freq: carrier * ratio,
1615 mod_index: index,
1616 waveform: Waveform::Sine,
1617 });
1618 let out = self.add_node(AudioNodeType::Output { volume: 0.8 });
1619 self.connect(fm, 0, out, 0);
1620 self.set_output(out);
1621 (fm, out)
1622 }
1623
1624 pub fn drum_chain(&mut self, is_snare: bool) -> NodeId {
1626 let noise = self.add_node(AudioNodeType::Noise { color: NoiseColor::White });
1627 let filt = self.add_node(AudioNodeType::BiquadFilter {
1628 filter_type: if is_snare { FilterType::BandPass } else { FilterType::LowPass },
1629 frequency: if is_snare { 3000.0 } else { 150.0 },
1630 q: 0.5, gain_db: 0.0,
1631 });
1632 let env = self.add_node(AudioNodeType::Adsr { attack: 0.001, decay: 0.1, sustain: 0.0, release: 0.05 });
1633 let comp = self.add_node(AudioNodeType::Compressor { threshold_db: -6.0, ratio: 4.0, attack_ms: 0.5, release_ms: 50.0 });
1634 let out = self.add_node(AudioNodeType::Output { volume: 1.0 });
1635
1636 self.connect(noise, 0, filt, 0);
1637 self.connect(filt, 0, env, 0);
1638 self.connect(env, 0, comp, 0);
1639 self.connect(comp, 0, out, 0);
1640 self.set_output(out);
1641 out
1642 }
1643}
1644
1645impl Default for AudioGraph {
1646 fn default() -> Self { Self::new() }
1647}
1648
1649#[cfg(test)]
1652mod tests {
1653 use super::*;
1654
1655 #[test]
1656 fn test_oscillator_produces_signal() {
1657 let mut node = AudioNode::new(NodeId(1), AudioNodeType::Oscillator {
1658 waveform: Waveform::Sine, frequency: 440.0, detune_cents: 0.0,
1659 });
1660 let out = node.process(&[]);
1661 let peak = out.peak();
1662 assert!(peak > 0.5, "sine oscillator should produce near-peak amplitude, got {}", peak);
1663 }
1664
1665 #[test]
1666 fn test_noise_not_silent() {
1667 let mut node = AudioNode::new(NodeId(1), AudioNodeType::Noise { color: NoiseColor::White });
1668 let out = node.process(&[]);
1669 assert!(out.peak() > 0.0);
1670 }
1671
1672 #[test]
1673 fn test_biquad_low_pass() {
1674 let mut node = AudioNode::new(NodeId(1), AudioNodeType::BiquadFilter {
1675 filter_type: FilterType::LowPass, frequency: 100.0, q: 0.7, gain_db: 0.0,
1676 });
1677 node.initialize();
1678 let mut noise_buf = AudioBuffer::new();
1679 for (i, s) in noise_buf.samples.iter_mut().enumerate() {
1680 *s = Waveform::Sine.sample(i as f32 * 8000.0 / SAMPLE_RATE); }
1682 let out = node.process(&[&noise_buf]);
1683 assert!(out.peak() < noise_buf.peak() * 0.5, "LP filter should attenuate high frequencies");
1685 }
1686
1687 #[test]
1688 fn test_adsr_envelope_shape() {
1689 let mut env = AdsrEnvelope::new(0.01, 0.05, 0.7, 0.1);
1690 env.note_on();
1691 let attack_samples = (0.01 * SAMPLE_RATE) as usize;
1693 for _ in 0..attack_samples {
1694 env.next_sample();
1695 }
1696 let v = env.next_sample();
1698 assert!(v > 0.8, "envelope should be near peak after attack, got {}", v);
1699 }
1700
1701 #[test]
1702 fn test_adsr_note_off_releases() {
1703 let mut env = AdsrEnvelope::new(0.001, 0.001, 0.8, 0.01);
1704 env.note_on();
1705 for _ in 0..((0.05 * SAMPLE_RATE) as usize) { env.next_sample(); }
1707 env.note_off();
1708 for _ in 0..((0.02 * SAMPLE_RATE) as usize) { env.next_sample(); }
1710 assert!(env.is_done() || env.level < 0.01, "envelope should release to near-zero");
1711 }
1712
1713 #[test]
1714 fn test_lfo_oscillates() {
1715 let mut lfo = Lfo::new(Waveform::Sine, 10.0);
1716 let samples: Vec<f32> = (0..1000).map(|_| lfo.next_sample()).collect();
1717 let min = samples.iter().cloned().fold(f32::INFINITY, f32::min);
1718 let max = samples.iter().cloned().fold(f32::NEG_INFINITY, f32::max);
1719 assert!(max > 0.9 && min < -0.9, "LFO should reach full amplitude");
1720 }
1721
1722 #[test]
1723 fn test_delay_line_delays() {
1724 let mut dl = DelayLine::new(4096);
1725 dl.set_delay_time(0.01); let impulse_sample = SAMPLE_RATE as usize / 100; let mut heard_at = 0;
1728 for i in 0..1024 {
1729 let input = if i == 0 { 1.0 } else { 0.0 };
1730 let out = dl.process(input);
1731 if out > 0.5 { heard_at = i; break; }
1732 }
1733 assert!(heard_at > 0 && heard_at <= impulse_sample + 5,
1734 "impulse should arrive after delay, heard at sample {}", heard_at);
1735 }
1736
1737 #[test]
1738 fn test_reverb_adds_tail() {
1739 let mut reverb = Reverb::new();
1740 let mut input = AudioBuffer::silence();
1741 input.samples[0] = 1.0; let out = reverb.process_buffer(&input);
1743 let nonzero = out.samples.iter().filter(|&&s| s.abs() > 1e-4).count();
1745 assert!(nonzero > 10, "reverb should produce a tail, got {} non-zero samples", nonzero);
1746 }
1747
1748 #[test]
1749 fn test_graph_topological_sort() {
1750 let mut graph = AudioGraph::new();
1751 let osc = graph.add_node(AudioNodeType::Oscillator { waveform: Waveform::Sine, frequency: 440.0, detune_cents: 0.0 });
1752 let gain = graph.add_node(AudioNodeType::Gain { gain_db: 0.0 });
1753 let out = graph.add_node(AudioNodeType::Output { volume: 1.0 });
1754 graph.connect(osc, 0, gain, 0);
1755 graph.connect(gain, 0, out, 0);
1756 graph.set_output(out);
1757 assert!(graph.sort_topologically().is_ok());
1758 assert_eq!(graph.topo_order, vec![osc, gain, out]);
1759 }
1760
1761 #[test]
1762 fn test_graph_process_block() {
1763 let mut graph = AudioGraph::new();
1764 graph.basic_synth_chain(Waveform::Sine, 440.0);
1765 graph.note_on(69, 1.0);
1767 let _ = graph.process_block(); let stereo = graph.process_block();
1769 assert!(stereo.peak() >= 0.0, "graph should process without panic");
1771 }
1772
1773 #[test]
1774 fn test_compressor_reduces_peaks() {
1775 let mut comp = Compressor::new(-6.0, 4.0);
1776 comp.makeup_gain = 0.0;
1777 let mut loud = AudioBuffer::new();
1778 loud.fill(2.0); comp.process_buffer(&mut loud);
1780 assert!(loud.peak() < 2.0, "compressor should reduce loud signal");
1781 }
1782
1783 #[test]
1784 fn test_distortion_clips() {
1785 let dist = Distortion::new(DistortionMode::HardClip, 5.0);
1786 let mut buf = AudioBuffer::new();
1787 buf.fill(0.5);
1788 dist.process_buffer(&mut buf);
1789 assert!(buf.peak() <= 1.0 + 1e-6, "hard clip should limit to ±1");
1790 }
1791
1792 #[test]
1793 fn test_fm_oscillator() {
1794 let mut node = AudioNode::new(NodeId(1), AudioNodeType::FmOscillator {
1795 carrier_freq: 440.0, mod_freq: 440.0, mod_index: 1.0, waveform: Waveform::Sine,
1796 });
1797 let out = node.process(&[]);
1798 assert!(out.peak() > 0.0, "FM oscillator should produce signal");
1799 }
1800
1801 #[test]
1802 fn test_waveform_samples() {
1803 for w in [Waveform::Sine, Waveform::Square, Waveform::Triangle, Waveform::Sawtooth] {
1804 let s = w.sample(0.25);
1805 assert!(s.is_finite(), "{:?} at 0.25 should be finite, got {}", w, s);
1806 }
1807 }
1808
1809 #[test]
1810 fn test_automation_lane() {
1811 let mut lane = AutomationLane::new(NodeId(1), "frequency");
1812 lane.add_point(0, 100.0);
1813 lane.add_point(100, 200.0);
1814 let v = lane.sample_at(50).unwrap();
1815 assert!((v - 150.0).abs() < 1.0, "automation should interpolate, got {}", v);
1816 }
1817
1818 #[test]
1819 fn test_drum_chain_builds() {
1820 let mut graph = AudioGraph::new();
1821 let out = graph.drum_chain(false); assert!(graph.nodes.len() >= 4);
1823 assert!(graph.edges.len() >= 3);
1824 let _ = out;
1825 }
1826}