// In the imp submodule we include the actual implementation use std::{collections::VecDeque, sync::Mutex}; use glib::prelude::*; use gst_audio::subclass::prelude::*; use once_cell::sync::Lazy; use byte_slice_cast::*; use atomic_refcell::AtomicRefCell; // The debug category we use below for our filter pub static CAT: Lazy = Lazy::new(|| { gst::DebugCategory::new( "rsiirfilter", gst::DebugColorFlags::empty(), Some("Rust IIR Filter"), ) }); #[derive(Default)] // This is the state of our filter struct State { a: Vec, b: Vec, x: VecDeque, y: VecDeque, } // This is the private data of our filter #[derive(Default)] pub struct IirFilter { coeffs: Mutex, Vec)>>, state: AtomicRefCell, } // This trait registers our type with the GObject object system and // provides the entry points for creating a new instance and setting // up the class data #[glib::object_subclass] impl ObjectSubclass for IirFilter { const NAME: &'static str = "RsIirFilter"; const ABSTRACT: bool = true; type Type = super::IirFilter; type ParentType = gst_audio::AudioFilter; type Class = super::Class; // Here we set default implementations for all the virtual methods. // This is mandatory for all virtual methods that are not `Option`s. fn class_init(class: &mut Self::Class) { class.set_rate = |obj, rate| obj.imp().set_rate_default(rate); } } // Implementation of glib::Object virtual methods impl ObjectImpl for IirFilter {} impl GstObjectImpl for IirFilter {} // Implementation of gst::Element virtual methods impl ElementImpl for IirFilter {} // Implementation of gst_base::BaseTransform virtual methods impl BaseTransformImpl for IirFilter { // Configure basetransform so that we are always running in-place, // don't passthrough on same caps and also never call transform_ip // in passthrough mode (which does not matter for us here). // // The way how our processing is implemented, in-place transformation // is simpler. const MODE: gst_base::subclass::BaseTransformMode = gst_base::subclass::BaseTransformMode::AlwaysInPlace; const PASSTHROUGH_ON_SAME_CAPS: bool = false; const TRANSFORM_IP_ON_PASSTHROUGH: bool = false; fn start(&self) -> Result<(), gst::ErrorMessage> { self.parent_start()?; *self.state.borrow_mut() = State::default(); Ok(()) } fn stop(&self) -> Result<(), gst::ErrorMessage> { self.parent_stop()?; *self.state.borrow_mut() = State::default(); Ok(()) } fn transform_ip(&self, buf: &mut gst::BufferRef) -> Result { let mut state = self.state.borrow_mut(); // Update coefficients if new coefficients were set { let mut coeffs = self.coeffs.lock().unwrap(); if let Some((a, b)) = coeffs.take() { state.x.clear(); state.y.clear(); if !a.is_empty() { state.y.resize(a.len() - 1, 0.0); } if !b.is_empty() { state.x.resize(b.len() - 1, 0.0); } state.a = a; state.b = b; } } if state.a.is_empty() | state.b.is_empty() { return Ok(gst::FlowSuccess::Ok); } let mut map = buf.map_writable().map_err(|_| { gst::error!(CAT, imp: self, "Failed to map buffer writable"); gst::FlowError::Error })?; let samples = map.as_mut_slice_of::().unwrap(); assert!(state.b.len() - 1 == state.x.len()); assert!(state.a.len() - 1 == state.y.len()); for sample in samples.iter_mut() { let mut val = state.b[0] * *sample as f64; for (b, x) in Iterator::zip(state.b.iter().skip(1), state.x.iter()) { val += b * x; } for (a, y) in Iterator::zip(state.a.iter().skip(1), state.y.iter()) { val -= a * y; } val /= state.a[0]; let _ = state.x.pop_back().unwrap(); state.x.push_front(*sample as f64); let _ = state.y.pop_back().unwrap(); state.y.push_front(val); *sample = val as f32; } Ok(gst::FlowSuccess::Ok) } } impl AudioFilterImpl for IirFilter { fn allowed_caps() -> &'static gst::Caps { static CAPS: std::sync::OnceLock = std::sync::OnceLock::new(); CAPS.get_or_init(|| { // On both of pads we can only handle F32 mono at any sample rate. gst_audio::AudioCapsBuilder::new_interleaved() .format(gst_audio::AUDIO_FORMAT_F32) .channels(1) .build() }) } fn setup(&self, info: &gst_audio::AudioInfo) -> Result<(), gst::LoggableError> { self.parent_setup(info)?; gst::debug!(CAT, imp: self, "Rate changed to {}", info.rate()); let obj = self.obj(); (obj.class().as_ref().set_rate)(&obj, info.rate()); Ok(()) } } /// Wrappers for public methods and associated helper functions. impl IirFilter { pub(super) fn set_coeffs(&self, a: Vec, b: Vec) { gst::debug!(CAT, imp: self, "Setting coefficients a: {a:?}, b: {b:?}"); *self.coeffs.lock().unwrap() = Some((a, b)); } } /// Default virtual method implementations. impl IirFilter { fn set_rate_default(&self, _rate: u32) {} }