Skip to content

Commit

Permalink
Merge branch 'RustAudio:master' into Issue#566
Browse files Browse the repository at this point in the history
  • Loading branch information
ugochukwu-850 authored Oct 7, 2024
2 parents 20daf24 + aee7ffb commit 04aaec1
Show file tree
Hide file tree
Showing 17 changed files with 1,291 additions and 25 deletions.
22 changes: 16 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Added
- Support for *ALAC/AIFF*
- New sources:
- Support for *ALAC/AIFF*
- Add `automatic_gain_control` source for dynamic audio level adjustment.
- New test signal generator sources:
- `SignalGenerator` source generates a sine, triangle, square wave or sawtooth
of a given frequency and sample rate.
- `Chirp` source generates a sine wave with a linearly-increasing
frequency over a given frequency range and duration.
- `white` and `pink` generate white or pink noise, respectively. These
sources depend on the `rand` crate and are guarded with the "noise"
feature.
- Documentation for the "noise" feature has been added to `lib.rs`.
- New Fade and Crossfade 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`
`fade_in` has been refactored to use the `linear_gain_ramp`
implementation.

### Fixed
- `Sink.try_seek` now updates `controls.position` before returning. Calls to `Sink.get_pos`
done immediately after a seek will now return the correct value.
done immediately after a seek will now return the correct value.

### Changed
- `SamplesBuffer` is now `Clone`
Expand All @@ -44,15 +54,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `Source` trait is now also implemented for `Box<dyn Source>` and `&mut Source`
- `fn new_vorbis` is now also available when the `symphonia-vorbis` feature is enabled

### Added
### Added
- Adds a new method `try_seek` to all sources. It returns either an error or
seeks to the given position. A few sources are "unsupported" they return the
error `Unsupported`.
- Adds `SpatialSink::clear()` bringing it in line with `Sink`

### Fixed
- channel upscaling now follows the 'WAVEFORMATEXTENSIBLE' format and no longer
repeats the last source channel on all extra output channels.
repeats the last source channel on all extra output channels.
Stereo content playing on a 5.1 speaker set will now only use the front left
and front right speaker instead of repeating the right sample on all speakers
except the front left one.
Expand Down
9 changes: 9 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,22 @@ 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 }

atomic_float = { version = "1.1.0", optional = true }

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

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"]
Expand Down Expand Up @@ -61,3 +66,7 @@ harness = false
[[example]]
name = "music_m4a"
required-features = ["symphonia-isomp4", "symphonia-aac"]

[[example]]
name = "noise_generator"
required-features = ["noise"]
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ See [the docs](https://docs.rs/rodio/latest/rodio/#alternative-decoder-backends)

[The documentation](http://docs.rs/rodio) contains an introduction to the library.

## Dependencies(Linux only)

Rodio uses `cpal` to send audio to the OS for playback. On Linux `cpal` needs the ALSA development files. These are provided as part of the libasound2-dev package on Debian and Ubuntu distributions and alsa-lib-devel on Fedora.

## License
[License]: #license

Expand Down
39 changes: 39 additions & 0 deletions benches/effects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,42 @@ fn amplify(bencher: Bencher) {
.with_inputs(|| TestSource::music_wav().to_f32s())
.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)
})
}

#[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
);

// 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)
})
}
41 changes: 41 additions & 0 deletions examples/noise_generator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//! Noise generator example. Use the "noise" feature to enable the noise generator sources.

#[cfg(feature = "noise")]
fn main() {
use rodio::source::{pink, white, Source};
use std::thread;
use std::time::Duration;

let (_stream, stream_handle) = rodio::OutputStream::try_default().unwrap();

let noise_duration = Duration::from_millis(1000);
let interval_duration = Duration::from_millis(1500);

stream_handle
.play_raw(
white(cpal::SampleRate(48000))
.amplify(0.1)
.take_duration(noise_duration),
)
.unwrap();
println!("Playing white noise");

thread::sleep(interval_duration);

stream_handle
.play_raw(
pink(cpal::SampleRate(48000))
.amplify(0.1)
.take_duration(noise_duration),
)
.unwrap();
println!("Playing pink noise");

thread::sleep(interval_duration);
}

#[cfg(not(feature = "noise"))]
fn main() {
println!("rodio has not been compiled with noise sources, use `--features noise` to enable this feature.");
println!("Exiting...");
}
83 changes: 83 additions & 0 deletions examples/signal_generator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//! Test signal generator example.

fn main() {
use rodio::source::{chirp, Function, SignalGenerator, Source};
use std::thread;
use std::time::Duration;

let (_stream, stream_handle) = rodio::OutputStream::try_default().unwrap();

let test_signal_duration = Duration::from_millis(1000);
let interval_duration = Duration::from_millis(1500);

println!("Playing 1000 Hz tone");
stream_handle
.play_raw(
SignalGenerator::new(cpal::SampleRate(48000), 1000.0, Function::Sine)
.amplify(0.1)
.take_duration(test_signal_duration),
)
.unwrap();

thread::sleep(interval_duration);

println!("Playing 10,000 Hz tone");
stream_handle
.play_raw(
SignalGenerator::new(cpal::SampleRate(48000), 10000.0, Function::Sine)
.amplify(0.1)
.take_duration(test_signal_duration),
)
.unwrap();

thread::sleep(interval_duration);

println!("Playing 440 Hz Triangle Wave");
stream_handle
.play_raw(
SignalGenerator::new(cpal::SampleRate(48000), 440.0, Function::Triangle)
.amplify(0.1)
.take_duration(test_signal_duration),
)
.unwrap();

thread::sleep(interval_duration);

println!("Playing 440 Hz Sawtooth Wave");
stream_handle
.play_raw(
SignalGenerator::new(cpal::SampleRate(48000), 440.0, Function::Sawtooth)
.amplify(0.1)
.take_duration(test_signal_duration),
)
.unwrap();

thread::sleep(interval_duration);

println!("Playing 440 Hz Square Wave");
stream_handle
.play_raw(
SignalGenerator::new(cpal::SampleRate(48000), 440.0, Function::Square)
.amplify(0.1)
.take_duration(test_signal_duration),
)
.unwrap();

thread::sleep(interval_duration);

println!("Playing 20-10000 Hz Sweep");
stream_handle
.play_raw(
chirp(
cpal::SampleRate(48000),
20.0,
10000.0,
Duration::from_secs(1),
)
.amplify(0.1)
.take_duration(test_signal_duration),
)
.unwrap();

thread::sleep(interval_duration);
}
48 changes: 48 additions & 0 deletions outreach/v0.20_announcement_and_call_for_userstories.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<!---
When 0.20.0 is released this announcement will be posted to r/rust and the
rust user forum. Also post in rust audio discord: https://discord.gg/8qW6q2k
-->

# Announcing rodio 0.20 and call for help


Rodio is an audio playback library. It can decode audio files, synthesize new
sounds, apply effects to sounds & mix them. Rodio has been part of the Rust
ecosystem for 9 years now! 🎉.

## New release
The rodio contributors have made many improvements in the last 5 months. Rodio can now:

- Seek back and forth through sound efficiently
- Track the playback position at sample accuracy!
- Generate more signals such as chirps, white & pink noise and different
wavesforms
- Automatically adjust the gain to limit the peak volume and change in loudness

This is ignoring the many fixes and smaller additions made by the many
contributors who helped out expand rodio.

## Call for help

In its 9 years of existence Rust has changed a lot. Further more Rodio is being
used for applications beyond its original scope. To improve rodio we believe its
time for larger (breaking) changes.

### User feedback
To ensure we make the right changes we want
to know what rodio is being used for and what you all would like to use it for.

We can use any input you have but are especially looking for users who are:
- using rodio and feel some part of the API is hard to use.
- have experienced footguns/pain point
- wanted to use rodio but could not make it fit their use-case (excluding complex
game audio (best served by [kira](https://crates.io/crates/kira)) and advanced
dsp). If you disagree and think rodio can server those excluded use-case too
let us know!

The best way to leave your feedback is a short user story on our issue
[tracker](https://github.com/RustAudio/rodio/issues). If that is not your thing
any other form posted there works too!

### Architecture & API
We can use input on our planned [changes](https://github.com/RustAudio/rodio/issues/614) and how to best implement them.
21 changes: 21 additions & 0 deletions src/conversions/sample.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ pub trait Sample: CpalSample {
/// Multiplies the value of this sample by the given amount.
fn amplify(self, value: f32) -> Self;

/// Converts the sample to an f32 value.
fn to_f32(self) -> f32;

/// Calls `saturating_add` on the sample.
fn saturating_add(self, other: Self) -> Self;

Expand All @@ -102,6 +105,12 @@ impl Sample for u16 {
((self as f32) * value) as u16
}

#[inline]
fn to_f32(self) -> f32 {
// Convert u16 to f32 in the range [-1.0, 1.0]
(self as f32 - 32768.0) / 32768.0
}

#[inline]
fn saturating_add(self, other: u16) -> u16 {
self.saturating_add(other)
Expand All @@ -125,6 +134,12 @@ impl Sample for i16 {
((self as f32) * value) as i16
}

#[inline]
fn to_f32(self) -> f32 {
// Convert i16 to f32 in the range [-1.0, 1.0]
self as f32 / 32768.0
}

#[inline]
fn saturating_add(self, other: i16) -> i16 {
self.saturating_add(other)
Expand All @@ -147,6 +162,12 @@ impl Sample for f32 {
self * value
}

#[inline]
fn to_f32(self) -> f32 {
// f32 is already in the correct format
self
}

#[inline]
fn saturating_add(self, other: f32) -> f32 {
self + other
Expand Down
7 changes: 6 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@
//! The "tracing" feature replaces the print to stderr when a stream error happens with a
//! recording an error event with tracing.
//!
//! ### Feature "Noise"
//!
//! The "noise" feature adds support for white and pink noise sources. This feature requires the
//! "rand" crate.
//!
//! ## How it works under the hood
//!
//! Rodio spawns a background thread that is dedicated to reading from the sources and sending
Expand All @@ -120,7 +125,7 @@
//! hardware. Therefore there is no restriction on the number of sounds that play simultaneously or
//! the number of sinks that can be created (except for the fact that creating too many will slow
//! down your program).
//!

#![cfg_attr(test, deny(missing_docs))]
pub use cpal::{
self, traits::DeviceTrait, Device, Devices, DevicesError, InputDevices, OutputDevices,
Expand Down
4 changes: 2 additions & 2 deletions src/sink.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,10 +223,10 @@ impl Sink {
///
/// # Errors
/// This function will return [`SeekError::NotSupported`] if one of the underlying
/// sources does not support seeking.
/// sources does not support seeking.
///
/// It will return an error if an implementation ran
/// into one during the seek.
/// into one during the seek.
///
/// When seeking beyond the end of a source this
/// function might return an error if the duration of the source is not known.
Expand Down
Loading

0 comments on commit 04aaec1

Please sign in to comment.