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}