From dffddfaf044e0cfc606b012f0b88a8eecf51c2d9 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Tue, 1 Mar 2022 17:29:09 +0100 Subject: [PATCH] Implement the block smoothers --- src/param/smoothing.rs | 50 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/src/param/smoothing.rs b/src/param/smoothing.rs index 3e15f7b7..d358b8ed 100644 --- a/src/param/smoothing.rs +++ b/src/param/smoothing.rs @@ -1,5 +1,5 @@ use atomic_float::AtomicF32; -use parking_lot::Mutex; +use parking_lot::{MappedMutexGuard, Mutex, MutexGuard}; use std::sync::atomic::{AtomicI32, Ordering}; /// Controls if and how parameters gets smoothed. @@ -124,7 +124,7 @@ impl Smoother { } /// Get the next value from this smoother. The value will be equal to the previous value once - // the smoothing period is over. This should be called exactly once per sample. + /// the smoothing period is over. This should be called exactly once per sample. // Yes, Clippy, like I said, this was intentional #[allow(clippy::should_implement_trait)] #[inline] @@ -132,6 +132,31 @@ impl Smoother { self.next_step(1) } + /// Produce smoothed values for an entire block of audio. Used in conjunction with + /// [crate::Buffer::iter_blocks()]. Make sure to call + /// [crate::Plugin::initialize_block_smoothers()] with the same maximum buffer block size as the + /// one passed to `iter_blocks()` in your [crate::Plugin::initialize()] function first to + /// allocate memory for the block smoothing. + /// + /// Returns a `None` value if the block length exceed's the allocated capacity. + #[inline] + pub fn next_block(&self, block_len: usize) -> Option> { + let mut block_values = self.block_values.lock(); + if block_values.len() < block_len { + return None; + } + + // TODO: As a small optimization we could split this up into two loops for the smoothed and + // unsmoothed parts. Another worthwhile optimization would be to remember if the + // buffer is already filled with the target value and [Self::is_smoothing()] is false. + // In that case we wouldn't need to do anything ehre. + (&mut block_values[..block_len]).fill_with(|| self.next()); + + Some(MutexGuard::map(block_values, |values| { + &mut values[..block_len] + })) + } + /// [Self::next()], but with the ability to skip forward in the smoother. [Self::next()] is /// equivalent to calling this function with a `steps` value of 1. Calling this function with a /// `steps` value of `n` means will cause you to skip the next `n - 1` values and return the @@ -204,6 +229,27 @@ impl Smoother { self.next_step(1) } + /// Produce smoothed values for an entire block of audio. Used in conjunction with + /// [crate::Buffer::iter_blocks()]. Make sure to call + /// [crate::Plugin::initialize_block_smoothers()] with the same maximum buffer block size as the + /// one passed to `iter_blocks()` in your [crate::Plugin::initialize()] function first to + /// allocate memory for the block smoothing. + /// + /// Returns a `None` value if the block length exceed's the allocated capacity. + #[inline] + pub fn next_block(&self, block_len: usize) -> Option> { + let mut block_values = self.block_values.lock(); + if block_values.len() < block_len { + return None; + } + + (&mut block_values[..block_len]).fill_with(|| self.next()); + + Some(MutexGuard::map(block_values, |values| { + &mut values[..block_len] + })) + } + /// [Self::next()], but with the ability to skip forward in the smoother. [Self::next()] is /// equivalent to calling this function with a `steps` value of 1. Calling this function with a /// `steps` value of `n` means will cause you to skip the next `n - 1` values and return the