Skip to main content

melody_bay/webaudio/nodes/
processing.rs

1#[derive(Debug, Clone)]
2pub struct StereoPannerNode {
3    id: NodeId,
4    graph: Arc<Mutex<GraphInner>>,
5}
6
7#[derive(Debug, Clone, Copy, PartialEq)]
8pub struct StereoPannerOptions {
9    pub pan: f32,
10}
11
12impl Default for StereoPannerOptions {
13    fn default() -> Self {
14        Self { pan: 0.0 }
15    }
16}
17
18impl StereoPannerNode {
19    #[must_use]
20    pub fn pan(&self) -> AudioParamHandle {
21        AudioParamHandle {
22            graph: Arc::clone(&self.graph),
23            id: self.pan_param(),
24        }
25    }
26
27    #[must_use]
28    fn pan_param(&self) -> ParamId {
29        ParamId {
30            node: self.id,
31            param: ParamKind::Pan,
32        }
33    }
34
35    #[must_use]
36    pub fn param(&self, name: &str) -> Option<AudioParamHandle> {
37        self.parameter(name)
38    }
39
40    #[must_use]
41    pub fn parameter(&self, name: &str) -> Option<AudioParamHandle> {
42        match name {
43            "pan" => Some(self.pan()),
44            _ => None,
45        }
46    }
47}
48
49impl From<StereoPannerNode> for NodeId {
50    fn from(value: StereoPannerNode) -> Self {
51        value.id
52    }
53}
54
55impl From<&StereoPannerNode> for NodeId {
56    fn from(value: &StereoPannerNode) -> Self {
57        value.id
58    }
59}
60
61impl_node_channel_config!(StereoPannerNode);
62
63#[derive(Debug, Clone)]
64pub struct BiquadFilterHandle {
65    id: NodeId,
66    graph: Arc<Mutex<GraphInner>>,
67}
68
69#[derive(Debug, Clone, Copy, PartialEq)]
70pub struct BiquadFilterOptions {
71    pub filter_type: BiquadFilterType,
72    pub frequency: f32,
73    pub detune: f32,
74    pub q: f32,
75    pub gain: f32,
76}
77
78impl Default for BiquadFilterOptions {
79    fn default() -> Self {
80        Self {
81            filter_type: BiquadFilterType::Lowpass,
82            frequency: 350.0,
83            detune: 0.0,
84            q: 1.0,
85            gain: 0.0,
86        }
87    }
88}
89
90impl BiquadFilterHandle {
91    #[must_use]
92    pub fn type_value(&self) -> BiquadFilterType {
93        let inner = self.graph.lock().expect("graph mutex poisoned");
94        if let NodeKind::BiquadFilter { kind, .. } = &inner.nodes[self.id.0].kind {
95            *kind
96        } else {
97            BiquadFilterType::Lowpass
98        }
99    }
100
101    pub fn set_type(&self, filter_type: BiquadFilterType) {
102        let mut inner = self.graph.lock().expect("graph mutex poisoned");
103        if let NodeKind::BiquadFilter {
104            kind: node_kind, ..
105        } = &mut inner.nodes[self.id.0].kind
106        {
107            *node_kind = filter_type;
108        }
109    }
110
111    #[must_use]
112    pub fn frequency(&self) -> AudioParamHandle {
113        AudioParamHandle {
114            graph: Arc::clone(&self.graph),
115            id: self.frequency_param(),
116        }
117    }
118
119    #[must_use]
120    pub fn detune(&self) -> AudioParamHandle {
121        AudioParamHandle {
122            graph: Arc::clone(&self.graph),
123            id: self.detune_param(),
124        }
125    }
126
127    #[must_use]
128    pub fn q(&self) -> AudioParamHandle {
129        AudioParamHandle {
130            graph: Arc::clone(&self.graph),
131            id: self.q_param(),
132        }
133    }
134
135    #[must_use]
136    pub fn gain(&self) -> AudioParamHandle {
137        AudioParamHandle {
138            graph: Arc::clone(&self.graph),
139            id: self.gain_param(),
140        }
141    }
142
143    #[must_use]
144    fn frequency_param(&self) -> ParamId {
145        ParamId {
146            node: self.id,
147            param: ParamKind::Frequency,
148        }
149    }
150
151    #[must_use]
152    fn detune_param(&self) -> ParamId {
153        ParamId {
154            node: self.id,
155            param: ParamKind::Detune,
156        }
157    }
158
159    #[must_use]
160    fn q_param(&self) -> ParamId {
161        ParamId {
162            node: self.id,
163            param: ParamKind::Q,
164        }
165    }
166
167    #[must_use]
168    fn gain_param(&self) -> ParamId {
169        ParamId {
170            node: self.id,
171            param: ParamKind::FilterGain,
172        }
173    }
174
175    #[must_use]
176    pub fn param(&self, name: &str) -> Option<AudioParamHandle> {
177        self.parameter(name)
178    }
179
180    #[must_use]
181    pub fn parameter(&self, name: &str) -> Option<AudioParamHandle> {
182        match name {
183            "frequency" => Some(self.frequency()),
184            "detune" => Some(self.detune()),
185            "Q" | "q" => Some(self.q()),
186            "gain" => Some(self.gain()),
187            _ => None,
188        }
189    }
190
191    pub fn get_frequency_response(
192        &self,
193        frequency_hz: &[f32],
194        mag_response: &mut [f32],
195        phase_response: &mut [f32],
196    ) -> Result<(), GraphError> {
197        if frequency_hz.len() != mag_response.len() || frequency_hz.len() != phase_response.len() {
198            return Err(GraphError::InvalidFrequencyResponse);
199        }
200        let inner = self.graph.lock().expect("graph mutex poisoned");
201        let NodeKind::BiquadFilter {
202            kind,
203            frequency,
204            detune,
205            q,
206            gain,
207        } = &inner.nodes[self.id.0].kind
208        else {
209            return Err(GraphError::UnknownNode);
210        };
211        let filter_frequency = frequency.value() * 2.0f32.powf(detune.value() / 1200.0);
212        let coefficients = BiquadCoefficients::new(
213            *kind,
214            filter_frequency,
215            q.value(),
216            gain.value(),
217            inner.sample_rate as f64,
218        );
219        let sample_rate = inner.sample_rate as f32;
220        for ((frequency_hz, mag), phase) in frequency_hz
221            .iter()
222            .zip(mag_response.iter_mut())
223            .zip(phase_response.iter_mut())
224        {
225            let (magnitude, phase_radians) =
226                coefficients.frequency_response(*frequency_hz, sample_rate);
227            *mag = magnitude;
228            *phase = phase_radians;
229        }
230        Ok(())
231    }
232}
233
234impl From<BiquadFilterHandle> for NodeId {
235    fn from(value: BiquadFilterHandle) -> Self {
236        value.id
237    }
238}
239
240impl From<&BiquadFilterHandle> for NodeId {
241    fn from(value: &BiquadFilterHandle) -> Self {
242        value.id
243    }
244}
245
246impl_node_channel_config!(BiquadFilterHandle);
247
248#[derive(Debug, Clone)]
249pub struct IirFilterNode {
250    id: NodeId,
251    graph: Arc<Mutex<GraphInner>>,
252}
253
254#[derive(Debug, Clone, PartialEq)]
255pub struct IirFilterOptions {
256    pub feedforward: Vec<f32>,
257    pub feedback: Vec<f32>,
258}
259
260impl IirFilterNode {
261    pub fn coefficients(
262        &self,
263        feedforward: impl IntoIterator<Item = f32>,
264        feedback: impl IntoIterator<Item = f32>,
265    ) -> Result<(), GraphError> {
266        let feedforward = feedforward.into_iter().collect::<Vec<_>>();
267        let feedback = feedback.into_iter().collect::<Vec<_>>();
268        validate_iir_coefficients(&feedforward, &feedback)?;
269        let mut inner = self.graph.lock().expect("graph mutex poisoned");
270        if let NodeKind::IirFilter {
271            feedforward: node_feedforward,
272            feedback: node_feedback,
273        } = &mut inner.nodes[self.id.0].kind
274        {
275            *node_feedforward = feedforward;
276            *node_feedback = feedback;
277        }
278        Ok(())
279    }
280
281    pub fn get_frequency_response(
282        &self,
283        frequency_hz: &[f32],
284        mag_response: &mut [f32],
285        phase_response: &mut [f32],
286    ) -> Result<(), GraphError> {
287        if frequency_hz.len() != mag_response.len() || frequency_hz.len() != phase_response.len() {
288            return Err(GraphError::InvalidFrequencyResponse);
289        }
290        let inner = self.graph.lock().expect("graph mutex poisoned");
291        let NodeKind::IirFilter {
292            feedforward,
293            feedback,
294        } = &inner.nodes[self.id.0].kind
295        else {
296            return Err(GraphError::UnknownNode);
297        };
298        let sample_rate = inner.sample_rate as f32;
299        for ((frequency_hz, mag), phase) in frequency_hz
300            .iter()
301            .zip(mag_response.iter_mut())
302            .zip(phase_response.iter_mut())
303        {
304            let (magnitude, phase_radians) =
305                iir_frequency_response(feedforward, feedback, *frequency_hz, sample_rate);
306            *mag = magnitude;
307            *phase = phase_radians;
308        }
309        Ok(())
310    }
311}
312
313impl From<IirFilterNode> for NodeId {
314    fn from(value: IirFilterNode) -> Self {
315        value.id
316    }
317}
318
319impl From<&IirFilterNode> for NodeId {
320    fn from(value: &IirFilterNode) -> Self {
321        value.id
322    }
323}
324
325impl_node_channel_config!(IirFilterNode);
326
327#[derive(Debug, Clone, Copy, PartialEq, Eq)]
328pub enum Oversample {
329    None,
330    TwoX,
331    FourX,
332}
333
334#[derive(Debug, Clone)]
335pub struct WaveShaperNode {
336    id: NodeId,
337    graph: Arc<Mutex<GraphInner>>,
338}
339
340#[derive(Debug, Clone, PartialEq)]
341pub struct WaveShaperOptions {
342    pub curve: Option<Vec<f32>>,
343    pub oversample: Oversample,
344}
345
346impl Default for WaveShaperOptions {
347    fn default() -> Self {
348        Self {
349            curve: None,
350            oversample: Oversample::None,
351        }
352    }
353}
354
355impl WaveShaperNode {
356    pub fn try_curve(&self, curve: impl IntoIterator<Item = f32>) -> Result<(), GraphError> {
357        let curve = curve.into_iter().collect::<Vec<_>>();
358        if curve.is_empty() || curve.iter().any(|sample| !sample.is_finite()) {
359            return Err(GraphError::InvalidWaveShaperCurve);
360        }
361        let mut inner = self.graph.lock().expect("graph mutex poisoned");
362        if let NodeKind::WaveShaper {
363            curve: node_curve, ..
364        } = &mut inner.nodes[self.id.0].kind
365        {
366            *node_curve = Some(curve);
367        }
368        Ok(())
369    }
370
371    pub fn clear_curve(&self) {
372        let mut inner = self.graph.lock().expect("graph mutex poisoned");
373        if let NodeKind::WaveShaper {
374            curve: node_curve, ..
375        } = &mut inner.nodes[self.id.0].kind
376        {
377            *node_curve = None;
378        }
379    }
380
381    pub fn set_oversample(&self, oversample: Oversample) {
382        let mut inner = self.graph.lock().expect("graph mutex poisoned");
383        if let NodeKind::WaveShaper {
384            oversample: node_oversample,
385            ..
386        } = &mut inner.nodes[self.id.0].kind
387        {
388            *node_oversample = oversample;
389        }
390    }
391
392    #[must_use]
393    pub fn curve_value(&self) -> Option<Vec<f32>> {
394        let inner = self.graph.lock().expect("graph mutex poisoned");
395        if let NodeKind::WaveShaper { curve, .. } = &inner.nodes[self.id.0].kind {
396            curve.clone()
397        } else {
398            None
399        }
400    }
401
402    #[must_use]
403    pub fn oversample_value(&self) -> Oversample {
404        let inner = self.graph.lock().expect("graph mutex poisoned");
405        if let NodeKind::WaveShaper { oversample, .. } = &inner.nodes[self.id.0].kind {
406            *oversample
407        } else {
408            Oversample::None
409        }
410    }
411}
412
413impl From<WaveShaperNode> for NodeId {
414    fn from(value: WaveShaperNode) -> Self {
415        value.id
416    }
417}
418
419impl From<&WaveShaperNode> for NodeId {
420    fn from(value: &WaveShaperNode) -> Self {
421        value.id
422    }
423}
424
425impl_node_channel_config!(WaveShaperNode);
426
427#[derive(Debug, Clone)]
428pub struct ConvolverNode {
429    id: NodeId,
430    graph: Arc<Mutex<GraphInner>>,
431}
432
433#[derive(Debug, Clone, PartialEq)]
434#[derive(Default)]
435pub struct ConvolverOptions {
436    pub buffer: Option<AudioBuffer>,
437    pub disable_normalization: bool,
438}
439
440
441impl ConvolverNode {
442    pub fn try_buffer(&self, buffer: AudioBuffer) -> Result<(), GraphError> {
443        let mut inner = self.graph.lock().expect("graph mutex poisoned");
444        if !matches!(buffer.number_of_channels(), 1 | 2 | 4)
445            || buffer.length() == 0
446            || buffer.sample_rate() != inner.sample_rate
447        {
448            return Err(GraphError::InvalidConvolverBuffer);
449        }
450        if let NodeKind::Convolver {
451            buffer: node_buffer,
452            normalize,
453            buffer_normalize,
454            ..
455        } = &mut inner.nodes[self.id.0].kind
456        {
457            *node_buffer = Some(buffer);
458            *buffer_normalize = *normalize;
459        }
460        Ok(())
461    }
462
463    pub fn clear_buffer(&self) {
464        let mut inner = self.graph.lock().expect("graph mutex poisoned");
465        if let NodeKind::Convolver {
466            buffer: node_buffer,
467            ..
468        } = &mut inner.nodes[self.id.0].kind
469        {
470            *node_buffer = None;
471        }
472    }
473
474    pub fn set_normalize(&self, normalize: bool) {
475        let mut inner = self.graph.lock().expect("graph mutex poisoned");
476        if let NodeKind::Convolver {
477            normalize: node_normalize,
478            ..
479        } = &mut inner.nodes[self.id.0].kind
480        {
481            *node_normalize = normalize;
482        }
483    }
484
485    #[must_use]
486    pub fn buffer_value(&self) -> Option<AudioBuffer> {
487        let inner = self.graph.lock().expect("graph mutex poisoned");
488        if let NodeKind::Convolver { buffer, .. } = &inner.nodes[self.id.0].kind {
489            buffer.clone()
490        } else {
491            None
492        }
493    }
494
495    #[must_use]
496    pub fn normalize_value(&self) -> bool {
497        let inner = self.graph.lock().expect("graph mutex poisoned");
498        if let NodeKind::Convolver { normalize, .. } = &inner.nodes[self.id.0].kind {
499            *normalize
500        } else {
501            true
502        }
503    }
504}
505
506impl From<ConvolverNode> for NodeId {
507    fn from(value: ConvolverNode) -> Self {
508        value.id
509    }
510}
511
512impl From<&ConvolverNode> for NodeId {
513    fn from(value: &ConvolverNode) -> Self {
514        value.id
515    }
516}
517
518impl_node_channel_config!(ConvolverNode);
519
520#[derive(Debug, Clone)]
521pub struct DelayNodeHandle {
522    id: NodeId,
523    graph: Arc<Mutex<GraphInner>>,
524}
525
526#[derive(Debug, Clone, Copy, PartialEq)]
527pub struct DelayOptions {
528    pub max_delay_time: f64,
529    pub delay_time: f32,
530}
531
532impl Default for DelayOptions {
533    fn default() -> Self {
534        Self {
535            max_delay_time: 1.0,
536            delay_time: 0.0,
537        }
538    }
539}
540
541impl DelayNodeHandle {
542    #[must_use]
543    pub fn delay_time(&self) -> AudioParamHandle {
544        AudioParamHandle {
545            graph: Arc::clone(&self.graph),
546            id: self.delay_time_param(),
547        }
548    }
549
550    #[must_use]
551    pub fn delay_time_value(&self) -> f32 {
552        let inner = self.graph.lock().expect("graph mutex poisoned");
553        if let NodeKind::Delay { delay_time, .. } = &inner.nodes[self.id.0].kind {
554            delay_time.value()
555        } else {
556            0.0
557        }
558    }
559
560    #[must_use]
561    pub fn max_delay_time_value(&self) -> f32 {
562        let inner = self.graph.lock().expect("graph mutex poisoned");
563        if let NodeKind::Delay { max_delay_time, .. } = &inner.nodes[self.id.0].kind {
564            max_delay_time.unwrap_or(1.0)
565        } else {
566            1.0
567        }
568    }
569
570    #[must_use]
571    fn delay_time_param(&self) -> ParamId {
572        ParamId {
573            node: self.id,
574            param: ParamKind::DelayTime,
575        }
576    }
577
578    #[must_use]
579    pub fn param(&self, name: &str) -> Option<AudioParamHandle> {
580        self.parameter(name)
581    }
582
583    #[must_use]
584    pub fn parameter(&self, name: &str) -> Option<AudioParamHandle> {
585        match name {
586            "delayTime" | "delay_time" => Some(self.delay_time()),
587            _ => None,
588        }
589    }
590}
591
592impl From<DelayNodeHandle> for NodeId {
593    fn from(value: DelayNodeHandle) -> Self {
594        value.id
595    }
596}
597
598impl From<&DelayNodeHandle> for NodeId {
599    fn from(value: &DelayNodeHandle) -> Self {
600        value.id
601    }
602}
603
604impl_node_channel_config!(DelayNodeHandle);
605
606#[derive(Debug, Clone)]
607pub struct DynamicsCompressorNode {
608    id: NodeId,
609    graph: Arc<Mutex<GraphInner>>,
610}
611
612#[derive(Debug, Clone, Copy, PartialEq)]
613pub struct DynamicsCompressorOptions {
614    pub threshold: f32,
615    pub knee: f32,
616    pub ratio: f32,
617    pub attack: f32,
618    pub release: f32,
619}
620
621impl Default for DynamicsCompressorOptions {
622    fn default() -> Self {
623        Self {
624            threshold: -24.0,
625            knee: 30.0,
626            ratio: 12.0,
627            attack: 0.003,
628            release: 0.25,
629        }
630    }
631}
632
633impl DynamicsCompressorNode {
634    #[must_use]
635    pub fn threshold(&self) -> AudioParamHandle {
636        AudioParamHandle {
637            graph: Arc::clone(&self.graph),
638            id: self.threshold_param(),
639        }
640    }
641
642    #[must_use]
643    pub fn knee(&self) -> AudioParamHandle {
644        AudioParamHandle {
645            graph: Arc::clone(&self.graph),
646            id: self.knee_param(),
647        }
648    }
649
650    #[must_use]
651    pub fn ratio(&self) -> AudioParamHandle {
652        AudioParamHandle {
653            graph: Arc::clone(&self.graph),
654            id: self.ratio_param(),
655        }
656    }
657
658    #[must_use]
659    pub fn attack(&self) -> AudioParamHandle {
660        AudioParamHandle {
661            graph: Arc::clone(&self.graph),
662            id: self.attack_param(),
663        }
664    }
665
666    #[must_use]
667    pub fn release(&self) -> AudioParamHandle {
668        AudioParamHandle {
669            graph: Arc::clone(&self.graph),
670            id: self.release_param(),
671        }
672    }
673
674    #[must_use]
675    pub fn reduction(&self) -> f32 {
676        let inner = self.graph.lock().expect("graph mutex poisoned");
677        if let NodeKind::DynamicsCompressor { reduction, .. } = &inner.nodes[self.id.0].kind {
678            f32::from_bits(reduction.load(Ordering::SeqCst))
679        } else {
680            0.0
681        }
682    }
683
684    #[must_use]
685    fn threshold_param(&self) -> ParamId {
686        ParamId {
687            node: self.id,
688            param: ParamKind::Threshold,
689        }
690    }
691
692    #[must_use]
693    fn knee_param(&self) -> ParamId {
694        ParamId {
695            node: self.id,
696            param: ParamKind::Knee,
697        }
698    }
699
700    #[must_use]
701    fn ratio_param(&self) -> ParamId {
702        ParamId {
703            node: self.id,
704            param: ParamKind::Ratio,
705        }
706    }
707
708    #[must_use]
709    fn attack_param(&self) -> ParamId {
710        ParamId {
711            node: self.id,
712            param: ParamKind::Attack,
713        }
714    }
715
716    #[must_use]
717    fn release_param(&self) -> ParamId {
718        ParamId {
719            node: self.id,
720            param: ParamKind::Release,
721        }
722    }
723
724    #[must_use]
725    pub fn param(&self, name: &str) -> Option<AudioParamHandle> {
726        self.parameter(name)
727    }
728
729    #[must_use]
730    pub fn parameter(&self, name: &str) -> Option<AudioParamHandle> {
731        match name {
732            "threshold" => Some(self.threshold()),
733            "knee" => Some(self.knee()),
734            "ratio" => Some(self.ratio()),
735            "attack" => Some(self.attack()),
736            "release" => Some(self.release()),
737            _ => None,
738        }
739    }
740}
741
742impl From<DynamicsCompressorNode> for NodeId {
743    fn from(value: DynamicsCompressorNode) -> Self {
744        value.id
745    }
746}
747
748impl From<&DynamicsCompressorNode> for NodeId {
749    fn from(value: &DynamicsCompressorNode) -> Self {
750        value.id
751    }
752}
753
754impl_node_channel_config!(DynamicsCompressorNode);
755
756#[derive(Debug, Clone)]
757pub struct AnalyserNode {
758    id: NodeId,
759    state: Arc<Mutex<AnalyserState>>,
760    graph: Arc<Mutex<GraphInner>>,
761}
762
763#[derive(Debug, Clone, Copy, PartialEq)]
764pub struct AnalyserOptions {
765    pub fft_size: usize,
766    pub min_decibels: f32,
767    pub max_decibels: f32,
768    pub smoothing_time_constant: f32,
769}
770
771impl Default for AnalyserOptions {
772    fn default() -> Self {
773        Self {
774            fft_size: 2048,
775            min_decibels: -100.0,
776            max_decibels: -30.0,
777            smoothing_time_constant: 0.8,
778        }
779    }
780}
781
782impl AnalyserNode {
783    pub fn try_fft_size(&self, size: usize) -> Result<(), GraphError> {
784        if !(32..=32768).contains(&size) || !size.is_power_of_two() {
785            return Err(GraphError::InvalidAnalyserConfig);
786        }
787        self.state
788            .lock()
789            .expect("analyser mutex poisoned")
790            .resize(size);
791        Ok(())
792    }
793
794    pub fn try_min_decibels(&self, decibels: f32) -> Result<(), GraphError> {
795        let mut state = self.state.lock().expect("analyser mutex poisoned");
796        if !decibels.is_finite() || decibels >= state.max_decibels {
797            return Err(GraphError::InvalidAnalyserConfig);
798        }
799        state.min_decibels = decibels;
800        Ok(())
801    }
802
803    pub fn try_max_decibels(&self, decibels: f32) -> Result<(), GraphError> {
804        let mut state = self.state.lock().expect("analyser mutex poisoned");
805        if !decibels.is_finite() || decibels <= state.min_decibels {
806            return Err(GraphError::InvalidAnalyserConfig);
807        }
808        state.max_decibels = decibels;
809        Ok(())
810    }
811
812    pub fn try_smoothing_time_constant(&self, smoothing: f32) -> Result<(), GraphError> {
813        if !(0.0..=1.0).contains(&smoothing) {
814            return Err(GraphError::InvalidAnalyserConfig);
815        }
816        let mut state = self.state.lock().expect("analyser mutex poisoned");
817        state.smoothing_time_constant = smoothing;
818        state.frequency_dirty = true;
819        Ok(())
820    }
821
822    #[must_use]
823    pub fn fft_size_value(&self) -> usize {
824        self.state
825            .lock()
826            .expect("analyser mutex poisoned")
827            .buffer
828            .len()
829    }
830
831    #[must_use]
832    pub fn frequency_bin_count(&self) -> usize {
833        self.fft_size_value() / 2
834    }
835
836    #[must_use]
837    pub fn min_decibels_value(&self) -> f32 {
838        self.state
839            .lock()
840            .expect("analyser mutex poisoned")
841            .min_decibels
842    }
843
844    #[must_use]
845    pub fn max_decibels_value(&self) -> f32 {
846        self.state
847            .lock()
848            .expect("analyser mutex poisoned")
849            .max_decibels
850    }
851
852    #[must_use]
853    pub fn smoothing_time_constant_value(&self) -> f32 {
854        self.state
855            .lock()
856            .expect("analyser mutex poisoned")
857            .smoothing_time_constant
858    }
859
860    #[must_use]
861    pub fn peak(&self) -> f32 {
862        self.state.lock().expect("analyser mutex poisoned").peak()
863    }
864
865    #[must_use]
866    pub fn rms(&self) -> f32 {
867        self.state.lock().expect("analyser mutex poisoned").rms()
868    }
869
870    #[must_use]
871    pub fn time_domain_data(&self) -> Vec<f32> {
872        self.float_time_domain_data()
873    }
874
875    #[must_use]
876    pub fn float_time_domain_data(&self) -> Vec<f32> {
877        self.state
878            .lock()
879            .expect("analyser mutex poisoned")
880            .time_domain_data()
881    }
882
883    #[must_use]
884    pub fn byte_time_domain_data(&self) -> Vec<u8> {
885        self.state
886            .lock()
887            .expect("analyser mutex poisoned")
888            .time_domain_data()
889            .into_iter()
890            .map(sample_to_byte)
891            .collect()
892    }
893
894    #[must_use]
895    pub fn float_frequency_data(&self) -> Vec<f32> {
896        self.state
897            .lock()
898            .expect("analyser mutex poisoned")
899            .frequency_data()
900    }
901
902    #[must_use]
903    pub fn byte_frequency_data(&self) -> Vec<u8> {
904        let mut state = self.state.lock().expect("analyser mutex poisoned");
905        let min_decibels = state.min_decibels;
906        let max_decibels = state.max_decibels;
907        state
908            .frequency_data()
909            .into_iter()
910            .map(|decibels| decibels_to_byte(decibels, min_decibels, max_decibels))
911            .collect()
912    }
913
914    pub fn get_float_time_domain_data(&self, destination: &mut [f32]) {
915        copy_available(&self.float_time_domain_data(), destination);
916    }
917
918    pub fn get_byte_time_domain_data(&self, destination: &mut [u8]) {
919        copy_available(&self.byte_time_domain_data(), destination);
920    }
921
922    pub fn get_float_frequency_data(&self, destination: &mut [f32]) {
923        copy_available(&self.float_frequency_data(), destination);
924    }
925
926    pub fn get_byte_frequency_data(&self, destination: &mut [u8]) {
927        copy_available(&self.byte_frequency_data(), destination);
928    }
929}
930
931fn copy_available<T: Copy>(source: &[T], destination: &mut [T]) {
932    let length = source.len().min(destination.len());
933    destination[..length].copy_from_slice(&source[..length]);
934}
935
936impl From<AnalyserNode> for NodeId {
937    fn from(value: AnalyserNode) -> Self {
938        value.id
939    }
940}
941
942impl From<&AnalyserNode> for NodeId {
943    fn from(value: &AnalyserNode) -> Self {
944        value.id
945    }
946}
947
948impl_node_channel_config!(AnalyserNode);
949
950#[derive(Debug, Clone)]
951pub struct ChannelSplitterNode {
952    id: NodeId,
953    graph: Arc<Mutex<GraphInner>>,
954    context_identity: usize,
955}
956
957#[derive(Debug, Clone, Copy, PartialEq, Eq)]
958pub struct ChannelSplitterOptions {
959    pub number_of_outputs: usize,
960}
961
962impl Default for ChannelSplitterOptions {
963    fn default() -> Self {
964        Self {
965            number_of_outputs: 6,
966        }
967    }
968}
969
970impl From<ChannelSplitterNode> for NodeId {
971    fn from(value: ChannelSplitterNode) -> Self {
972        value.id
973    }
974}
975
976impl From<&ChannelSplitterNode> for NodeId {
977    fn from(value: &ChannelSplitterNode) -> Self {
978        value.id
979    }
980}
981
982impl_node_channel_config!(ChannelSplitterNode);
983
984#[derive(Debug, Clone)]
985pub struct ChannelMergerNode {
986    id: NodeId,
987    graph: Arc<Mutex<GraphInner>>,
988    context_identity: usize,
989}
990
991#[derive(Debug, Clone, Copy, PartialEq, Eq)]
992pub struct ChannelMergerOptions {
993    pub number_of_inputs: usize,
994}
995
996impl Default for ChannelMergerOptions {
997    fn default() -> Self {
998        Self {
999            number_of_inputs: 6,
1000        }
1001    }
1002}
1003
1004impl From<ChannelMergerNode> for NodeId {
1005    fn from(value: ChannelMergerNode) -> Self {
1006        value.id
1007    }
1008}
1009
1010impl From<&ChannelMergerNode> for NodeId {
1011    fn from(value: &ChannelMergerNode) -> Self {
1012        value.id
1013    }
1014}
1015
1016impl_node_channel_config!(ChannelMergerNode);
1017
1018#[derive(Debug, Clone)]
1019pub struct AudioWorkletNode {
1020    id: NodeId,
1021    graph: Arc<Mutex<GraphInner>>,
1022}
1023
1024impl AudioWorkletNode {
1025    #[must_use]
1026    pub fn parameter(&self, name: &str) -> Option<AudioParamHandle> {
1027        let param = self.param_id(name)?;
1028        let graph = self.graph.lock().expect("graph mutex poisoned");
1029        graph.param(param)?;
1030        Some(AudioParamHandle {
1031            graph: self.graph.clone(),
1032            id: param,
1033        })
1034    }
1035
1036    #[must_use]
1037    pub fn param(&self, name: &str) -> Option<AudioParamHandle> {
1038        self.parameter(name)
1039    }
1040
1041    fn param_id(&self, name: &str) -> Option<ParamId> {
1042        let graph = self.graph.lock().expect("graph mutex poisoned");
1043        let node = graph.nodes.get(self.id.0)?;
1044        let NodeKind::AudioWorklet { parameters, .. } = &node.kind else {
1045            return None;
1046        };
1047        parameters
1048            .iter()
1049            .position(|(parameter_name, _)| parameter_name == name)
1050            .map(|index| ParamId {
1051                node: self.id,
1052                param: ParamKind::WorkletParam(index),
1053            })
1054    }
1055}
1056
1057impl From<AudioWorkletNode> for NodeId {
1058    fn from(value: AudioWorkletNode) -> Self {
1059        value.id
1060    }
1061}
1062
1063impl From<&AudioWorkletNode> for NodeId {
1064    fn from(value: &AudioWorkletNode) -> Self {
1065        value.id
1066    }
1067}
1068
1069impl_node_channel_config!(AudioWorkletNode);
1070
1071#[derive(Debug, Clone, PartialEq)]
1072pub struct AudioWorkletNodeOptions {
1073    pub number_of_inputs: usize,
1074    pub number_of_outputs: usize,
1075    pub output_channel_count: Option<Vec<usize>>,
1076    pub parameter_descriptors: Vec<AudioWorkletParameterDescriptor>,
1077    pub parameter_data: HashMap<String, f32>,
1078    pub processor_options: HashMap<String, String>,
1079}
1080
1081impl Default for AudioWorkletNodeOptions {
1082    fn default() -> Self {
1083        Self {
1084            number_of_inputs: 1,
1085            number_of_outputs: 1,
1086            output_channel_count: None,
1087            parameter_descriptors: Vec::new(),
1088            parameter_data: HashMap::new(),
1089            processor_options: HashMap::new(),
1090        }
1091    }
1092}
1093
1094#[derive(Debug, Clone, PartialEq)]
1095pub struct AudioWorkletParameterDescriptor {
1096    pub name: String,
1097    pub default_value: f32,
1098    pub min_value: f32,
1099    pub max_value: f32,
1100    pub automation_rate: AutomationRate,
1101}
1102
1103#[derive(Debug, Clone, PartialEq)]
1104pub struct AudioWorkletProcessContext {
1105    pub current_time: f64,
1106    pub sample_dt: f64,
1107    pub parameters: HashMap<String, f32>,
1108    pub parameter_values: HashMap<String, Vec<f32>>,
1109    pub processor_options: HashMap<String, String>,
1110}
1111
1112pub trait AudioWorkletProcessor: Send {
1113    fn process(
1114        &mut self,
1115        inputs: &[Vec<Vec<f32>>],
1116        outputs: &mut [Vec<Vec<f32>>],
1117        context: AudioWorkletProcessContext,
1118    ) -> bool;
1119}
1120
1121#[derive(Clone)]
1122struct AudioWorkletProcessorNode {
1123    processor: Arc<Mutex<Box<dyn AudioWorkletProcessor>>>,
1124    active: Arc<AtomicBool>,
1125}
1126
1127pub(crate) struct AudioWorkletRenderQuantum {
1128    pub(crate) inputs: Vec<Vec<Vec<f32>>>,
1129    pub(crate) output_channel_count: Vec<usize>,
1130    pub(crate) time: f64,
1131    pub(crate) sample_dt: f64,
1132    pub(crate) parameters: HashMap<String, f32>,
1133    pub(crate) parameter_values: HashMap<String, Vec<f32>>,
1134    pub(crate) processor_options: HashMap<String, String>,
1135}
1136
1137impl AudioWorkletProcessorNode {
1138    fn new(processor: impl AudioWorkletProcessor + 'static) -> Self {
1139        Self {
1140            processor: Arc::new(Mutex::new(Box::new(processor))),
1141            active: Arc::new(AtomicBool::new(true)),
1142        }
1143    }
1144
1145    fn process_quantum(&self, quantum: AudioWorkletRenderQuantum) -> Vec<AudioBus> {
1146        let output_channels = quantum.output_channel_count.iter().sum::<usize>();
1147        if !self.active.load(Ordering::SeqCst) {
1148            return vec![AudioBus::silent(output_channels.max(1)); RENDER_QUANTUM_SIZE_USIZE];
1149        }
1150        let mut outputs = quantum
1151            .output_channel_count
1152            .iter()
1153            .map(|channels| vec![vec![0.0; RENDER_QUANTUM_SIZE_USIZE]; (*channels).max(1)])
1154            .collect::<Vec<_>>();
1155        let continue_processing = self
1156            .processor
1157            .lock()
1158            .expect("audio worklet processor mutex poisoned")
1159            .process(
1160                &quantum.inputs,
1161                &mut outputs,
1162                AudioWorkletProcessContext {
1163                    current_time: quantum.time,
1164                    sample_dt: quantum.sample_dt,
1165                    parameters: quantum.parameters,
1166                    parameter_values: quantum.parameter_values,
1167                    processor_options: quantum.processor_options,
1168                },
1169            );
1170        if !continue_processing {
1171            self.active.store(false, Ordering::SeqCst);
1172        }
1173        (0..RENDER_QUANTUM_SIZE_USIZE)
1174            .map(|frame| {
1175                let channels = outputs
1176                    .iter()
1177                    .flat_map(|port| {
1178                        port.iter()
1179                            .map(|channel| channel.get(frame).copied().unwrap_or(0.0))
1180                    })
1181                    .collect::<Vec<_>>();
1182                if channels.is_empty() {
1183                    return AudioBus::silent(1);
1184                }
1185                AudioBus::from_channels(channels)
1186            })
1187            .collect()
1188    }
1189}
1190
1191impl fmt::Debug for AudioWorkletProcessorNode {
1192    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1193        f.debug_struct("AudioWorkletProcessorNode")
1194            .field("active", &self.active.load(Ordering::SeqCst))
1195            .finish()
1196    }
1197}
1198
1199#[derive(Debug, Clone)]
1200struct AnalyserState {
1201    buffer: Vec<f32>,
1202    previous_frequency: Vec<f32>,
1203    frequency_data: Vec<f32>,
1204    frequency_dirty: bool,
1205    cursor: usize,
1206    filled: bool,
1207    min_decibels: f32,
1208    max_decibels: f32,
1209    smoothing_time_constant: f32,
1210}
1211
1212impl AnalyserState {
1213    fn new(size: usize) -> Self {
1214        let size = size.max(1);
1215        Self {
1216            buffer: vec![0.0; size],
1217            previous_frequency: vec![0.0; size / 2],
1218            frequency_data: vec![f32::NEG_INFINITY; size / 2],
1219            frequency_dirty: true,
1220            cursor: 0,
1221            filled: false,
1222            min_decibels: -100.0,
1223            max_decibels: -30.0,
1224            smoothing_time_constant: 0.8,
1225        }
1226    }
1227
1228    fn resize(&mut self, size: usize) {
1229        let min_decibels = self.min_decibels;
1230        let max_decibels = self.max_decibels;
1231        let smoothing_time_constant = self.smoothing_time_constant;
1232        *self = Self::new(size);
1233        self.min_decibels = min_decibels;
1234        self.max_decibels = max_decibels;
1235        self.smoothing_time_constant = smoothing_time_constant;
1236    }
1237
1238    fn push_bus(&mut self, bus: &AudioBus) {
1239        self.push_sample(downmix_bus_to_mono(bus.clone()));
1240    }
1241
1242    fn push_sample(&mut self, sample: f32) {
1243        self.buffer[self.cursor] = sample;
1244        self.frequency_dirty = true;
1245        self.cursor = (self.cursor + 1) % self.buffer.len();
1246        if self.cursor == 0 {
1247            self.filled = true;
1248        }
1249    }
1250
1251    fn time_domain_data(&self) -> Vec<f32> {
1252        if self.filled {
1253            self.buffer[self.cursor..]
1254                .iter()
1255                .chain(self.buffer[..self.cursor].iter())
1256                .copied()
1257                .collect()
1258        } else {
1259            self.buffer.clone()
1260        }
1261    }
1262
1263    fn observed_time_domain_data(&self) -> Vec<f32> {
1264        if self.filled {
1265            self.time_domain_data()
1266        } else {
1267            self.buffer[..self.cursor].to_vec()
1268        }
1269    }
1270
1271    fn peak(&self) -> f32 {
1272        self.observed_time_domain_data()
1273            .into_iter()
1274            .map(f32::abs)
1275            .fold(0.0, f32::max)
1276    }
1277
1278    fn rms(&self) -> f32 {
1279        let data = self.observed_time_domain_data();
1280        if data.is_empty() {
1281            return 0.0;
1282        }
1283        let sum = data.iter().map(|sample| sample * sample).sum::<f32>();
1284        (sum / data.len() as f32).sqrt()
1285    }
1286
1287    fn frequency_data(&mut self) -> Vec<f32> {
1288        if !self.frequency_dirty {
1289            return self.frequency_data.clone();
1290        }
1291        let data = self.time_domain_data();
1292        let size = self.buffer.len();
1293        let bin_count = size / 2;
1294        if self.previous_frequency.len() != bin_count {
1295            self.previous_frequency = vec![0.0; bin_count];
1296        }
1297        if self.frequency_data.len() != bin_count {
1298            self.frequency_data = vec![f32::NEG_INFINITY; bin_count];
1299        }
1300        if data.is_empty() || bin_count == 0 {
1301            return Vec::new();
1302        }
1303
1304        let smoothing = self.smoothing_time_constant;
1305        let windowed = data
1306            .iter()
1307            .enumerate()
1308            .map(|(index, sample)| *sample * blackman_window(index, size))
1309            .collect::<Vec<_>>();
1310        let mut bins = Vec::with_capacity(bin_count);
1311        for bin in 0..bin_count {
1312            let mut real = 0.0;
1313            let mut imag = 0.0;
1314            for (index, sample) in windowed.iter().enumerate() {
1315                let angle = TAU * bin as f32 * index as f32 / size as f32;
1316                real += sample * angle.cos();
1317                imag -= sample * angle.sin();
1318            }
1319            let magnitude = (real.mul_add(real, imag * imag)).sqrt() / size as f32;
1320            let previous = self.previous_frequency[bin];
1321            let smoothed = previous * smoothing + magnitude * (1.0 - smoothing);
1322            self.previous_frequency[bin] = smoothed;
1323            bins.push(20.0 * smoothed.max(1.0e-12).log10());
1324        }
1325        self.frequency_data = bins;
1326        self.frequency_dirty = false;
1327        self.frequency_data.clone()
1328    }
1329}
1330
1331fn blackman_window(index: usize, size: usize) -> f32 {
1332    if size == 0 {
1333        return 0.0;
1334    }
1335    let phase = TAU * index as f32 / size as f32;
1336    0.42 - 0.5 * phase.cos() + 0.08 * (2.0 * phase).cos()
1337}
1338
1339fn sample_to_byte(sample: f32) -> u8 {
1340    (128.0 * (1.0 + sample.clamp(-1.0, 1.0)))
1341        .floor()
1342        .clamp(0.0, 255.0) as u8
1343}
1344
1345fn decibels_to_byte(decibels: f32, min_decibels: f32, max_decibels: f32) -> u8 {
1346    if max_decibels <= min_decibels {
1347        return 0;
1348    }
1349    (((decibels - min_decibels) / (max_decibels - min_decibels)).clamp(0.0, 1.0) * 255.0).floor()
1350        as u8
1351}
1352
1353#[cfg(test)]
1354mod analyser_byte_conversion_tests {
1355    use super::*;
1356
1357    #[test]
1358    fn decibels_to_byte_floors_scaled_frequency_bins() {
1359        assert_eq!(decibels_to_byte(-65.0, -100.0, -30.0), 127);
1360    }
1361}