Skip to content

Commit 820f6bb

Browse files
committed
Add integer-samples feature and fix zero detection tests
- Add integer-samples feature to optionally use i16 instead of f32 samples - Fix unreliable zero detection in WAV and MP4 tests using Sample::is_zero() - Improve test reliability by checking !all(is_zero) instead of any(!=0.0) The new feature allows applications to choose between floating-point and fixed-point sample representation based on their needs, while maintaining f32 the default for best sound quality.
1 parent 48cc442 commit 820f6bb

File tree

13 files changed

+64
-28
lines changed

13 files changed

+64
-28
lines changed

Cargo.toml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,28 @@ default = ["playback", "flac", "vorbis", "wav", "mp3"]
3030
tracing = ["dep:tracing"]
3131
experimental = ["dep:atomic_float"]
3232
playback = ["dep:cpal"]
33+
integer-samples = []
3334

3435
flac = ["claxon"]
3536
vorbis = ["lewton"]
3637
wav = ["hound"]
3738
mp3 = ["symphonia-mp3"]
3839
minimp3 = ["dep:minimp3_fixed"]
40+
3941
noise = ["rand"]
42+
4043
wasm-bindgen = ["cpal/wasm-bindgen"]
4144
cpal-shared-stdcxx = ["cpal/oboe-shared-stdcxx"]
45+
4246
symphonia-aac = ["symphonia/aac"]
43-
symphonia-all = ["symphonia-aac", "symphonia-flac", "symphonia-isomp4", "symphonia-mp3", "symphonia-vorbis", "symphonia-wav"]
47+
symphonia-all = [
48+
"symphonia-aac",
49+
"symphonia-flac",
50+
"symphonia-isomp4",
51+
"symphonia-mp3",
52+
"symphonia-vorbis",
53+
"symphonia-wav",
54+
]
4455
symphonia-flac = ["symphonia/flac"]
4556
symphonia-isomp4 = ["symphonia/isomp4"]
4657
symphonia-mp3 = ["symphonia/mp3"]

benches/shared.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::io::Cursor;
22
use std::time::Duration;
33
use std::vec;
44

5+
use dasp_sample::Sample;
56
use rodio::{ChannelCount, SampleRate, Source};
67

78
pub struct TestSource<T> {
@@ -57,7 +58,11 @@ impl TestSource<f32> {
5758
channels: sound.channels(),
5859
sample_rate: sound.sample_rate(),
5960
total_duration: duration,
60-
samples: sound.into_iter().collect::<Vec<_>>().into_iter(),
61+
samples: sound
62+
.into_iter()
63+
.map(|s| s.to_sample())
64+
.collect::<Vec<_>>()
65+
.into_iter(),
6166
}
6267
}
6368

@@ -70,7 +75,7 @@ impl TestSource<f32> {
7075
total_duration,
7176
} = self;
7277
let samples = samples
73-
.map(|s| dasp_sample::Sample::from_sample(s))
78+
.map(|s| s.to_sample())
7479
.collect::<Vec<_>>()
7580
.into_iter();
7681
TestSource {

src/conversions/sample.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ where
7272
/// You can implement this trait on your own type as well if you wish so.
7373
///
7474
pub trait Sample: DaspSample + ToSample<f32> {
75+
/// The value corresponding to the absence of sound.
76+
const ZERO_VALUE: Self = DaspSample::EQUILIBRIUM;
77+
7578
/// Linear interpolation between two samples.
7679
///
7780
/// The result should be equivalent to
@@ -93,9 +96,9 @@ pub trait Sample: DaspSample + ToSample<f32> {
9396
/// Calls `saturating_add` on the sample.
9497
fn saturating_add(self, other: Self) -> Self;
9598

96-
/// Returns the value corresponding to the absence of sound.
97-
fn zero_value() -> Self {
98-
Self::EQUILIBRIUM
99+
/// Returns true if the sample is the zero value.
100+
fn is_zero(self) -> bool {
101+
self == Self::ZERO_VALUE
99102
}
100103
}
101104

src/decoder/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ mod vorbis;
3131
#[cfg(all(feature = "wav", not(feature = "symphonia-wav")))]
3232
mod wav;
3333

34+
#[cfg(feature = "integer-samples")]
35+
/// Output format of the decoders.
36+
pub type DecoderSample = i16;
37+
#[cfg(not(feature = "integer-samples"))]
3438
/// Output format of the decoders.
3539
pub type DecoderSample = f32;
3640

src/lib.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,11 +133,21 @@
133133
//! The "tracing" feature replaces the print to stderr when a stream error happens with a
134134
//! recording an error event with tracing.
135135
//!
136-
//! ### Feature "Noise"
136+
//! ### Feature "noise"
137137
//!
138138
//! The "noise" feature adds support for white and pink noise sources. This feature requires the
139139
//! "rand" crate.
140140
//!
141+
//! ### Feature "playback"
142+
//!
143+
//! The "playback" feature adds support for playing audio. This feature requires the "cpal" crate.
144+
//!
145+
//! ### Feature "integer-samples"
146+
//!
147+
//! The "integer-samples" changes the output format of the decoders to use `i16` instead of `f32`.
148+
//! This is useful if you want to decode audio on an exotic, low-spec or old device that does not
149+
//! have hardware support for floating-point operations.
150+
//!
141151
//! ## How it works under the hood
142152
//!
143153
//! Rodio spawns a background thread that is dedicated to reading from the sources and sending

src/mixer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ where
197197
}
198198

199199
fn sum_current_sources(&mut self) -> S {
200-
let mut sum = S::zero_value();
200+
let mut sum = S::ZERO_VALUE;
201201

202202
for mut source in self.current_sources.drain(..) {
203203
if let Some(value) = source.next() {

src/source/channel_volume.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,7 @@ where
3636
let mut sample = None;
3737
for _ in 0..input.channels() {
3838
if let Some(s) = input.next() {
39-
sample = Some(
40-
sample
41-
.get_or_insert_with(I::Item::zero_value)
42-
.saturating_add(s),
43-
);
39+
sample = Some(sample.get_or_insert(I::Item::ZERO_VALUE).saturating_add(s));
4440
}
4541
}
4642
ChannelVolume {
@@ -96,7 +92,7 @@ where
9692
if let Some(s) = self.input.next() {
9793
self.current_sample = Some(
9894
self.current_sample
99-
.get_or_insert_with(I::Item::zero_value)
95+
.get_or_insert(I::Item::ZERO_VALUE)
10096
.saturating_add(s),
10197
);
10298
}

src/source/delay.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ where
7070
fn next(&mut self) -> Option<<I as Iterator>::Item> {
7171
if self.remaining_samples >= 1 {
7272
self.remaining_samples -= 1;
73-
Some(Sample::zero_value())
73+
Some(Sample::ZERO_VALUE)
7474
} else {
7575
self.input.next()
7676
}

src/source/pausable.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,12 @@ where
8282
fn next(&mut self) -> Option<I::Item> {
8383
if self.remaining_paused_samples > 0 {
8484
self.remaining_paused_samples -= 1;
85-
return Some(I::Item::zero_value());
85+
return Some(I::Item::ZERO_VALUE);
8686
}
8787

8888
if let Some(paused_channels) = self.paused_channels {
8989
self.remaining_paused_samples = paused_channels - 1;
90-
return Some(I::Item::zero_value());
90+
return Some(I::Item::ZERO_VALUE);
9191
}
9292

9393
self.input.next()

src/source/zero.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,12 @@ where
5454
if let Some(num_samples) = self.num_samples {
5555
if num_samples > 0 {
5656
self.num_samples = Some(num_samples - 1);
57-
Some(S::zero_value())
57+
Some(S::ZERO_VALUE)
5858
} else {
5959
None
6060
}
6161
} else {
62-
Some(S::zero_value())
62+
Some(S::ZERO_VALUE)
6363
}
6464
}
6565
}

0 commit comments

Comments
 (0)