2023-12-03 13:01:53 +00:00
|
|
|
// In the imp submodule we include the actual implementation
|
|
|
|
|
|
|
|
use std::{collections::VecDeque, sync::Mutex};
|
|
|
|
|
2024-01-29 16:31:21 +00:00
|
|
|
use glib::prelude::*;
|
2023-12-03 13:01:53 +00:00
|
|
|
use gst_audio::subclass::prelude::*;
|
2024-01-29 16:31:21 +00:00
|
|
|
use once_cell::sync::Lazy;
|
2023-12-03 13:01:53 +00:00
|
|
|
|
|
|
|
use byte_slice_cast::*;
|
|
|
|
|
|
|
|
use atomic_refcell::AtomicRefCell;
|
|
|
|
|
|
|
|
// The debug category we use below for our filter
|
|
|
|
pub static CAT: Lazy<gst::DebugCategory> = 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<f64>,
|
|
|
|
b: Vec<f64>,
|
|
|
|
x: VecDeque<f64>,
|
|
|
|
y: VecDeque<f64>,
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is the private data of our filter
|
|
|
|
#[derive(Default)]
|
|
|
|
pub struct IirFilter {
|
|
|
|
coeffs: Mutex<Option<(Vec<f64>, Vec<f64>)>>,
|
|
|
|
state: AtomicRefCell<State>,
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
2023-12-09 09:58:14 +00:00
|
|
|
// Here we set default implementations for all the virtual methods.
|
|
|
|
// This is mandatory for all virtual methods that are not `Option`s.
|
2023-12-03 13:01:53 +00:00
|
|
|
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<gst::FlowSuccess, gst::FlowError> {
|
|
|
|
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::<f32>().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 {
|
2024-01-29 16:31:21 +00:00
|
|
|
static CAPS: std::sync::OnceLock<gst::Caps> = std::sync::OnceLock::new();
|
|
|
|
CAPS.get_or_init(|| {
|
2023-12-03 13:01:53 +00:00
|
|
|
// 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()
|
2024-01-29 16:31:21 +00:00
|
|
|
})
|
2023-12-03 13:01:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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<f64>, b: Vec<f64>) {
|
|
|
|
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) {}
|
|
|
|
}
|