use crate::context::{AsBaseAudioContext, AudioContextRegistration, AudioParamId};
use crate::param::{AudioParam, AudioParamOptions};
use crate::render::{AudioParamValues, AudioProcessor, AudioRenderQuantum};
use crate::{SampleRate, RENDER_QUANTUM_SIZE};
use super::{AudioNode, ChannelConfig, ChannelConfigOptions, ChannelInterpretation};
use std::cell::{Cell, RefCell, RefMut};
use std::rc::Rc;
pub struct DelayOptions {
pub max_delay_time: f64,
pub delay_time: f64,
pub channel_config: ChannelConfigOptions,
}
impl Default for DelayOptions {
fn default() -> Self {
Self {
max_delay_time: 1.,
delay_time: 0.,
channel_config: ChannelConfigOptions::default(),
}
}
}
pub struct DelayNode {
reader_registration: AudioContextRegistration,
writer_registration: AudioContextRegistration,
delay_time: AudioParam,
channel_config: ChannelConfig,
}
impl AudioNode for DelayNode {
fn registration(&self) -> &AudioContextRegistration {
&self.writer_registration
}
fn channel_config_raw(&self) -> &ChannelConfig {
&self.channel_config
}
fn number_of_inputs(&self) -> u32 {
1
}
fn number_of_outputs(&self) -> u32 {
1
}
fn connect_at<'a>(
&self,
dest: &'a dyn AudioNode,
output: u32,
input: u32,
) -> Result<&'a dyn AudioNode, crate::IndexSizeError> {
if self.context() != dest.context() {
panic!("attempting to connect nodes from different contexts");
}
if self.number_of_outputs() <= output || dest.number_of_inputs() <= input {
return Err(crate::IndexSizeError {});
}
self.context()
.connect(self.reader_registration.id(), dest.id(), output, input);
Ok(dest)
}
fn disconnect<'a>(&self, dest: &'a dyn AudioNode) -> &'a dyn AudioNode {
if self.context() != dest.context() {
panic!("attempting to disconnect nodes from different contexts");
}
self.context()
.disconnect(self.reader_registration.id(), dest.id());
dest
}
fn disconnect_all(&self) {
self.context().disconnect_all(self.reader_registration.id());
}
}
impl DelayNode {
pub fn new<C: AsBaseAudioContext>(context: &C, options: DelayOptions) -> Self {
let sample_rate = context.sample_rate_raw().0 as f64;
if options.max_delay_time <= 0. || options.max_delay_time >= 180. {
panic!("NotSupportedError: MUST be greater than zero and less than three minutes");
}
let quantum_duration = 1. / sample_rate * RENDER_QUANTUM_SIZE as f64;
let max_delay_time = options.max_delay_time.max(quantum_duration);
let num_samples = max_delay_time * sample_rate + 1.;
let num_quanta =
(num_samples.ceil() as usize + RENDER_QUANTUM_SIZE - 1) / RENDER_QUANTUM_SIZE;
let ring_buffer = Vec::with_capacity(num_quanta);
let shared_ring_buffer = Rc::new(RefCell::new(ring_buffer));
let shared_ring_buffer_clone = shared_ring_buffer.clone();
let last_written_index = Rc::new(Cell::<Option<usize>>::new(None));
let last_written_index_clone = last_written_index.clone();
context.base().register(move |writer_registration| {
let node = context.base().register(move |reader_registration| {
let param_opts = AudioParamOptions {
min_value: 0.,
max_value: max_delay_time as f32,
default_value: 0.,
automation_rate: crate::param::AutomationRate::A,
};
let (param, proc) = context
.base()
.create_audio_param(param_opts, reader_registration.id());
param.set_value_at_time(options.delay_time as f32, 0.);
let reader_render = DelayReader {
delay_time: proc,
ring_buffer: shared_ring_buffer_clone,
index: 0,
last_written_index: last_written_index_clone,
last_written_index_checked: None,
internal_buffer: Vec::<f32>::with_capacity(crate::MAX_CHANNELS),
};
let node = DelayNode {
reader_registration,
writer_registration,
channel_config: options.channel_config.into(),
delay_time: param,
};
(node, Box::new(reader_render))
});
let writer_render = DelayWriter {
ring_buffer: shared_ring_buffer,
index: 0,
last_written_index,
};
(node, Box::new(writer_render))
})
}
pub fn delay_time(&self) -> &AudioParam {
&self.delay_time
}
}
struct DelayWriter {
ring_buffer: Rc<RefCell<Vec<AudioRenderQuantum>>>,
index: usize,
last_written_index: Rc<Cell<Option<usize>>>,
}
struct DelayReader {
delay_time: AudioParamId,
ring_buffer: Rc<RefCell<Vec<AudioRenderQuantum>>>,
index: usize,
last_written_index: Rc<Cell<Option<usize>>>,
last_written_index_checked: Option<usize>,
internal_buffer: Vec<f32>,
}
unsafe impl Send for DelayWriter {}
unsafe impl Send for DelayReader {}
trait RingBufferChecker {
fn ring_buffer_mut(&self) -> RefMut<Vec<AudioRenderQuantum>>;
#[inline(always)]
fn check_ring_buffer_size(&self, render_quantum: &AudioRenderQuantum) {
let mut ring_buffer = self.ring_buffer_mut();
if ring_buffer.len() < ring_buffer.capacity() {
let len = ring_buffer.capacity();
let mut silence = render_quantum.clone();
silence.make_silent();
ring_buffer.resize(len, silence);
}
}
}
impl Drop for DelayWriter {
fn drop(&mut self) {
let last_written_index = if self.index == 0 {
self.ring_buffer.borrow().capacity() - 1
} else {
self.index - 1
};
self.last_written_index.set(Some(last_written_index));
}
}
impl RingBufferChecker for DelayWriter {
#[inline(always)]
fn ring_buffer_mut(&self) -> RefMut<Vec<AudioRenderQuantum>> {
self.ring_buffer.borrow_mut()
}
}
impl AudioProcessor for DelayWriter {
fn process(
&mut self,
inputs: &[AudioRenderQuantum],
outputs: &mut [AudioRenderQuantum],
_params: AudioParamValues,
_timestamp: f64,
_sample_rate: SampleRate,
) -> bool {
let input = inputs[0].clone();
let output = &mut outputs[0];
self.check_ring_buffer_size(&input);
self.check_ring_buffer_up_down_mix(&input);
let mut buffer = self.ring_buffer.borrow_mut();
buffer[self.index] = input;
self.index = (self.index + 1) % buffer.capacity();
output.make_silent();
false
}
}
impl DelayWriter {
#[inline(always)]
fn check_ring_buffer_up_down_mix(&self, input: &AudioRenderQuantum) {
let mut ring_buffer = self.ring_buffer_mut();
let buffer_number_of_channels = ring_buffer[0].number_of_channels();
let input_number_of_channels = input.number_of_channels();
if buffer_number_of_channels != input_number_of_channels {
for render_quantum in ring_buffer.iter_mut() {
render_quantum.mix(input_number_of_channels, ChannelInterpretation::Speakers);
}
}
}
}
impl RingBufferChecker for DelayReader {
#[inline(always)]
fn ring_buffer_mut(&self) -> RefMut<Vec<AudioRenderQuantum>> {
self.ring_buffer.borrow_mut()
}
}
impl AudioProcessor for DelayReader {
fn process(
&mut self,
_inputs: &[AudioRenderQuantum], outputs: &mut [AudioRenderQuantum],
params: AudioParamValues,
_timestamp: f64,
sample_rate: SampleRate,
) -> bool {
let output = &mut outputs[0];
self.check_ring_buffer_size(output);
let ring_buffer = self.ring_buffer.borrow();
let number_of_channels = ring_buffer[0].number_of_channels();
if self.internal_buffer.len() != number_of_channels {
self.internal_buffer.resize(number_of_channels, 0.);
}
output.set_number_of_channels(number_of_channels);
let sample_rate = sample_rate.0 as f64;
let dt = 1. / sample_rate;
let quantum_duration = RENDER_QUANTUM_SIZE as f64 * dt;
let delay_param = params.get(&self.delay_time);
for (index, delay) in delay_param.iter().enumerate() {
let clamped_delay = (*delay as f64).max(quantum_duration);
let num_samples = clamped_delay * sample_rate;
let position = index as f64 - num_samples;
let prev_position = position.floor();
let (prev_block_index, prev_frame_index) =
self.find_frame_adress_at_position(prev_position);
let next_position = position.ceil();
let (next_block_index, next_frame_index) =
self.find_frame_adress_at_position(next_position);
let k = (position - position.floor()) as f32;
let k_inv = 1. - k;
for channel_number in 0..number_of_channels {
let prev_sample =
ring_buffer[prev_block_index].channel_data(channel_number)[prev_frame_index];
let next_sample =
ring_buffer[next_block_index].channel_data(channel_number)[next_frame_index];
let value = k_inv * prev_sample + k * next_sample;
self.internal_buffer[channel_number] = value;
}
output.set_channels_values_at(index, &self.internal_buffer);
}
if matches!(self.last_written_index_checked, Some(index) if index == self.index) {
return false;
}
let last_written_index = self.last_written_index.get();
if last_written_index.is_some() && self.last_written_index_checked.is_none() {
self.last_written_index_checked = last_written_index;
}
self.index = (self.index + 1) % ring_buffer.capacity();
true
}
}
impl DelayReader {
#[inline(always)]
fn find_frame_adress_at_position(&self, position: f64) -> (usize, usize) {
let num_frames = RENDER_QUANTUM_SIZE as i32;
let buffer_len = self.ring_buffer.borrow().len() as i32;
let current_index = self.index as i32;
let block_offset = (position / num_frames as f64).floor();
let mut block_index = current_index + block_offset as i32;
if block_index < 0 {
block_index += buffer_len;
}
let mut frame_offset = position as i32 % num_frames;
if frame_offset == 0 {
frame_offset = -num_frames;
}
let frame_index = num_frames + frame_offset;
(block_index as usize, frame_index as usize)
}
}
#[cfg(test)]
mod tests {
use float_eq::assert_float_eq;
use crate::context::OfflineAudioContext;
use crate::SampleRate;
use super::*;
#[test]
fn test_sample_accurate() {
for delay_in_samples in [128., 131., 197.].iter() {
let sample_rate = SampleRate(128);
let mut context = OfflineAudioContext::new(1, 256, sample_rate);
let delay = context.create_delay(2.);
delay.delay_time.set_value(delay_in_samples / 128.);
delay.connect(&context.destination());
let mut dirac = context.create_buffer(1, 1, sample_rate);
dirac.copy_to_channel(&[1.], 0);
let src = context.create_buffer_source();
src.connect(&delay);
src.set_buffer(dirac);
src.start_at(0.);
let result = context.start_rendering();
let channel = result.get_channel_data(0);
let mut expected = vec![0.; 256];
expected[*delay_in_samples as usize] = 1.;
assert_float_eq!(channel[..], expected[..], abs_all <= 0.);
}
}
#[test]
fn test_sub_sample_accurate() {
{
let delay_in_samples = 128.5;
let sample_rate = SampleRate(128);
let mut context = OfflineAudioContext::new(1, 256, sample_rate);
let delay = context.create_delay(2.);
delay.delay_time.set_value(delay_in_samples / 128.);
delay.connect(&context.destination());
let mut dirac = context.create_buffer(1, 1, sample_rate);
dirac.copy_to_channel(&[1.], 0);
let src = context.create_buffer_source();
src.connect(&delay);
src.set_buffer(dirac);
src.start_at(0.);
let result = context.start_rendering();
let channel = result.get_channel_data(0);
let mut expected = vec![0.; 256];
expected[128] = 0.5;
expected[129] = 0.5;
assert_float_eq!(channel[..], expected[..], abs_all <= 0.);
}
{
let delay_in_samples = 128.8;
let sample_rate = SampleRate(128);
let mut context = OfflineAudioContext::new(1, 256, sample_rate);
let delay = context.create_delay(2.);
delay.delay_time.set_value(delay_in_samples / 128.);
delay.connect(&context.destination());
let mut dirac = context.create_buffer(1, 1, sample_rate);
dirac.copy_to_channel(&[1.], 0);
let src = context.create_buffer_source();
src.connect(&delay);
src.set_buffer(dirac);
src.start_at(0.);
let result = context.start_rendering();
let channel = result.get_channel_data(0);
let mut expected = vec![0.; 256];
expected[128] = 0.2;
expected[129] = 0.8;
assert_float_eq!(channel[..], expected[..], abs_all <= 1e-5);
}
}
#[test]
fn test_multichannel() {
let delay_in_samples = 128.;
let sample_rate = SampleRate(128);
let mut context = OfflineAudioContext::new(2, 2 * 128, sample_rate);
let delay = context.create_delay(2.);
delay.delay_time.set_value(delay_in_samples / 128.);
delay.connect(&context.destination());
let mut two_chan_dirac = context.create_buffer(2, 256, sample_rate);
two_chan_dirac.copy_to_channel(&[1.], 0);
two_chan_dirac.copy_to_channel(&[0., 1.], 1);
let src = context.create_buffer_source();
src.connect(&delay);
src.set_buffer(two_chan_dirac);
src.start_at(0.);
let result = context.start_rendering();
let channel_left = result.get_channel_data(0);
let mut expected_left = vec![0.; 256];
expected_left[128] = 1.;
assert_float_eq!(channel_left[..], expected_left[..], abs_all <= 0.);
let channel_right = result.get_channel_data(1);
let mut expected_right = vec![0.; 256];
expected_right[128 + 1] = 1.;
assert_float_eq!(channel_right[..], expected_right[..], abs_all <= 0.);
}
#[test]
fn test_input_number_of_channels_change() {
let delay_in_samples = 128.;
let sample_rate = SampleRate(128);
let mut context = OfflineAudioContext::new(2, 3 * 128, sample_rate);
let delay = context.create_delay(2.);
delay.delay_time.set_value(delay_in_samples / 128.);
delay.connect(&context.destination());
let mut one_chan_dirac = context.create_buffer(1, 128, sample_rate);
one_chan_dirac.copy_to_channel(&[1.], 0);
let src1 = context.create_buffer_source();
src1.connect(&delay);
src1.set_buffer(one_chan_dirac);
src1.start_at(0.);
let mut two_chan_dirac = context.create_buffer(2, 256, sample_rate);
two_chan_dirac.copy_to_channel(&[1.], 0);
two_chan_dirac.copy_to_channel(&[0., 1.], 1);
let src2 = context.create_buffer_source();
src2.connect(&delay);
src2.set_buffer(two_chan_dirac);
src2.start_at(1.);
let result = context.start_rendering();
let channel_left = result.get_channel_data(0);
let mut expected_left = vec![0.; 3 * 128];
expected_left[128] = 1.;
expected_left[256] = 1.;
assert_float_eq!(channel_left[..], expected_left[..], abs_all <= 0.);
let channel_right = result.get_channel_data(1);
let mut expected_right = vec![0.; 3 * 128];
expected_right[128] = 1.;
expected_right[256 + 1] = 1.;
assert_float_eq!(channel_right[..], expected_right[..], abs_all <= 0.);
}
#[test]
fn test_node_stays_alive_long_enough() {
for _ in 0..10 {
let sample_rate = SampleRate(128);
let mut context = OfflineAudioContext::new(1, 5 * 128, sample_rate);
{
let delay = context.create_delay(1.);
delay.delay_time.set_value(1.);
delay.connect(&context.destination());
let mut dirac = context.create_buffer(1, 1, sample_rate);
dirac.copy_to_channel(&[1.], 0);
let src = context.create_buffer_source();
src.connect(&delay);
src.set_buffer(dirac);
src.start_at(3.);
}
let result = context.start_rendering();
let mut expected = vec![0.; 5 * 128];
expected[4 * 128] = 1.;
assert_float_eq!(result.get_channel_data(0), &expected[..], abs_all <= 0.);
}
}
#[test]
fn test_max_delay_multiple_of_quantum_size() {
for _ in 0..10 {
let sample_rate = SampleRate(128);
let mut context = OfflineAudioContext::new(1, 256, sample_rate);
let delay = context.create_delay(1.);
delay.delay_time.set_value(1.);
delay.connect(&context.destination());
let mut dirac = context.create_buffer(1, 1, sample_rate);
dirac.copy_to_channel(&[1.], 0);
let src = context.create_buffer_source();
src.connect(&delay);
src.set_buffer(dirac);
src.start_at(0.);
let result = context.start_rendering();
let channel = result.get_channel_data(0);
let mut expected = vec![0.; 256];
expected[128] = 1.;
assert_float_eq!(channel[..], expected[..], abs_all <= 0.);
}
for _ in 0..10 {
let sample_rate = SampleRate(128);
let mut context = OfflineAudioContext::new(1, 3 * 128, sample_rate);
let delay = context.create_delay(2.);
delay.delay_time.set_value(2.);
delay.connect(&context.destination());
let mut dirac = context.create_buffer(1, 1, sample_rate);
dirac.copy_to_channel(&[1.], 0);
let src = context.create_buffer_source();
src.connect(&delay);
src.set_buffer(dirac);
src.start_at(0.);
let result = context.start_rendering();
let channel = result.get_channel_data(0);
let mut expected = vec![0.; 3 * 128];
expected[256] = 1.;
assert_float_eq!(channel[..], expected[..], abs_all <= 0.);
}
}
#[test]
fn test_max_delay_smaller_than_quantum_size() {
for _ in 0..10 {
let sample_rate = SampleRate(128);
let mut context = OfflineAudioContext::new(1, 256, sample_rate);
let delay = context.create_delay(0.5); delay.delay_time.set_value(0.5); delay.connect(&context.destination());
let mut dirac = context.create_buffer(1, 1, sample_rate);
dirac.copy_to_channel(&[1.], 0);
let src = context.create_buffer_source();
src.connect(&delay);
src.set_buffer(dirac);
src.start_at(0.);
let result = context.start_rendering();
let channel = result.get_channel_data(0);
let mut expected = vec![0.; 256];
expected[128] = 1.;
assert_float_eq!(channel[..], expected[..], abs_all <= 0.);
}
}
}