Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
`GeneratorFunction`.
- Minimal builds without `cpal` audio output are now supported.
See `README.md` for instructions. (#349)
- Added `Sample::is_zero()` method for checking zero samples.

### Changed
- Breaking: `OutputStreamBuilder` should now be used to initialize an audio output stream.
Expand All @@ -26,6 +27,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Breaking: `Sink::try_new` renamed to `connect_new` and does not return error anymore.
`Sink::new_idle` was renamed to `new`.
- Breaking: In the `Source` trait, the method `current_frame_len()` was renamed to `current_span_len()`.
- Breaking: `Decoder` now outputs `f32` samples by default instead of `i16`.
Enable the `integer-decoder` to revert to `i16` samples.
- The term 'frame' was renamed to 'span' in the crate and documentation.

### Fixed
Expand All @@ -34,6 +37,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Symphonia decoder `total_duration` incorrect value caused by conversion from `Time` to `Duration`.
- An issue with `SignalGenerator` that caused it to create increasingly distorted waveforms
over long run times has been corrected. (#201)
- WAV and FLAC decoder duration calculation now calculated once and handles very large files
correctly
- Removed unwrap() calls in MP3, WAV, FLAC and Vorbis format detection for better error handling

### Deprecated
- Deprecated `Sample::zero_value()` function in favor of `Sample::ZERO_VALUE` constant

# Version 0.20.1 (2024-11-08)

Expand Down
13 changes: 12 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,28 @@ default = ["playback", "flac", "vorbis", "wav", "mp3"]
tracing = ["dep:tracing"]
experimental = ["dep:atomic_float"]
playback = ["dep:cpal"]
integer-decoder = []

flac = ["claxon"]
vorbis = ["lewton"]
wav = ["hound"]
mp3 = ["symphonia-mp3"]
minimp3 = ["dep:minimp3_fixed"]

noise = ["rand"]

wasm-bindgen = ["cpal/wasm-bindgen"]
cpal-shared-stdcxx = ["cpal/oboe-shared-stdcxx"]

symphonia-aac = ["symphonia/aac"]
symphonia-all = ["symphonia-aac", "symphonia-flac", "symphonia-isomp4", "symphonia-mp3", "symphonia-vorbis", "symphonia-wav"]
symphonia-all = [
"symphonia-aac",
"symphonia-flac",
"symphonia-isomp4",
"symphonia-mp3",
"symphonia-vorbis",
"symphonia-wav",
]
symphonia-flac = ["symphonia/flac"]
symphonia-isomp4 = ["symphonia/isomp4"]
symphonia-mp3 = ["symphonia/mp3"]
Expand Down
7 changes: 3 additions & 4 deletions benches/conversions.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
use dasp_sample::FromSample;
use divan::Bencher;
use rodio::Source;
use rodio::{decoder::DecoderSample, Source};

mod shared;
use shared::TestSource;

fn main() {
divan::main();
}

#[divan::bench(types = [i16, u16, f32])]
fn from_i16_to<T: rodio::Sample + FromSample<i16>>(bencher: Bencher) {
fn from_sample_to<T: rodio::Sample + FromSample<DecoderSample>>(bencher: Bencher) {
bencher
.with_inputs(|| TestSource::music_wav())
.with_inputs(|| shared::music_wav())
.bench_values(|source| {
source
.convert_samples::<T>()
Expand Down
84 changes: 38 additions & 46 deletions benches/effects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,84 +4,76 @@ use divan::Bencher;
use rodio::Source;

mod shared;
use shared::TestSource;
use shared::music_wav;

fn main() {
divan::main();
}

#[divan::bench]
fn reverb(bencher: Bencher) {
bencher
.with_inputs(|| TestSource::music_wav())
.bench_values(|source| {
source
.buffered()
.reverb(Duration::from_secs_f32(0.05), 0.3)
.for_each(divan::black_box_drop)
})
bencher.with_inputs(|| music_wav()).bench_values(|source| {
source
.buffered()
.reverb(Duration::from_secs_f32(0.05), 0.3)
.for_each(divan::black_box_drop)
})
}

#[divan::bench]
fn high_pass(bencher: Bencher) {
bencher
.with_inputs(|| TestSource::music_wav().to_f32s())
.with_inputs(|| music_wav().to_f32s())
.bench_values(|source| source.high_pass(200).for_each(divan::black_box_drop))
}

#[divan::bench]
fn fade_out(bencher: Bencher) {
bencher
.with_inputs(|| TestSource::music_wav())
.bench_values(|source| {
source
.fade_out(Duration::from_secs(5))
.for_each(divan::black_box_drop)
})
bencher.with_inputs(|| music_wav()).bench_values(|source| {
source
.fade_out(Duration::from_secs(5))
.for_each(divan::black_box_drop)
})
}

#[divan::bench]
fn amplify(bencher: Bencher) {
bencher
.with_inputs(|| TestSource::music_wav().to_f32s())
.with_inputs(|| music_wav())
.bench_values(|source| source.amplify(0.8).for_each(divan::black_box_drop))
}

#[divan::bench]
fn agc_enabled(bencher: Bencher) {
bencher
.with_inputs(|| TestSource::music_wav().to_f32s())
.bench_values(|source| {
source
.automatic_gain_control(
1.0, // target_level
4.0, // attack_time (in seconds)
0.005, // release_time (in seconds)
5.0, // absolute_max_gain
)
.for_each(divan::black_box_drop)
})
bencher.with_inputs(|| music_wav()).bench_values(|source| {
source
.automatic_gain_control(
1.0, // target_level
4.0, // attack_time (in seconds)
0.005, // release_time (in seconds)
5.0, // absolute_max_gain
)
.for_each(divan::black_box_drop)
})
}

#[cfg(feature = "experimental")]
#[divan::bench]
fn agc_disabled(bencher: Bencher) {
bencher
.with_inputs(|| TestSource::music_wav().to_f32s())
.bench_values(|source| {
// Create the AGC source
let amplified_source = source.automatic_gain_control(
1.0, // target_level
4.0, // attack_time (in seconds)
0.005, // release_time (in seconds)
5.0, // absolute_max_gain
);
bencher.with_inputs(|| music_wav()).bench_values(|source| {
// Create the AGC source
let amplified_source = source.automatic_gain_control(
1.0, // target_level
4.0, // attack_time (in seconds)
0.005, // release_time (in seconds)
5.0, // absolute_max_gain
);

// Get the control handle and disable AGC
let agc_control = amplified_source.get_agc_control();
agc_control.store(false, std::sync::atomic::Ordering::Relaxed);
// Get the control handle and disable AGC
let agc_control = amplified_source.get_agc_control();
agc_control.store(false, std::sync::atomic::Ordering::Relaxed);

// Process the audio stream with AGC disabled
amplified_source.for_each(divan::black_box_drop)
})
// Process the audio stream with AGC disabled
amplified_source.for_each(divan::black_box_drop)
})
}
12 changes: 7 additions & 5 deletions benches/resampler.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use divan::Bencher;
use rodio::decoder::DecoderSample;
use rodio::source::UniformSourceIterator;

mod shared;
use shared::music_wav;

use rodio::Source;
use shared::TestSource;

fn main() {
divan::main();
Expand All @@ -13,11 +15,11 @@ fn main() {
fn no_resampling(bencher: Bencher) {
bencher
.with_inputs(|| {
let source = TestSource::<i16>::music_wav();
let source = music_wav();
(source.channels(), source.sample_rate(), source)
})
.bench_values(|(channels, sample_rate, source)| {
UniformSourceIterator::<_, i16>::new(source, channels, sample_rate)
UniformSourceIterator::<_, DecoderSample>::new(source, channels, sample_rate)
.for_each(divan::black_box_drop)
})
}
Expand All @@ -32,11 +34,11 @@ const COMMON_SAMPLE_RATES: [u32; 12] = [
fn resample_to(bencher: Bencher, target_sample_rate: u32) {
bencher
.with_inputs(|| {
let source = TestSource::<i16>::music_wav();
let source = music_wav();
(source.channels(), source)
})
.bench_values(|(channels, source)| {
UniformSourceIterator::<_, i16>::new(source, channels, target_sample_rate)
UniformSourceIterator::<_, DecoderSample>::new(source, channels, target_sample_rate)
.for_each(divan::black_box_drop)
})
}
56 changes: 33 additions & 23 deletions benches/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::io::Cursor;
use std::time::Duration;
use std::vec;

use rodio::{ChannelCount, SampleRate, Source};
use rodio::{decoder::DecoderSample, ChannelCount, Sample, SampleRate, Source};

pub struct TestSource<T> {
samples: vec::IntoIter<T>,
Expand All @@ -14,53 +14,42 @@ pub struct TestSource<T> {
impl<T> Iterator for TestSource<T> {
type Item = T;

#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.samples.next()
}
}

impl<T> ExactSizeIterator for TestSource<T> {
#[inline]
fn len(&self) -> usize {
self.samples.len()
}
}

impl<T: rodio::Sample> Source for TestSource<T> {
impl<T: Sample> Source for TestSource<T> {
#[inline]
fn current_span_len(&self) -> Option<usize> {
None // forever
}

#[inline]
fn channels(&self) -> ChannelCount {
self.channels
}

#[inline]
fn sample_rate(&self) -> SampleRate {
self.sample_rate
}

#[inline]
fn total_duration(&self) -> Option<Duration> {
Some(self.total_duration)
}
}

impl TestSource<i16> {
pub fn music_wav() -> Self {
let data = include_bytes!("../assets/music.wav");
let cursor = Cursor::new(data);

let duration = Duration::from_secs(10);
let sound = rodio::Decoder::new(cursor)
.expect("music.wav is correctly encoded & wav is supported")
.take_duration(duration);

TestSource {
channels: sound.channels(),
sample_rate: sound.sample_rate(),
total_duration: duration,
samples: sound.into_iter().collect::<Vec<_>>().into_iter(),
}
}

#[allow(unused, reason = "not everything from shared is used in all libs")]
pub fn to_f32s(self) -> TestSource<f32> {
let TestSource {
Expand All @@ -69,10 +58,7 @@ impl TestSource<i16> {
sample_rate,
total_duration,
} = self;
let samples = samples
.map(|s| dasp_sample::Sample::from_sample(s))
.collect::<Vec<_>>()
.into_iter();
let samples = samples.map(|s| s.to_f32()).collect::<Vec<_>>().into_iter();
TestSource {
samples,
channels,
Expand All @@ -81,3 +67,27 @@ impl TestSource<i16> {
}
}
}

impl TestSource<f32> {
#[allow(unused, reason = "not everything from shared is used in all libs")]
pub fn to_f32s(self) -> TestSource<f32> {
self
}
}

pub fn music_wav() -> TestSource<DecoderSample> {
let data = include_bytes!("../assets/music.wav");
let cursor = Cursor::new(data);

let duration = Duration::from_secs(10);
let sound = rodio::Decoder::new(cursor)
.expect("music.wav is correctly encoded & wav is supported")
.take_duration(duration);

TestSource {
channels: sound.channels(),
sample_rate: sound.sample_rate(),
total_duration: duration,
samples: sound.into_iter().collect::<Vec<_>>().into_iter(),
}
}
4 changes: 1 addition & 3 deletions src/conversions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,5 @@ pub use self::sample::Sample;
pub use self::sample_rate::SampleRateConverter;

mod channels;
// TODO: < shouldn't be public ; there's a bug in Rust 1.4 and below that makes This
// `pub` mandatory
pub mod sample;
mod sample;
mod sample_rate;
Loading
Loading