2018-11-21 15:09:07 +00:00
|
|
|
// Copyright (C) 2017,2018 Sebastian Dröge <sebastian@centricular.com>
|
2017-10-05 15:07:52 +00:00
|
|
|
//
|
2022-01-15 18:40:12 +00:00
|
|
|
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
|
|
|
|
// If a copy of the MPL was not distributed with this file, You can obtain one at
|
|
|
|
// <https://mozilla.org/MPL/2.0/>.
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
2017-10-05 15:07:52 +00:00
|
|
|
|
2021-06-03 18:20:54 +00:00
|
|
|
use gst::glib;
|
2017-10-05 15:07:52 +00:00
|
|
|
use gst::prelude::*;
|
2018-11-21 15:09:07 +00:00
|
|
|
use gst::subclass::prelude::*;
|
|
|
|
use gst_base::subclass::prelude::*;
|
2017-10-05 15:07:52 +00:00
|
|
|
|
|
|
|
use std::sync::Mutex;
|
2022-08-09 11:18:12 +00:00
|
|
|
use std::{cmp, u64};
|
2017-10-05 15:07:52 +00:00
|
|
|
|
|
|
|
use byte_slice_cast::*;
|
|
|
|
|
|
|
|
use num_traits::cast::{FromPrimitive, ToPrimitive};
|
2018-04-05 08:35:34 +00:00
|
|
|
use num_traits::float::Float;
|
2017-10-05 15:07:52 +00:00
|
|
|
|
2020-11-22 11:16:10 +00:00
|
|
|
use once_cell::sync::Lazy;
|
|
|
|
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
|
|
|
gst::DebugCategory::new(
|
2019-11-20 22:06:16 +00:00
|
|
|
"rsaudioecho",
|
|
|
|
gst::DebugColorFlags::empty(),
|
|
|
|
Some("Rust Audio Echo Filter"),
|
2020-11-22 11:16:10 +00:00
|
|
|
)
|
|
|
|
});
|
2019-11-20 22:06:16 +00:00
|
|
|
|
2020-11-14 16:52:56 +00:00
|
|
|
use super::ring_buffer::RingBuffer;
|
|
|
|
|
2021-05-25 14:37:48 +00:00
|
|
|
const DEFAULT_MAX_DELAY: gst::ClockTime = gst::ClockTime::SECOND;
|
|
|
|
const DEFAULT_DELAY: gst::ClockTime = gst::ClockTime::from_seconds(500);
|
2017-10-05 15:07:52 +00:00
|
|
|
const DEFAULT_INTENSITY: f64 = 0.5;
|
|
|
|
const DEFAULT_FEEDBACK: f64 = 0.0;
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
struct Settings {
|
2021-05-25 14:37:48 +00:00
|
|
|
pub max_delay: gst::ClockTime,
|
|
|
|
pub delay: gst::ClockTime,
|
2017-10-05 15:07:52 +00:00
|
|
|
pub intensity: f64,
|
|
|
|
pub feedback: f64,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for Settings {
|
|
|
|
fn default() -> Self {
|
|
|
|
Settings {
|
|
|
|
max_delay: DEFAULT_MAX_DELAY,
|
|
|
|
delay: DEFAULT_DELAY,
|
|
|
|
intensity: DEFAULT_INTENSITY,
|
|
|
|
feedback: DEFAULT_FEEDBACK,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct State {
|
|
|
|
info: gst_audio::AudioInfo,
|
|
|
|
buffer: RingBuffer,
|
|
|
|
}
|
|
|
|
|
2021-03-07 16:22:24 +00:00
|
|
|
#[derive(Default)]
|
2020-11-14 16:52:56 +00:00
|
|
|
pub struct AudioEcho {
|
2017-10-05 15:07:52 +00:00
|
|
|
settings: Mutex<Settings>,
|
|
|
|
state: Mutex<Option<State>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AudioEcho {
|
2018-11-21 15:09:07 +00:00
|
|
|
fn process<F: Float + ToPrimitive + FromPrimitive>(
|
|
|
|
data: &mut [F],
|
|
|
|
state: &mut State,
|
|
|
|
settings: &Settings,
|
|
|
|
) {
|
2021-05-25 14:37:48 +00:00
|
|
|
let delay_frames = (settings.delay
|
|
|
|
* (state.info.channels() as u64)
|
|
|
|
* (state.info.rate() as u64))
|
|
|
|
.seconds() as usize;
|
2018-11-21 15:09:07 +00:00
|
|
|
|
|
|
|
for (i, (o, e)) in data.iter_mut().zip(state.buffer.iter(delay_frames)) {
|
|
|
|
let inp = (*i).to_f64().unwrap();
|
|
|
|
let out = inp + settings.intensity * e;
|
|
|
|
*o = inp + settings.feedback * e;
|
|
|
|
*i = FromPrimitive::from_f64(out).unwrap();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-07 16:22:24 +00:00
|
|
|
#[glib::object_subclass]
|
2018-11-21 15:09:07 +00:00
|
|
|
impl ObjectSubclass for AudioEcho {
|
|
|
|
const NAME: &'static str = "RsAudioEcho";
|
2020-11-14 16:52:56 +00:00
|
|
|
type Type = super::AudioEcho;
|
2018-11-21 15:09:07 +00:00
|
|
|
type ParentType = gst_base::BaseTransform;
|
2021-01-21 18:21:29 +00:00
|
|
|
}
|
2017-10-05 15:07:52 +00:00
|
|
|
|
2021-01-21 18:21:29 +00:00
|
|
|
impl ObjectImpl for AudioEcho {
|
|
|
|
fn properties() -> &'static [glib::ParamSpec] {
|
|
|
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
|
|
|
vec![
|
2022-08-18 12:04:15 +00:00
|
|
|
glib::ParamSpecUInt64::builder("max-delay")
|
|
|
|
.nick("Maximum Delay")
|
|
|
|
.blurb("Maximum delay of the echo in nanoseconds (can't be changed in PLAYING or PAUSED state)")
|
|
|
|
.maximum(u64::MAX - 1)
|
|
|
|
.default_value(DEFAULT_MAX_DELAY.nseconds())
|
|
|
|
.mutable_ready()
|
|
|
|
.build(),
|
|
|
|
glib::ParamSpecUInt64::builder("delay")
|
|
|
|
.nick("Delay")
|
|
|
|
.blurb("Delay of the echo in nanoseconds")
|
|
|
|
.maximum(u64::MAX - 1)
|
|
|
|
.default_value(DEFAULT_DELAY.nseconds())
|
|
|
|
.mutable_ready()
|
|
|
|
.build(),
|
|
|
|
glib::ParamSpecDouble::builder("intensity")
|
|
|
|
.nick("Intensity")
|
|
|
|
.blurb("Intensity of the echo")
|
|
|
|
.minimum(0.0)
|
|
|
|
.maximum(1.0)
|
|
|
|
.default_value(DEFAULT_INTENSITY)
|
|
|
|
.mutable_ready()
|
|
|
|
.build(),
|
|
|
|
glib::ParamSpecDouble::builder("feedback")
|
|
|
|
.nick("Feedback")
|
|
|
|
.blurb("Amount of feedback")
|
|
|
|
.minimum(0.0)
|
|
|
|
.maximum(1.0)
|
|
|
|
.default_value(DEFAULT_FEEDBACK)
|
|
|
|
.mutable_ready()
|
|
|
|
.build(),
|
2021-01-21 18:21:29 +00:00
|
|
|
]
|
|
|
|
});
|
2017-10-05 15:07:52 +00:00
|
|
|
|
2021-01-21 18:21:29 +00:00
|
|
|
PROPERTIES.as_ref()
|
2017-10-05 15:07:52 +00:00
|
|
|
}
|
|
|
|
|
2022-10-09 13:06:59 +00:00
|
|
|
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
2021-04-12 12:49:54 +00:00
|
|
|
match pspec.name() {
|
2021-01-21 18:21:29 +00:00
|
|
|
"max-delay" => {
|
2017-10-05 15:07:52 +00:00
|
|
|
let mut settings = self.settings.lock().unwrap();
|
2017-12-31 18:56:00 +00:00
|
|
|
if self.state.lock().unwrap().is_none() {
|
2021-05-25 14:37:48 +00:00
|
|
|
settings.max_delay =
|
|
|
|
gst::ClockTime::from_nseconds(value.get().expect("type checked upstream"));
|
2017-12-31 18:56:00 +00:00
|
|
|
}
|
2017-10-05 15:07:52 +00:00
|
|
|
}
|
2021-01-21 18:21:29 +00:00
|
|
|
"delay" => {
|
2017-10-05 15:07:52 +00:00
|
|
|
let mut settings = self.settings.lock().unwrap();
|
2021-05-25 14:37:48 +00:00
|
|
|
settings.delay =
|
|
|
|
gst::ClockTime::from_nseconds(value.get().expect("type checked upstream"));
|
2017-10-05 15:07:52 +00:00
|
|
|
}
|
2021-01-21 18:21:29 +00:00
|
|
|
"intensity" => {
|
2017-10-05 15:07:52 +00:00
|
|
|
let mut settings = self.settings.lock().unwrap();
|
2021-04-25 12:41:22 +00:00
|
|
|
settings.intensity = value.get().expect("type checked upstream");
|
2017-10-05 15:07:52 +00:00
|
|
|
}
|
2021-01-21 18:21:29 +00:00
|
|
|
"feedback" => {
|
2017-10-05 15:07:52 +00:00
|
|
|
let mut settings = self.settings.lock().unwrap();
|
2021-04-25 12:41:22 +00:00
|
|
|
settings.feedback = value.get().expect("type checked upstream");
|
2017-10-05 15:07:52 +00:00
|
|
|
}
|
|
|
|
_ => unimplemented!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-09 13:06:59 +00:00
|
|
|
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
2021-04-12 12:49:54 +00:00
|
|
|
match pspec.name() {
|
2021-01-21 18:21:29 +00:00
|
|
|
"max-delay" => {
|
2017-10-05 15:07:52 +00:00
|
|
|
let settings = self.settings.lock().unwrap();
|
2020-11-19 15:55:57 +00:00
|
|
|
settings.max_delay.to_value()
|
2017-10-05 15:07:52 +00:00
|
|
|
}
|
2021-01-21 18:21:29 +00:00
|
|
|
"delay" => {
|
2017-10-05 15:07:52 +00:00
|
|
|
let settings = self.settings.lock().unwrap();
|
2020-11-19 15:55:57 +00:00
|
|
|
settings.delay.to_value()
|
2017-10-05 15:07:52 +00:00
|
|
|
}
|
2021-01-21 18:21:29 +00:00
|
|
|
"intensity" => {
|
2017-10-05 15:07:52 +00:00
|
|
|
let settings = self.settings.lock().unwrap();
|
2020-11-19 15:55:57 +00:00
|
|
|
settings.intensity.to_value()
|
2017-10-05 15:07:52 +00:00
|
|
|
}
|
2021-01-21 18:21:29 +00:00
|
|
|
"feedback" => {
|
2017-10-05 15:07:52 +00:00
|
|
|
let settings = self.settings.lock().unwrap();
|
2020-11-19 15:55:57 +00:00
|
|
|
settings.feedback.to_value()
|
2017-10-05 15:07:52 +00:00
|
|
|
}
|
|
|
|
_ => unimplemented!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-23 08:57:31 +00:00
|
|
|
impl GstObjectImpl for AudioEcho {}
|
|
|
|
|
2021-01-21 18:21:29 +00:00
|
|
|
impl ElementImpl for AudioEcho {
|
|
|
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
|
|
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
|
|
|
gst::subclass::ElementMetadata::new(
|
|
|
|
"Audio echo",
|
|
|
|
"Filter/Effect/Audio",
|
|
|
|
"Adds an echo or reverb effect to an audio stream",
|
|
|
|
"Sebastian Dröge <sebastian@centricular.com>",
|
|
|
|
)
|
|
|
|
});
|
|
|
|
|
|
|
|
Some(&*ELEMENT_METADATA)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
|
|
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
2022-08-10 10:45:52 +00:00
|
|
|
let caps = gst_audio::AudioCapsBuilder::new_interleaved()
|
2022-08-09 11:18:12 +00:00
|
|
|
.format_list([gst_audio::AUDIO_FORMAT_F32, gst_audio::AUDIO_FORMAT_F64])
|
2021-11-06 07:34:10 +00:00
|
|
|
.build();
|
2021-01-21 18:21:29 +00:00
|
|
|
let src_pad_template = gst::PadTemplate::new(
|
|
|
|
"src",
|
|
|
|
gst::PadDirection::Src,
|
|
|
|
gst::PadPresence::Always,
|
|
|
|
&caps,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let sink_pad_template = gst::PadTemplate::new(
|
|
|
|
"sink",
|
|
|
|
gst::PadDirection::Sink,
|
|
|
|
gst::PadPresence::Always,
|
|
|
|
&caps,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
vec![src_pad_template, sink_pad_template]
|
|
|
|
});
|
|
|
|
|
|
|
|
PAD_TEMPLATES.as_ref()
|
|
|
|
}
|
|
|
|
}
|
2017-10-05 15:07:52 +00:00
|
|
|
|
2018-11-21 15:09:07 +00:00
|
|
|
impl BaseTransformImpl for AudioEcho {
|
2021-01-21 18:21:29 +00:00
|
|
|
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;
|
|
|
|
|
2022-10-09 13:06:59 +00:00
|
|
|
fn transform_ip(&self, buf: &mut gst::BufferRef) -> Result<gst::FlowSuccess, gst::FlowError> {
|
2017-10-05 15:07:52 +00:00
|
|
|
let mut settings = *self.settings.lock().unwrap();
|
|
|
|
settings.delay = cmp::min(settings.max_delay, settings.delay);
|
|
|
|
|
|
|
|
let mut state_guard = self.state.lock().unwrap();
|
2019-01-11 23:45:05 +00:00
|
|
|
let state = state_guard.as_mut().ok_or(gst::FlowError::NotNegotiated)?;
|
2017-10-05 15:07:52 +00:00
|
|
|
|
2019-12-18 05:50:10 +00:00
|
|
|
let mut map = buf.map_writable().map_err(|_| gst::FlowError::Error)?;
|
2017-10-05 15:07:52 +00:00
|
|
|
|
|
|
|
match state.info.format() {
|
|
|
|
gst_audio::AUDIO_FORMAT_F64 => {
|
2018-06-01 09:02:18 +00:00
|
|
|
let data = map.as_mut_slice_of::<f64>().unwrap();
|
2017-10-05 15:07:52 +00:00
|
|
|
Self::process(data, state, &settings);
|
2017-10-07 01:41:45 +00:00
|
|
|
}
|
2017-10-05 15:07:52 +00:00
|
|
|
gst_audio::AUDIO_FORMAT_F32 => {
|
2018-06-01 09:02:18 +00:00
|
|
|
let data = map.as_mut_slice_of::<f32>().unwrap();
|
2017-10-05 15:07:52 +00:00
|
|
|
Self::process(data, state, &settings);
|
2017-10-07 01:41:45 +00:00
|
|
|
}
|
2019-01-11 23:45:05 +00:00
|
|
|
_ => return Err(gst::FlowError::NotNegotiated),
|
2017-10-05 15:07:52 +00:00
|
|
|
}
|
|
|
|
|
2019-01-11 23:45:05 +00:00
|
|
|
Ok(gst::FlowSuccess::Ok)
|
2017-10-05 15:07:52 +00:00
|
|
|
}
|
|
|
|
|
2022-10-09 13:06:59 +00:00
|
|
|
fn set_caps(&self, incaps: &gst::Caps, outcaps: &gst::Caps) -> Result<(), gst::LoggableError> {
|
2017-10-05 15:07:52 +00:00
|
|
|
if incaps != outcaps {
|
2020-12-20 18:43:45 +00:00
|
|
|
return Err(gst::loggable_error!(
|
2019-11-20 22:06:16 +00:00
|
|
|
CAT,
|
|
|
|
"Input and output caps are not the same"
|
|
|
|
));
|
2017-10-05 15:07:52 +00:00
|
|
|
}
|
|
|
|
|
2019-12-14 15:26:20 +00:00
|
|
|
let info = gst_audio::AudioInfo::from_caps(incaps)
|
2020-12-20 18:43:45 +00:00
|
|
|
.map_err(|_| gst::loggable_error!(CAT, "Failed to parse input caps"))?;
|
2017-10-05 15:07:52 +00:00
|
|
|
let max_delay = self.settings.lock().unwrap().max_delay;
|
2021-05-25 14:37:48 +00:00
|
|
|
let size = (max_delay * (info.rate() as u64)).seconds() as usize;
|
|
|
|
let buffer_size = size * (info.channels() as usize);
|
2017-10-05 15:07:52 +00:00
|
|
|
|
|
|
|
*self.state.lock().unwrap() = Some(State {
|
2018-10-11 10:49:10 +00:00
|
|
|
info,
|
2021-05-25 14:37:48 +00:00
|
|
|
buffer: RingBuffer::new(buffer_size),
|
2017-10-05 15:07:52 +00:00
|
|
|
});
|
|
|
|
|
2019-11-20 22:06:16 +00:00
|
|
|
Ok(())
|
2017-10-05 15:07:52 +00:00
|
|
|
}
|
|
|
|
|
2022-10-09 13:06:59 +00:00
|
|
|
fn stop(&self) -> Result<(), gst::ErrorMessage> {
|
2017-10-05 15:07:52 +00:00
|
|
|
// Drop state
|
|
|
|
let _ = self.state.lock().unwrap().take();
|
|
|
|
|
2019-01-26 15:07:51 +00:00
|
|
|
Ok(())
|
2017-10-05 15:07:52 +00:00
|
|
|
}
|
|
|
|
}
|