audio-master 1.1.2

Rich Audio API based on libsamplerate and cpal.
Documentation
use biquad::{Coefficients, DirectForm1, Q_BUTTERWORTH_F32, ToHertz, Type};
use num_traits::Float;

use crate::{
    audio_buffer::{AudioBuffer, AudioBufferImpl, AudioChannelLayout},
    consts::{MAX_FREQ_CUT, MIN_FREQ_CUT},
    float_type::FloatType,
};

use super::audio_math::{AudioFilter, AudioFilterImpl, AudioFilterPrivateImpl};

impl<T: FloatType + Float> AudioFilterImpl<T> for AudioFilter<T> {
    fn new(
        low_cut: f32,
        high_cut: f32,
        sample_rate: f32,
        channel_layout: AudioChannelLayout,
    ) -> Self {
        assert!(
            sample_rate > 0.0,
            "Sample rate must be positive, got {}",
            sample_rate
        );
        assert!(
            low_cut < high_cut,
            "Low cutoff ({:.2} Hz) must be less than high cutoff ({:.2} Hz)",
            low_cut,
            high_cut
        );

        let low_cut = low_cut.clamp(MIN_FREQ_CUT, MAX_FREQ_CUT);
        let high_cut = high_cut.clamp(MIN_FREQ_CUT, MAX_FREQ_CUT);

        let highpass = Coefficients::<T>::from_params(
            Type::HighPass,
            sample_rate.hz(),
            low_cut.hz(),
            Q_BUTTERWORTH_F32.into(),
        )
        .unwrap();

        let lowpass = Coefficients::<T>::from_params(
            Type::LowPass,
            sample_rate.hz(),
            high_cut.hz(),
            Q_BUTTERWORTH_F32.into(),
        )
        .unwrap();

        let mut lowpasses: Vec<DirectForm1<T>> = Vec::new();
        lowpasses.resize_with(channel_layout as usize, || DirectForm1::<T>::new(lowpass));

        let mut highpasses: Vec<DirectForm1<T>> = Vec::new();
        highpasses.resize_with(channel_layout as usize, || DirectForm1::<T>::new(highpass));

        return Self {
            lowpass: lowpasses,
            highpass: highpasses,
            low_cut,
            high_cut,
            sample_rate,
            channel_layout,
        };
    }

    fn set_channel_layout(&mut self, channel_layout: AudioChannelLayout) {
        if self.channel_layout != channel_layout {
            *self = AudioFilter::new(
                self.low_cut,
                self.high_cut,
                self.sample_rate,
                channel_layout,
            );
        }
    }

    fn process(&mut self, input: &mut AudioBuffer<T>) {
        assert!(
            input.channel_layout() == self.channel_layout,
            "Input channel layout ({:?}) does not match filter channel layout ({:?})",
            input.channel_layout(),
            self.channel_layout
        );

        for ch in 0..self.channel_layout as usize {
            let channel_data = input.get_channel_mut(ch);
            self.process_channel(channel_data, ch);
        }
    }

    fn set_low_cut(&mut self, low_cut: f32) {
        let low_cut = low_cut.clamp(MIN_FREQ_CUT, MAX_FREQ_CUT);
        assert!(
            low_cut < self.high_cut,
            "Low cutoff ({:.2} Hz) must be less than high cutoff ({:.2} Hz)",
            low_cut,
            self.high_cut
        );

        let highpass = Coefficients::<T>::from_params(
            Type::HighPass,
            self.sample_rate.hz(),
            low_cut.hz(),
            Q_BUTTERWORTH_F32.into(),
        )
        .unwrap();

        for hp in self.highpass.iter_mut() {
            *hp = DirectForm1::<T>::new(highpass);
        }
    }

    fn set_high_cut(&mut self, high_cut: f32) {
        let high_cut = high_cut.clamp(MIN_FREQ_CUT, MAX_FREQ_CUT);
        assert!(
            high_cut > self.low_cut,
            "High cutoff ({:.2} Hz) must be greater than low cutoff ({:.2} Hz)",
            high_cut,
            self.low_cut
        );

        let lowpass = Coefficients::<T>::from_params(
            Type::LowPass,
            self.sample_rate.hz(),
            high_cut.hz(),
            Q_BUTTERWORTH_F32.into(),
        )
        .unwrap();

        for lp in self.lowpass.iter_mut() {
            *lp = DirectForm1::<T>::new(lowpass);
        }
    }

    fn set_sample_rate(&mut self, sample_rate: f32) {
        if self.sample_rate == sample_rate {
            return;
        }

        assert!(
            sample_rate > 0.0,
            "Sample rate must be positive, got {}",
            sample_rate
        );
        self.sample_rate = sample_rate;

        let highpass = Coefficients::<T>::from_params(
            Type::HighPass,
            sample_rate.hz(),
            self.low_cut.hz(),
            Q_BUTTERWORTH_F32.into(),
        )
        .unwrap();

        let lowpass = Coefficients::<T>::from_params(
            Type::LowPass,
            sample_rate.hz(),
            self.high_cut.hz(),
            Q_BUTTERWORTH_F32.into(),
        )
        .unwrap();

        for lp in self.lowpass.iter_mut() {
            *lp = DirectForm1::<T>::new(lowpass);
        }

        for hp in self.highpass.iter_mut() {
            *hp = DirectForm1::<T>::new(highpass);
        }
    }

    #[inline]
    fn get_high_cut(&self) -> f32 {
        return self.high_cut;
    }

    #[inline]
    fn get_low_cut(&self) -> f32 {
        return self.low_cut;
    }

    #[inline]
    fn get_sample_rate(&self) -> f32 {
        return self.sample_rate;
    }
}