Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/RustAudio/rodio into synt…
Browse files Browse the repository at this point in the history
…h-waveforms
  • Loading branch information
iluvcapra committed Sep 29, 2024
2 parents 56fcce7 + 2e5fc2e commit 09c5f31
Show file tree
Hide file tree
Showing 26 changed files with 299 additions and 54 deletions.
13 changes: 13 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@ lewton = { version = "0.10", optional = true }
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"
rand = { version = "0.8.5", features = ["small_rng"], optional = true }
tracing = { version = "0.1.40", optional = true }

[features]
default = ["flac", "vorbis", "wav", "mp3"]
tracing = ["dep:tracing"]

flac = ["claxon"]
vorbis = ["lewton"]
Expand All @@ -46,6 +49,16 @@ quickcheck = "0.9.2"
rstest = "0.18.2"
rstest_reuse = "0.6.0"
approx = "0.5.1"
dasp_sample = "0.11.0"
divan = "0.1.14"

[[bench]]
name = "effects"
harness = false

[[bench]]
name = "conversions"
harness = false

[[example]]
name = "music_m4a"
Expand Down
21 changes: 21 additions & 0 deletions benches/conversions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use cpal::FromSample;
use divan::Bencher;
use rodio::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) {
bencher
.with_inputs(|| TestSource::music_wav())
.bench_values(|source| {
source
.convert_samples::<T>()
.for_each(divan::black_box_drop)
})
}
48 changes: 48 additions & 0 deletions benches/effects.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use std::time::Duration;

use divan::Bencher;
use rodio::Source;

mod shared;
use shared::TestSource;

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)
})
}

#[divan::bench]
fn high_pass(bencher: Bencher) {
bencher
.with_inputs(|| TestSource::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)
})
}

#[divan::bench]
fn amplify(bencher: Bencher) {
bencher
.with_inputs(|| TestSource::music_wav().to_f32s())
.bench_values(|source| source.amplify(0.8).for_each(divan::black_box_drop))
}
83 changes: 83 additions & 0 deletions benches/shared.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use std::io::Cursor;
use std::time::Duration;
use std::vec;

use rodio::Source;

pub struct TestSource<T> {
samples: vec::IntoIter<T>,
channels: u16,
sample_rate: u32,
total_duration: Duration,
}

impl<T> Iterator for TestSource<T> {
type Item = T;

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

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

impl<T: rodio::Sample> Source for TestSource<T> {
fn current_frame_len(&self) -> Option<usize> {
None // forever
}

fn channels(&self) -> u16 {
self.channels
}

fn sample_rate(&self) -> u32 {
self.sample_rate
}

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 {
samples,
channels,
sample_rate,
total_duration,
} = self;
let samples = samples
.map(|s| cpal::Sample::from_sample(s))
.collect::<Vec<_>>()
.into_iter();
TestSource {
samples,
channels,
sample_rate,
total_duration,
}
}
}
12 changes: 11 additions & 1 deletion src/decoder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ mod mp3;
#[cfg(feature = "symphonia")]
mod read_seek_source;
#[cfg(feature = "symphonia")]
/// Symphonia decoders types
pub mod symphonia;
#[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))]
mod vorbis;
Expand All @@ -36,11 +37,15 @@ pub struct Decoder<R>(DecoderImpl<R>)
where
R: Read + Seek;

/// Source of audio samples from decoding a file that never ends. When the
/// end of the file is reached the decoder starts again from the beginning.
///
/// Supports MP3, WAV, Vorbis and Flac.
pub struct LoopedDecoder<R>(DecoderImpl<R>)
where
R: Read + Seek;

// can not really reduce the size of the VorbisDecoder. There are not any
// Cannot really reduce the size of the VorbisDecoder. There are not any
// arrays just a lot of struct fields.
#[allow(clippy::large_enum_variant)]
enum DecoderImpl<R>
Expand Down Expand Up @@ -239,6 +244,10 @@ where
#[cfg(not(feature = "symphonia"))]
Err(DecoderError::UnrecognizedFormat)
}

/// Builds a new looped decoder.
///
/// Attempts to automatically detect the format of the source of data.
pub fn new_looped(data: R) -> Result<LoopedDecoder<R>, DecoderError> {
Self::new(data).map(LoopedDecoder::new)
}
Expand Down Expand Up @@ -329,6 +338,7 @@ where
}
}

#[allow(missing_docs)] // Reason: will be removed, see: #612
#[derive(Debug)]
pub enum Mp4Type {
Mp4,
Expand Down
7 changes: 6 additions & 1 deletion src/decoder/symphonia.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,20 +208,25 @@ impl Source for SymphoniaDecoder {
}
}

/// Error returned when the try_seek implementation of the symphonia decoder fails.
#[derive(Debug, thiserror::Error)]
pub enum SeekError {
/// Could not get next packet while refining seek position
#[error("Could not get next packet while refining seek position: {0:?}")]
Refining(symphonia::core::errors::Error),
/// Format reader failed to seek
#[error("Format reader failed to seek: {0:?}")]
BaseSeek(symphonia::core::errors::Error),
/// Decoding failed retrying on the next packet failed
#[error("Decoding failed retrying on the next packet failed: {0:?}")]
Retrying(symphonia::core::errors::Error),
/// Decoding failed on multiple consecutive packets
#[error("Decoding failed on multiple consecutive packets: {0:?}")]
Decoding(symphonia::core::errors::Error),
}

impl SymphoniaDecoder {
/// note frame offset must be set after
/// Note frame offset must be set after
fn refine_position(&mut self, seek_res: SeekedTo) -> Result<(), source::SeekError> {
let mut samples_to_pass = seek_res.required_ts - seek_res.actual_ts;
let packet = loop {
Expand Down
9 changes: 9 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,15 @@
//! to avoid adding extra crates to your binary.
//! See the [available feature flags](https://docs.rs/crate/rodio/latest/features) for all options.
//!
//! ## Optional Features
//!
//! Rodio provides several optional features that are guarded with feature gates.
//!
//! ### Feature "tracing"
//!
//! The "tracing" feature replaces the print to stderr when a stream error happens with a
//! recording an error event with tracing.
//!
//! ## How it works under the hood
//!
//! Rodio spawns a background thread that is dedicated to reading from the sources and sending
Expand Down
2 changes: 1 addition & 1 deletion src/sink.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ impl Sink {
.periodic_access(Duration::from_millis(5), move |src| {
if controls.stopped.load(Ordering::SeqCst) {
src.stop();
*controls.position.lock().unwrap() = Duration::ZERO;
*controls.position.lock().unwrap() = Duration::ZERO;
}
{
let mut to_clear = controls.to_clear.lock().unwrap();
Expand Down
1 change: 1 addition & 0 deletions src/source/blt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ where
}
}

/// This applies an audio filter, it can be a high or low pass filter.
#[derive(Clone, Debug)]
pub struct BltFilter<I> {
input: I,
Expand Down
8 changes: 5 additions & 3 deletions src/source/channel_volume.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ where
I: Source,
I::Item: Sample,
{
/// Wrap the input source and make it mono. Play that mono sound to each
/// channel at the volume set by the user. The volume can be changed using
/// [`ChannelVolume::set_volume`].
pub fn new(mut input: I, channel_volumes: Vec<f32>) -> ChannelVolume<I>
where
I: Source,
Expand All @@ -48,8 +51,8 @@ where
}
}

/// Sets the volume for a given channel number. Will panic if channel number
/// was invalid.
/// Sets the volume for a given channel number. Will panic if channel number
/// is invalid.
pub fn set_volume(&mut self, channel: usize, volume: f32) {
self.channel_volumes[channel] = volume;
}
Expand Down Expand Up @@ -82,7 +85,6 @@ where

#[inline]
fn next(&mut self) -> Option<I::Item> {
// return value
let ret = self
.current_sample
.map(|sample| sample.amplify(self.channel_volumes[self.current_channel]));
Expand Down
5 changes: 5 additions & 0 deletions src/source/crossfade.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ where
input_fadeout.mix(input_fadein)
}

/// 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
/// covered.
pub type Crossfade<I1, I2> = Mix<TakeDuration<I1>, FadeIn<TakeDuration<I2>>>;

#[cfg(test)]
Expand Down
4 changes: 3 additions & 1 deletion src/source/done.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{Sample, Source};

use super::SeekError;

/// When the inner source is empty this decrements an `AtomicUsize`.
/// When the inner source is empty this decrements a `AtomicUsize`.
#[derive(Debug, Clone)]
pub struct Done<I> {
input: I,
Expand All @@ -15,6 +15,8 @@ pub struct Done<I> {
}

impl<I> Done<I> {
/// When the inner source is empty the AtomicUsize passed in is decremented.
/// If it was zero it will overflow negatively.
#[inline]
pub fn new(input: I, signal: Arc<AtomicUsize>) -> Done<I> {
Done {
Expand Down
2 changes: 2 additions & 0 deletions src/source/empty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ impl<S> Default for Empty<S> {
}

impl<S> Empty<S> {
/// An empty source that immediately ends without ever returning a sample to
/// play
#[inline]
pub fn new() -> Empty<S> {
Empty(PhantomData)
Expand Down
6 changes: 6 additions & 0 deletions src/source/empty_callback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@ use super::SeekError;

/// An empty source which executes a callback function
pub struct EmptyCallback<S> {
#[allow(missing_docs)] // See: https://github.com/RustAudio/rodio/issues/615
pub phantom_data: PhantomData<S>,
#[allow(missing_docs)] // See: https://github.com/RustAudio/rodio/issues/615
pub callback: Box<dyn Send + Fn()>,
}

impl<S> EmptyCallback<S> {
#[inline]
/// Create an empty source which executes a callback function.
/// Example use-case:
///
/// Detect and do something when the source before this one has ended.
pub fn new(callback: Box<dyn Send + Fn()>) -> EmptyCallback<S> {
EmptyCallback {
phantom_data: PhantomData,
Expand Down
Loading

0 comments on commit 09c5f31

Please sign in to comment.