From fe912a829eef7e6576c0395e74629019c45d375e Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Tue, 23 May 2023 12:15:12 -0700 Subject: [PATCH 01/44] Added linear gain filter --- src/source/fadein.rs | 2 +- src/source/linear_ramp.rs | 117 ++++++++++++++++++++++++++++++++++++++ src/source/mod.rs | 12 ++++ 3 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 src/source/linear_ramp.rs diff --git a/src/source/fadein.rs b/src/source/fadein.rs index 25d83d2..52818fa 100644 --- a/src/source/fadein.rs +++ b/src/source/fadein.rs @@ -84,7 +84,7 @@ where impl Source for FadeIn where I: Source, - I::Item: Sample, +I::Item: Sample, { #[inline] fn current_frame_len(&self) -> Option { diff --git a/src/source/linear_ramp.rs b/src/source/linear_ramp.rs new file mode 100644 index 0000000..ee652c9 --- /dev/null +++ b/src/source/linear_ramp.rs @@ -0,0 +1,117 @@ +use std::time::Duration; + +use crate::{Sample, Source}; + +/// Internal function that builds a `LinearRamp` object. +pub fn linear_gain_ramp(input: I, + duration: Duration, + start_gain: f32, + end_gain: f32) -> LinearGainRamp +where + I: Source, + I::Item: Sample, +{ + let duration = duration.as_secs() * 1000000000 + duration.subsec_nanos() as u64; + + LinearGainRamp { + input, + remaining_ns: duration as f32, + total_ns: duration as f32, + start_gain, + end_gain + } +} + +/// Filter that adds a linear gain ramp to the source over a given time range. +#[derive(Clone, Debug)] +pub struct LinearGainRamp { + input: I, + remaining_ns: f32, + total_ns: f32, + start_gain: f32, + end_gain: f32 +} + +impl LinearGainRamp +where + I: Source, + I::Item: Sample +{ + /// Returns a reference to the innner source. + #[inline] + pub fn inner(&self) -> &I { + &self.input + } + + /// Returns a mutable reference to the inner source. + #[inline] + pub fn inner_mut(&mut self) -> &mut I { + &mut self.input + } + + /// Returns the inner source. + #[inline] + pub fn into_inner(self) -> I { + self.input + } +} + +impl Iterator for LinearGainRamp +where + I: Source, + I::Item: Sample, +{ + type Item = I::Item; + + #[inline] + fn next(&mut self) -> Option { + if self.remaining_ns <= 0.0 { + return self.input.next(); + } + + let factor : f32 = f32::lerp(self.start_gain, self.end_gain, self.remaining_ns as u32, self.total_ns as u32); + + self.remaining_ns -= + 1000000000.0 / (self.input.sample_rate() as f32 * self.channels() as f32); + + self.input.next().map(|value| value.amplify(factor)) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.input.size_hint() + } +} + +impl ExactSizeIterator for LinearGainRamp +where + I: Source + ExactSizeIterator, + I::Item: Sample, +{ +} + +impl Source for LinearGainRamp +where + I: Source, +I::Item: Sample, +{ + #[inline] + fn current_frame_len(&self) -> Option { + self.input.current_frame_len() + } + + #[inline] + fn channels(&self) -> u16 { + self.input.channels() + } + + #[inline] + fn sample_rate(&self) -> u32 { + self.input.sample_rate() + } + + #[inline] + fn total_duration(&self) -> Option { + self.input.total_duration() + } +} diff --git a/src/source/mod.rs b/src/source/mod.rs index c2d595a..1ef2ed5 100644 --- a/src/source/mod.rs +++ b/src/source/mod.rs @@ -18,6 +18,7 @@ pub use self::empty_callback::EmptyCallback; pub use self::fadein::FadeIn; pub use self::from_factory::{from_factory, FromFactoryIter}; pub use self::from_iter::{from_iter, FromIter}; +pub use self::linear_ramp::LinearGainRamp; pub use self::mix::Mix; pub use self::pausable::Pausable; pub use self::periodic::PeriodicAccess; @@ -45,6 +46,7 @@ mod empty_callback; mod fadein; mod from_factory; mod from_iter; +mod linear_ramp; mod mix; mod pausable; mod periodic; @@ -245,6 +247,16 @@ where { fadein::fadein(self, duration) } + + /// Applies a linear gain ramp to the sound + #[inline] + fn linear_gain_ramp(self, duration: Duration, start_value: f32, end_value: f32) -> LinearGainRamp + where + Self: Sized + { + linear_ramp::linear_gain_ramp(self, duration, start_value, end_value) + } + /// Calls the `access` closure on `Self` the first time the source is iterated and every /// time `period` elapses. From aae35abcacf0ed28e6f379ee4ec6cf56730e7802 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Tue, 23 May 2023 12:26:57 -0700 Subject: [PATCH 02/44] Did rustfmt --- src/source/linear_ramp.rs | 31 +++++++++++++++++++------------ src/source/mod.rs | 14 +++++++++----- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/source/linear_ramp.rs b/src/source/linear_ramp.rs index ee652c9..c3a9e63 100644 --- a/src/source/linear_ramp.rs +++ b/src/source/linear_ramp.rs @@ -3,10 +3,12 @@ use std::time::Duration; use crate::{Sample, Source}; /// Internal function that builds a `LinearRamp` object. -pub fn linear_gain_ramp(input: I, - duration: Duration, - start_gain: f32, - end_gain: f32) -> LinearGainRamp +pub fn linear_gain_ramp( + input: I, + duration: Duration, + start_gain: f32, + end_gain: f32, +) -> LinearGainRamp where I: Source, I::Item: Sample, @@ -18,24 +20,24 @@ where remaining_ns: duration as f32, total_ns: duration as f32, start_gain, - end_gain + end_gain, } } -/// Filter that adds a linear gain ramp to the source over a given time range. +/// Filter that adds a linear gain ramp to the source over a given time range. #[derive(Clone, Debug)] pub struct LinearGainRamp { input: I, remaining_ns: f32, total_ns: f32, start_gain: f32, - end_gain: f32 + end_gain: f32, } -impl LinearGainRamp +impl LinearGainRamp where I: Source, - I::Item: Sample + I::Item: Sample, { /// Returns a reference to the innner source. #[inline] @@ -68,8 +70,13 @@ where if self.remaining_ns <= 0.0 { return self.input.next(); } - - let factor : f32 = f32::lerp(self.start_gain, self.end_gain, self.remaining_ns as u32, self.total_ns as u32); + + let factor: f32 = f32::lerp( + self.start_gain, + self.end_gain, + self.remaining_ns as u32, + self.total_ns as u32, + ); self.remaining_ns -= 1000000000.0 / (self.input.sample_rate() as f32 * self.channels() as f32); @@ -93,7 +100,7 @@ where impl Source for LinearGainRamp where I: Source, -I::Item: Sample, + I::Item: Sample, { #[inline] fn current_frame_len(&self) -> Option { diff --git a/src/source/mod.rs b/src/source/mod.rs index 1ef2ed5..78b5ec8 100644 --- a/src/source/mod.rs +++ b/src/source/mod.rs @@ -247,17 +247,21 @@ where { fadein::fadein(self, duration) } - + /// Applies a linear gain ramp to the sound #[inline] - fn linear_gain_ramp(self, duration: Duration, start_value: f32, end_value: f32) -> LinearGainRamp - where - Self: Sized + fn linear_gain_ramp( + self, + duration: Duration, + start_value: f32, + end_value: f32, + ) -> LinearGainRamp + where + Self: Sized, { linear_ramp::linear_gain_ramp(self, duration, start_value, end_value) } - /// Calls the `access` closure on `Self` the first time the source is iterated and every /// time `period` elapses. /// From 17e2d4773b854bea1f19725f8789ad7cacbd4d4b Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Tue, 23 May 2023 12:35:00 -0700 Subject: [PATCH 03/44] Missed one rustfmt --- src/source/fadein.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/source/fadein.rs b/src/source/fadein.rs index 52818fa..25d83d2 100644 --- a/src/source/fadein.rs +++ b/src/source/fadein.rs @@ -84,7 +84,7 @@ where impl Source for FadeIn where I: Source, -I::Item: Sample, + I::Item: Sample, { #[inline] fn current_frame_len(&self) -> Option { From 84db58ba27c54d758d788176bdf10df98eaf87f6 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 14 Jul 2024 10:34:22 -0700 Subject: [PATCH 04/44] Implemented try_seek() --- src/source/linear_ramp.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/source/linear_ramp.rs b/src/source/linear_ramp.rs index c3a9e63..59c6eb5 100644 --- a/src/source/linear_ramp.rs +++ b/src/source/linear_ramp.rs @@ -1,6 +1,7 @@ use std::time::Duration; use crate::{Sample, Source}; +use super::SeekError; /// Internal function that builds a `LinearRamp` object. pub fn linear_gain_ramp( @@ -101,7 +102,7 @@ impl Source for LinearGainRamp where I: Source, I::Item: Sample, -{ +{ #[inline] fn current_frame_len(&self) -> Option { self.input.current_frame_len() @@ -121,4 +122,9 @@ where fn total_duration(&self) -> Option { self.input.total_duration() } + + #[inline] + fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> { + self.input.try_seek(pos) + } } From 54cc52d44d55eb47498bc3490e4fcb6d7caf24d3 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 14 Jul 2024 10:41:16 -0700 Subject: [PATCH 05/44] Added end gain clamping behavior. --- src/source/linear_ramp.rs | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/source/linear_ramp.rs b/src/source/linear_ramp.rs index 59c6eb5..d302bd4 100644 --- a/src/source/linear_ramp.rs +++ b/src/source/linear_ramp.rs @@ -9,6 +9,7 @@ pub fn linear_gain_ramp( duration: Duration, start_gain: f32, end_gain: f32, + clamp_end: bool ) -> LinearGainRamp where I: Source, @@ -22,6 +23,7 @@ where total_ns: duration as f32, start_gain, end_gain, + clamp_end } } @@ -33,6 +35,7 @@ pub struct LinearGainRamp { total_ns: f32, start_gain: f32, end_gain: f32, + clamp_end: bool, } impl LinearGainRamp @@ -68,16 +71,22 @@ where #[inline] fn next(&mut self) -> Option { - if self.remaining_ns <= 0.0 { - return self.input.next(); - } + let factor: f32; - let factor: f32 = f32::lerp( - self.start_gain, - self.end_gain, - self.remaining_ns as u32, - self.total_ns as u32, - ); + if self.remaining_ns <= 0.0 { + if self.clamp_end { + factor = self.end_gain; + } else { + factor = 1.0f32; + } + } else { + factor = f32::lerp( + self.start_gain, + self.end_gain, + self.remaining_ns as u32, + self.total_ns as u32, + ); + } self.remaining_ns -= 1000000000.0 / (self.input.sample_rate() as f32 * self.channels() as f32); From fa951d928a8ed141dd24a0104e7f870a522d4c9a Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 14 Jul 2024 10:45:49 -0700 Subject: [PATCH 06/44] Fixed to linear_gain_ramp in mod.rs, added documentation --- src/source/mod.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/source/mod.rs b/src/source/mod.rs index b3c0c3a..7d52f70 100644 --- a/src/source/mod.rs +++ b/src/source/mod.rs @@ -252,17 +252,22 @@ where } /// Applies a linear gain ramp to the sound + /// + /// If `clamp_end` is `true`, all samples subsequent to the end of the ramp + /// will be scaled by the `end_value`. If `clamp_end` is `false`, all + /// subsequent samples will not have any scaling applied. #[inline] fn linear_gain_ramp( self, duration: Duration, start_value: f32, end_value: f32, + clamp_end: bool ) -> LinearGainRamp where Self: Sized, { - linear_ramp::linear_gain_ramp(self, duration, start_value, end_value) + linear_ramp::linear_gain_ramp(self, duration, start_value, end_value, clamp_end) } /// Calls the `access` closure on `Self` the first time the source is iterated and every From 184bb0545442ffa16774abbbe2459f0d56bd3692 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 14 Jul 2024 10:47:19 -0700 Subject: [PATCH 07/44] Typo --- src/source/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/source/mod.rs b/src/source/mod.rs index 7d52f70..885dff2 100644 --- a/src/source/mod.rs +++ b/src/source/mod.rs @@ -251,7 +251,7 @@ where fadein::fadein(self, duration) } - /// Applies a linear gain ramp to the sound + /// Applies a linear gain ramp to the sound. /// /// If `clamp_end` is `true`, all samples subsequent to the end of the ramp /// will be scaled by the `end_value`. If `clamp_end` is `false`, all From 9c0706afbfcd82391515a242fa44fb6b8ed1a402 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 14 Jul 2024 10:50:52 -0700 Subject: [PATCH 08/44] Added assertion to guard against 0-duration ramps --- src/source/linear_ramp.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/source/linear_ramp.rs b/src/source/linear_ramp.rs index d302bd4..2c34db1 100644 --- a/src/source/linear_ramp.rs +++ b/src/source/linear_ramp.rs @@ -15,12 +15,13 @@ where I: Source, I::Item: Sample, { - let duration = duration.as_secs() * 1000000000 + duration.subsec_nanos() as u64; + let duration_nanos = duration.as_secs() * 1000000000 + duration.subsec_nanos() as u64; + assert!(duration_nanos > 0); LinearGainRamp { input, - remaining_ns: duration as f32, - total_ns: duration as f32, + remaining_ns: duration_nanos as f32, + total_ns: duration_nanos as f32, start_gain, end_gain, clamp_end From 947c4b7f3ed319c1083f3b2e3ebd705bc0cca9c0 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 14 Jul 2024 10:54:59 -0700 Subject: [PATCH 09/44] Rustfmt --- src/source/linear_ramp.rs | 10 +++++----- src/source/mod.rs | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/source/linear_ramp.rs b/src/source/linear_ramp.rs index 2c34db1..5a7510d 100644 --- a/src/source/linear_ramp.rs +++ b/src/source/linear_ramp.rs @@ -1,7 +1,7 @@ use std::time::Duration; -use crate::{Sample, Source}; use super::SeekError; +use crate::{Sample, Source}; /// Internal function that builds a `LinearRamp` object. pub fn linear_gain_ramp( @@ -9,7 +9,7 @@ pub fn linear_gain_ramp( duration: Duration, start_gain: f32, end_gain: f32, - clamp_end: bool + clamp_end: bool, ) -> LinearGainRamp where I: Source, @@ -24,7 +24,7 @@ where total_ns: duration_nanos as f32, start_gain, end_gain, - clamp_end + clamp_end, } } @@ -72,7 +72,7 @@ where #[inline] fn next(&mut self) -> Option { - let factor: f32; + let factor: f32; if self.remaining_ns <= 0.0 { if self.clamp_end { @@ -112,7 +112,7 @@ impl Source for LinearGainRamp where I: Source, I::Item: Sample, -{ +{ #[inline] fn current_frame_len(&self) -> Option { self.input.current_frame_len() diff --git a/src/source/mod.rs b/src/source/mod.rs index 885dff2..657ddfe 100644 --- a/src/source/mod.rs +++ b/src/source/mod.rs @@ -254,7 +254,7 @@ where /// Applies a linear gain ramp to the sound. /// /// If `clamp_end` is `true`, all samples subsequent to the end of the ramp - /// will be scaled by the `end_value`. If `clamp_end` is `false`, all + /// will be scaled by the `end_value`. If `clamp_end` is `false`, all /// subsequent samples will not have any scaling applied. #[inline] fn linear_gain_ramp( @@ -262,7 +262,7 @@ where duration: Duration, start_value: f32, end_value: f32, - clamp_end: bool + clamp_end: bool, ) -> LinearGainRamp where Self: Sized, From 908ae07a6f84c97b883c895ef635d61302fa6c9b Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 14 Jul 2024 10:59:11 -0700 Subject: [PATCH 10/44] Now using as_secs_f32() --- src/source/linear_ramp.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/source/linear_ramp.rs b/src/source/linear_ramp.rs index 5a7510d..ff53e4d 100644 --- a/src/source/linear_ramp.rs +++ b/src/source/linear_ramp.rs @@ -15,8 +15,8 @@ where I: Source, I::Item: Sample, { - let duration_nanos = duration.as_secs() * 1000000000 + duration.subsec_nanos() as u64; - assert!(duration_nanos > 0); + let duration_nanos = duration.as_secs_f32(); + assert!(duration_nanos > 0.0f32); LinearGainRamp { input, From e2ed0296e0d64b7bf2fb5ac55d4f010cbbe20fac Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 14 Jul 2024 11:06:13 -0700 Subject: [PATCH 11/44] Clarified member name and logic "elapsed_ns" --- src/source/linear_ramp.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/source/linear_ramp.rs b/src/source/linear_ramp.rs index ff53e4d..7ec99ed 100644 --- a/src/source/linear_ramp.rs +++ b/src/source/linear_ramp.rs @@ -20,7 +20,7 @@ where LinearGainRamp { input, - remaining_ns: duration_nanos as f32, + elapsed_ns: duration_nanos as f32, total_ns: duration_nanos as f32, start_gain, end_gain, @@ -32,7 +32,7 @@ where #[derive(Clone, Debug)] pub struct LinearGainRamp { input: I, - remaining_ns: f32, + elapsed_ns: f32, total_ns: f32, start_gain: f32, end_gain: f32, @@ -73,8 +73,9 @@ where #[inline] fn next(&mut self) -> Option { let factor: f32; + let remaining_ns = self.total_ns - self.elapsed_ns; - if self.remaining_ns <= 0.0 { + if remaining_ns <= 0.0 { if self.clamp_end { factor = self.end_gain; } else { @@ -84,13 +85,12 @@ where factor = f32::lerp( self.start_gain, self.end_gain, - self.remaining_ns as u32, + remaining_ns as u32, self.total_ns as u32, ); } - self.remaining_ns -= - 1000000000.0 / (self.input.sample_rate() as f32 * self.channels() as f32); + self.elapsed_ns += 1000000000.0 / (self.input.sample_rate() as f32 * self.channels() as f32); self.input.next().map(|value| value.amplify(factor)) } From aa2edb3dab910b5c55d84dc2ef6d60b201b0dc87 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 14 Jul 2024 11:06:43 -0700 Subject: [PATCH 12/44] Rustfmt --- src/source/linear_ramp.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/source/linear_ramp.rs b/src/source/linear_ramp.rs index 7ec99ed..c9187d6 100644 --- a/src/source/linear_ramp.rs +++ b/src/source/linear_ramp.rs @@ -90,7 +90,8 @@ where ); } - self.elapsed_ns += 1000000000.0 / (self.input.sample_rate() as f32 * self.channels() as f32); + self.elapsed_ns += + 1000000000.0 / (self.input.sample_rate() as f32 * self.channels() as f32); self.input.next().map(|value| value.amplify(factor)) } From 9be1df5e55b2bcd5f56c349b9dd75b876c2ee27f Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 14 Jul 2024 11:08:14 -0700 Subject: [PATCH 13/44] Removed a thinko and unnecessary cast --- src/source/linear_ramp.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/source/linear_ramp.rs b/src/source/linear_ramp.rs index c9187d6..cfa565b 100644 --- a/src/source/linear_ramp.rs +++ b/src/source/linear_ramp.rs @@ -20,8 +20,8 @@ where LinearGainRamp { input, - elapsed_ns: duration_nanos as f32, - total_ns: duration_nanos as f32, + elapsed_ns: 0.0f32, + total_ns: duration_nanos, start_gain, end_gain, clamp_end, From 8864b22e04085e4d9c9cb9e04f23b4893f224175 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 14 Jul 2024 11:41:13 -0700 Subject: [PATCH 14/44] Implementing fade-in as a wrapper ...around LinearRamp --- src/source/fadein.rs | 39 +++++++++++++-------------------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/src/source/fadein.rs b/src/source/fadein.rs index 68cf429..7a15f69 100644 --- a/src/source/fadein.rs +++ b/src/source/fadein.rs @@ -2,7 +2,7 @@ use std::time::Duration; use crate::{Sample, Source}; -use super::SeekError; +use super::{linear_ramp::linear_gain_ramp, LinearGainRamp, SeekError}; /// Internal function that builds a `FadeIn` object. pub fn fadein(input: I, duration: Duration) -> FadeIn @@ -10,21 +10,15 @@ where I: Source, I::Item: Sample, { - let duration = duration.as_secs() * 1000000000 + duration.subsec_nanos() as u64; - FadeIn { - input, - remaining_ns: duration as f32, - total_ns: duration as f32, + ramp: linear_gain_ramp(input, duration, 0.0f32, 1.0f32, false), } } /// Filter that modifies raises the volume from silence over a time period. #[derive(Clone, Debug)] pub struct FadeIn { - input: I, - remaining_ns: f32, - total_ns: f32, + ramp: LinearGainRamp } impl FadeIn @@ -35,19 +29,19 @@ where /// Returns a reference to the inner source. #[inline] pub fn inner(&self) -> &I { - &self.input + self.ramp.inner() } /// Returns a mutable reference to the inner source. #[inline] pub fn inner_mut(&mut self) -> &mut I { - &mut self.input + self.ramp.inner_mut() } /// Returns the inner source. #[inline] pub fn into_inner(self) -> I { - self.input + self.ramp.into_inner() } } @@ -60,19 +54,12 @@ where #[inline] fn next(&mut self) -> Option { - if self.remaining_ns <= 0.0 { - return self.input.next(); - } - - let factor = 1.0 - self.remaining_ns / self.total_ns; - self.remaining_ns -= - 1000000000.0 / (self.input.sample_rate() as f32 * self.channels() as f32); - self.input.next().map(|value| value.amplify(factor)) + self.inner_mut().next() } #[inline] fn size_hint(&self) -> (usize, Option) { - self.input.size_hint() + self.inner().size_hint() } } @@ -90,26 +77,26 @@ where { #[inline] fn current_frame_len(&self) -> Option { - self.input.current_frame_len() + self.inner().current_frame_len() } #[inline] fn channels(&self) -> u16 { - self.input.channels() + self.inner().channels() } #[inline] fn sample_rate(&self) -> u32 { - self.input.sample_rate() + self.inner().sample_rate() } #[inline] fn total_duration(&self) -> Option { - self.input.total_duration() + self.inner().total_duration() } #[inline] fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> { - self.input.try_seek(pos) + self.inner_mut().try_seek(pos) } } From a3f36c50f1061c912e20dcc664f40d36ad05f42c Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 14 Jul 2024 11:42:10 -0700 Subject: [PATCH 15/44] Adding test block to linear_ramp.rs --- src/source/linear_ramp.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/source/linear_ramp.rs b/src/source/linear_ramp.rs index cfa565b..cfb3536 100644 --- a/src/source/linear_ramp.rs +++ b/src/source/linear_ramp.rs @@ -139,3 +139,8 @@ where self.input.try_seek(pos) } } + +#[cfg(test)] +mod tests { + +} From 7fe76c7e951b14b274ec3cb9b45cb36b6913cfe6 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 14 Jul 2024 12:07:38 -0700 Subject: [PATCH 16/44] Renamed this field for consistency with other library structs. --- src/source/fadein.rs | 10 +++++----- src/source/linear_ramp.rs | 11 +++++++++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/source/fadein.rs b/src/source/fadein.rs index 7a15f69..8a28bd9 100644 --- a/src/source/fadein.rs +++ b/src/source/fadein.rs @@ -11,14 +11,14 @@ where I::Item: Sample, { FadeIn { - ramp: linear_gain_ramp(input, duration, 0.0f32, 1.0f32, false), + input: linear_gain_ramp(input, duration, 0.0f32, 1.0f32, false), } } /// Filter that modifies raises the volume from silence over a time period. #[derive(Clone, Debug)] pub struct FadeIn { - ramp: LinearGainRamp + input: LinearGainRamp } impl FadeIn @@ -29,19 +29,19 @@ where /// Returns a reference to the inner source. #[inline] pub fn inner(&self) -> &I { - self.ramp.inner() + self.input.inner() } /// Returns a mutable reference to the inner source. #[inline] pub fn inner_mut(&mut self) -> &mut I { - self.ramp.inner_mut() + self.input.inner_mut() } /// Returns the inner source. #[inline] pub fn into_inner(self) -> I { - self.ramp.into_inner() + self.input.into_inner() } } diff --git a/src/source/linear_ramp.rs b/src/source/linear_ramp.rs index cfb3536..31c0aed 100644 --- a/src/source/linear_ramp.rs +++ b/src/source/linear_ramp.rs @@ -25,6 +25,7 @@ where start_gain, end_gain, clamp_end, + sample_idx: 0u64, } } @@ -37,6 +38,7 @@ pub struct LinearGainRamp { start_gain: f32, end_gain: f32, clamp_end: bool, + sample_idx: u64 } impl LinearGainRamp @@ -82,6 +84,8 @@ where factor = 1.0f32; } } else { + self.sample_idx += 1; + factor = f32::lerp( self.start_gain, self.end_gain, @@ -90,8 +94,11 @@ where ); } - self.elapsed_ns += - 1000000000.0 / (self.input.sample_rate() as f32 * self.channels() as f32); + if self.sample_idx % (self.channels() as u64) == 0 { + self.elapsed_ns += + 1000000000.0 / (self.input.sample_rate() as f32); + } + self.input.next().map(|value| value.amplify(factor)) } From 9d63a9c393e6747fa1f273e77ef033b2c5c844d0 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 14 Jul 2024 13:13:15 -0700 Subject: [PATCH 17/44] A note to myself --- src/source/linear_ramp.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/source/linear_ramp.rs b/src/source/linear_ramp.rs index 31c0aed..5080259 100644 --- a/src/source/linear_ramp.rs +++ b/src/source/linear_ramp.rs @@ -94,6 +94,9 @@ where ); } + // FIXME: the way this use to work was calculating a new elapsed value for every sample, + // but this is not exactly correct for multichannel inputs. This is a new implementation + // but it is presently causing the crossfade unit tests to fail. if self.sample_idx % (self.channels() as u64) == 0 { self.elapsed_ns += 1000000000.0 / (self.input.sample_rate() as f32); From 52dc4a5f5f786d6948837d295c1a020139752a10 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sat, 3 Aug 2024 16:51:24 -0700 Subject: [PATCH 18/44] Have reworked ramp logic All tests pass, the "lerp" function doesn't seem to work, I want to look into this further. --- src/source/crossfade.rs | 11 +++++-- src/source/fadein.rs | 4 +-- src/source/linear_ramp.rs | 69 +++++++++++++++++++++++++++++++-------- 3 files changed, 66 insertions(+), 18 deletions(-) diff --git a/src/source/crossfade.rs b/src/source/crossfade.rs index d9d29db..2059ef2 100644 --- a/src/source/crossfade.rs +++ b/src/source/crossfade.rs @@ -5,9 +5,11 @@ use cpal::FromSample; use crate::source::{FadeIn, Mix, TakeDuration}; use crate::{Sample, Source}; -/// Mixes one sound fading out with another sound fading in for the given duration. +/// Mixes one sound fading out with another sound fading in for the given +/// duration. /// -/// Only the crossfaded portion (beginning of fadeout, beginning of fadein) is returned. +/// Only the crossfaded portion (beginning of fadeout, beginning of fadein) is +/// returned. pub fn crossfade( input_fadeout: I1, input_fadein: I2, @@ -37,7 +39,7 @@ mod tests { } #[test] - fn test_crossfade() { + fn test_crossfade_with_self() { let source1 = dummysource(10); let source2 = dummysource(10); let mut mixed = crossfade( @@ -51,7 +53,10 @@ mod tests { assert_eq!(mixed.next(), Some(4.0)); assert_eq!(mixed.next(), Some(5.0)); assert_eq!(mixed.next(), None); + } + #[test] + fn test_crossfade() { let source1 = dummysource(10); let source2 = dummysource(10).amplify(0.0); let mut mixed = crossfade( diff --git a/src/source/fadein.rs b/src/source/fadein.rs index 8a28bd9..dcf118e 100644 --- a/src/source/fadein.rs +++ b/src/source/fadein.rs @@ -54,12 +54,12 @@ where #[inline] fn next(&mut self) -> Option { - self.inner_mut().next() + self.input.next() } #[inline] fn size_hint(&self) -> (usize, Option) { - self.inner().size_hint() + self.input.size_hint() } } diff --git a/src/source/linear_ramp.rs b/src/source/linear_ramp.rs index 5080259..cc64e85 100644 --- a/src/source/linear_ramp.rs +++ b/src/source/linear_ramp.rs @@ -15,7 +15,7 @@ where I: Source, I::Item: Sample, { - let duration_nanos = duration.as_secs_f32(); + let duration_nanos = duration.as_nanos() as f32; assert!(duration_nanos > 0.0f32); LinearGainRamp { @@ -77,7 +77,7 @@ where let factor: f32; let remaining_ns = self.total_ns - self.elapsed_ns; - if remaining_ns <= 0.0 { + if remaining_ns < 0.0 { if self.clamp_end { factor = self.end_gain; } else { @@ -85,18 +85,14 @@ where } } else { self.sample_idx += 1; - - factor = f32::lerp( - self.start_gain, - self.end_gain, - remaining_ns as u32, - self.total_ns as u32, - ); + + let p = self.elapsed_ns / self.total_ns; + factor = self.start_gain * (1.0f32 - p) + self.end_gain * p; } - // FIXME: the way this use to work was calculating a new elapsed value for every sample, - // but this is not exactly correct for multichannel inputs. This is a new implementation - // but it is presently causing the crossfade unit tests to fail. + println!("Factor: {}, remaining: {}, total: {}", factor, remaining_ns, + self.total_ns); + if self.sample_idx % (self.channels() as u64) == 0 { self.elapsed_ns += 1000000000.0 / (self.input.sample_rate() as f32); @@ -152,5 +148,52 @@ where #[cfg(test)] mod tests { - + use super::*; + use crate::buffer::SamplesBuffer; + + fn dummysource(length: u8) -> SamplesBuffer { + // shamelessly copied from crossfade.rs + let data: Vec = (1..=length).map(f32::from).collect(); + SamplesBuffer::new(1, 1, data) + } + + #[test] + fn test_linearramp() { + let source1 = dummysource(10); + let mut faded = linear_gain_ramp(source1, + Duration::from_secs(4), + 0.0, 1.0, true); + + assert_eq!(faded.next(), Some(0.0)); + assert_eq!(faded.next(), Some(0.5)); + assert_eq!(faded.next(), Some(1.5)); + assert_eq!(faded.next(), Some(3.0)); + assert_eq!(faded.next(), Some(5.0)); + assert_eq!(faded.next(), Some(6.0)); + assert_eq!(faded.next(), Some(7.0)); + assert_eq!(faded.next(), Some(8.0)); + assert_eq!(faded.next(), Some(9.0)); + assert_eq!(faded.next(), Some(10.0)); + assert_eq!(faded.next(), None); + } + + #[test] + fn test_linearramp_clamped() { + let source1 = dummysource(10); + let mut faded = linear_gain_ramp(source1, + Duration::from_secs(4), + 0.0, 0.5, true); + + assert_eq!(faded.next(), Some(0.0)); + assert_eq!(faded.next(), Some(0.25)); + assert_eq!(faded.next(), Some(0.75)); + assert_eq!(faded.next(), Some(1.5)); + assert_eq!(faded.next(), Some(2.5)); + assert_eq!(faded.next(), Some(3.0)); + assert_eq!(faded.next(), Some(3.5)); + assert_eq!(faded.next(), Some(4.0)); + assert_eq!(faded.next(), Some(4.5)); + assert_eq!(faded.next(), Some(5.0)); + assert_eq!(faded.next(), None); + } } From 65a8745cd6329274472a27fe54cb9cda4ab5b0ea Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sat, 3 Aug 2024 16:54:02 -0700 Subject: [PATCH 19/44] Removed a console line --- src/source/linear_ramp.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/source/linear_ramp.rs b/src/source/linear_ramp.rs index cc64e85..d485a1a 100644 --- a/src/source/linear_ramp.rs +++ b/src/source/linear_ramp.rs @@ -90,9 +90,6 @@ where factor = self.start_gain * (1.0f32 - p) + self.end_gain * p; } - println!("Factor: {}, remaining: {}, total: {}", factor, remaining_ns, - self.total_ns); - if self.sample_idx % (self.channels() as u64) == 0 { self.elapsed_ns += 1000000000.0 / (self.input.sample_rate() as f32); From 97a15e25c4c1927b0efaf63e816472de293b5a6c Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sat, 3 Aug 2024 16:56:20 -0700 Subject: [PATCH 20/44] Made a formatting change for clippy --- src/conversions/sample.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conversions/sample.rs b/src/conversions/sample.rs index 7ee9762..7f593f7 100644 --- a/src/conversions/sample.rs +++ b/src/conversions/sample.rs @@ -67,7 +67,7 @@ where /// - For `u16`, silence corresponds to the value `u16::max_value() / 2`. The minimum and maximum /// amplitudes are represented by `0` and `u16::max_value()` respectively. /// - For `f32`, silence corresponds to the value `0.0`. The minimum and maximum amplitudes are -/// represented by `-1.0` and `1.0` respectively. +/// represented by `-1.0` and `1.0` respectively. /// /// You can implement this trait on your own type as well if you wish so. /// From 490e3e67376636c0c4e5adec9cd0aaa1de07e626 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sat, 3 Aug 2024 16:58:29 -0700 Subject: [PATCH 21/44] Rustup --- src/source/linear_ramp.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/source/linear_ramp.rs b/src/source/linear_ramp.rs index d485a1a..94a89b8 100644 --- a/src/source/linear_ramp.rs +++ b/src/source/linear_ramp.rs @@ -38,7 +38,7 @@ pub struct LinearGainRamp { start_gain: f32, end_gain: f32, clamp_end: bool, - sample_idx: u64 + sample_idx: u64, } impl LinearGainRamp @@ -85,17 +85,15 @@ where } } else { self.sample_idx += 1; - + let p = self.elapsed_ns / self.total_ns; - factor = self.start_gain * (1.0f32 - p) + self.end_gain * p; + factor = self.start_gain * (1.0f32 - p) + self.end_gain * p; } if self.sample_idx % (self.channels() as u64) == 0 { - self.elapsed_ns += - 1000000000.0 / (self.input.sample_rate() as f32); + self.elapsed_ns += 1000000000.0 / (self.input.sample_rate() as f32); } - self.input.next().map(|value| value.amplify(factor)) } @@ -157,9 +155,7 @@ mod tests { #[test] fn test_linearramp() { let source1 = dummysource(10); - let mut faded = linear_gain_ramp(source1, - Duration::from_secs(4), - 0.0, 1.0, true); + let mut faded = linear_gain_ramp(source1, Duration::from_secs(4), 0.0, 1.0, true); assert_eq!(faded.next(), Some(0.0)); assert_eq!(faded.next(), Some(0.5)); @@ -177,9 +173,7 @@ mod tests { #[test] fn test_linearramp_clamped() { let source1 = dummysource(10); - let mut faded = linear_gain_ramp(source1, - Duration::from_secs(4), - 0.0, 0.5, true); + let mut faded = linear_gain_ramp(source1, Duration::from_secs(4), 0.0, 0.5, true); assert_eq!(faded.next(), Some(0.0)); assert_eq!(faded.next(), Some(0.25)); From c3acdc338ba294d02b86d37f4d2a822e1a758cac Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sat, 3 Aug 2024 16:58:58 -0700 Subject: [PATCH 22/44] Rustup --- src/source/fadein.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/source/fadein.rs b/src/source/fadein.rs index dcf118e..eac958f 100644 --- a/src/source/fadein.rs +++ b/src/source/fadein.rs @@ -18,7 +18,7 @@ where /// Filter that modifies raises the volume from silence over a time period. #[derive(Clone, Debug)] pub struct FadeIn { - input: LinearGainRamp + input: LinearGainRamp, } impl FadeIn From 627fe35b466168b52c8671900fea26b726aa977e Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sat, 3 Aug 2024 17:01:02 -0700 Subject: [PATCH 23/44] Rustfmt --- src/source/crossfade.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/source/crossfade.rs b/src/source/crossfade.rs index 2059ef2..a8ea20c 100644 --- a/src/source/crossfade.rs +++ b/src/source/crossfade.rs @@ -5,10 +5,10 @@ use cpal::FromSample; use crate::source::{FadeIn, Mix, TakeDuration}; use crate::{Sample, Source}; -/// Mixes one sound fading out with another sound fading in for the given +/// Mixes one sound fading out with another sound fading in for the given /// duration. /// -/// Only the crossfaded portion (beginning of fadeout, beginning of fadein) is +/// Only the crossfaded portion (beginning of fadeout, beginning of fadein) is /// returned. pub fn crossfade( input_fadeout: I1, From d5c9e0d62cb02075c7f2bfe70ef7924ae11fa466 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 4 Aug 2024 12:48:24 -0700 Subject: [PATCH 24/44] Code review stuff... Changed test dummysource to constant value Implemented try_seek --- src/source/linear_ramp.rs | 44 +++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/source/linear_ramp.rs b/src/source/linear_ramp.rs index 94a89b8..8587671 100644 --- a/src/source/linear_ramp.rs +++ b/src/source/linear_ramp.rs @@ -137,6 +137,7 @@ where #[inline] fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> { + self.elapsed_ns += pos.as_nanos() as f32; self.input.try_seek(pos) } } @@ -146,45 +147,44 @@ mod tests { use super::*; use crate::buffer::SamplesBuffer; - fn dummysource(length: u8) -> SamplesBuffer { - // shamelessly copied from crossfade.rs - let data: Vec = (1..=length).map(f32::from).collect(); + fn const_source(length: u8, value: f32) -> SamplesBuffer { + let data: Vec = (1..=length).map(|_| value).collect(); SamplesBuffer::new(1, 1, data) } #[test] fn test_linearramp() { - let source1 = dummysource(10); + let source1 = const_source(10, 1.0f32); let mut faded = linear_gain_ramp(source1, Duration::from_secs(4), 0.0, 1.0, true); assert_eq!(faded.next(), Some(0.0)); + assert_eq!(faded.next(), Some(0.25)); assert_eq!(faded.next(), Some(0.5)); - assert_eq!(faded.next(), Some(1.5)); - assert_eq!(faded.next(), Some(3.0)); - assert_eq!(faded.next(), Some(5.0)); - assert_eq!(faded.next(), Some(6.0)); - assert_eq!(faded.next(), Some(7.0)); - assert_eq!(faded.next(), Some(8.0)); - assert_eq!(faded.next(), Some(9.0)); - assert_eq!(faded.next(), Some(10.0)); + assert_eq!(faded.next(), Some(0.75)); + assert_eq!(faded.next(), Some(1.0)); + assert_eq!(faded.next(), Some(1.0)); + assert_eq!(faded.next(), Some(1.0)); + assert_eq!(faded.next(), Some(1.0)); + assert_eq!(faded.next(), Some(1.0)); + assert_eq!(faded.next(), Some(1.0)); assert_eq!(faded.next(), None); } #[test] fn test_linearramp_clamped() { - let source1 = dummysource(10); + let source1 = const_source(10, 1.0f32); let mut faded = linear_gain_ramp(source1, Duration::from_secs(4), 0.0, 0.5, true); - assert_eq!(faded.next(), Some(0.0)); + assert_eq!(faded.next(), Some(0.0)); // fading in... + assert_eq!(faded.next(), Some(0.125)); assert_eq!(faded.next(), Some(0.25)); - assert_eq!(faded.next(), Some(0.75)); - assert_eq!(faded.next(), Some(1.5)); - assert_eq!(faded.next(), Some(2.5)); - assert_eq!(faded.next(), Some(3.0)); - assert_eq!(faded.next(), Some(3.5)); - assert_eq!(faded.next(), Some(4.0)); - assert_eq!(faded.next(), Some(4.5)); - assert_eq!(faded.next(), Some(5.0)); + assert_eq!(faded.next(), Some(0.375)); + assert_eq!(faded.next(), Some(0.5)); // fade is done + assert_eq!(faded.next(), Some(0.5)); + assert_eq!(faded.next(), Some(0.5)); + assert_eq!(faded.next(), Some(0.5)); + assert_eq!(faded.next(), Some(0.5)); + assert_eq!(faded.next(), Some(0.5)); assert_eq!(faded.next(), None); } } From 9f9a1724d4ac028cb09466b4bd97c341438b4ecf Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 4 Aug 2024 13:31:47 -0700 Subject: [PATCH 25/44] Added assert_float_eq package. --- Cargo.toml | 1 + src/source/linear_ramp.rs | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6d53723..0bdf315 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ minimp3_fixed = { version = "0.5.4", optional = true} symphonia = { version = "0.5.4", optional = true, default-features = false } crossbeam-channel = { version = "0.5.8", optional = true } thiserror = "1.0.49" +assert_float_eq = "1.1.3" [features] default = ["flac", "vorbis", "wav", "mp3"] diff --git a/src/source/linear_ramp.rs b/src/source/linear_ramp.rs index 8587671..276637f 100644 --- a/src/source/linear_ramp.rs +++ b/src/source/linear_ramp.rs @@ -147,13 +147,25 @@ mod tests { use super::*; use crate::buffer::SamplesBuffer; + /// Create a SamplesBuffer of identical samples with value `value`. + /// Returned buffer is one channel and has s sample rate of 1 hz. fn const_source(length: u8, value: f32) -> SamplesBuffer { let data: Vec = (1..=length).map(|_| value).collect(); SamplesBuffer::new(1, 1, data) } + /// Create a SamplesBuffer of repeating sample values from `values`. + fn cycle_source(length: u8, values: Vec) -> SamplesBuffer { + let data: Vec = (1..=length) + .enumerate() + .map(|(i, _)| values[i % values.len()]) + .collect(); + + SamplesBuffer::new(1, 1, data) + } + #[test] - fn test_linearramp() { + fn test_linear_ramp() { let source1 = const_source(10, 1.0f32); let mut faded = linear_gain_ramp(source1, Duration::from_secs(4), 0.0, 1.0, true); @@ -171,7 +183,7 @@ mod tests { } #[test] - fn test_linearramp_clamped() { + fn test_linear_ramp_clamped() { let source1 = const_source(10, 1.0f32); let mut faded = linear_gain_ramp(source1, Duration::from_secs(4), 0.0, 0.5, true); @@ -187,4 +199,21 @@ mod tests { assert_eq!(faded.next(), Some(0.5)); assert_eq!(faded.next(), None); } + + #[test] + fn test_linear_ramp_seek() { + let source1 = cycle_source(20, vec![0.0f32, 0.4f32, 0.8f32]); + let mut faded = linear_gain_ramp(source1, Duration::from_secs(10), 0.0, 1.0, true); + + assert_eq!(faded.next(), Some(0.0)); // source value 0 + assert_eq!(faded.next(), Some(0.04)); // source value 0.4, ramp gain 0.1 + assert_eq!(faded.next(), Some(0.16)); // source value 0.8, ramp gain 0.2 + if let Ok(_result) = faded.try_seek(Duration::from_secs(5)) { + assert_eq!(faded.next(), Some(0.64)); // source value 0.8, ramp gain 0.8 + assert_eq!(faded.next(), Some(0.0)); // source value 0, ramp gain 0.9 + assert_eq!(faded.next(), Some(0.4)); // source value 0.4. ramp gain 1.0 + } else { + panic!("try_seek failed!"); + } + } } From 5f9c521a8b822bf06badba4c332d555778e480b0 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 4 Aug 2024 13:33:09 -0700 Subject: [PATCH 26/44] Revert "Code review stuff..." This reverts commit d5c9e0d62cb02075c7f2bfe70ef7924ae11fa466. --- src/source/linear_ramp.rs | 51 +++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/src/source/linear_ramp.rs b/src/source/linear_ramp.rs index 276637f..d5e0c0d 100644 --- a/src/source/linear_ramp.rs +++ b/src/source/linear_ramp.rs @@ -137,7 +137,6 @@ where #[inline] fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> { - self.elapsed_ns += pos.as_nanos() as f32; self.input.try_seek(pos) } } @@ -147,10 +146,16 @@ mod tests { use super::*; use crate::buffer::SamplesBuffer; +<<<<<<< HEAD /// Create a SamplesBuffer of identical samples with value `value`. /// Returned buffer is one channel and has s sample rate of 1 hz. fn const_source(length: u8, value: f32) -> SamplesBuffer { let data: Vec = (1..=length).map(|_| value).collect(); +======= + fn dummysource(length: u8) -> SamplesBuffer { + // shamelessly copied from crossfade.rs + let data: Vec = (1..=length).map(f32::from).collect(); +>>>>>>> parent of d5c9e0d (Code review stuff...) SamplesBuffer::new(1, 1, data) } @@ -165,38 +170,48 @@ mod tests { } #[test] +<<<<<<< HEAD fn test_linear_ramp() { let source1 = const_source(10, 1.0f32); +======= + fn test_linearramp() { + let source1 = dummysource(10); +>>>>>>> parent of d5c9e0d (Code review stuff...) let mut faded = linear_gain_ramp(source1, Duration::from_secs(4), 0.0, 1.0, true); assert_eq!(faded.next(), Some(0.0)); - assert_eq!(faded.next(), Some(0.25)); assert_eq!(faded.next(), Some(0.5)); - assert_eq!(faded.next(), Some(0.75)); - assert_eq!(faded.next(), Some(1.0)); - assert_eq!(faded.next(), Some(1.0)); - assert_eq!(faded.next(), Some(1.0)); - assert_eq!(faded.next(), Some(1.0)); - assert_eq!(faded.next(), Some(1.0)); - assert_eq!(faded.next(), Some(1.0)); + assert_eq!(faded.next(), Some(1.5)); + assert_eq!(faded.next(), Some(3.0)); + assert_eq!(faded.next(), Some(5.0)); + assert_eq!(faded.next(), Some(6.0)); + assert_eq!(faded.next(), Some(7.0)); + assert_eq!(faded.next(), Some(8.0)); + assert_eq!(faded.next(), Some(9.0)); + assert_eq!(faded.next(), Some(10.0)); assert_eq!(faded.next(), None); } #[test] +<<<<<<< HEAD fn test_linear_ramp_clamped() { let source1 = const_source(10, 1.0f32); +======= + fn test_linearramp_clamped() { + let source1 = dummysource(10); +>>>>>>> parent of d5c9e0d (Code review stuff...) let mut faded = linear_gain_ramp(source1, Duration::from_secs(4), 0.0, 0.5, true); - assert_eq!(faded.next(), Some(0.0)); // fading in... - assert_eq!(faded.next(), Some(0.125)); + assert_eq!(faded.next(), Some(0.0)); assert_eq!(faded.next(), Some(0.25)); - assert_eq!(faded.next(), Some(0.375)); - assert_eq!(faded.next(), Some(0.5)); // fade is done - assert_eq!(faded.next(), Some(0.5)); - assert_eq!(faded.next(), Some(0.5)); - assert_eq!(faded.next(), Some(0.5)); - assert_eq!(faded.next(), Some(0.5)); - assert_eq!(faded.next(), Some(0.5)); + assert_eq!(faded.next(), Some(0.75)); + assert_eq!(faded.next(), Some(1.5)); + assert_eq!(faded.next(), Some(2.5)); + assert_eq!(faded.next(), Some(3.0)); + assert_eq!(faded.next(), Some(3.5)); + assert_eq!(faded.next(), Some(4.0)); + assert_eq!(faded.next(), Some(4.5)); + assert_eq!(faded.next(), Some(5.0)); assert_eq!(faded.next(), None); } From 78fffb4ec65226c8a0191dc12bc6fd3930ab8069 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 4 Aug 2024 13:33:44 -0700 Subject: [PATCH 27/44] Implementing seek test --- src/source/linear_ramp.rs | 51 ++++++++++++++------------------------- 1 file changed, 18 insertions(+), 33 deletions(-) diff --git a/src/source/linear_ramp.rs b/src/source/linear_ramp.rs index d5e0c0d..276637f 100644 --- a/src/source/linear_ramp.rs +++ b/src/source/linear_ramp.rs @@ -137,6 +137,7 @@ where #[inline] fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> { + self.elapsed_ns += pos.as_nanos() as f32; self.input.try_seek(pos) } } @@ -146,16 +147,10 @@ mod tests { use super::*; use crate::buffer::SamplesBuffer; -<<<<<<< HEAD /// Create a SamplesBuffer of identical samples with value `value`. /// Returned buffer is one channel and has s sample rate of 1 hz. fn const_source(length: u8, value: f32) -> SamplesBuffer { let data: Vec = (1..=length).map(|_| value).collect(); -======= - fn dummysource(length: u8) -> SamplesBuffer { - // shamelessly copied from crossfade.rs - let data: Vec = (1..=length).map(f32::from).collect(); ->>>>>>> parent of d5c9e0d (Code review stuff...) SamplesBuffer::new(1, 1, data) } @@ -170,48 +165,38 @@ mod tests { } #[test] -<<<<<<< HEAD fn test_linear_ramp() { let source1 = const_source(10, 1.0f32); -======= - fn test_linearramp() { - let source1 = dummysource(10); ->>>>>>> parent of d5c9e0d (Code review stuff...) let mut faded = linear_gain_ramp(source1, Duration::from_secs(4), 0.0, 1.0, true); assert_eq!(faded.next(), Some(0.0)); + assert_eq!(faded.next(), Some(0.25)); assert_eq!(faded.next(), Some(0.5)); - assert_eq!(faded.next(), Some(1.5)); - assert_eq!(faded.next(), Some(3.0)); - assert_eq!(faded.next(), Some(5.0)); - assert_eq!(faded.next(), Some(6.0)); - assert_eq!(faded.next(), Some(7.0)); - assert_eq!(faded.next(), Some(8.0)); - assert_eq!(faded.next(), Some(9.0)); - assert_eq!(faded.next(), Some(10.0)); + assert_eq!(faded.next(), Some(0.75)); + assert_eq!(faded.next(), Some(1.0)); + assert_eq!(faded.next(), Some(1.0)); + assert_eq!(faded.next(), Some(1.0)); + assert_eq!(faded.next(), Some(1.0)); + assert_eq!(faded.next(), Some(1.0)); + assert_eq!(faded.next(), Some(1.0)); assert_eq!(faded.next(), None); } #[test] -<<<<<<< HEAD fn test_linear_ramp_clamped() { let source1 = const_source(10, 1.0f32); -======= - fn test_linearramp_clamped() { - let source1 = dummysource(10); ->>>>>>> parent of d5c9e0d (Code review stuff...) let mut faded = linear_gain_ramp(source1, Duration::from_secs(4), 0.0, 0.5, true); - assert_eq!(faded.next(), Some(0.0)); + assert_eq!(faded.next(), Some(0.0)); // fading in... + assert_eq!(faded.next(), Some(0.125)); assert_eq!(faded.next(), Some(0.25)); - assert_eq!(faded.next(), Some(0.75)); - assert_eq!(faded.next(), Some(1.5)); - assert_eq!(faded.next(), Some(2.5)); - assert_eq!(faded.next(), Some(3.0)); - assert_eq!(faded.next(), Some(3.5)); - assert_eq!(faded.next(), Some(4.0)); - assert_eq!(faded.next(), Some(4.5)); - assert_eq!(faded.next(), Some(5.0)); + assert_eq!(faded.next(), Some(0.375)); + assert_eq!(faded.next(), Some(0.5)); // fade is done + assert_eq!(faded.next(), Some(0.5)); + assert_eq!(faded.next(), Some(0.5)); + assert_eq!(faded.next(), Some(0.5)); + assert_eq!(faded.next(), Some(0.5)); + assert_eq!(faded.next(), Some(0.5)); assert_eq!(faded.next(), None); } From e72de34a938deaf9681cc31ede33b883472f6f79 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 4 Aug 2024 13:45:35 -0700 Subject: [PATCH 28/44] Implemented linear_ramp_seek tests Also integratd assert_float_eq --- src/lib.rs | 4 ++++ src/source/linear_ramp.rs | 15 ++++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6f6c505..2c907e6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -118,6 +118,10 @@ pub use cpal::{ SupportedStreamConfig, }; +#[cfg(test)] +#[macro_use] +extern crate assert_float_eq; + mod conversions; mod sink; mod spatial_sink; diff --git a/src/source/linear_ramp.rs b/src/source/linear_ramp.rs index 276637f..cd1da0f 100644 --- a/src/source/linear_ramp.rs +++ b/src/source/linear_ramp.rs @@ -1,5 +1,6 @@ use std::time::Duration; + use super::SeekError; use crate::{Sample, Source}; @@ -169,7 +170,7 @@ mod tests { let source1 = const_source(10, 1.0f32); let mut faded = linear_gain_ramp(source1, Duration::from_secs(4), 0.0, 1.0, true); - assert_eq!(faded.next(), Some(0.0)); + assert_float_absolute_eq!(faded.next().unwrap(), 0.0); assert_eq!(faded.next(), Some(0.25)); assert_eq!(faded.next(), Some(0.5)); assert_eq!(faded.next(), Some(0.75)); @@ -205,13 +206,13 @@ mod tests { let source1 = cycle_source(20, vec![0.0f32, 0.4f32, 0.8f32]); let mut faded = linear_gain_ramp(source1, Duration::from_secs(10), 0.0, 1.0, true); - assert_eq!(faded.next(), Some(0.0)); // source value 0 - assert_eq!(faded.next(), Some(0.04)); // source value 0.4, ramp gain 0.1 - assert_eq!(faded.next(), Some(0.16)); // source value 0.8, ramp gain 0.2 + assert_float_absolute_eq!(faded.next().unwrap(), 0.0); // source value 0 + assert_float_absolute_eq!(faded.next().unwrap(), 0.04); // source value 0.4, ramp gain 0.1 + assert_float_absolute_eq!(faded.next().unwrap(), 0.16); // source value 0.8, ramp gain 0.2 if let Ok(_result) = faded.try_seek(Duration::from_secs(5)) { - assert_eq!(faded.next(), Some(0.64)); // source value 0.8, ramp gain 0.8 - assert_eq!(faded.next(), Some(0.0)); // source value 0, ramp gain 0.9 - assert_eq!(faded.next(), Some(0.4)); // source value 0.4. ramp gain 1.0 + assert_float_absolute_eq!(faded.next().unwrap(), 0.64); // source value 0.8, ramp gain 0.8 + assert_float_absolute_eq!(faded.next().unwrap(), 0.0); // source value 0, ramp gain 0.9 + assert_float_absolute_eq!(faded.next().unwrap(), 0.4); // source value 0.4. ramp gain 1.0 } else { panic!("try_seek failed!"); } From e58165357829e23b8fd7db12b40adeafdd31f18c Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 4 Aug 2024 13:46:54 -0700 Subject: [PATCH 29/44] Rustfmt --- src/decoder/mod.rs | 2 +- src/source/linear_ramp.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index 1ae4cba..837a919 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -14,7 +14,7 @@ use crate::Source; #[cfg(feature = "symphonia")] use self::read_seek_source::ReadSeekSource; #[cfg(feature = "symphonia")] -use ::symphonia::core::io::{MediaSource, MediaSourceStream}; +use symphonia::core::io::{MediaSource, MediaSourceStream}; #[cfg(all(feature = "flac", not(feature = "symphonia-flac")))] mod flac; diff --git a/src/source/linear_ramp.rs b/src/source/linear_ramp.rs index cd1da0f..41a313c 100644 --- a/src/source/linear_ramp.rs +++ b/src/source/linear_ramp.rs @@ -1,6 +1,5 @@ use std::time::Duration; - use super::SeekError; use crate::{Sample, Source}; From 5056a88b9f9566f41589303b1091d60956986b87 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 4 Aug 2024 13:55:53 -0700 Subject: [PATCH 30/44] Twiddles --- src/lib.rs | 8 ++++---- src/source/linear_ramp.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2c907e6..a94bbd4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -118,10 +118,6 @@ pub use cpal::{ SupportedStreamConfig, }; -#[cfg(test)] -#[macro_use] -extern crate assert_float_eq; - mod conversions; mod sink; mod spatial_sink; @@ -140,3 +136,7 @@ pub use crate::sink::Sink; pub use crate::source::Source; pub use crate::spatial_sink::SpatialSink; pub use crate::stream::{OutputStream, OutputStreamHandle, PlayError, StreamError}; + +#[cfg(test)] +#[macro_use] +extern crate assert_float_eq; diff --git a/src/source/linear_ramp.rs b/src/source/linear_ramp.rs index 41a313c..d978bd5 100644 --- a/src/source/linear_ramp.rs +++ b/src/source/linear_ramp.rs @@ -169,7 +169,7 @@ mod tests { let source1 = const_source(10, 1.0f32); let mut faded = linear_gain_ramp(source1, Duration::from_secs(4), 0.0, 1.0, true); - assert_float_absolute_eq!(faded.next().unwrap(), 0.0); + assert_eq!(faded.next(), Some(0.0)); assert_eq!(faded.next(), Some(0.25)); assert_eq!(faded.next(), Some(0.5)); assert_eq!(faded.next(), Some(0.75)); From 5e73ffd4aac81e45adf76b721d2a1c37632c34cc Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 4 Aug 2024 13:57:49 -0700 Subject: [PATCH 31/44] Removed a typo (I think rustfmt did this!) --- src/decoder/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index 837a919..1ae4cba 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -14,7 +14,7 @@ use crate::Source; #[cfg(feature = "symphonia")] use self::read_seek_source::ReadSeekSource; #[cfg(feature = "symphonia")] -use symphonia::core::io::{MediaSource, MediaSourceStream}; +use ::symphonia::core::io::{MediaSource, MediaSourceStream}; #[cfg(all(feature = "flac", not(feature = "symphonia-flac")))] mod flac; From 98c008d7ea49e1264f8733ac7d50074c8e03a5a6 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 4 Aug 2024 14:43:39 -0700 Subject: [PATCH 32/44] Fixed try_seek() to seek from absolute start and not the current position. --- src/source/linear_ramp.rs | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/source/linear_ramp.rs b/src/source/linear_ramp.rs index d978bd5..5349f8e 100644 --- a/src/source/linear_ramp.rs +++ b/src/source/linear_ramp.rs @@ -137,7 +137,7 @@ where #[inline] fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> { - self.elapsed_ns += pos.as_nanos() as f32; + self.elapsed_ns = pos.as_nanos() as f32; self.input.try_seek(pos) } } @@ -148,7 +148,7 @@ mod tests { use crate::buffer::SamplesBuffer; /// Create a SamplesBuffer of identical samples with value `value`. - /// Returned buffer is one channel and has s sample rate of 1 hz. + /// Returned buffer is one channel and has a sample rate of 1 hz. fn const_source(length: u8, value: f32) -> SamplesBuffer { let data: Vec = (1..=length).map(|_| value).collect(); SamplesBuffer::new(1, 1, data) @@ -208,12 +208,29 @@ mod tests { assert_float_absolute_eq!(faded.next().unwrap(), 0.0); // source value 0 assert_float_absolute_eq!(faded.next().unwrap(), 0.04); // source value 0.4, ramp gain 0.1 assert_float_absolute_eq!(faded.next().unwrap(), 0.16); // source value 0.8, ramp gain 0.2 + if let Ok(_result) = faded.try_seek(Duration::from_secs(5)) { - assert_float_absolute_eq!(faded.next().unwrap(), 0.64); // source value 0.8, ramp gain 0.8 - assert_float_absolute_eq!(faded.next().unwrap(), 0.0); // source value 0, ramp gain 0.9 - assert_float_absolute_eq!(faded.next().unwrap(), 0.4); // source value 0.4. ramp gain 1.0 + assert_float_absolute_eq!(faded.next().unwrap(), 0.40); // source value 0.8, ramp gain 0.5 + assert_float_absolute_eq!(faded.next().unwrap(), 0.0); // source value 0, ramp gain 0.6 + assert_float_absolute_eq!(faded.next().unwrap(), 0.28); // source value 0.4. ramp gain 0.7 } else { - panic!("try_seek failed!"); + panic!("try_seek() failed!"); + } + + if let Ok(_result) = faded.try_seek(Duration::from_secs(0)) { + assert_float_absolute_eq!(faded.next().unwrap(), 0.0); // source value 0, ramp gain 0.0 + assert_float_absolute_eq!(faded.next().unwrap(), 0.04); // source value 0.4, ramp gain 0.1 + assert_float_absolute_eq!(faded.next().unwrap(), 0.16); // source value 0.8. ramp gain 0.2 + } else { + panic!("try_seek() failed!"); + } + + if let Ok(_result) = faded.try_seek(Duration::from_secs(10)) { + assert_float_absolute_eq!(faded.next().unwrap(), 0.4); // source value 0.4, ramp gain 1.0 + assert_float_absolute_eq!(faded.next().unwrap(), 0.8); // source value 0.8, ramp gain 1.0 + assert_float_absolute_eq!(faded.next().unwrap(), 0.0); // source value 0. ramp gain 1.0 + } else { + panic!("try_seek() failed!"); } } } From 0e3aaa9ceac739afca8d4680089920b78578f8b6 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 4 Aug 2024 16:15:38 -0700 Subject: [PATCH 33/44] Moved assert_float_eq to dev-dependencies --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 0bdf315..9f84cc7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,6 @@ minimp3_fixed = { version = "0.5.4", optional = true} symphonia = { version = "0.5.4", optional = true, default-features = false } crossbeam-channel = { version = "0.5.8", optional = true } thiserror = "1.0.49" -assert_float_eq = "1.1.3" [features] default = ["flac", "vorbis", "wav", "mp3"] @@ -44,6 +43,7 @@ symphonia-aiff = ["symphonia/aiff", "symphonia/pcm"] quickcheck = "0.9.2" rstest = "0.18.2" rstest_reuse = "0.6.0" +assert_float_eq = "1.1.3" [[example]] name = "music_m4a" From 8f53addbdee6d2fe466bf1b460bc168c92f5c3b8 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 4 Aug 2024 16:28:37 -0700 Subject: [PATCH 34/44] Implemented a little fade envelope on the notes. --- examples/mix_multiple_sources.rs | 44 +++++++++++++++++++------------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/examples/mix_multiple_sources.rs b/examples/mix_multiple_sources.rs index 3363d5f..a716839 100644 --- a/examples/mix_multiple_sources.rs +++ b/examples/mix_multiple_sources.rs @@ -1,5 +1,5 @@ use rodio::source::{SineWave, Source}; -use rodio::{dynamic_mixer, OutputStream, Sink}; +use rodio::{dynamic_mixer, OutputStream, Sink, queue}; use std::time::Duration; fn main() { @@ -11,24 +11,32 @@ fn main() { // Create four unique sources. The frequencies used here correspond // notes in the key of C and in octave 4: C4, or middle C on a piano, // E4, G4, and A4 respectively. - let source_c = SineWave::new(261.63) - .take_duration(Duration::from_secs_f32(1.)) - .amplify(0.20); - let source_e = SineWave::new(329.63) - .take_duration(Duration::from_secs_f32(1.)) - .amplify(0.20); - let source_g = SineWave::new(392.0) - .take_duration(Duration::from_secs_f32(1.)) - .amplify(0.20); - let source_a = SineWave::new(440.0) - .take_duration(Duration::from_secs_f32(1.)) - .amplify(0.20); - // Add sources C, E, G, and A to the mixer controller. - controller.add(source_c); - controller.add(source_e); - controller.add(source_g); - controller.add(source_a); + let notes = vec![261.63, 329.63, 392.0, 440.0]; + + notes.into_iter().for_each(|f| { + let note_source = SineWave::new(f); + + let (tx, rx) = queue::queue(false); + + let note_body = note_source + .clone() + .take_duration(Duration::from_secs_f32(1.0)) + .amplify(0.20) + .fade_in(Duration::from_secs_f32(0.1)); + + let note_end = note_source + .clone() + .skip_duration(Duration::from_secs_f32(1.0)) + .take_duration(Duration::from_secs_f32(1.0)) + .amplify(0.20) + .linear_gain_ramp(Duration::from_secs_f32(1.0), 1.0, 0.0, true); + + tx.append(note_body); + tx.append(note_end); + + controller.add(rx); + }); // Append the dynamic mixer to the sink to play a C major 6th chord. sink.append(mixer); From 6ba19a649d08b598f55030b722084053acded53c Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 4 Aug 2024 16:30:20 -0700 Subject: [PATCH 35/44] Rustfmt --- examples/mix_multiple_sources.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/mix_multiple_sources.rs b/examples/mix_multiple_sources.rs index a716839..2f3c7fc 100644 --- a/examples/mix_multiple_sources.rs +++ b/examples/mix_multiple_sources.rs @@ -1,5 +1,5 @@ use rodio::source::{SineWave, Source}; -use rodio::{dynamic_mixer, OutputStream, Sink, queue}; +use rodio::{dynamic_mixer, queue, OutputStream, Sink}; use std::time::Duration; fn main() { @@ -16,7 +16,7 @@ fn main() { notes.into_iter().for_each(|f| { let note_source = SineWave::new(f); - + let (tx, rx) = queue::queue(false); let note_body = note_source @@ -31,7 +31,7 @@ fn main() { .take_duration(Duration::from_secs_f32(1.0)) .amplify(0.20) .linear_gain_ramp(Duration::from_secs_f32(1.0), 1.0, 0.0, true); - + tx.append(note_body); tx.append(note_end); From f2f93e69470d21de1f6e27e282bcfd1a8c84d202 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 4 Aug 2024 16:40:39 -0700 Subject: [PATCH 36/44] Switched-out assert_float_eq for approx Different macro library for testing float equality --- Cargo.toml | 2 +- src/lib.rs | 2 +- src/source/linear_ramp.rs | 24 ++++++++++++------------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9f84cc7..aa75167 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,7 +43,7 @@ symphonia-aiff = ["symphonia/aiff", "symphonia/pcm"] quickcheck = "0.9.2" rstest = "0.18.2" rstest_reuse = "0.6.0" -assert_float_eq = "1.1.3" +approx = "0.5.1" [[example]] name = "music_m4a" diff --git a/src/lib.rs b/src/lib.rs index a94bbd4..0c86ba9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -139,4 +139,4 @@ pub use crate::stream::{OutputStream, OutputStreamHandle, PlayError, StreamError #[cfg(test)] #[macro_use] -extern crate assert_float_eq; +extern crate approx; diff --git a/src/source/linear_ramp.rs b/src/source/linear_ramp.rs index 5349f8e..5be8b39 100644 --- a/src/source/linear_ramp.rs +++ b/src/source/linear_ramp.rs @@ -205,30 +205,30 @@ mod tests { let source1 = cycle_source(20, vec![0.0f32, 0.4f32, 0.8f32]); let mut faded = linear_gain_ramp(source1, Duration::from_secs(10), 0.0, 1.0, true); - assert_float_absolute_eq!(faded.next().unwrap(), 0.0); // source value 0 - assert_float_absolute_eq!(faded.next().unwrap(), 0.04); // source value 0.4, ramp gain 0.1 - assert_float_absolute_eq!(faded.next().unwrap(), 0.16); // source value 0.8, ramp gain 0.2 + assert_abs_diff_eq!(faded.next().unwrap(), 0.0); // source value 0 + assert_abs_diff_eq!(faded.next().unwrap(), 0.04); // source value 0.4, ramp gain 0.1 + assert_abs_diff_eq!(faded.next().unwrap(), 0.16); // source value 0.8, ramp gain 0.2 if let Ok(_result) = faded.try_seek(Duration::from_secs(5)) { - assert_float_absolute_eq!(faded.next().unwrap(), 0.40); // source value 0.8, ramp gain 0.5 - assert_float_absolute_eq!(faded.next().unwrap(), 0.0); // source value 0, ramp gain 0.6 - assert_float_absolute_eq!(faded.next().unwrap(), 0.28); // source value 0.4. ramp gain 0.7 + assert_abs_diff_eq!(faded.next().unwrap(), 0.40); // source value 0.8, ramp gain 0.5 + assert_abs_diff_eq!(faded.next().unwrap(), 0.0); // source value 0, ramp gain 0.6 + assert_abs_diff_eq!(faded.next().unwrap(), 0.28); // source value 0.4. ramp gain 0.7 } else { panic!("try_seek() failed!"); } if let Ok(_result) = faded.try_seek(Duration::from_secs(0)) { - assert_float_absolute_eq!(faded.next().unwrap(), 0.0); // source value 0, ramp gain 0.0 - assert_float_absolute_eq!(faded.next().unwrap(), 0.04); // source value 0.4, ramp gain 0.1 - assert_float_absolute_eq!(faded.next().unwrap(), 0.16); // source value 0.8. ramp gain 0.2 + assert_abs_diff_eq!(faded.next().unwrap(), 0.0); // source value 0, ramp gain 0.0 + assert_abs_diff_eq!(faded.next().unwrap(), 0.04); // source value 0.4, ramp gain 0.1 + assert_abs_diff_eq!(faded.next().unwrap(), 0.16); // source value 0.8. ramp gain 0.2 } else { panic!("try_seek() failed!"); } if let Ok(_result) = faded.try_seek(Duration::from_secs(10)) { - assert_float_absolute_eq!(faded.next().unwrap(), 0.4); // source value 0.4, ramp gain 1.0 - assert_float_absolute_eq!(faded.next().unwrap(), 0.8); // source value 0.8, ramp gain 1.0 - assert_float_absolute_eq!(faded.next().unwrap(), 0.0); // source value 0. ramp gain 1.0 + assert_abs_diff_eq!(faded.next().unwrap(), 0.4); // source value 0.4, ramp gain 1.0 + assert_abs_diff_eq!(faded.next().unwrap(), 0.8); // source value 0.8, ramp gain 1.0 + assert_abs_diff_eq!(faded.next().unwrap(), 0.0); // source value 0. ramp gain 1.0 } else { panic!("try_seek() failed!"); } From 52e7d4aaa82415838b609aa2cda768fe00fe361c Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 4 Aug 2024 17:07:50 -0700 Subject: [PATCH 37/44] Added a fadeout source --- examples/mix_multiple_sources.rs | 2 +- src/source/fadeout.rs | 102 +++++++++++++++++++++++++++++++ src/source/mod.rs | 11 ++++ 3 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 src/source/fadeout.rs diff --git a/examples/mix_multiple_sources.rs b/examples/mix_multiple_sources.rs index 2f3c7fc..983dd07 100644 --- a/examples/mix_multiple_sources.rs +++ b/examples/mix_multiple_sources.rs @@ -30,7 +30,7 @@ fn main() { .skip_duration(Duration::from_secs_f32(1.0)) .take_duration(Duration::from_secs_f32(1.0)) .amplify(0.20) - .linear_gain_ramp(Duration::from_secs_f32(1.0), 1.0, 0.0, true); + .fade_out(Duration::from_secs_f32(1.0)); tx.append(note_body); tx.append(note_end); diff --git a/src/source/fadeout.rs b/src/source/fadeout.rs new file mode 100644 index 0000000..74a32e0 --- /dev/null +++ b/src/source/fadeout.rs @@ -0,0 +1,102 @@ +use std::time::Duration; + +use crate::{Sample, Source}; + +use super::{linear_ramp::linear_gain_ramp, LinearGainRamp, SeekError}; + +/// Internal function that builds a `FadeOut` object. +pub fn fadeout(input: I, duration: Duration) -> FadeOut +where + I: Source, + I::Item: Sample, +{ + FadeOut { + input: linear_gain_ramp(input, duration, 1.0f32, 0.0f32, true), + } +} + +/// Filter that modifies raises the volume from silence over a time period. +#[derive(Clone, Debug)] +pub struct FadeOut { + input: LinearGainRamp, +} + +impl FadeOut +where + I: Source, + I::Item: Sample, +{ + /// Returns a reference to the inner source. + #[inline] + pub fn inner(&self) -> &I { + self.input.inner() + } + + /// Returns a mutable reference to the inner source. + #[inline] + pub fn inner_mut(&mut self) -> &mut I { + self.input.inner_mut() + } + + /// Returns the inner source. + #[inline] + pub fn into_inner(self) -> I { + self.input.into_inner() + } +} + +impl Iterator for FadeOut +where + I: Source, + I::Item: Sample, +{ + type Item = I::Item; + + #[inline] + fn next(&mut self) -> Option { + self.input.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.input.size_hint() + } +} + +impl ExactSizeIterator for FadeOut +where + I: Source + ExactSizeIterator, + I::Item: Sample, +{ +} + +impl Source for FadeOut +where + I: Source, + I::Item: Sample, +{ + #[inline] + fn current_frame_len(&self) -> Option { + self.inner().current_frame_len() + } + + #[inline] + fn channels(&self) -> u16 { + self.inner().channels() + } + + #[inline] + fn sample_rate(&self) -> u32 { + self.inner().sample_rate() + } + + #[inline] + fn total_duration(&self) -> Option { + self.inner().total_duration() + } + + #[inline] + fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> { + self.inner_mut().try_seek(pos) + } +} diff --git a/src/source/mod.rs b/src/source/mod.rs index 657ddfe..1f9f40d 100644 --- a/src/source/mod.rs +++ b/src/source/mod.rs @@ -16,6 +16,7 @@ pub use self::done::Done; pub use self::empty::Empty; pub use self::empty_callback::EmptyCallback; pub use self::fadein::FadeIn; +pub use self::fadeout::FadeOut; pub use self::from_factory::{from_factory, FromFactoryIter}; pub use self::from_iter::{from_iter, FromIter}; pub use self::linear_ramp::LinearGainRamp; @@ -45,6 +46,7 @@ mod done; mod empty; mod empty_callback; mod fadein; +mod fadeout; mod from_factory; mod from_iter; mod linear_ramp; @@ -251,6 +253,15 @@ where fadein::fadein(self, duration) } + /// Fades out the sound. + #[inline] + fn fade_out(self, duration: Duration) -> FadeOut + where + Self: Sized, + { + fadeout::fadeout(self, duration) + } + /// Applies a linear gain ramp to the sound. /// /// If `clamp_end` is `true`, all samples subsequent to the end of the ramp From 9351746e0be5c11f2b1a9ea1ee2f1a65775125aa Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Sun, 4 Aug 2024 18:10:45 -0700 Subject: [PATCH 38/44] Fixed comments --- src/source/fadeout.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/source/fadeout.rs b/src/source/fadeout.rs index 74a32e0..b71de92 100644 --- a/src/source/fadeout.rs +++ b/src/source/fadeout.rs @@ -15,7 +15,7 @@ where } } -/// Filter that modifies raises the volume from silence over a time period. +/// Filter that modifies lowers the volume to silence over a time period. #[derive(Clone, Debug)] pub struct FadeOut { input: LinearGainRamp, From cd5207127307a3616d30511eb9814545831cc4b1 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Mon, 5 Aug 2024 09:51:08 -0700 Subject: [PATCH 39/44] Revert changes to this for now --- examples/mix_multiple_sources.rs | 44 +++++++++++++------------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/examples/mix_multiple_sources.rs b/examples/mix_multiple_sources.rs index 983dd07..3363d5f 100644 --- a/examples/mix_multiple_sources.rs +++ b/examples/mix_multiple_sources.rs @@ -1,5 +1,5 @@ use rodio::source::{SineWave, Source}; -use rodio::{dynamic_mixer, queue, OutputStream, Sink}; +use rodio::{dynamic_mixer, OutputStream, Sink}; use std::time::Duration; fn main() { @@ -11,32 +11,24 @@ fn main() { // Create four unique sources. The frequencies used here correspond // notes in the key of C and in octave 4: C4, or middle C on a piano, // E4, G4, and A4 respectively. + let source_c = SineWave::new(261.63) + .take_duration(Duration::from_secs_f32(1.)) + .amplify(0.20); + let source_e = SineWave::new(329.63) + .take_duration(Duration::from_secs_f32(1.)) + .amplify(0.20); + let source_g = SineWave::new(392.0) + .take_duration(Duration::from_secs_f32(1.)) + .amplify(0.20); + let source_a = SineWave::new(440.0) + .take_duration(Duration::from_secs_f32(1.)) + .amplify(0.20); - let notes = vec![261.63, 329.63, 392.0, 440.0]; - - notes.into_iter().for_each(|f| { - let note_source = SineWave::new(f); - - let (tx, rx) = queue::queue(false); - - let note_body = note_source - .clone() - .take_duration(Duration::from_secs_f32(1.0)) - .amplify(0.20) - .fade_in(Duration::from_secs_f32(0.1)); - - let note_end = note_source - .clone() - .skip_duration(Duration::from_secs_f32(1.0)) - .take_duration(Duration::from_secs_f32(1.0)) - .amplify(0.20) - .fade_out(Duration::from_secs_f32(1.0)); - - tx.append(note_body); - tx.append(note_end); - - controller.add(rx); - }); + // Add sources C, E, G, and A to the mixer controller. + controller.add(source_c); + controller.add(source_e); + controller.add(source_g); + controller.add(source_a); // Append the dynamic mixer to the sink to play a C major 6th chord. sink.append(mixer); From 972147ae57e0d33613782eb4110b2aaba7c12914 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Mon, 5 Aug 2024 09:55:44 -0700 Subject: [PATCH 40/44] Updated changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff36459..63faa80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Support for *ALAC/AIFF* +- New sources: + - `fade_out` fades an input out using a linear gain fade. + - `linear_gain_ramp` applies a linear gain change to a sound over a + given duration. `fade_out` is implemented as a `linear_gain_ramp` and + `fade_in` has been refactored to use the `linear_gain_ramp` + implementation. ### Changed - `SamplesBuffer` is now `Clone` From 97cf232798c3fb95a6dc30cde3552ad4ea667816 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Mon, 5 Aug 2024 10:08:02 -0700 Subject: [PATCH 41/44] Revert "Updated changelog" This reverts commit 972147ae57e0d33613782eb4110b2aaba7c12914. --- CHANGELOG.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63faa80..ff36459 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,12 +9,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Support for *ALAC/AIFF* -- New sources: - - `fade_out` fades an input out using a linear gain fade. - - `linear_gain_ramp` applies a linear gain change to a sound over a - given duration. `fade_out` is implemented as a `linear_gain_ramp` and - `fade_in` has been refactored to use the `linear_gain_ramp` - implementation. ### Changed - `SamplesBuffer` is now `Clone` From c541f352114df32636e40c8727722db21e5bc305 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Mon, 5 Aug 2024 10:09:19 -0700 Subject: [PATCH 42/44] Redid changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff36459..63faa80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Support for *ALAC/AIFF* +- New sources: + - `fade_out` fades an input out using a linear gain fade. + - `linear_gain_ramp` applies a linear gain change to a sound over a + given duration. `fade_out` is implemented as a `linear_gain_ramp` and + `fade_in` has been refactored to use the `linear_gain_ramp` + implementation. ### Changed - `SamplesBuffer` is now `Clone` From 7c39451138c4d398a6cdf036634ebe6625c48df9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 11:53:49 +0200 Subject: [PATCH 43/44] refactor, move test dep use into test mod --- src/lib.rs | 3 --- src/source/linear_ramp.rs | 2 ++ 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0c86ba9..4344a5e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -137,6 +137,3 @@ pub use crate::source::Source; pub use crate::spatial_sink::SpatialSink; pub use crate::stream::{OutputStream, OutputStreamHandle, PlayError, StreamError}; -#[cfg(test)] -#[macro_use] -extern crate approx; diff --git a/src/source/linear_ramp.rs b/src/source/linear_ramp.rs index 5be8b39..0e46a68 100644 --- a/src/source/linear_ramp.rs +++ b/src/source/linear_ramp.rs @@ -144,6 +144,8 @@ where #[cfg(test)] mod tests { + use approx::assert_abs_diff_eq; + use super::*; use crate::buffer::SamplesBuffer; From 4805661cd1aa26c98129cd15cd8e98e821c4d30e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 9 Aug 2024 12:04:06 +0200 Subject: [PATCH 44/44] formatting --- src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 4344a5e..6f6c505 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -136,4 +136,3 @@ pub use crate::sink::Sink; pub use crate::source::Source; pub use crate::spatial_sink::SpatialSink; pub use crate::stream::{OutputStream, OutputStreamHandle, PlayError, StreamError}; -