Skip to content

Commit 396653e

Browse files
committed
test and fix take_duration fadeout
1 parent a571f59 commit 396653e

File tree

8 files changed

+78
-17
lines changed

8 files changed

+78
-17
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1919
- Minimal builds without `cpal` audio output are now supported.
2020
See `README.md` for instructions. (#349)
2121
- Added `Sample::is_zero()` method for checking zero samples.
22+
- Added `TakeDuration::with_fadeout` similar to `TakeDuration::fadeout` but returns `Self`
2223

2324
### Changed
2425
- Breaking: `OutputStreamBuilder` should now be used to initialize an audio output stream.
@@ -34,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3435
- Breaking: Sources wrapping an existing source had a public factory method
3536
which is now removed. Something like: `source::amplify(unamplified, 1.2)` must now be
3637
written as `unamplified.amplify(1.2)`.
38+
- Breaking: `TakeDuration::set_filter_fadeout()` has been removed. Use `TakeDuration.fadeout(true)`.
3739
- `SignalGenerator`'s `Function` is now Copy.
3840

3941

src/source/crossfade.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ where
1616
I1: Source,
1717
I2: Source,
1818
{
19-
let mut input_fadeout = input_fadeout.take_duration(duration);
20-
input_fadeout.set_filter_fadeout();
19+
let input_fadeout = input_fadeout.take_duration(duration).with_fadeout(true);
2120
let input_fadein = input_fadein.take_duration(duration).fade_in(duration);
2221
input_fadeout.mix(input_fadein)
2322
}

src/source/linear_ramp.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ mod tests {
143143
use crate::Sample;
144144

145145
/// Create a SamplesBuffer of identical samples with value `value`.
146-
/// Returned buffer is one channel and has a sample rate of 1 hz.
146+
/// Returned buffer is one channel and has a sample rate of 1 Hz.
147147
fn const_source(length: u8, value: Sample) -> SamplesBuffer {
148148
let data: Vec<f32> = (1..=length).map(|_| value).collect();
149149
SamplesBuffer::new(1, 1, data)

src/source/mix.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ where
4444
let s1 = self.input1.next();
4545
let s2 = self.input2.next();
4646

47-
match (s1, s2) {
47+
match dbg!((s1, s2)) {
4848
(Some(s1), Some(s2)) => Some(s1 + s2),
4949
(Some(s1), None) => Some(s1),
5050
(None, Some(s2)) => Some(s2),

src/source/take_duration.rs

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,17 @@ where
6464
self.input
6565
}
6666

67+
/// Optionally the truncated source end with a FadeOut. The fade-out
68+
/// covers the entire length of the take source.
69+
pub fn fadeout(&mut self, enabled: bool) {
70+
self.fadeout = enabled;
71+
}
72+
6773
/// Make the truncated source end with a FadeOut. The fade-out covers the
6874
/// entire length of the take source.
69-
pub fn set_filter_fadeout(&mut self) {
70-
self.fadeout = true;
75+
pub fn with_fadeout(mut self, enabled: bool) -> Self {
76+
self.fadeout = enabled;
77+
self
7178
}
7279

7380
/// Remove any filter set.
@@ -98,6 +105,14 @@ where
98105
type Item = <I as Iterator>::Item;
99106

100107
// implementation is adapted of skip_duration
108+
//
109+
// if tuples are frames you could define fadeout as this:
110+
// [(1.0, 1.0), (1.0, 1.0), (1.0, 1.0)]
111+
// -> [(1.0, 1.0), (0.5, 0.5), (0.0, 0.0)]
112+
// instead because its simpler, faster and what previous rodio versions did we do:
113+
// [(1.0, 1.0), (1.0, 1.0), (1.0, 1.0)]
114+
// -> [(1.0, .83), (.66, 0.5), (.33, .16)]
115+
// at normal sample_rates you do not hear a difference
101116
fn next(&mut self) -> Option<<I as Iterator>::Item> {
102117
if self.input.parameters_changed() {
103118
self.remaining_ns -= self.duration_taken();
@@ -114,13 +129,15 @@ where
114129
return None;
115130
};
116131

117-
self.samples_taken += 1;
118-
if self.fadeout {
132+
let ret = if self.fadeout {
119133
let total = self.requested_duration.as_nanos() as u64;
120-
Some(sample * self.remaining_ns as f32 / total as f32)
134+
let remaining = self.remaining_ns - self.duration_taken();
135+
Some(sample * remaining as f32 / total as f32)
121136
} else {
122137
Some(sample)
123-
}
138+
};
139+
self.samples_taken += 1;
140+
ret
124141
}
125142
}
126143

tests/take_duration.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ fn ends_on_frame_boundary(#[values(1.483, 2.999)] seconds_to_skip: f32) {
1919
let got = source
2020
.clone()
2121
.take_duration(Duration::from_secs_f32(seconds_to_skip))
22+
// fadeout enables extra logic, run it too to check for bounds/overflow issues
23+
.with_fadeout(true)
24+
2225
.count();
2326
assert!(got % source.channels() as usize == 0)
2427
}
@@ -49,6 +52,7 @@ fn param_changes_during_skip(#[values(6, 11)] seconds_to_skip: u64) {
4952
let took = source
5053
.clone()
5154
.take_duration(Duration::from_secs(seconds_to_skip))
55+
.with_fadeout(true)
5256
.count();
5357

5458
let spans = source.spans;
@@ -68,6 +72,33 @@ fn param_changes_during_skip(#[values(6, 11)] seconds_to_skip: u64) {
6872
);
6973
}
7074

75+
#[test]
76+
fn fadeout() {
77+
let span_duration = Duration::from_secs(5);
78+
let source = TestSource::new()
79+
.with_span(
80+
TestSpan::ones()
81+
.with_sample_rate(5)
82+
.with_channel_count(1)
83+
.with_exact_duration(span_duration),
84+
)
85+
.with_span(
86+
TestSpan::ones()
87+
.with_sample_rate(5)
88+
.with_channel_count(2)
89+
.with_exact_duration(span_duration),
90+
);
91+
92+
let fade_out = source
93+
.take_duration(span_duration.mul_f32(1.5))
94+
.with_fadeout(true)
95+
.collect::<Vec<_>>();
96+
dbg!(&fade_out);
97+
assert_eq!(fade_out.first(), Some(&1.0));
98+
// fade_out ends the step before zero
99+
assert!(fade_out.last().unwrap() > &0.0);
100+
}
101+
71102
#[rstest]
72103
fn samples_taken(
73104
#[values(1, 2, 4)] channels: ChannelCount,
@@ -86,6 +117,7 @@ fn samples_taken(
86117

87118
let samples = test_buffer
88119
.take_duration(Duration::from_secs(seconds_to_take as u64))
120+
.with_fadeout(true)
89121
.count();
90122

91123
let seconds_we_can_take = seconds_to_take.min(seconds);

tests/take_span.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ mod test_support;
77
fn param_changes_during_skip() {
88
let source = TestSource::new()
99
.with_span(
10-
TestSpan::sample_indexes()
10+
TestSpan::sample_counter()
1111
.with_sample_rate(10)
1212
.with_channel_count(1)
1313
.with_sample_count(10),
1414
)
1515
.with_span(
16-
TestSpan::sample_indexes()
16+
TestSpan::sample_counter()
1717
.with_sample_rate(20)
1818
.with_channel_count(2)
1919
.with_sample_count(10),

tests/test_support/mod.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub enum SampleSource {
1818
Silence,
1919
List(Vec<f32>),
2020
SampleIndex,
21+
Ones,
2122
}
2223

2324
impl SampleSource {
@@ -29,6 +30,10 @@ impl SampleSource {
2930
channels: ChannelCount,
3031
numb_samples: usize,
3132
) -> Option<Sample> {
33+
if pos >= numb_samples {
34+
return None;
35+
}
36+
3237
match self {
3338
SampleSource::SignalGen {
3439
function,
@@ -41,12 +46,11 @@ impl SampleSource {
4146
.collect();
4247
samples.get(pos).copied()
4348
}
44-
SampleSource::SampleIndex if pos < numb_samples => Some(pos_in_source as f32),
45-
SampleSource::SampleIndex => None,
4649
SampleSource::SignalGen { samples, .. } => samples.get(pos).copied(),
47-
SampleSource::Silence { .. } if pos < numb_samples => Some(0.0),
48-
SampleSource::Silence { .. } => None,
4950
SampleSource::List(list) => list.get(pos).copied(),
51+
SampleSource::SampleIndex => Some(pos_in_source as f32),
52+
SampleSource::Silence { .. } => Some(0.0),
53+
SampleSource::Ones => Some(1.0),
5054
}
5155
}
5256
}
@@ -74,7 +78,14 @@ impl TestSpan {
7478
channels: 1,
7579
}
7680
}
77-
pub fn sample_indexes() -> TestSpanBuilder {
81+
pub fn ones() -> TestSpanBuilder {
82+
TestSpanBuilder {
83+
sample_source: SampleSource::Ones,
84+
sample_rate: 1,
85+
channels: 1,
86+
}
87+
}
88+
pub fn sample_counter() -> TestSpanBuilder {
7889
TestSpanBuilder {
7990
sample_source: SampleSource::SampleIndex,
8091
sample_rate: 1,

0 commit comments

Comments
 (0)