1use core::ops::Range;
2
3use firewheel_core::{
4 channel_config::{ChannelConfig, ChannelCount},
5 diff::{Diff, Patch},
6 dsp::{
7 coeff_update::{CoeffUpdateFactor, CoeffUpdateMask},
8 declick::{DeclickFadeCurve, Declicker},
9 filter::{
10 butterworth::Q_BUTTERWORTH_ORD2,
11 smoothing_filter::DEFAULT_SMOOTH_SECONDS,
12 svf::{SvfCoeff, SvfCoeffSimd, SvfStateSimd},
13 },
14 volume::{db_to_amp, Volume},
15 },
16 event::ProcEvents,
17 node::{
18 AudioNode, AudioNodeInfo, AudioNodeProcessor, ConstructProcessorContext, ProcBuffers,
19 ProcExtra, ProcInfo, ProcStreamCtx, ProcessStatus,
20 },
21 param::smoother::{SmoothedParam, SmootherConfig},
22 StreamInfo,
23};
24
25pub const DEFAULT_Q: f32 = Q_BUTTERWORTH_ORD2;
26
27pub const DEFAULT_MIN_HZ: f32 = 20.0;
28pub const DEFAULT_MAX_HZ: f32 = 20_480.0;
29pub const DEFAULT_MIN_Q: f32 = 0.02;
30pub const DEFAULT_MAX_Q: f32 = 40.0;
31pub const DEFAULT_MIN_GAIN_DB: f32 = -24.0;
32pub const DEFAULT_MAX_GAIN_DB: f32 = 24.0;
33
34pub type SvfMonoNode = SvfNode<1>;
35pub type SvfStereoNode = SvfNode<2>;
36
37#[derive(Debug, Clone, PartialEq)]
39#[cfg_attr(feature = "bevy", derive(bevy_ecs::prelude::Component))]
40#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
41#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
42pub struct SvfNodeConfig {
43 pub freq_range: Range<f32>,
50
51 pub q_range: Range<f32>,
58
59 pub gain_db_range: Range<f32>,
66}
67
68impl Default for SvfNodeConfig {
69 fn default() -> Self {
70 Self {
71 freq_range: DEFAULT_MIN_HZ..DEFAULT_MAX_HZ,
72 q_range: DEFAULT_MIN_Q..DEFAULT_MAX_Q,
73 gain_db_range: DEFAULT_MIN_GAIN_DB..DEFAULT_MAX_GAIN_DB,
74 }
75 }
76}
77
78#[derive(Default, Diff, Patch, Debug, Clone, Copy, PartialEq, Eq)]
80#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
81#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
82pub enum SvfType {
83 #[default]
85 Lowpass,
86 LowpassX2,
88 Highpass,
90 HighpassX2,
92 Bandpass,
94 LowShelf,
95 HighShelf,
96 Bell,
97 Notch,
98 Allpass,
99}
100
101#[cfg_attr(feature = "bevy", derive(bevy_ecs::prelude::Component))]
106#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
107#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
108#[derive(Diff, Patch, Debug, Clone, Copy, PartialEq)]
109pub struct SvfNode<const CHANNELS: usize> {
110 pub filter_type: SvfType,
112
113 pub cutoff_hz: f32,
115 pub q_factor: f32,
126 pub gain: Volume,
133 pub enabled: bool,
135
136 pub smooth_seconds: f32,
140
141 pub coeff_update_factor: CoeffUpdateFactor,
153}
154
155impl<const CHANNELS: usize> Default for SvfNode<CHANNELS> {
156 fn default() -> Self {
157 Self {
158 filter_type: SvfType::Lowpass,
159 cutoff_hz: 1_000.0,
160 q_factor: DEFAULT_Q,
161 gain: Volume::Decibels(0.0),
162 enabled: true,
163 smooth_seconds: DEFAULT_SMOOTH_SECONDS,
164 coeff_update_factor: CoeffUpdateFactor(5),
165 }
166 }
167}
168
169impl<const CHANNELS: usize> SvfNode<CHANNELS> {
170 pub const fn from_lowpass(cutoff_hz: f32, q_factor: f32, enabled: bool) -> Self {
176 Self {
177 filter_type: SvfType::Lowpass,
178 cutoff_hz,
179 q_factor,
180 gain: Volume::UNITY_GAIN,
181 enabled,
182 smooth_seconds: DEFAULT_SMOOTH_SECONDS,
183 coeff_update_factor: CoeffUpdateFactor(5),
184 }
185 }
186
187 pub const fn from_lowpass_x2(cutoff_hz: f32, q_factor: f32, enabled: bool) -> Self {
193 Self {
194 filter_type: SvfType::LowpassX2,
195 cutoff_hz,
196 q_factor,
197 gain: Volume::UNITY_GAIN,
198 enabled,
199 smooth_seconds: DEFAULT_SMOOTH_SECONDS,
200 coeff_update_factor: CoeffUpdateFactor(5),
201 }
202 }
203
204 pub const fn from_highpass(cutoff_hz: f32, q_factor: f32, enabled: bool) -> Self {
210 Self {
211 filter_type: SvfType::Highpass,
212 cutoff_hz,
213 q_factor,
214 gain: Volume::UNITY_GAIN,
215 enabled,
216 smooth_seconds: DEFAULT_SMOOTH_SECONDS,
217 coeff_update_factor: CoeffUpdateFactor(5),
218 }
219 }
220
221 pub const fn from_highpass_x2(cutoff_hz: f32, q_factor: f32, enabled: bool) -> Self {
227 Self {
228 filter_type: SvfType::HighpassX2,
229 cutoff_hz,
230 q_factor,
231 gain: Volume::UNITY_GAIN,
232 enabled,
233 smooth_seconds: DEFAULT_SMOOTH_SECONDS,
234 coeff_update_factor: CoeffUpdateFactor(5),
235 }
236 }
237
238 pub const fn from_bandpass(cutoff_hz: f32, q_factor: f32, enabled: bool) -> Self {
244 Self {
245 filter_type: SvfType::Bandpass,
246 cutoff_hz,
247 q_factor,
248 gain: Volume::UNITY_GAIN,
249 enabled,
250 smooth_seconds: DEFAULT_SMOOTH_SECONDS,
251 coeff_update_factor: CoeffUpdateFactor(5),
252 }
253 }
254
255 pub const fn from_lowshelf(cutoff_hz: f32, gain: Volume, q_factor: f32, enabled: bool) -> Self {
262 Self {
263 filter_type: SvfType::LowShelf,
264 cutoff_hz,
265 q_factor,
266 gain,
267 enabled,
268 smooth_seconds: DEFAULT_SMOOTH_SECONDS,
269 coeff_update_factor: CoeffUpdateFactor(5),
270 }
271 }
272
273 pub const fn from_highshelf(
280 cutoff_hz: f32,
281 gain: Volume,
282 q_factor: f32,
283 enabled: bool,
284 ) -> Self {
285 Self {
286 filter_type: SvfType::HighShelf,
287 cutoff_hz,
288 q_factor,
289 gain,
290 enabled,
291 smooth_seconds: DEFAULT_SMOOTH_SECONDS,
292 coeff_update_factor: CoeffUpdateFactor(5),
293 }
294 }
295
296 pub const fn from_bell(cutoff_hz: f32, gain: Volume, q_factor: f32, enabled: bool) -> Self {
303 Self {
304 filter_type: SvfType::Bell,
305 cutoff_hz,
306 q_factor,
307 gain,
308 enabled,
309 smooth_seconds: DEFAULT_SMOOTH_SECONDS,
310 coeff_update_factor: CoeffUpdateFactor(5),
311 }
312 }
313
314 pub const fn from_notch(cutoff_hz: f32, q_factor: f32, enabled: bool) -> Self {
320 Self {
321 filter_type: SvfType::Notch,
322 cutoff_hz,
323 q_factor,
324 gain: Volume::UNITY_GAIN,
325 enabled,
326 smooth_seconds: DEFAULT_SMOOTH_SECONDS,
327 coeff_update_factor: CoeffUpdateFactor(5),
328 }
329 }
330
331 pub const fn from_allpass(cutoff_hz: f32, q_factor: f32, enabled: bool) -> Self {
337 Self {
338 filter_type: SvfType::Allpass,
339 cutoff_hz,
340 q_factor,
341 gain: Volume::UNITY_GAIN,
342 enabled,
343 smooth_seconds: DEFAULT_SMOOTH_SECONDS,
344 coeff_update_factor: CoeffUpdateFactor(5),
345 }
346 }
347
348 pub const fn set_lowpass(&mut self, cutoff_hz: f32, q_factor: f32) {
353 self.filter_type = SvfType::Lowpass;
354 self.cutoff_hz = cutoff_hz;
355 self.q_factor = q_factor;
356 }
357
358 pub const fn set_lowpass_x2(&mut self, cutoff_hz: f32, q_factor: f32) {
363 self.filter_type = SvfType::LowpassX2;
364 self.cutoff_hz = cutoff_hz;
365 self.q_factor = q_factor;
366 }
367
368 pub const fn set_highpass(&mut self, cutoff_hz: f32, q_factor: f32) {
373 self.filter_type = SvfType::Highpass;
374 self.cutoff_hz = cutoff_hz;
375 self.q_factor = q_factor;
376 }
377
378 pub const fn set_highpass_x2(&mut self, cutoff_hz: f32, q_factor: f32) {
383 self.filter_type = SvfType::HighpassX2;
384 self.cutoff_hz = cutoff_hz;
385 self.q_factor = q_factor;
386 }
387
388 pub const fn set_bandpass(&mut self, cutoff_hz: f32, q_factor: f32) {
393 self.filter_type = SvfType::Bandpass;
394 self.cutoff_hz = cutoff_hz;
395 self.q_factor = q_factor;
396 }
397
398 pub const fn set_lowshelf(&mut self, cutoff_hz: f32, gain: Volume, q_factor: f32) {
404 self.filter_type = SvfType::LowShelf;
405 self.cutoff_hz = cutoff_hz;
406 self.gain = gain;
407 self.q_factor = q_factor;
408 }
409
410 pub const fn set_highshelf(&mut self, cutoff_hz: f32, gain: Volume, q_factor: f32) {
416 self.filter_type = SvfType::HighShelf;
417 self.cutoff_hz = cutoff_hz;
418 self.gain = gain;
419 self.q_factor = q_factor;
420 }
421
422 pub const fn set_bell(&mut self, cutoff_hz: f32, gain: Volume, q_factor: f32) {
428 self.filter_type = SvfType::Bell;
429 self.cutoff_hz = cutoff_hz;
430 self.gain = gain;
431 self.q_factor = q_factor;
432 }
433
434 pub const fn set_notch(&mut self, cutoff_hz: f32, q_factor: f32) {
439 self.filter_type = SvfType::Notch;
440 self.cutoff_hz = cutoff_hz;
441 self.q_factor = q_factor;
442 }
443
444 pub const fn set_allpass(&mut self, cutoff_hz: f32, q_factor: f32) {
449 self.filter_type = SvfType::Allpass;
450 self.cutoff_hz = cutoff_hz;
451 self.q_factor = q_factor;
452 }
453
454 pub const fn set_gain_linear(&mut self, linear: f32) {
465 self.gain = Volume::Linear(linear);
466 }
467
468 pub const fn set_gain_decibels(&mut self, decibels: f32) {
476 self.gain = Volume::Decibels(decibels);
477 }
478}
479
480impl<const CHANNELS: usize> AudioNode for SvfNode<CHANNELS> {
481 type Configuration = SvfNodeConfig;
482
483 fn info(&self, _config: &Self::Configuration) -> AudioNodeInfo {
484 AudioNodeInfo::new()
485 .debug_name("svf")
486 .channel_config(ChannelConfig {
487 num_inputs: ChannelCount::new(CHANNELS as u32).unwrap(),
488 num_outputs: ChannelCount::new(CHANNELS as u32).unwrap(),
489 })
490 }
491
492 fn construct_processor(
493 &self,
494 config: &Self::Configuration,
495 cx: ConstructProcessorContext,
496 ) -> impl AudioNodeProcessor {
497 let cutoff_hz = self
498 .cutoff_hz
499 .clamp(config.freq_range.start, config.freq_range.end);
500 let q_factor = self
501 .q_factor
502 .clamp(config.q_range.start, config.q_range.end);
503
504 let min_gain = db_to_amp(config.gain_db_range.start);
505 let max_gain = db_to_amp(config.gain_db_range.end);
506 let mut gain = self.gain.amp().clamp(min_gain, max_gain);
507 if gain > 0.99999 && gain < 1.00001 {
508 gain = 1.0;
509 }
510
511 let mut new_self = Processor {
512 filter_0: SvfStateSimd::<CHANNELS>::default(),
513 filter_1: SvfStateSimd::<CHANNELS>::default(),
514 num_filters: 0,
515 filter_0_coeff: SvfCoeffSimd::<CHANNELS>::default(),
516 filter_1_coeff: SvfCoeffSimd::<CHANNELS>::default(),
517 filter_type: self.filter_type,
518 cutoff_hz: SmoothedParam::new(
519 cutoff_hz,
520 SmootherConfig {
521 smooth_seconds: self.smooth_seconds,
522 ..Default::default()
523 },
524 cx.stream_info.sample_rate,
525 ),
526 q_factor: SmoothedParam::new(
527 q_factor,
528 SmootherConfig {
529 smooth_seconds: self.smooth_seconds,
530 ..Default::default()
531 },
532 cx.stream_info.sample_rate,
533 ),
534 gain: SmoothedParam::new(
535 gain,
536 SmootherConfig {
537 smooth_seconds: self.smooth_seconds,
538 ..Default::default()
539 },
540 cx.stream_info.sample_rate,
541 ),
542 enable_declicker: Declicker::from_enabled(self.enabled),
543 freq_range: config.freq_range.clone(),
544 q_range: config.q_range.clone(),
545 gain_range: min_gain..max_gain,
546 coeff_update_mask: self.coeff_update_factor.mask(),
547 };
548
549 new_self.calc_coefficients(cx.stream_info.sample_rate_recip as f32);
550
551 new_self
552 }
553}
554
555struct Processor<const CHANNELS: usize> {
556 filter_0: SvfStateSimd<CHANNELS>,
557 filter_1: SvfStateSimd<CHANNELS>,
558 num_filters: usize,
559
560 filter_0_coeff: SvfCoeffSimd<CHANNELS>,
561 filter_1_coeff: SvfCoeffSimd<CHANNELS>,
562
563 filter_type: SvfType,
564 cutoff_hz: SmoothedParam,
565 q_factor: SmoothedParam,
566 gain: SmoothedParam,
567
568 enable_declicker: Declicker,
569
570 freq_range: Range<f32>,
571 q_range: Range<f32>,
572 gain_range: Range<f32>,
573 coeff_update_mask: CoeffUpdateMask,
574}
575
576impl<const CHANNELS: usize> Processor<CHANNELS> {
577 pub fn calc_coefficients(&mut self, sample_rate_recip: f32) {
578 let cutoff_hz = self.cutoff_hz.target_value();
579 let q = self.q_factor.target_value();
580 let gain = self.gain.target_value();
581
582 match self.filter_type {
583 SvfType::Lowpass => {
584 self.num_filters = 1;
585
586 self.filter_0_coeff =
587 SvfCoeffSimd::splat(SvfCoeff::lowpass_ord2(cutoff_hz, q, sample_rate_recip));
588 }
589 SvfType::LowpassX2 => {
590 self.num_filters = 2;
591
592 let [coeff_0, coeff_1] = SvfCoeff::lowpass_ord4(cutoff_hz, q, sample_rate_recip);
593 self.filter_0_coeff = SvfCoeffSimd::splat(coeff_0);
594 self.filter_1_coeff = SvfCoeffSimd::splat(coeff_1);
595 }
596 SvfType::Highpass => {
597 self.num_filters = 1;
598
599 self.filter_0_coeff =
600 SvfCoeffSimd::splat(SvfCoeff::highpass_ord2(cutoff_hz, q, sample_rate_recip));
601 }
602 SvfType::HighpassX2 => {
603 self.num_filters = 2;
604
605 let [coeff_0, coeff_1] = SvfCoeff::highpass_ord4(cutoff_hz, q, sample_rate_recip);
606 self.filter_0_coeff = SvfCoeffSimd::splat(coeff_0);
607 self.filter_1_coeff = SvfCoeffSimd::splat(coeff_1);
608 }
609 SvfType::Bandpass => {
610 self.num_filters = 2;
611
612 self.filter_0_coeff =
613 SvfCoeffSimd::splat(SvfCoeff::lowpass_ord2(cutoff_hz, q, sample_rate_recip));
614 self.filter_1_coeff =
615 SvfCoeffSimd::splat(SvfCoeff::highpass_ord2(cutoff_hz, q, sample_rate_recip));
616 }
617 SvfType::LowShelf => {
618 self.num_filters = 1;
619
620 self.filter_0_coeff =
621 SvfCoeffSimd::splat(SvfCoeff::low_shelf(cutoff_hz, q, gain, sample_rate_recip));
622 }
623 SvfType::HighShelf => {
624 self.num_filters = 1;
625
626 self.filter_0_coeff = SvfCoeffSimd::splat(SvfCoeff::high_shelf(
627 cutoff_hz,
628 q,
629 gain,
630 sample_rate_recip,
631 ));
632 }
633 SvfType::Bell => {
634 self.num_filters = 1;
635
636 self.filter_0_coeff =
637 SvfCoeffSimd::splat(SvfCoeff::bell(cutoff_hz, q, gain, sample_rate_recip));
638 }
639 SvfType::Notch => {
640 self.num_filters = 1;
641
642 self.filter_0_coeff =
643 SvfCoeffSimd::splat(SvfCoeff::notch(cutoff_hz, q, sample_rate_recip));
644 }
645 SvfType::Allpass => {
646 self.num_filters = 1;
647
648 self.filter_0_coeff =
649 SvfCoeffSimd::splat(SvfCoeff::allpass(cutoff_hz, q, sample_rate_recip));
650 }
651 }
652
653 if self.num_filters == 1 {
654 self.filter_1.reset();
655 }
656 }
657
658 fn loop_lowpass_ord2(
659 &mut self,
660 info: &ProcInfo,
661 inputs: &[&[f32]],
662 outputs: &mut [&mut [f32]],
663 ) {
664 assert!(inputs.len() == CHANNELS);
665 assert!(outputs.len() == CHANNELS);
666 for ch in inputs.iter() {
667 assert!(ch.len() >= info.frames);
668 }
669 for ch in outputs.iter() {
670 assert!(ch.len() >= info.frames);
671 }
672
673 for i in 0..info.frames {
674 let cutoff_hz = self.cutoff_hz.next_smoothed();
675 let q = self.q_factor.next_smoothed();
676
677 if self.coeff_update_mask.do_update(i) {
684 self.filter_0_coeff = SvfCoeffSimd::splat(SvfCoeff::lowpass_ord2(
685 cutoff_hz,
686 q,
687 info.sample_rate_recip as f32,
688 ));
689 }
690
691 let s: [f32; CHANNELS] = core::array::from_fn(|ch_i| {
692 unsafe { *inputs.get_unchecked(ch_i).get_unchecked(i) }
694 });
695
696 let out = self.filter_0.process(s, &self.filter_0_coeff);
697
698 for ch_i in 0..CHANNELS {
699 unsafe {
701 *outputs.get_unchecked_mut(ch_i).get_unchecked_mut(i) = out[ch_i];
702 }
703 }
704 }
705 }
706
707 fn loop_lowpass_ord4(
708 &mut self,
709 info: &ProcInfo,
710 inputs: &[&[f32]],
711 outputs: &mut [&mut [f32]],
712 ) {
713 assert!(inputs.len() == CHANNELS);
714 assert!(outputs.len() == CHANNELS);
715 for ch in inputs.iter() {
716 assert!(ch.len() >= info.frames);
717 }
718 for ch in outputs.iter() {
719 assert!(ch.len() >= info.frames);
720 }
721
722 for i in 0..info.frames {
723 let cutoff_hz = self.cutoff_hz.next_smoothed();
724 let q = self.q_factor.next_smoothed();
725
726 if self.coeff_update_mask.do_update(i) {
733 let [coeff_0, coeff_1] =
734 SvfCoeff::lowpass_ord4(cutoff_hz, q, info.sample_rate_recip as f32);
735 self.filter_0_coeff = SvfCoeffSimd::splat(coeff_0);
736 self.filter_1_coeff = SvfCoeffSimd::splat(coeff_1);
737 }
738
739 let s: [f32; CHANNELS] = core::array::from_fn(|ch_i| {
740 unsafe { *inputs.get_unchecked(ch_i).get_unchecked(i) }
742 });
743
744 let s = self.filter_0.process(s, &self.filter_0_coeff);
745 let out = self.filter_1.process(s, &self.filter_1_coeff);
746
747 for ch_i in 0..CHANNELS {
748 unsafe {
750 *outputs.get_unchecked_mut(ch_i).get_unchecked_mut(i) = out[ch_i];
751 }
752 }
753 }
754 }
755
756 fn loop_highpass_ord2(
757 &mut self,
758 info: &ProcInfo,
759 inputs: &[&[f32]],
760 outputs: &mut [&mut [f32]],
761 ) {
762 assert!(inputs.len() == CHANNELS);
763 assert!(outputs.len() == CHANNELS);
764 for ch in inputs.iter() {
765 assert!(ch.len() >= info.frames);
766 }
767 for ch in outputs.iter() {
768 assert!(ch.len() >= info.frames);
769 }
770
771 for i in 0..info.frames {
772 let cutoff_hz = self.cutoff_hz.next_smoothed();
773 let q = self.q_factor.next_smoothed();
774
775 if self.coeff_update_mask.do_update(i) {
782 self.filter_0_coeff = SvfCoeffSimd::splat(SvfCoeff::highpass_ord2(
783 cutoff_hz,
784 q,
785 info.sample_rate_recip as f32,
786 ));
787 }
788
789 let s: [f32; CHANNELS] = core::array::from_fn(|ch_i| {
790 unsafe { *inputs.get_unchecked(ch_i).get_unchecked(i) }
792 });
793
794 let out = self.filter_0.process(s, &self.filter_0_coeff);
795
796 for ch_i in 0..CHANNELS {
797 unsafe {
799 *outputs.get_unchecked_mut(ch_i).get_unchecked_mut(i) = out[ch_i];
800 }
801 }
802 }
803 }
804
805 fn loop_highpass_ord4(
806 &mut self,
807 info: &ProcInfo,
808 inputs: &[&[f32]],
809 outputs: &mut [&mut [f32]],
810 ) {
811 assert!(inputs.len() == CHANNELS);
812 assert!(outputs.len() == CHANNELS);
813 for ch in inputs.iter() {
814 assert!(ch.len() >= info.frames);
815 }
816 for ch in outputs.iter() {
817 assert!(ch.len() >= info.frames);
818 }
819
820 for i in 0..info.frames {
821 let cutoff_hz = self.cutoff_hz.next_smoothed();
822 let q = self.q_factor.next_smoothed();
823
824 if self.coeff_update_mask.do_update(i) {
831 let [coeff_0, coeff_1] =
832 SvfCoeff::highpass_ord4(cutoff_hz, q, info.sample_rate_recip as f32);
833 self.filter_0_coeff = SvfCoeffSimd::splat(coeff_0);
834 self.filter_1_coeff = SvfCoeffSimd::splat(coeff_1);
835 }
836
837 let s: [f32; CHANNELS] = core::array::from_fn(|ch_i| {
838 unsafe { *inputs.get_unchecked(ch_i).get_unchecked(i) }
840 });
841
842 let s = self.filter_0.process(s, &self.filter_0_coeff);
843 let out = self.filter_1.process(s, &self.filter_1_coeff);
844
845 for ch_i in 0..CHANNELS {
846 unsafe {
848 *outputs.get_unchecked_mut(ch_i).get_unchecked_mut(i) = out[ch_i];
849 }
850 }
851 }
852 }
853
854 fn loop_bandpass(&mut self, info: &ProcInfo, inputs: &[&[f32]], outputs: &mut [&mut [f32]]) {
855 assert!(inputs.len() == CHANNELS);
856 assert!(outputs.len() == CHANNELS);
857 for ch in inputs.iter() {
858 assert!(ch.len() >= info.frames);
859 }
860 for ch in outputs.iter() {
861 assert!(ch.len() >= info.frames);
862 }
863
864 for i in 0..info.frames {
865 let cutoff_hz = self.cutoff_hz.next_smoothed();
866 let q = self.q_factor.next_smoothed();
867
868 if self.coeff_update_mask.do_update(i) {
875 self.filter_0_coeff = SvfCoeffSimd::splat(SvfCoeff::lowpass_ord2(
876 cutoff_hz,
877 q,
878 info.sample_rate_recip as f32,
879 ));
880 self.filter_1_coeff = SvfCoeffSimd::splat(SvfCoeff::highpass_ord2(
881 cutoff_hz,
882 q,
883 info.sample_rate_recip as f32,
884 ));
885 }
886
887 let s: [f32; CHANNELS] = core::array::from_fn(|ch_i| {
888 unsafe { *inputs.get_unchecked(ch_i).get_unchecked(i) }
890 });
891
892 let s = self.filter_0.process(s, &self.filter_0_coeff);
893 let out = self.filter_1.process(s, &self.filter_1_coeff);
894
895 for ch_i in 0..CHANNELS {
896 unsafe {
898 *outputs.get_unchecked_mut(ch_i).get_unchecked_mut(i) = out[ch_i];
899 }
900 }
901 }
902 }
903
904 fn loop_low_shelf(&mut self, info: &ProcInfo, inputs: &[&[f32]], outputs: &mut [&mut [f32]]) {
905 assert!(inputs.len() == CHANNELS);
906 assert!(outputs.len() == CHANNELS);
907 for ch in inputs.iter() {
908 assert!(ch.len() >= info.frames);
909 }
910 for ch in outputs.iter() {
911 assert!(ch.len() >= info.frames);
912 }
913
914 for i in 0..info.frames {
915 let cutoff_hz = self.cutoff_hz.next_smoothed();
916 let q = self.q_factor.next_smoothed();
917 let gain = self.gain.next_smoothed();
918
919 if self.coeff_update_mask.do_update(i) {
926 self.filter_0_coeff = SvfCoeffSimd::splat(SvfCoeff::low_shelf(
927 cutoff_hz,
928 q,
929 gain,
930 info.sample_rate_recip as f32,
931 ));
932 }
933
934 let s: [f32; CHANNELS] = core::array::from_fn(|ch_i| {
935 unsafe { *inputs.get_unchecked(ch_i).get_unchecked(i) }
937 });
938
939 let out = self.filter_0.process(s, &self.filter_0_coeff);
940
941 for ch_i in 0..CHANNELS {
942 unsafe {
944 *outputs.get_unchecked_mut(ch_i).get_unchecked_mut(i) = out[ch_i];
945 }
946 }
947 }
948 }
949
950 fn loop_high_shelf(&mut self, info: &ProcInfo, inputs: &[&[f32]], outputs: &mut [&mut [f32]]) {
951 assert!(inputs.len() == CHANNELS);
952 assert!(outputs.len() == CHANNELS);
953 for ch in inputs.iter() {
954 assert!(ch.len() >= info.frames);
955 }
956 for ch in outputs.iter() {
957 assert!(ch.len() >= info.frames);
958 }
959
960 for i in 0..info.frames {
961 let cutoff_hz = self.cutoff_hz.next_smoothed();
962 let q = self.q_factor.next_smoothed();
963 let gain = self.gain.next_smoothed();
964
965 if self.coeff_update_mask.do_update(i) {
972 self.filter_0_coeff = SvfCoeffSimd::splat(SvfCoeff::high_shelf(
973 cutoff_hz,
974 q,
975 gain,
976 info.sample_rate_recip as f32,
977 ));
978 }
979
980 let s: [f32; CHANNELS] = core::array::from_fn(|ch_i| {
981 unsafe { *inputs.get_unchecked(ch_i).get_unchecked(i) }
983 });
984
985 let out = self.filter_0.process(s, &self.filter_0_coeff);
986
987 for ch_i in 0..CHANNELS {
988 unsafe {
990 *outputs.get_unchecked_mut(ch_i).get_unchecked_mut(i) = out[ch_i];
991 }
992 }
993 }
994 }
995
996 fn loop_bell(&mut self, info: &ProcInfo, inputs: &[&[f32]], outputs: &mut [&mut [f32]]) {
997 assert!(inputs.len() == CHANNELS);
998 assert!(outputs.len() == CHANNELS);
999 for ch in inputs.iter() {
1000 assert!(ch.len() >= info.frames);
1001 }
1002 for ch in outputs.iter() {
1003 assert!(ch.len() >= info.frames);
1004 }
1005
1006 for i in 0..info.frames {
1007 let cutoff_hz = self.cutoff_hz.next_smoothed();
1008 let q = self.q_factor.next_smoothed();
1009 let gain = self.gain.next_smoothed();
1010
1011 if self.coeff_update_mask.do_update(i) {
1018 self.filter_0_coeff = SvfCoeffSimd::splat(SvfCoeff::bell(
1019 cutoff_hz,
1020 q,
1021 gain,
1022 info.sample_rate_recip as f32,
1023 ));
1024 }
1025
1026 let s: [f32; CHANNELS] = core::array::from_fn(|ch_i| {
1027 unsafe { *inputs.get_unchecked(ch_i).get_unchecked(i) }
1029 });
1030
1031 let out = self.filter_0.process(s, &self.filter_0_coeff);
1032
1033 for ch_i in 0..CHANNELS {
1034 unsafe {
1036 *outputs.get_unchecked_mut(ch_i).get_unchecked_mut(i) = out[ch_i];
1037 }
1038 }
1039 }
1040 }
1041
1042 fn loop_notch(&mut self, info: &ProcInfo, inputs: &[&[f32]], outputs: &mut [&mut [f32]]) {
1043 assert!(inputs.len() == CHANNELS);
1044 assert!(outputs.len() == CHANNELS);
1045 for ch in inputs.iter() {
1046 assert!(ch.len() >= info.frames);
1047 }
1048 for ch in outputs.iter() {
1049 assert!(ch.len() >= info.frames);
1050 }
1051
1052 for i in 0..info.frames {
1053 let cutoff_hz = self.cutoff_hz.next_smoothed();
1054 let q = self.q_factor.next_smoothed();
1055
1056 if self.coeff_update_mask.do_update(i) {
1063 self.filter_0_coeff = SvfCoeffSimd::splat(SvfCoeff::notch(
1064 cutoff_hz,
1065 q,
1066 info.sample_rate_recip as f32,
1067 ));
1068 }
1069
1070 let s: [f32; CHANNELS] = core::array::from_fn(|ch_i| {
1071 unsafe { *inputs.get_unchecked(ch_i).get_unchecked(i) }
1073 });
1074
1075 let out = self.filter_0.process(s, &self.filter_0_coeff);
1076
1077 for ch_i in 0..CHANNELS {
1078 unsafe {
1080 *outputs.get_unchecked_mut(ch_i).get_unchecked_mut(i) = out[ch_i];
1081 }
1082 }
1083 }
1084 }
1085
1086 fn loop_allpass(&mut self, info: &ProcInfo, inputs: &[&[f32]], outputs: &mut [&mut [f32]]) {
1087 assert!(inputs.len() == CHANNELS);
1088 assert!(outputs.len() == CHANNELS);
1089 for ch in inputs.iter() {
1090 assert!(ch.len() >= info.frames);
1091 }
1092 for ch in outputs.iter() {
1093 assert!(ch.len() >= info.frames);
1094 }
1095
1096 for i in 0..info.frames {
1097 let cutoff_hz = self.cutoff_hz.next_smoothed();
1098 let q = self.q_factor.next_smoothed();
1099
1100 if self.coeff_update_mask.do_update(i) {
1107 self.filter_0_coeff = SvfCoeffSimd::splat(SvfCoeff::allpass(
1108 cutoff_hz,
1109 q,
1110 info.sample_rate_recip as f32,
1111 ));
1112 }
1113
1114 let s: [f32; CHANNELS] = core::array::from_fn(|ch_i| {
1115 unsafe { *inputs.get_unchecked(ch_i).get_unchecked(i) }
1117 });
1118
1119 let out = self.filter_0.process(s, &self.filter_0_coeff);
1120
1121 for ch_i in 0..CHANNELS {
1122 unsafe {
1124 *outputs.get_unchecked_mut(ch_i).get_unchecked_mut(i) = out[ch_i];
1125 }
1126 }
1127 }
1128 }
1129}
1130
1131impl<const CHANNELS: usize> AudioNodeProcessor for Processor<CHANNELS> {
1132 fn process(
1133 &mut self,
1134 info: &ProcInfo,
1135 buffers: ProcBuffers,
1136 events: &mut ProcEvents,
1137 extra: &mut ProcExtra,
1138 ) -> ProcessStatus {
1139 let mut params_changed = false;
1140
1141 for patch in events.drain_patches::<SvfNode<CHANNELS>>() {
1142 match patch {
1143 SvfNodePatch::FilterType(filter_type) => {
1144 params_changed = true;
1145 self.filter_type = filter_type;
1146 }
1147 SvfNodePatch::CutoffHz(cutoff) => {
1148 params_changed = true;
1149 self.cutoff_hz
1150 .set_value(cutoff.clamp(self.freq_range.start, self.freq_range.end));
1151 }
1152 SvfNodePatch::QFactor(q_factor) => {
1153 params_changed = true;
1154 self.q_factor
1155 .set_value(q_factor.clamp(self.q_range.start, self.q_range.end));
1156 }
1157 SvfNodePatch::Gain(gain) => {
1158 params_changed = true;
1159 let mut gain = gain.amp().clamp(self.gain_range.start, self.gain_range.end);
1160 if gain > 0.99999 && gain < 1.00001 {
1161 gain = 1.0;
1162 }
1163 self.gain.set_value(gain);
1164 }
1165 SvfNodePatch::Enabled(enabled) => {
1166 self.enable_declicker
1168 .fade_to_enabled(enabled, &extra.declick_values);
1169 }
1170 SvfNodePatch::SmoothSeconds(seconds) => {
1171 self.cutoff_hz.set_smooth_seconds(seconds, info.sample_rate);
1172 }
1173 SvfNodePatch::CoeffUpdateFactor(f) => {
1174 self.coeff_update_mask = f.mask();
1175 }
1176 }
1177 }
1178
1179 if self.enable_declicker.disabled() {
1180 return ProcessStatus::Bypass;
1182 }
1183
1184 if info.in_silence_mask.all_channels_silent(CHANNELS) && self.enable_declicker.has_settled()
1185 {
1186 self.cutoff_hz.reset_to_target();
1191 self.filter_0.reset();
1192 self.filter_1.reset();
1193 self.enable_declicker.reset_to_target();
1194
1195 return ProcessStatus::ClearAllOutputs;
1196 }
1197
1198 if self.cutoff_hz.is_smoothing() || self.q_factor.is_smoothing() || self.gain.is_smoothing()
1199 {
1200 match self.filter_type {
1201 SvfType::Lowpass => self.loop_lowpass_ord2(info, buffers.inputs, buffers.outputs),
1202 SvfType::LowpassX2 => self.loop_lowpass_ord4(info, buffers.inputs, buffers.outputs),
1203 SvfType::Highpass => self.loop_highpass_ord2(info, buffers.inputs, buffers.outputs),
1204 SvfType::HighpassX2 => {
1205 self.loop_highpass_ord4(info, buffers.inputs, buffers.outputs)
1206 }
1207 SvfType::Bandpass => self.loop_bandpass(info, buffers.inputs, buffers.outputs),
1208 SvfType::LowShelf => self.loop_low_shelf(info, buffers.inputs, buffers.outputs),
1209 SvfType::HighShelf => self.loop_high_shelf(info, buffers.inputs, buffers.outputs),
1210 SvfType::Bell => self.loop_bell(info, buffers.inputs, buffers.outputs),
1211 SvfType::Notch => self.loop_notch(info, buffers.inputs, buffers.outputs),
1212 SvfType::Allpass => self.loop_allpass(info, buffers.inputs, buffers.outputs),
1213 }
1214
1215 if self.cutoff_hz.settle() && self.q_factor.settle() && self.gain.settle() {
1216 self.calc_coefficients(info.sample_rate_recip as f32);
1217 }
1218 } else {
1219 if params_changed {
1222 self.calc_coefficients(info.sample_rate_recip as f32);
1223 }
1224
1225 assert!(buffers.inputs.len() == CHANNELS);
1226 assert!(buffers.outputs.len() == CHANNELS);
1227 for ch in buffers.inputs.iter() {
1228 assert!(ch.len() >= info.frames);
1229 }
1230 for ch in buffers.outputs.iter() {
1231 assert!(ch.len() >= info.frames);
1232 }
1233
1234 if self.num_filters == 1 {
1235 for i in 0..info.frames {
1236 let s: [f32; CHANNELS] = core::array::from_fn(|ch_i| {
1237 unsafe { *buffers.inputs.get_unchecked(ch_i).get_unchecked(i) }
1239 });
1240
1241 let out = self.filter_0.process(s, &self.filter_0_coeff);
1242
1243 for ch_i in 0..CHANNELS {
1244 unsafe {
1246 *buffers.outputs.get_unchecked_mut(ch_i).get_unchecked_mut(i) =
1247 out[ch_i];
1248 }
1249 }
1250 }
1251 } else {
1252 for i in 0..info.frames {
1253 let s: [f32; CHANNELS] = core::array::from_fn(|ch_i| {
1254 unsafe { *buffers.inputs.get_unchecked(ch_i).get_unchecked(i) }
1256 });
1257
1258 let s = self.filter_0.process(s, &self.filter_0_coeff);
1259 let out = self.filter_1.process(s, &self.filter_1_coeff);
1260
1261 for ch_i in 0..CHANNELS {
1262 unsafe {
1264 *buffers.outputs.get_unchecked_mut(ch_i).get_unchecked_mut(i) =
1265 out[ch_i];
1266 }
1267 }
1268 }
1269 }
1270 }
1271
1272 self.enable_declicker.process_crossfade(
1274 buffers.inputs,
1275 buffers.outputs,
1276 info.frames,
1277 &extra.declick_values,
1278 DeclickFadeCurve::Linear,
1279 );
1280
1281 ProcessStatus::OutputsModified
1282 }
1283
1284 fn new_stream(&mut self, stream_info: &StreamInfo, _context: &mut ProcStreamCtx) {
1285 self.cutoff_hz.update_sample_rate(stream_info.sample_rate);
1286 self.q_factor.update_sample_rate(stream_info.sample_rate);
1287 self.gain.update_sample_rate(stream_info.sample_rate);
1288
1289 self.calc_coefficients(stream_info.sample_rate_recip as f32);
1290 }
1291}