Use only interior mutability for smoothers
This is needed in preparation for removing the need to have `*mut` pointers to parameters.
This commit is contained in:
parent
7f33f172cb
commit
5966e353da
1 changed files with 100 additions and 45 deletions
|
@ -34,7 +34,7 @@ pub enum SmoothingStyle {
|
||||||
// TODO: We need to use atomics here so we can share the params object with the GUI. Is there a
|
// TODO: We need to use atomics here so we can share the params object with the GUI. Is there a
|
||||||
// better alternative to allow the process function to mutate these smoothers?
|
// better alternative to allow the process function to mutate these smoothers?
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Smoother<T> {
|
pub struct Smoother<T: Smoothable> {
|
||||||
/// The kind of snoothing that needs to be applied, if any.
|
/// The kind of snoothing that needs to be applied, if any.
|
||||||
pub style: SmoothingStyle,
|
pub style: SmoothingStyle,
|
||||||
/// The number of steps of smoothing left to take.
|
/// The number of steps of smoothing left to take.
|
||||||
|
@ -48,18 +48,18 @@ pub struct Smoother<T> {
|
||||||
///
|
///
|
||||||
/// In the case of the `Exponential` smoothing style this is the coefficient `x` that the
|
/// In the case of the `Exponential` smoothing style this is the coefficient `x` that the
|
||||||
/// previous sample is multplied by.
|
/// previous sample is multplied by.
|
||||||
step_size: f32,
|
step_size: AtomicF32,
|
||||||
/// The value for the current sample. Always stored as floating point for obvious reasons.
|
/// The value for the current sample. Always stored as floating point for obvious reasons.
|
||||||
current: AtomicF32,
|
current: AtomicF32,
|
||||||
/// The value we're smoothing towards
|
/// The value we're smoothing towards
|
||||||
target: T,
|
target: T::Atomic,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An iterator that continuously produces smoothed values. Can be used as an alternative to the
|
/// An iterator that continuously produces smoothed values. Can be used as an alternative to the
|
||||||
/// block-based smoothing API. Since the iterator itself is infinite, you can use
|
/// block-based smoothing API. Since the iterator itself is infinite, you can use
|
||||||
/// [`Smoother::is_smoothing()`] and [`Smoother::steps_left()`] to get information on the current
|
/// [`Smoother::is_smoothing()`] and [`Smoother::steps_left()`] to get information on the current
|
||||||
/// smoothing status.
|
/// smoothing status.
|
||||||
pub struct SmootherIter<'a, T> {
|
pub struct SmootherIter<'a, T: Smoothable> {
|
||||||
smoother: &'a Smoother<T>,
|
smoother: &'a Smoother<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,9 +146,18 @@ impl SmoothingStyle {
|
||||||
|
|
||||||
/// A type that can be smoothed. This exists just to avoid duplicate explicit implementations for
|
/// A type that can be smoothed. This exists just to avoid duplicate explicit implementations for
|
||||||
/// the smoothers.
|
/// the smoothers.
|
||||||
pub trait Smoothable: Default + Copy {
|
pub trait Smoothable: Default + Clone + Copy {
|
||||||
|
/// The atomic representation of `Self`.
|
||||||
|
type Atomic: Default;
|
||||||
|
|
||||||
fn to_f32(self) -> f32;
|
fn to_f32(self) -> f32;
|
||||||
fn from_f32(value: f32) -> Self;
|
fn from_f32(value: f32) -> Self;
|
||||||
|
|
||||||
|
fn atomic_new(value: Self) -> Self::Atomic;
|
||||||
|
/// A relaxed atomic load.
|
||||||
|
fn atomic_load(this: &Self::Atomic) -> Self;
|
||||||
|
/// A relaxed atomic store.
|
||||||
|
fn atomic_store(this: &Self::Atomic, value: Self);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Smoothable> Default for Smoother<T> {
|
impl<T: Smoothable> Default for Smoother<T> {
|
||||||
|
@ -172,16 +181,16 @@ impl<T: Smoothable> Iterator for SmootherIter<'_, T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Clone> Clone for Smoother<T> {
|
impl<T: Smoothable> Clone for Smoother<T> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
// We can't derive clone because of the atomics, but these atomics are only here to allow
|
// We can't derive clone because of the atomics, but these atomics are only here to allow
|
||||||
// Send+Sync interior mutability
|
// Send+Sync interior mutability
|
||||||
Self {
|
Self {
|
||||||
style: self.style,
|
style: self.style,
|
||||||
steps_left: AtomicI32::new(self.steps_left.load(Ordering::Relaxed)),
|
steps_left: AtomicI32::new(self.steps_left.load(Ordering::Relaxed)),
|
||||||
step_size: self.step_size,
|
step_size: AtomicF32::new(self.step_size.load(Ordering::Relaxed)),
|
||||||
current: AtomicF32::new(self.current.load(Ordering::Relaxed)),
|
current: AtomicF32::new(self.current.load(Ordering::Relaxed)),
|
||||||
target: self.target.clone(),
|
target: T::atomic_new(T::atomic_load(&self.target)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -223,23 +232,25 @@ impl<T: Smoothable> Smoother<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reset the smoother the specified value.
|
/// Reset the smoother the specified value.
|
||||||
pub fn reset(&mut self, value: T) {
|
pub fn reset(&self, value: T) {
|
||||||
self.target = value;
|
T::atomic_store(&self.target, value);
|
||||||
self.current.store(value.to_f32(), Ordering::Relaxed);
|
self.current.store(value.to_f32(), Ordering::Relaxed);
|
||||||
self.steps_left.store(0, Ordering::Relaxed);
|
self.steps_left.store(0, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the target value.
|
/// Set the target value.
|
||||||
pub fn set_target(&mut self, sample_rate: f32, target: T) {
|
pub fn set_target(&self, sample_rate: f32, target: T) {
|
||||||
self.target = target;
|
T::atomic_store(&self.target, target);
|
||||||
|
|
||||||
let steps_left = self.style.num_steps(sample_rate) as i32;
|
let steps_left = self.style.num_steps(sample_rate) as i32;
|
||||||
self.steps_left.store(steps_left, Ordering::Relaxed);
|
self.steps_left.store(steps_left, Ordering::Relaxed);
|
||||||
|
|
||||||
let current = self.current.load(Ordering::Relaxed);
|
let current = self.current.load(Ordering::Relaxed);
|
||||||
self.step_size = self
|
let target_f32 = target.to_f32();
|
||||||
.style
|
self.step_size.store(
|
||||||
.step_size(current, self.target.to_f32(), steps_left as u32);
|
self.style.step_size(current, target_f32, steps_left as u32),
|
||||||
|
Ordering::Relaxed,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the next value from this smoother. The value will be equal to the previous value once
|
/// Get the next value from this smoother. The value will be equal to the previous value once
|
||||||
|
@ -248,11 +259,14 @@ impl<T: Smoothable> Smoother<T> {
|
||||||
#[allow(clippy::should_implement_trait)]
|
#[allow(clippy::should_implement_trait)]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn next(&self) -> T {
|
pub fn next(&self) -> T {
|
||||||
|
let target = T::atomic_load(&self.target);
|
||||||
|
|
||||||
// NOTE: This used to be implemented in terms of `next_step()`, but this is more efficient
|
// NOTE: This used to be implemented in terms of `next_step()`, but this is more efficient
|
||||||
// for the common use case of single steps
|
// for the common use case of single steps
|
||||||
if self.steps_left.load(Ordering::Relaxed) > 0 {
|
if self.steps_left.load(Ordering::Relaxed) > 0 {
|
||||||
let current = self.current.load(Ordering::Relaxed);
|
let current = self.current.load(Ordering::Relaxed);
|
||||||
let target = self.target.to_f32();
|
let target_f32 = target.to_f32();
|
||||||
|
let step_size = self.step_size.load(Ordering::Relaxed);
|
||||||
|
|
||||||
// The number of steps usually won't fit exactly, so make sure we don't end up with
|
// The number of steps usually won't fit exactly, so make sure we don't end up with
|
||||||
// quantization errors on overshoots or undershoots. We also need to account for the
|
// quantization errors on overshoots or undershoots. We also need to account for the
|
||||||
|
@ -262,15 +276,15 @@ impl<T: Smoothable> Smoother<T> {
|
||||||
let old_steps_left = self.steps_left.fetch_sub(1, Ordering::Relaxed);
|
let old_steps_left = self.steps_left.fetch_sub(1, Ordering::Relaxed);
|
||||||
let new = if old_steps_left == 1 {
|
let new = if old_steps_left == 1 {
|
||||||
self.steps_left.store(0, Ordering::Relaxed);
|
self.steps_left.store(0, Ordering::Relaxed);
|
||||||
target
|
target_f32
|
||||||
} else {
|
} else {
|
||||||
self.style.next(current, target, self.step_size)
|
self.style.next(current, target_f32, step_size)
|
||||||
};
|
};
|
||||||
self.current.store(new, Ordering::Relaxed);
|
self.current.store(new, Ordering::Relaxed);
|
||||||
|
|
||||||
T::from_f32(new)
|
T::from_f32(new)
|
||||||
} else {
|
} else {
|
||||||
self.target
|
target
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,9 +296,12 @@ impl<T: Smoothable> Smoother<T> {
|
||||||
pub fn next_step(&self, steps: u32) -> T {
|
pub fn next_step(&self, steps: u32) -> T {
|
||||||
nih_debug_assert_ne!(steps, 0);
|
nih_debug_assert_ne!(steps, 0);
|
||||||
|
|
||||||
|
let target = T::atomic_load(&self.target);
|
||||||
|
|
||||||
if self.steps_left.load(Ordering::Relaxed) > 0 {
|
if self.steps_left.load(Ordering::Relaxed) > 0 {
|
||||||
let current = self.current.load(Ordering::Relaxed);
|
let current = self.current.load(Ordering::Relaxed);
|
||||||
let target = self.target.to_f32();
|
let target_f32 = target.to_f32();
|
||||||
|
let step_size = self.step_size.load(Ordering::Relaxed);
|
||||||
|
|
||||||
// The number of steps usually won't fit exactly, so make sure we don't end up with
|
// The number of steps usually won't fit exactly, so make sure we don't end up with
|
||||||
// quantization errors on overshoots or undershoots. We also need to account for the
|
// quantization errors on overshoots or undershoots. We also need to account for the
|
||||||
|
@ -294,15 +311,15 @@ impl<T: Smoothable> Smoother<T> {
|
||||||
let old_steps_left = self.steps_left.fetch_sub(steps as i32, Ordering::Relaxed);
|
let old_steps_left = self.steps_left.fetch_sub(steps as i32, Ordering::Relaxed);
|
||||||
let new = if old_steps_left <= steps as i32 {
|
let new = if old_steps_left <= steps as i32 {
|
||||||
self.steps_left.store(0, Ordering::Relaxed);
|
self.steps_left.store(0, Ordering::Relaxed);
|
||||||
target
|
target_f32
|
||||||
} else {
|
} else {
|
||||||
self.style.next_step(current, target, self.step_size, steps)
|
self.style.next_step(current, target_f32, step_size, steps)
|
||||||
};
|
};
|
||||||
self.current.store(new, Ordering::Relaxed);
|
self.current.store(new, Ordering::Relaxed);
|
||||||
|
|
||||||
T::from_f32(new)
|
T::from_f32(new)
|
||||||
} else {
|
} else {
|
||||||
self.target
|
target
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,6 +346,8 @@ impl<T: Smoothable> Smoother<T> {
|
||||||
|
|
||||||
/// The same as [`next_block()`][Self::next_block()], but filling the entire slice.
|
/// The same as [`next_block()`][Self::next_block()], but filling the entire slice.
|
||||||
pub fn next_block_exact(&self, block_values: &mut [T]) {
|
pub fn next_block_exact(&self, block_values: &mut [T]) {
|
||||||
|
let target = T::atomic_load(&self.target);
|
||||||
|
|
||||||
// `self.next()` will yield the current value if the parameter is no longer smoothing, but
|
// `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
|
// 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
|
// values in `block_values` would require smoothing and the rest don't. Instead, we'll just
|
||||||
|
@ -338,34 +357,35 @@ impl<T: Smoothable> Smoother<T> {
|
||||||
let num_smoothed_values = block_values.len().min(steps_left);
|
let num_smoothed_values = block_values.len().min(steps_left);
|
||||||
if num_smoothed_values > 0 {
|
if num_smoothed_values > 0 {
|
||||||
let mut current = self.current.load(Ordering::Relaxed);
|
let mut current = self.current.load(Ordering::Relaxed);
|
||||||
let target = self.target.to_f32();
|
let target_f32 = target.to_f32();
|
||||||
|
let step_size = self.step_size.load(Ordering::Relaxed);
|
||||||
|
|
||||||
if num_smoothed_values == steps_left {
|
if num_smoothed_values == steps_left {
|
||||||
// This is the same as calling `next()` `num_smoothed_values` times, but with some
|
// This is the same as calling `next()` `num_smoothed_values` times, but with some
|
||||||
// conditionals optimized out
|
// conditionals optimized out
|
||||||
block_values[..num_smoothed_values - 1].fill_with(|| {
|
block_values[..num_smoothed_values - 1].fill_with(|| {
|
||||||
current = self.style.next(current, target, self.step_size);
|
current = self.style.next(current, target_f32, step_size);
|
||||||
T::from_f32(current)
|
T::from_f32(current)
|
||||||
});
|
});
|
||||||
|
|
||||||
// In `next()` the last step snaps the value to the target value, so we'll do the
|
// In `next()` the last step snaps the value to the target value, so we'll do the
|
||||||
// same thing here
|
// same thing here
|
||||||
current = target.to_f32();
|
current = target_f32.to_f32();
|
||||||
block_values[num_smoothed_values - 1] = self.target;
|
block_values[num_smoothed_values - 1] = target;
|
||||||
} else {
|
} else {
|
||||||
block_values[..num_smoothed_values].fill_with(|| {
|
block_values[..num_smoothed_values].fill_with(|| {
|
||||||
current = self.style.next(current, target, self.step_size);
|
current = self.style.next(current, target_f32, step_size);
|
||||||
T::from_f32(current)
|
T::from_f32(current)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
block_values[num_smoothed_values..].fill(self.target);
|
block_values[num_smoothed_values..].fill(target);
|
||||||
|
|
||||||
self.current.store(current, Ordering::Relaxed);
|
self.current.store(current, Ordering::Relaxed);
|
||||||
self.steps_left
|
self.steps_left
|
||||||
.fetch_sub(num_smoothed_values as i32, Ordering::Relaxed);
|
.fetch_sub(num_smoothed_values as i32, Ordering::Relaxed);
|
||||||
} else {
|
} else {
|
||||||
block_values.fill(self.target);
|
block_values.fill(target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -392,12 +412,13 @@ impl<T: Smoothable> Smoother<T> {
|
||||||
) {
|
) {
|
||||||
// This works exactly the same as `next_block_exact()`, except for the addition of the
|
// This works exactly the same as `next_block_exact()`, except for the addition of the
|
||||||
// mapping function
|
// mapping function
|
||||||
let target = self.target.to_f32();
|
let target_f32 = T::atomic_load(&self.target).to_f32();
|
||||||
|
|
||||||
let steps_left = self.steps_left.load(Ordering::Relaxed) as usize;
|
let steps_left = self.steps_left.load(Ordering::Relaxed) as usize;
|
||||||
let num_smoothed_values = block_values.len().min(steps_left);
|
let num_smoothed_values = block_values.len().min(steps_left);
|
||||||
if num_smoothed_values > 0 {
|
if num_smoothed_values > 0 {
|
||||||
let mut current = self.current.load(Ordering::Relaxed);
|
let mut current = self.current.load(Ordering::Relaxed);
|
||||||
|
let step_size = self.step_size.load(Ordering::Relaxed);
|
||||||
|
|
||||||
// See `next_block_exact()` for more details
|
// See `next_block_exact()` for more details
|
||||||
if num_smoothed_values == steps_left {
|
if num_smoothed_values == steps_left {
|
||||||
|
@ -406,19 +427,19 @@ impl<T: Smoothable> Smoother<T> {
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.take(num_smoothed_values - 1)
|
.take(num_smoothed_values - 1)
|
||||||
{
|
{
|
||||||
current = self.style.next(current, target, self.step_size);
|
current = self.style.next(current, target_f32, step_size);
|
||||||
*value = f(idx, current);
|
*value = f(idx, current);
|
||||||
}
|
}
|
||||||
|
|
||||||
current = target.to_f32();
|
current = target_f32.to_f32();
|
||||||
block_values[num_smoothed_values - 1] = f(num_smoothed_values - 1, target);
|
block_values[num_smoothed_values - 1] = f(num_smoothed_values - 1, target_f32);
|
||||||
} else {
|
} else {
|
||||||
for (idx, value) in block_values
|
for (idx, value) in block_values
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.take(num_smoothed_values)
|
.take(num_smoothed_values)
|
||||||
{
|
{
|
||||||
current = self.style.next(current, target, self.step_size);
|
current = self.style.next(current, target_f32, step_size);
|
||||||
*value = f(idx, current);
|
*value = f(idx, current);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -428,7 +449,7 @@ impl<T: Smoothable> Smoother<T> {
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.skip(num_smoothed_values)
|
.skip(num_smoothed_values)
|
||||||
{
|
{
|
||||||
*value = f(idx, target);
|
*value = f(idx, target_f32);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.current.store(current, Ordering::Relaxed);
|
self.current.store(current, Ordering::Relaxed);
|
||||||
|
@ -436,13 +457,15 @@ impl<T: Smoothable> Smoother<T> {
|
||||||
.fetch_sub(num_smoothed_values as i32, Ordering::Relaxed);
|
.fetch_sub(num_smoothed_values as i32, Ordering::Relaxed);
|
||||||
} else {
|
} else {
|
||||||
for (idx, value) in block_values.iter_mut().enumerate() {
|
for (idx, value) in block_values.iter_mut().enumerate() {
|
||||||
*value = f(idx, target);
|
*value = f(idx, target_f32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Smoothable for f32 {
|
impl Smoothable for f32 {
|
||||||
|
type Atomic = AtomicF32;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn to_f32(self) -> f32 {
|
fn to_f32(self) -> f32 {
|
||||||
self
|
self
|
||||||
|
@ -452,9 +475,26 @@ impl Smoothable for f32 {
|
||||||
fn from_f32(value: f32) -> Self {
|
fn from_f32(value: f32) -> Self {
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn atomic_new(value: Self) -> Self::Atomic {
|
||||||
|
AtomicF32::new(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn atomic_load(this: &Self::Atomic) -> Self {
|
||||||
|
this.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn atomic_store(this: &Self::Atomic, value: Self) {
|
||||||
|
this.store(value, Ordering::Relaxed)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Smoothable for i32 {
|
impl Smoothable for i32 {
|
||||||
|
type Atomic = AtomicI32;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn to_f32(self) -> f32 {
|
fn to_f32(self) -> f32 {
|
||||||
self as f32
|
self as f32
|
||||||
|
@ -464,6 +504,21 @@ impl Smoothable for i32 {
|
||||||
fn from_f32(value: f32) -> Self {
|
fn from_f32(value: f32) -> Self {
|
||||||
value.round() as i32
|
value.round() as i32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn atomic_new(value: Self) -> Self::Atomic {
|
||||||
|
AtomicI32::new(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn atomic_load(this: &Self::Atomic) -> Self {
|
||||||
|
this.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn atomic_store(this: &Self::Atomic, value: Self) {
|
||||||
|
this.store(value, Ordering::Relaxed)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -524,7 +579,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn linear_f32_smoothing() {
|
fn linear_f32_smoothing() {
|
||||||
let mut smoother: Smoother<f32> = Smoother::new(SmoothingStyle::Linear(100.0));
|
let smoother: Smoother<f32> = Smoother::new(SmoothingStyle::Linear(100.0));
|
||||||
smoother.reset(10.0);
|
smoother.reset(10.0);
|
||||||
assert_eq!(smoother.next(), 10.0);
|
assert_eq!(smoother.next(), 10.0);
|
||||||
|
|
||||||
|
@ -540,7 +595,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn linear_i32_smoothing() {
|
fn linear_i32_smoothing() {
|
||||||
let mut smoother: Smoother<i32> = Smoother::new(SmoothingStyle::Linear(100.0));
|
let smoother: Smoother<i32> = Smoother::new(SmoothingStyle::Linear(100.0));
|
||||||
smoother.reset(10);
|
smoother.reset(10);
|
||||||
assert_eq!(smoother.next(), 10);
|
assert_eq!(smoother.next(), 10);
|
||||||
|
|
||||||
|
@ -555,7 +610,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn logarithmic_f32_smoothing() {
|
fn logarithmic_f32_smoothing() {
|
||||||
let mut smoother: Smoother<f32> = Smoother::new(SmoothingStyle::Logarithmic(100.0));
|
let smoother: Smoother<f32> = Smoother::new(SmoothingStyle::Logarithmic(100.0));
|
||||||
smoother.reset(10.0);
|
smoother.reset(10.0);
|
||||||
assert_eq!(smoother.next(), 10.0);
|
assert_eq!(smoother.next(), 10.0);
|
||||||
|
|
||||||
|
@ -571,7 +626,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn logarithmic_i32_smoothing() {
|
fn logarithmic_i32_smoothing() {
|
||||||
let mut smoother: Smoother<i32> = Smoother::new(SmoothingStyle::Logarithmic(100.0));
|
let smoother: Smoother<i32> = Smoother::new(SmoothingStyle::Logarithmic(100.0));
|
||||||
smoother.reset(10);
|
smoother.reset(10);
|
||||||
assert_eq!(smoother.next(), 10);
|
assert_eq!(smoother.next(), 10);
|
||||||
|
|
||||||
|
@ -587,7 +642,7 @@ mod tests {
|
||||||
/// Same as [linear_f32_smoothing], but skipping steps instead.
|
/// Same as [linear_f32_smoothing], but skipping steps instead.
|
||||||
#[test]
|
#[test]
|
||||||
fn skipping_linear_f32_smoothing() {
|
fn skipping_linear_f32_smoothing() {
|
||||||
let mut smoother: Smoother<f32> = Smoother::new(SmoothingStyle::Linear(100.0));
|
let smoother: Smoother<f32> = Smoother::new(SmoothingStyle::Linear(100.0));
|
||||||
smoother.reset(10.0);
|
smoother.reset(10.0);
|
||||||
assert_eq!(smoother.next(), 10.0);
|
assert_eq!(smoother.next(), 10.0);
|
||||||
|
|
||||||
|
@ -600,7 +655,7 @@ mod tests {
|
||||||
/// Same as [linear_i32_smoothing], but skipping steps instead.
|
/// Same as [linear_i32_smoothing], but skipping steps instead.
|
||||||
#[test]
|
#[test]
|
||||||
fn skipping_linear_i32_smoothing() {
|
fn skipping_linear_i32_smoothing() {
|
||||||
let mut smoother: Smoother<i32> = Smoother::new(SmoothingStyle::Linear(100.0));
|
let smoother: Smoother<i32> = Smoother::new(SmoothingStyle::Linear(100.0));
|
||||||
smoother.reset(10);
|
smoother.reset(10);
|
||||||
assert_eq!(smoother.next(), 10);
|
assert_eq!(smoother.next(), 10);
|
||||||
|
|
||||||
|
@ -613,7 +668,7 @@ mod tests {
|
||||||
/// Same as [logarithmic_f32_smoothing], but skipping steps instead.
|
/// Same as [logarithmic_f32_smoothing], but skipping steps instead.
|
||||||
#[test]
|
#[test]
|
||||||
fn skipping_logarithmic_f32_smoothing() {
|
fn skipping_logarithmic_f32_smoothing() {
|
||||||
let mut smoother: Smoother<f32> = Smoother::new(SmoothingStyle::Logarithmic(100.0));
|
let smoother: Smoother<f32> = Smoother::new(SmoothingStyle::Logarithmic(100.0));
|
||||||
smoother.reset(10.0);
|
smoother.reset(10.0);
|
||||||
assert_eq!(smoother.next(), 10.0);
|
assert_eq!(smoother.next(), 10.0);
|
||||||
|
|
||||||
|
@ -626,7 +681,7 @@ mod tests {
|
||||||
/// Same as [logarithmic_i32_smoothing], but skipping steps instead.
|
/// Same as [logarithmic_i32_smoothing], but skipping steps instead.
|
||||||
#[test]
|
#[test]
|
||||||
fn skipping_logarithmic_i32_smoothing() {
|
fn skipping_logarithmic_i32_smoothing() {
|
||||||
let mut smoother: Smoother<i32> = Smoother::new(SmoothingStyle::Logarithmic(100.0));
|
let smoother: Smoother<i32> = Smoother::new(SmoothingStyle::Logarithmic(100.0));
|
||||||
smoother.reset(10);
|
smoother.reset(10);
|
||||||
assert_eq!(smoother.next(), 10);
|
assert_eq!(smoother.next(), 10);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue