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
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
|
|
use glib;
|
2018-11-21 15:09:07 +00:00
|
|
|
use glib::subclass;
|
|
|
|
use glib::subclass::prelude::*;
|
2017-10-05 15:07:52 +00:00
|
|
|
use gst;
|
|
|
|
use gst::prelude::*;
|
2018-11-21 15:09:07 +00:00
|
|
|
use gst::subclass::prelude::*;
|
2017-10-05 15:07:52 +00:00
|
|
|
use gst_audio;
|
2018-11-21 15:09:07 +00:00
|
|
|
use gst_base;
|
|
|
|
use gst_base::subclass::prelude::*;
|
2017-10-05 15:07:52 +00:00
|
|
|
|
|
|
|
use std::sync::Mutex;
|
2018-05-01 14:16:12 +00:00
|
|
|
use std::{cmp, i32, iter, 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
|
|
|
|
2017-12-20 19:36:50 +00:00
|
|
|
const DEFAULT_MAX_DELAY: u64 = gst::SECOND_VAL;
|
2017-11-11 12:02:55 +00:00
|
|
|
const DEFAULT_DELAY: u64 = 500 * gst::MSECOND_VAL;
|
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 {
|
|
|
|
pub max_delay: u64,
|
|
|
|
pub delay: u64,
|
|
|
|
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,
|
|
|
|
}
|
|
|
|
|
|
|
|
struct AudioEcho {
|
|
|
|
cat: gst::DebugCategory,
|
|
|
|
settings: Mutex<Settings>,
|
|
|
|
state: Mutex<Option<State>>,
|
|
|
|
}
|
|
|
|
|
2018-11-21 15:09:07 +00:00
|
|
|
static PROPERTIES: [subclass::Property; 4] = [
|
2018-12-18 09:23:45 +00:00
|
|
|
subclass::Property("max-delay", |name| {
|
|
|
|
glib::ParamSpec::uint64(name,
|
2017-10-05 15:07:52 +00:00
|
|
|
"Maximum Delay",
|
|
|
|
"Maximum delay of the echo in nanoseconds (can't be changed in PLAYING or PAUSED state)",
|
2018-11-21 15:09:07 +00:00
|
|
|
0, u64::MAX,
|
2017-10-05 15:07:52 +00:00
|
|
|
DEFAULT_MAX_DELAY,
|
2018-11-21 15:09:07 +00:00
|
|
|
glib::ParamFlags::READWRITE,
|
|
|
|
)
|
|
|
|
}),
|
2018-12-18 09:23:45 +00:00
|
|
|
subclass::Property("delay", |name| {
|
2018-11-21 15:09:07 +00:00
|
|
|
glib::ParamSpec::uint64(
|
2018-12-18 09:23:45 +00:00
|
|
|
name,
|
2018-11-21 15:09:07 +00:00
|
|
|
"Delay",
|
|
|
|
"Delay of the echo in nanoseconds",
|
|
|
|
0,
|
|
|
|
u64::MAX,
|
|
|
|
DEFAULT_DELAY,
|
|
|
|
glib::ParamFlags::READWRITE,
|
|
|
|
)
|
|
|
|
}),
|
2018-12-18 09:23:45 +00:00
|
|
|
subclass::Property("intensity", |name| {
|
2018-11-21 15:09:07 +00:00
|
|
|
glib::ParamSpec::double(
|
2018-12-18 09:23:45 +00:00
|
|
|
name,
|
2018-11-21 15:09:07 +00:00
|
|
|
"Intensity",
|
|
|
|
"Intensity of the echo",
|
|
|
|
0.0,
|
|
|
|
1.0,
|
|
|
|
DEFAULT_INTENSITY,
|
|
|
|
glib::ParamFlags::READWRITE,
|
|
|
|
)
|
|
|
|
}),
|
2018-12-18 09:23:45 +00:00
|
|
|
subclass::Property("feedback", |name| {
|
2018-11-21 15:09:07 +00:00
|
|
|
glib::ParamSpec::double(
|
2018-12-18 09:23:45 +00:00
|
|
|
name,
|
2018-11-21 15:09:07 +00:00
|
|
|
"Feedback",
|
|
|
|
"Amount of feedback",
|
|
|
|
0.0,
|
|
|
|
1.0,
|
|
|
|
DEFAULT_FEEDBACK,
|
|
|
|
glib::ParamFlags::READWRITE,
|
|
|
|
)
|
|
|
|
}),
|
2017-10-05 15:07:52 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
impl AudioEcho {
|
2018-11-21 15:09:07 +00:00
|
|
|
fn process<F: Float + ToPrimitive + FromPrimitive>(
|
|
|
|
data: &mut [F],
|
|
|
|
state: &mut State,
|
|
|
|
settings: &Settings,
|
|
|
|
) {
|
|
|
|
let delay_frames = (settings.delay as usize)
|
|
|
|
* (state.info.channels() as usize)
|
|
|
|
* (state.info.rate() as usize)
|
|
|
|
/ (gst::SECOND_VAL as usize);
|
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ObjectSubclass for AudioEcho {
|
|
|
|
const NAME: &'static str = "RsAudioEcho";
|
|
|
|
type ParentType = gst_base::BaseTransform;
|
|
|
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
|
|
|
type Class = subclass::simple::ClassStruct<Self>;
|
|
|
|
|
|
|
|
glib_object_subclass!();
|
|
|
|
|
|
|
|
fn new() -> Self {
|
2017-10-05 15:07:52 +00:00
|
|
|
Self {
|
|
|
|
cat: gst::DebugCategory::new(
|
2017-12-31 18:56:00 +00:00
|
|
|
"rsaudioecho",
|
2017-10-05 15:07:52 +00:00
|
|
|
gst::DebugColorFlags::empty(),
|
2019-05-23 20:55:54 +00:00
|
|
|
Some("Rust audioecho effect"),
|
2017-10-05 15:07:52 +00:00
|
|
|
),
|
|
|
|
settings: Mutex::new(Default::default()),
|
|
|
|
state: Mutex::new(None),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-21 15:09:07 +00:00
|
|
|
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
|
2017-10-05 15:07:52 +00:00
|
|
|
klass.set_metadata(
|
|
|
|
"Audio echo",
|
|
|
|
"Filter/Effect/Audio",
|
|
|
|
"Adds an echo or reverb effect to an audio stream",
|
|
|
|
"Sebastian Dröge <sebastian@centricular.com>",
|
|
|
|
);
|
|
|
|
|
|
|
|
let caps = gst::Caps::new_simple(
|
|
|
|
"audio/x-raw",
|
|
|
|
&[
|
|
|
|
(
|
|
|
|
"format",
|
|
|
|
&gst::List::new(&[
|
|
|
|
&gst_audio::AUDIO_FORMAT_F32.to_string(),
|
|
|
|
&gst_audio::AUDIO_FORMAT_F64.to_string(),
|
|
|
|
]),
|
|
|
|
),
|
|
|
|
("rate", &gst::IntRange::<i32>::new(0, i32::MAX)),
|
|
|
|
("channels", &gst::IntRange::<i32>::new(0, i32::MAX)),
|
|
|
|
("layout", &"interleaved"),
|
|
|
|
],
|
|
|
|
);
|
|
|
|
let src_pad_template = gst::PadTemplate::new(
|
|
|
|
"src",
|
|
|
|
gst::PadDirection::Src,
|
|
|
|
gst::PadPresence::Always,
|
|
|
|
&caps,
|
2019-01-29 15:26:40 +00:00
|
|
|
)
|
|
|
|
.unwrap();
|
2017-10-05 15:07:52 +00:00
|
|
|
klass.add_pad_template(src_pad_template);
|
|
|
|
|
|
|
|
let sink_pad_template = gst::PadTemplate::new(
|
|
|
|
"sink",
|
|
|
|
gst::PadDirection::Sink,
|
|
|
|
gst::PadPresence::Always,
|
|
|
|
&caps,
|
2019-01-29 15:26:40 +00:00
|
|
|
)
|
|
|
|
.unwrap();
|
2017-10-05 15:07:52 +00:00
|
|
|
klass.add_pad_template(sink_pad_template);
|
|
|
|
|
|
|
|
klass.install_properties(&PROPERTIES);
|
|
|
|
|
2018-11-21 15:09:07 +00:00
|
|
|
klass.configure(
|
|
|
|
gst_base::subclass::BaseTransformMode::AlwaysInPlace,
|
|
|
|
false,
|
|
|
|
false,
|
|
|
|
);
|
2017-10-05 15:07:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-21 15:09:07 +00:00
|
|
|
impl ObjectImpl for AudioEcho {
|
|
|
|
glib_object_impl!();
|
|
|
|
|
|
|
|
fn set_property(&self, _obj: &glib::Object, id: usize, value: &glib::Value) {
|
|
|
|
let prop = &PROPERTIES[id];
|
2017-10-05 15:07:52 +00:00
|
|
|
|
|
|
|
match *prop {
|
2018-11-21 15:09:07 +00:00
|
|
|
subclass::Property("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() {
|
|
|
|
settings.max_delay = value.get().unwrap();
|
|
|
|
}
|
2017-10-05 15:07:52 +00:00
|
|
|
}
|
2018-11-21 15:09:07 +00:00
|
|
|
subclass::Property("delay", ..) => {
|
2017-10-05 15:07:52 +00:00
|
|
|
let mut settings = self.settings.lock().unwrap();
|
|
|
|
settings.delay = value.get().unwrap();
|
|
|
|
}
|
2018-11-21 15:09:07 +00:00
|
|
|
subclass::Property("intensity", ..) => {
|
2017-10-05 15:07:52 +00:00
|
|
|
let mut settings = self.settings.lock().unwrap();
|
|
|
|
settings.intensity = value.get().unwrap();
|
|
|
|
}
|
2018-11-21 15:09:07 +00:00
|
|
|
subclass::Property("feedback", ..) => {
|
2017-10-05 15:07:52 +00:00
|
|
|
let mut settings = self.settings.lock().unwrap();
|
|
|
|
settings.feedback = value.get().unwrap();
|
|
|
|
}
|
|
|
|
_ => unimplemented!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-21 15:09:07 +00:00
|
|
|
fn get_property(&self, _obj: &glib::Object, id: usize) -> Result<glib::Value, ()> {
|
|
|
|
let prop = &PROPERTIES[id];
|
2017-10-05 15:07:52 +00:00
|
|
|
|
|
|
|
match *prop {
|
2018-11-21 15:09:07 +00:00
|
|
|
subclass::Property("max-delay", ..) => {
|
2017-10-05 15:07:52 +00:00
|
|
|
let settings = self.settings.lock().unwrap();
|
2017-12-31 18:56:00 +00:00
|
|
|
Ok(settings.max_delay.to_value())
|
2017-10-05 15:07:52 +00:00
|
|
|
}
|
2018-11-21 15:09:07 +00:00
|
|
|
subclass::Property("delay", ..) => {
|
2017-10-05 15:07:52 +00:00
|
|
|
let settings = self.settings.lock().unwrap();
|
|
|
|
Ok(settings.delay.to_value())
|
|
|
|
}
|
2018-11-21 15:09:07 +00:00
|
|
|
subclass::Property("intensity", ..) => {
|
2017-10-05 15:07:52 +00:00
|
|
|
let settings = self.settings.lock().unwrap();
|
|
|
|
Ok(settings.intensity.to_value())
|
|
|
|
}
|
2018-11-21 15:09:07 +00:00
|
|
|
subclass::Property("feedback", ..) => {
|
2017-10-05 15:07:52 +00:00
|
|
|
let settings = self.settings.lock().unwrap();
|
|
|
|
Ok(settings.feedback.to_value())
|
|
|
|
}
|
|
|
|
_ => unimplemented!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-21 15:09:07 +00:00
|
|
|
impl ElementImpl for AudioEcho {}
|
2017-10-05 15:07:52 +00:00
|
|
|
|
2018-11-21 15:09:07 +00:00
|
|
|
impl BaseTransformImpl for AudioEcho {
|
|
|
|
fn transform_ip(
|
|
|
|
&self,
|
|
|
|
_element: &gst_base::BaseTransform,
|
|
|
|
buf: &mut gst::BufferRef,
|
2019-01-11 23:45:05 +00:00
|
|
|
) -> 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-01-11 23:45:05 +00:00
|
|
|
let mut map = buf.map_writable().ok_or(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
|
|
|
}
|
|
|
|
|
2018-11-21 15:09:07 +00:00
|
|
|
fn set_caps(
|
|
|
|
&self,
|
|
|
|
_element: &gst_base::BaseTransform,
|
|
|
|
incaps: &gst::Caps,
|
|
|
|
outcaps: &gst::Caps,
|
|
|
|
) -> bool {
|
2017-10-05 15:07:52 +00:00
|
|
|
if incaps != outcaps {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
let info = match gst_audio::AudioInfo::from_caps(incaps) {
|
|
|
|
None => return false,
|
|
|
|
Some(info) => info,
|
|
|
|
};
|
|
|
|
|
|
|
|
let max_delay = self.settings.lock().unwrap().max_delay;
|
2017-11-11 12:02:55 +00:00
|
|
|
let size = max_delay * (info.rate() as u64) / gst::SECOND_VAL;
|
2017-10-05 15:07:52 +00:00
|
|
|
let buffer_size = size * (info.channels() as u64);
|
|
|
|
|
|
|
|
*self.state.lock().unwrap() = Some(State {
|
2018-10-11 10:49:10 +00:00
|
|
|
info,
|
2017-10-05 15:07:52 +00:00
|
|
|
buffer: RingBuffer::new(buffer_size as usize),
|
|
|
|
});
|
|
|
|
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
2019-01-26 15:07:51 +00:00
|
|
|
fn stop(&self, _element: &gst_base::BaseTransform) -> 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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-04 18:46:07 +00:00
|
|
|
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
2019-06-04 09:04:06 +00:00
|
|
|
gst::Element::register(
|
|
|
|
Some(plugin),
|
|
|
|
"rsaudioecho",
|
|
|
|
gst::Rank::None,
|
|
|
|
AudioEcho::get_type(),
|
|
|
|
)
|
2017-10-05 15:07:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct RingBuffer {
|
2017-10-07 01:41:25 +00:00
|
|
|
buffer: Box<[f64]>,
|
2017-10-05 15:07:52 +00:00
|
|
|
pos: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RingBuffer {
|
|
|
|
fn new(size: usize) -> Self {
|
|
|
|
let mut buffer = Vec::with_capacity(size as usize);
|
|
|
|
buffer.extend(iter::repeat(0.0).take(size as usize));
|
|
|
|
|
|
|
|
Self {
|
2017-10-07 01:41:25 +00:00
|
|
|
buffer: buffer.into_boxed_slice(),
|
2017-10-05 15:07:52 +00:00
|
|
|
pos: 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn iter(&mut self, delay: usize) -> RingBufferIter {
|
|
|
|
RingBufferIter::new(self, delay)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct RingBufferIter<'a> {
|
2017-10-07 08:38:35 +00:00
|
|
|
buffer: &'a mut [f64],
|
|
|
|
buffer_pos: &'a mut usize,
|
2017-10-05 15:07:52 +00:00
|
|
|
read_pos: usize,
|
|
|
|
write_pos: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> RingBufferIter<'a> {
|
|
|
|
fn new(buffer: &'a mut RingBuffer, delay: usize) -> RingBufferIter<'a> {
|
2017-10-07 01:41:25 +00:00
|
|
|
let size = buffer.buffer.len();
|
2017-10-05 15:07:52 +00:00
|
|
|
|
2017-10-07 01:41:25 +00:00
|
|
|
assert!(size >= delay);
|
|
|
|
assert_ne!(size, 0);
|
2017-10-05 15:07:52 +00:00
|
|
|
|
2017-10-07 01:41:25 +00:00
|
|
|
let read_pos = (size - delay + buffer.pos) % size;
|
|
|
|
let write_pos = buffer.pos % size;
|
2017-10-05 15:07:52 +00:00
|
|
|
|
2017-10-07 08:38:35 +00:00
|
|
|
let buffer_pos = &mut buffer.pos;
|
|
|
|
let buffer = &mut buffer.buffer;
|
|
|
|
|
2017-10-05 15:07:52 +00:00
|
|
|
RingBufferIter {
|
2018-10-11 10:49:10 +00:00
|
|
|
buffer,
|
|
|
|
buffer_pos,
|
|
|
|
read_pos,
|
|
|
|
write_pos,
|
2017-10-05 15:07:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Iterator for RingBufferIter<'a> {
|
|
|
|
type Item = (&'a mut f64, f64);
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
2017-10-07 01:41:25 +00:00
|
|
|
let res = unsafe {
|
2017-10-07 08:38:35 +00:00
|
|
|
let r = *self.buffer.get_unchecked(self.read_pos);
|
|
|
|
let w = self.buffer.get_unchecked_mut(self.write_pos);
|
2017-10-07 01:41:25 +00:00
|
|
|
// Cast needed to get from &mut f64 to &'a mut f64
|
|
|
|
(&mut *(w as *mut f64), r)
|
|
|
|
};
|
2017-10-05 15:07:52 +00:00
|
|
|
|
2017-10-07 10:14:18 +00:00
|
|
|
let size = self.buffer.len();
|
|
|
|
self.write_pos = (self.write_pos + 1) % size;
|
|
|
|
self.read_pos = (self.read_pos + 1) % size;
|
2017-10-07 01:41:25 +00:00
|
|
|
|
|
|
|
Some(res)
|
2017-10-05 15:07:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Drop for RingBufferIter<'a> {
|
|
|
|
fn drop(&mut self) {
|
2017-10-07 08:38:35 +00:00
|
|
|
*self.buffer_pos = self.write_pos;
|
2017-10-05 15:07:52 +00:00
|
|
|
}
|
|
|
|
}
|