Redesign mapped block smoothing
The non-mapped version is now split off and the mapped version is much better suited for array based modulation. Check the breaking changes document and the new docstring for more information.
This commit is contained in:
parent
99437c6011
commit
a21daef96b
|
@ -6,6 +6,16 @@ new and what's changed, this document lists all breaking changes in reverse
|
|||
chronological order. If a new feature did not require any changes to existing
|
||||
code then it will not be listed here.
|
||||
|
||||
## [2022-09-04]
|
||||
|
||||
- `Smoother::next_block_mapped()` and `Smoother::next_block_exact_mapped()` have
|
||||
been redesigned. They now take an index of the element being generated and the
|
||||
float representation of the smoothed value. This makes it easier to use them
|
||||
for modulation, and it makes it possible to smoothly modulate integers and
|
||||
other stepped parameters. Additionally, the mapping functions are now also
|
||||
called for every produced value, even if the smoother has already finished
|
||||
smoothing and is always producing the same value.
|
||||
|
||||
## [2022-08-19]
|
||||
|
||||
- `Plugin::DEFAULT_NUM_INPUTS` and `Plugin::DEFAULT_NUM_OUTPUTS` have been
|
||||
|
|
|
@ -324,24 +324,11 @@ impl<T: Smoothable> Smoother<T> {
|
|||
///
|
||||
/// Panics if `block_len > block_values.len()`.
|
||||
pub fn next_block(&self, block_values: &mut [T], block_len: usize) {
|
||||
self.next_block_exact_mapped(&mut block_values[..block_len], |x| x)
|
||||
self.next_block_exact(&mut block_values[..block_len])
|
||||
}
|
||||
|
||||
/// The same as [`next_block()`][Self::next_block()], but filling the entire slice.
|
||||
pub fn next_block_exact(&self, block_values: &mut [T]) {
|
||||
self.next_block_exact_mapped(block_values, |x| x)
|
||||
}
|
||||
|
||||
/// The same as [`next_block()`][Self::next_block()], but with a function applied to each
|
||||
/// produced value. Useful when applying modulation to a smoothed parameter. The mapping
|
||||
/// function is only applied once for the final target value.
|
||||
pub fn next_block_mapped(&self, block_values: &mut [T], block_len: usize, f: impl Fn(T) -> T) {
|
||||
self.next_block_exact_mapped(&mut block_values[..block_len], f)
|
||||
}
|
||||
|
||||
/// The same as [`next_block_exact()`][Self::next_block()], but with a function applied to each
|
||||
/// produced value. Useful when applying modulation to a smoothed parameter.
|
||||
pub fn next_block_exact_mapped(&self, block_values: &mut [T], f: impl Fn(T) -> T) {
|
||||
// `self.next()` will yield the current value if the parameter is no longer smoothing, but
|
||||
// it's a bit of a waste to continuesly call that if only the first couple or none of the
|
||||
// values in `block_values` would require smoothing and the rest don't. Instead, we'll just
|
||||
|
@ -354,9 +341,10 @@ impl<T: Smoothable> Smoother<T> {
|
|||
// conditionals optimized out
|
||||
let mut current = self.current.load(Ordering::Relaxed);
|
||||
let target = self.target.to_f32();
|
||||
|
||||
block_values[..num_smoothed_values].fill_with(|| {
|
||||
current = self.style.next(current, target, self.step_size);
|
||||
f(T::from_f32(current))
|
||||
T::from_f32(current)
|
||||
});
|
||||
|
||||
// In `next()` the last step snaps the value to the target value. Since we're computing
|
||||
|
@ -364,16 +352,84 @@ impl<T: Smoothable> Smoother<T> {
|
|||
// instead to avoid the conditional.
|
||||
if num_smoothed_values == steps_left {
|
||||
current = target.to_f32();
|
||||
block_values[num_smoothed_values - 1..].fill(f(self.target));
|
||||
block_values[num_smoothed_values - 1..].fill(self.target);
|
||||
} else {
|
||||
block_values[num_smoothed_values..].fill(f(self.target));
|
||||
block_values[num_smoothed_values..].fill(self.target);
|
||||
}
|
||||
|
||||
self.current.store(current, Ordering::Relaxed);
|
||||
self.steps_left
|
||||
.fetch_sub(num_smoothed_values as i32, Ordering::Relaxed);
|
||||
} else {
|
||||
block_values.fill(f(self.target));
|
||||
block_values.fill(self.target);
|
||||
}
|
||||
}
|
||||
|
||||
/// The same as [`next_block()`][Self::next_block()], but with a function applied to each
|
||||
/// produced value. The mapping function takes an index in the block and a floating point
|
||||
/// representation of the smoother's current value. This allows the modulation to be consistent
|
||||
/// during smoothing. Additionally, the mapping function is always called even if the smoothing
|
||||
/// is finished.
|
||||
pub fn next_block_mapped(
|
||||
&self,
|
||||
block_values: &mut [T],
|
||||
block_len: usize,
|
||||
f: impl FnMut(usize, f32) -> T,
|
||||
) {
|
||||
self.next_block_exact_mapped(&mut block_values[..block_len], f)
|
||||
}
|
||||
|
||||
/// The same as [`next_block_exact()`][Self::next_block()], but with a function applied to each
|
||||
/// produced value. Useful when applying modulation to a smoothed parameter.
|
||||
pub fn next_block_exact_mapped(
|
||||
&self,
|
||||
block_values: &mut [T],
|
||||
mut f: impl FnMut(usize, f32) -> T,
|
||||
) {
|
||||
// This works exactly the same as `next_block_exact()`, except for the addition of the
|
||||
// mapping function
|
||||
let target = self.target.to_f32();
|
||||
|
||||
let steps_left = self.steps_left.load(Ordering::Relaxed) as usize;
|
||||
let num_smoothed_values = block_values.len().min(steps_left);
|
||||
if num_smoothed_values > 0 {
|
||||
let mut current = self.current.load(Ordering::Relaxed);
|
||||
|
||||
for (idx, value) in block_values
|
||||
.iter_mut()
|
||||
.enumerate()
|
||||
.take(num_smoothed_values)
|
||||
{
|
||||
current = self.style.next(current, target, self.step_size);
|
||||
*value = f(idx, current);
|
||||
}
|
||||
|
||||
if num_smoothed_values == steps_left {
|
||||
current = target.to_f32();
|
||||
for (idx, value) in block_values
|
||||
.iter_mut()
|
||||
.enumerate()
|
||||
.skip(num_smoothed_values - 1)
|
||||
{
|
||||
*value = f(idx, target);
|
||||
}
|
||||
} else {
|
||||
for (idx, value) in block_values
|
||||
.iter_mut()
|
||||
.enumerate()
|
||||
.skip(num_smoothed_values)
|
||||
{
|
||||
*value = f(idx, target);
|
||||
}
|
||||
}
|
||||
|
||||
self.current.store(current, Ordering::Relaxed);
|
||||
self.steps_left
|
||||
.fetch_sub(num_smoothed_values as i32, Ordering::Relaxed);
|
||||
} else {
|
||||
for (idx, value) in block_values.iter_mut().enumerate() {
|
||||
*value = f(idx, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue