1use core::ops::Range;
2
3use firewheel_core::node::NodeError;
4use firewheel_core::{
5 StreamInfo,
6 channel_config::{ChannelConfig, ChannelCount},
7 diff::{Diff, Patch},
8 dsp::{
9 coeff_update::{CoeffUpdateFactor, CoeffUpdateMask},
10 filter::{
11 butterworth::Q_BUTTERWORTH_ORD2,
12 smoothing_filter::DEFAULT_SMOOTH_SECONDS,
13 svf::{SvfCoeff, SvfCoeffSimd, SvfStateSimd},
14 },
15 volume::{Volume, db_to_amp},
16 },
17 event::ProcEvents,
18 node::{
19 AudioNode, AudioNodeInfo, AudioNodeProcessor, ConstructProcessorContext, ProcBuffers,
20 ProcExtra, ProcInfo, ProcStreamCtx, ProcessStatus,
21 },
22 param::smoother::{SmoothedParam, SmootherConfig},
23};
24
25pub const DEFAULT_Q: f32 = Q_BUTTERWORTH_ORD2;
26pub const DEFAULT_MIN_HZ: f32 = 20.0;
27pub const DEFAULT_MAX_HZ: f32 = 20_480.0;
28pub const DEFAULT_MIN_Q: f32 = 0.02;
29pub const DEFAULT_MAX_Q: f32 = 40.0;
30pub const DEFAULT_MIN_GAIN_DB: f32 = -24.0;
31pub const DEFAULT_MAX_GAIN_DB: f32 = 24.0;
32
33#[derive(Debug, Clone, PartialEq)]
35#[cfg_attr(feature = "bevy", derive(bevy_ecs::prelude::Component))]
36#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
37#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
38pub struct SvfNodeConfig {
39 pub freq_range: Range<f32>,
46
47 pub q_range: Range<f32>,
54
55 pub gain_db_range: Range<f32>,
62}
63
64impl Default for SvfNodeConfig {
65 fn default() -> Self {
66 Self {
67 freq_range: DEFAULT_MIN_HZ..DEFAULT_MAX_HZ,
68 q_range: DEFAULT_MIN_Q..DEFAULT_MAX_Q,
69 gain_db_range: DEFAULT_MIN_GAIN_DB..DEFAULT_MAX_GAIN_DB,
70 }
71 }
72}
73
74#[derive(Default, Diff, Patch, Debug, Clone, Copy, PartialEq, Eq)]
76#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
77#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
78pub enum SvfType {
79 #[default]
81 Lowpass,
82 LowpassX2,
84 Highpass,
86 HighpassX2,
88 Bandpass,
90 LowShelf,
91 HighShelf,
92 Bell,
93 Notch,
94 Allpass,
95}
96
97pub type SvfMonoNode = SvfNode<1>;
98pub type SvfStereoNode = SvfNode<2>;
99
100#[cfg_attr(feature = "bevy", derive(bevy_ecs::prelude::Component))]
105#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
106#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
107#[derive(Diff, Patch, Debug, Clone, Copy, PartialEq)]
108pub struct SvfNode<const CHANNELS: usize = 2> {
109 pub filter_type: SvfType,
111
112 pub cutoff_hz: f32,
114 pub q_factor: f32,
125 pub gain: Volume,
132
133 pub smooth_seconds: f32,
139
140 pub coeff_update_factor: CoeffUpdateFactor,
152}
153
154impl<const CHANNELS: usize> Default for SvfNode<CHANNELS> {
155 fn default() -> Self {
156 Self {
157 filter_type: SvfType::Lowpass,
158 cutoff_hz: 1_000.0,
159 q_factor: DEFAULT_Q,
160 gain: Volume::Decibels(0.0),
161 smooth_seconds: DEFAULT_SMOOTH_SECONDS,
162 coeff_update_factor: CoeffUpdateFactor::default(),
163 }
164 }
165}
166
167impl<const CHANNELS: usize> SvfNode<CHANNELS> {
168 pub const fn from_lowpass(cutoff_hz: f32, q_factor: f32) -> Self {
174 Self {
175 filter_type: SvfType::Lowpass,
176 cutoff_hz,
177 q_factor,
178 gain: Volume::UNITY_GAIN,
179 smooth_seconds: DEFAULT_SMOOTH_SECONDS,
180 coeff_update_factor: CoeffUpdateFactor(5),
181 }
182 }
183
184 pub const fn from_lowpass_x2(cutoff_hz: f32, q_factor: f32) -> Self {
190 Self {
191 filter_type: SvfType::LowpassX2,
192 cutoff_hz,
193 q_factor,
194 gain: Volume::UNITY_GAIN,
195 smooth_seconds: DEFAULT_SMOOTH_SECONDS,
196 coeff_update_factor: CoeffUpdateFactor(5),
197 }
198 }
199
200 pub const fn from_highpass(cutoff_hz: f32, q_factor: f32) -> Self {
206 Self {
207 filter_type: SvfType::Highpass,
208 cutoff_hz,
209 q_factor,
210 gain: Volume::UNITY_GAIN,
211 smooth_seconds: DEFAULT_SMOOTH_SECONDS,
212 coeff_update_factor: CoeffUpdateFactor(5),
213 }
214 }
215
216 pub const fn from_highpass_x2(cutoff_hz: f32, q_factor: f32) -> Self {
222 Self {
223 filter_type: SvfType::HighpassX2,
224 cutoff_hz,
225 q_factor,
226 gain: Volume::UNITY_GAIN,
227 smooth_seconds: DEFAULT_SMOOTH_SECONDS,
228 coeff_update_factor: CoeffUpdateFactor(5),
229 }
230 }
231
232 pub const fn from_bandpass(cutoff_hz: f32, q_factor: f32) -> Self {
238 Self {
239 filter_type: SvfType::Bandpass,
240 cutoff_hz,
241 q_factor,
242 gain: Volume::UNITY_GAIN,
243 smooth_seconds: DEFAULT_SMOOTH_SECONDS,
244 coeff_update_factor: CoeffUpdateFactor(5),
245 }
246 }
247
248 pub const fn from_lowshelf(cutoff_hz: f32, gain: Volume, q_factor: f32) -> Self {
255 Self {
256 filter_type: SvfType::LowShelf,
257 cutoff_hz,
258 q_factor,
259 gain,
260 smooth_seconds: DEFAULT_SMOOTH_SECONDS,
261 coeff_update_factor: CoeffUpdateFactor(5),
262 }
263 }
264
265 pub const fn from_highshelf(cutoff_hz: f32, gain: Volume, q_factor: f32) -> Self {
272 Self {
273 filter_type: SvfType::HighShelf,
274 cutoff_hz,
275 q_factor,
276 gain,
277 smooth_seconds: DEFAULT_SMOOTH_SECONDS,
278 coeff_update_factor: CoeffUpdateFactor(5),
279 }
280 }
281
282 pub const fn from_bell(cutoff_hz: f32, gain: Volume, q_factor: f32) -> Self {
289 Self {
290 filter_type: SvfType::Bell,
291 cutoff_hz,
292 q_factor,
293 gain,
294 smooth_seconds: DEFAULT_SMOOTH_SECONDS,
295 coeff_update_factor: CoeffUpdateFactor(5),
296 }
297 }
298
299 pub const fn from_notch(cutoff_hz: f32, q_factor: f32) -> Self {
305 Self {
306 filter_type: SvfType::Notch,
307 cutoff_hz,
308 q_factor,
309 gain: Volume::UNITY_GAIN,
310 smooth_seconds: DEFAULT_SMOOTH_SECONDS,
311 coeff_update_factor: CoeffUpdateFactor(5),
312 }
313 }
314
315 pub const fn from_allpass(cutoff_hz: f32, q_factor: f32) -> Self {
321 Self {
322 filter_type: SvfType::Allpass,
323 cutoff_hz,
324 q_factor,
325 gain: Volume::UNITY_GAIN,
326 smooth_seconds: DEFAULT_SMOOTH_SECONDS,
327 coeff_update_factor: CoeffUpdateFactor(5),
328 }
329 }
330
331 pub const fn set_lowpass(&mut self, cutoff_hz: f32, q_factor: f32) {
336 self.filter_type = SvfType::Lowpass;
337 self.cutoff_hz = cutoff_hz;
338 self.q_factor = q_factor;
339 }
340
341 pub const fn set_lowpass_x2(&mut self, cutoff_hz: f32, q_factor: f32) {
346 self.filter_type = SvfType::LowpassX2;
347 self.cutoff_hz = cutoff_hz;
348 self.q_factor = q_factor;
349 }
350
351 pub const fn set_highpass(&mut self, cutoff_hz: f32, q_factor: f32) {
356 self.filter_type = SvfType::Highpass;
357 self.cutoff_hz = cutoff_hz;
358 self.q_factor = q_factor;
359 }
360
361 pub const fn set_highpass_x2(&mut self, cutoff_hz: f32, q_factor: f32) {
366 self.filter_type = SvfType::HighpassX2;
367 self.cutoff_hz = cutoff_hz;
368 self.q_factor = q_factor;
369 }
370
371 pub const fn set_bandpass(&mut self, cutoff_hz: f32, q_factor: f32) {
376 self.filter_type = SvfType::Bandpass;
377 self.cutoff_hz = cutoff_hz;
378 self.q_factor = q_factor;
379 }
380
381 pub const fn set_lowshelf(&mut self, cutoff_hz: f32, gain: Volume, q_factor: f32) {
387 self.filter_type = SvfType::LowShelf;
388 self.cutoff_hz = cutoff_hz;
389 self.gain = gain;
390 self.q_factor = q_factor;
391 }
392
393 pub const fn set_highshelf(&mut self, cutoff_hz: f32, gain: Volume, q_factor: f32) {
399 self.filter_type = SvfType::HighShelf;
400 self.cutoff_hz = cutoff_hz;
401 self.gain = gain;
402 self.q_factor = q_factor;
403 }
404
405 pub const fn set_bell(&mut self, cutoff_hz: f32, gain: Volume, q_factor: f32) {
411 self.filter_type = SvfType::Bell;
412 self.cutoff_hz = cutoff_hz;
413 self.gain = gain;
414 self.q_factor = q_factor;
415 }
416
417 pub const fn set_notch(&mut self, cutoff_hz: f32, q_factor: f32) {
422 self.filter_type = SvfType::Notch;
423 self.cutoff_hz = cutoff_hz;
424 self.q_factor = q_factor;
425 }
426
427 pub const fn set_allpass(&mut self, cutoff_hz: f32, q_factor: f32) {
432 self.filter_type = SvfType::Allpass;
433 self.cutoff_hz = cutoff_hz;
434 self.q_factor = q_factor;
435 }
436
437 pub const fn set_gain_linear(&mut self, linear: f32) {
448 self.gain = Volume::Linear(linear);
449 }
450
451 pub const fn set_gain_decibels(&mut self, decibels: f32) {
459 self.gain = Volume::Decibels(decibels);
460 }
461}
462
463impl<const CHANNELS: usize> AudioNode for SvfNode<CHANNELS> {
464 type Configuration = SvfNodeConfig;
465
466 fn info(&self, _config: &Self::Configuration) -> Result<AudioNodeInfo, NodeError> {
467 Ok(AudioNodeInfo::new()
468 .debug_name("svf")
469 .channel_config(ChannelConfig {
470 num_inputs: ChannelCount::new(CHANNELS as u32).unwrap(),
471 num_outputs: ChannelCount::new(CHANNELS as u32).unwrap(),
472 })
473 .in_place_buffers(true))
477 }
478
479 fn construct_processor(
480 &self,
481 config: &Self::Configuration,
482 cx: ConstructProcessorContext,
483 ) -> Result<impl AudioNodeProcessor, NodeError> {
484 let cutoff_hz = self
485 .cutoff_hz
486 .clamp(config.freq_range.start, config.freq_range.end);
487 let q_factor = self
488 .q_factor
489 .clamp(config.q_range.start, config.q_range.end);
490
491 let min_gain = db_to_amp(config.gain_db_range.start);
492 let max_gain = db_to_amp(config.gain_db_range.end);
493 let mut gain = self.gain.amp().clamp(min_gain, max_gain);
494 if gain > 0.99999 && gain < 1.00001 {
495 gain = 1.0;
496 }
497
498 let mut new_self = Processor {
499 filter_0: SvfStateSimd::<CHANNELS>::default(),
500 filter_1: SvfStateSimd::<CHANNELS>::default(),
501 num_filters: 0,
502 filter_0_coeff: SvfCoeffSimd::<CHANNELS>::default(),
503 filter_1_coeff: SvfCoeffSimd::<CHANNELS>::default(),
504 filter_type: self.filter_type,
505 cutoff_hz: SmoothedParam::new(
506 cutoff_hz,
507 config.freq_range.end - config.freq_range.start,
508 SmootherConfig {
509 smooth_seconds: self.smooth_seconds,
510 ..Default::default()
511 },
512 cx.stream_info.sample_rate,
513 ),
514 q_factor: SmoothedParam::new(
515 q_factor,
516 config.q_range.end - config.q_range.start,
517 SmootherConfig {
518 smooth_seconds: self.smooth_seconds,
519 ..Default::default()
520 },
521 cx.stream_info.sample_rate,
522 ),
523 gain: SmoothedParam::new(
524 gain,
525 max_gain - min_gain,
526 SmootherConfig {
527 smooth_seconds: self.smooth_seconds,
528 ..Default::default()
529 },
530 cx.stream_info.sample_rate,
531 ),
532 freq_range: config.freq_range.clone(),
533 q_range: config.q_range.clone(),
534 gain_range: min_gain..max_gain,
535 coeff_update_mask: self.coeff_update_factor.mask(),
536 params_changed: false,
537 };
538
539 new_self.update_coefficients(
540 new_self.cutoff_hz.target_value(),
541 new_self.q_factor.target_value(),
542 new_self.gain.target_value(),
543 cx.stream_info.sample_rate_recip as f32,
544 );
545
546 Ok(new_self)
547 }
548}
549
550struct Processor<const CHANNELS: usize> {
551 filter_0: SvfStateSimd<CHANNELS>,
552 filter_1: SvfStateSimd<CHANNELS>,
553 num_filters: usize,
554
555 filter_0_coeff: SvfCoeffSimd<CHANNELS>,
556 filter_1_coeff: SvfCoeffSimd<CHANNELS>,
557
558 filter_type: SvfType,
559 cutoff_hz: SmoothedParam,
560 q_factor: SmoothedParam,
561 gain: SmoothedParam,
562
563 freq_range: Range<f32>,
564 q_range: Range<f32>,
565 gain_range: Range<f32>,
566 coeff_update_mask: CoeffUpdateMask,
567 params_changed: bool,
568}
569
570impl<const CHANNELS: usize> Processor<CHANNELS> {
571 #[cold]
572 #[inline(never)]
573 fn update_coefficients(&mut self, cutoff_hz: f32, q: f32, gain: f32, sample_rate_recip: f32) {
574 match self.filter_type {
575 SvfType::Lowpass => {
576 self.num_filters = 1;
577
578 self.filter_0_coeff =
579 SvfCoeffSimd::splat(SvfCoeff::lowpass_ord2(cutoff_hz, q, sample_rate_recip));
580 }
581 SvfType::LowpassX2 => {
582 self.num_filters = 2;
583
584 let [coeff_0, coeff_1] = SvfCoeff::lowpass_ord4(cutoff_hz, q, sample_rate_recip);
585 self.filter_0_coeff = SvfCoeffSimd::splat(coeff_0);
586 self.filter_1_coeff = SvfCoeffSimd::splat(coeff_1);
587 }
588 SvfType::Highpass => {
589 self.num_filters = 1;
590
591 self.filter_0_coeff =
592 SvfCoeffSimd::splat(SvfCoeff::highpass_ord2(cutoff_hz, q, sample_rate_recip));
593 }
594 SvfType::HighpassX2 => {
595 self.num_filters = 2;
596
597 let [coeff_0, coeff_1] = SvfCoeff::highpass_ord4(cutoff_hz, q, sample_rate_recip);
598 self.filter_0_coeff = SvfCoeffSimd::splat(coeff_0);
599 self.filter_1_coeff = SvfCoeffSimd::splat(coeff_1);
600 }
601 SvfType::Bandpass => {
602 self.num_filters = 2;
603
604 self.filter_0_coeff =
605 SvfCoeffSimd::splat(SvfCoeff::lowpass_ord2(cutoff_hz, q, sample_rate_recip));
606 self.filter_1_coeff =
607 SvfCoeffSimd::splat(SvfCoeff::highpass_ord2(cutoff_hz, q, sample_rate_recip));
608 }
609 SvfType::LowShelf => {
610 self.num_filters = 1;
611
612 self.filter_0_coeff =
613 SvfCoeffSimd::splat(SvfCoeff::low_shelf(cutoff_hz, q, gain, sample_rate_recip));
614 }
615 SvfType::HighShelf => {
616 self.num_filters = 1;
617
618 self.filter_0_coeff = SvfCoeffSimd::splat(SvfCoeff::high_shelf(
619 cutoff_hz,
620 q,
621 gain,
622 sample_rate_recip,
623 ));
624 }
625 SvfType::Bell => {
626 self.num_filters = 1;
627
628 self.filter_0_coeff =
629 SvfCoeffSimd::splat(SvfCoeff::bell(cutoff_hz, q, gain, sample_rate_recip));
630 }
631 SvfType::Notch => {
632 self.num_filters = 1;
633
634 self.filter_0_coeff =
635 SvfCoeffSimd::splat(SvfCoeff::notch(cutoff_hz, q, sample_rate_recip));
636 }
637 SvfType::Allpass => {
638 self.num_filters = 1;
639
640 self.filter_0_coeff =
641 SvfCoeffSimd::splat(SvfCoeff::allpass(cutoff_hz, q, sample_rate_recip));
642 }
643 }
644
645 if self.num_filters == 1 {
646 self.filter_1.reset();
647 }
648 }
649
650 fn smoothing_loop_single(&mut self, info: &ProcInfo, outputs: &mut [&mut [f32]]) {
653 assert!(outputs.len() == CHANNELS);
654 for ch in outputs.iter() {
655 assert!(ch.len() >= info.frames);
656 }
657
658 for i in 0..info.frames {
659 let cutoff_hz = self.cutoff_hz.next_smoothed();
660 let q = self.q_factor.next_smoothed();
661
662 if self.coeff_update_mask.do_update(i) {
664 self.update_coefficients(cutoff_hz, q, 0.0, info.sample_rate_recip as f32);
665 }
666
667 let s: [f32; CHANNELS] = core::array::from_fn(|ch_i| {
668 unsafe { *outputs.get_unchecked(ch_i).get_unchecked(i) }
670 });
671
672 let out = self.filter_0.process(s, &self.filter_0_coeff);
673
674 for (ch_i, &o) in out.iter().enumerate().take(CHANNELS) {
675 unsafe {
677 *outputs.get_unchecked_mut(ch_i).get_unchecked_mut(i) = o;
678 }
679 }
680 }
681 }
682
683 fn smoothing_loop_single_with_gain(&mut self, info: &ProcInfo, outputs: &mut [&mut [f32]]) {
686 assert!(outputs.len() == CHANNELS);
687 for ch in outputs.iter() {
688 assert!(ch.len() >= info.frames);
689 }
690
691 for i in 0..info.frames {
692 let cutoff_hz = self.cutoff_hz.next_smoothed();
693 let q = self.q_factor.next_smoothed();
694 let gain = self.gain.next_smoothed();
695
696 if self.coeff_update_mask.do_update(i) {
698 self.update_coefficients(cutoff_hz, q, gain, info.sample_rate_recip as f32);
699 }
700
701 let s: [f32; CHANNELS] = core::array::from_fn(|ch_i| {
702 unsafe { *outputs.get_unchecked(ch_i).get_unchecked(i) }
704 });
705
706 let out = self.filter_0.process(s, &self.filter_0_coeff);
707
708 for (ch_i, &o) in out.iter().enumerate().take(CHANNELS) {
709 unsafe {
711 *outputs.get_unchecked_mut(ch_i).get_unchecked_mut(i) = o;
712 }
713 }
714 }
715 }
716
717 fn smoothing_loop_dual(&mut self, info: &ProcInfo, outputs: &mut [&mut [f32]]) {
720 assert!(outputs.len() == CHANNELS);
721 for ch in outputs.iter() {
722 assert!(ch.len() >= info.frames);
723 }
724
725 for i in 0..info.frames {
726 let cutoff_hz = self.cutoff_hz.next_smoothed();
727 let q = self.q_factor.next_smoothed();
728
729 if self.coeff_update_mask.do_update(i) {
731 self.update_coefficients(cutoff_hz, q, 0.0, info.sample_rate_recip as f32);
732 }
733
734 let s: [f32; CHANNELS] = core::array::from_fn(|ch_i| {
735 unsafe { *outputs.get_unchecked(ch_i).get_unchecked(i) }
737 });
738
739 let s = self.filter_0.process(s, &self.filter_0_coeff);
740 let out = self.filter_1.process(s, &self.filter_1_coeff);
741
742 for (ch_i, &o) in out.iter().enumerate().take(CHANNELS) {
743 unsafe {
745 *outputs.get_unchecked_mut(ch_i).get_unchecked_mut(i) = o;
746 }
747 }
748 }
749 }
750}
751
752impl<const CHANNELS: usize> Processor<CHANNELS> {
753 fn reset(&mut self) {
754 self.cutoff_hz.reset_to_target();
755 self.filter_0.reset();
756 self.filter_1.reset();
757 }
758}
759
760impl<const CHANNELS: usize> AudioNodeProcessor for Processor<CHANNELS> {
761 fn events(&mut self, info: &ProcInfo, events: &mut ProcEvents, _extra: &mut ProcExtra) {
762 for patch in events.drain_patches::<SvfNode<CHANNELS>>() {
763 match patch {
764 SvfNodePatch::FilterType(filter_type) => {
765 self.params_changed = true;
766 self.filter_type = filter_type;
767 }
768 SvfNodePatch::CutoffHz(cutoff) => {
769 self.params_changed = true;
770 self.cutoff_hz
771 .set_value(cutoff.clamp(self.freq_range.start, self.freq_range.end));
772 }
773 SvfNodePatch::QFactor(q_factor) => {
774 self.params_changed = true;
775 self.q_factor
776 .set_value(q_factor.clamp(self.q_range.start, self.q_range.end));
777 }
778 SvfNodePatch::Gain(gain) => {
779 self.params_changed = true;
780 let mut gain = gain.amp().clamp(self.gain_range.start, self.gain_range.end);
781 if gain > 0.99999 && gain < 1.00001 {
782 gain = 1.0;
783 }
784 self.gain.set_value(gain);
785 }
786 SvfNodePatch::SmoothSeconds(seconds) => {
787 self.cutoff_hz.set_smooth_seconds(seconds, info.sample_rate);
788 }
789 SvfNodePatch::CoeffUpdateFactor(f) => {
790 self.coeff_update_mask = f.mask();
791 }
792 }
793 }
794 }
795
796 fn bypassed(&mut self, _bypassed: bool) {
797 self.reset();
798 }
799
800 fn process(
801 &mut self,
802 info: &ProcInfo,
803 buffers: ProcBuffers,
804 _extra: &mut ProcExtra,
805 ) -> ProcessStatus {
806 debug_assert_eq!(buffers.inputs.len(), 0);
808
809 if info.out_silence_mask.all_channels_silent(CHANNELS) {
810 self.reset();
815
816 return ProcessStatus::ClearAllOutputs;
817 }
818
819 if self.cutoff_hz.is_smoothing() || self.q_factor.is_smoothing() || self.gain.is_smoothing()
820 {
821 match self.filter_type {
822 SvfType::Lowpass | SvfType::Highpass | SvfType::Notch | SvfType::Allpass => {
823 self.smoothing_loop_single(info, buffers.outputs)
824 }
825 SvfType::LowShelf | SvfType::HighShelf | SvfType::Bell => {
826 self.smoothing_loop_single_with_gain(info, buffers.outputs)
827 }
828 SvfType::LowpassX2 | SvfType::HighpassX2 | SvfType::Bandpass => {
829 self.smoothing_loop_dual(info, buffers.outputs)
830 }
831 }
832
833 if self.cutoff_hz.settle() && self.q_factor.settle() && self.gain.settle() {
834 self.update_coefficients(
835 self.cutoff_hz.target_value(),
836 self.q_factor.target_value(),
837 self.gain.target_value(),
838 info.sample_rate_recip as f32,
839 );
840 }
841 } else {
842 if self.params_changed {
845 self.params_changed = false;
846 self.update_coefficients(
847 self.cutoff_hz.target_value(),
848 self.q_factor.target_value(),
849 self.gain.target_value(),
850 info.sample_rate_recip as f32,
851 );
852 }
853
854 assert!(buffers.outputs.len() == CHANNELS);
855 for ch in buffers.outputs.iter() {
856 assert!(ch.len() >= info.frames);
857 }
858
859 if self.num_filters == 1 {
860 for i in 0..info.frames {
861 let s: [f32; CHANNELS] = core::array::from_fn(|ch_i| {
862 unsafe { *buffers.outputs.get_unchecked(ch_i).get_unchecked(i) }
864 });
865
866 let out = self.filter_0.process(s, &self.filter_0_coeff);
867
868 for (ch_i, &o) in out.iter().enumerate().take(CHANNELS) {
869 unsafe {
871 *buffers.outputs.get_unchecked_mut(ch_i).get_unchecked_mut(i) = o;
872 }
873 }
874 }
875 } else {
876 for i in 0..info.frames {
877 let s: [f32; CHANNELS] = core::array::from_fn(|ch_i| {
878 unsafe { *buffers.outputs.get_unchecked(ch_i).get_unchecked(i) }
880 });
881
882 let s = self.filter_0.process(s, &self.filter_0_coeff);
883 let out = self.filter_1.process(s, &self.filter_1_coeff);
884
885 for (ch_i, &o) in out.iter().enumerate().take(CHANNELS) {
886 unsafe {
888 *buffers.outputs.get_unchecked_mut(ch_i).get_unchecked_mut(i) = o;
889 }
890 }
891 }
892 }
893 }
894
895 ProcessStatus::OutputsModified
896 }
897
898 fn new_stream(&mut self, stream_info: &StreamInfo, _context: &mut ProcStreamCtx) {
899 self.cutoff_hz.update_sample_rate(stream_info.sample_rate);
900 self.q_factor.update_sample_rate(stream_info.sample_rate);
901 self.gain.update_sample_rate(stream_info.sample_rate);
902
903 self.update_coefficients(
904 self.cutoff_hz.target_value(),
905 self.q_factor.target_value(),
906 self.gain.target_value(),
907 stream_info.sample_rate_recip as f32,
908 );
909 }
910}