Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/RustAudio/rodio into feat…
Browse files Browse the repository at this point in the history
…ure-channelbitmask
  • Loading branch information
iluvcapra committed Jan 1, 2025
2 parents e1d822f + ea45c17 commit 3c8e5b0
Show file tree
Hide file tree
Showing 71 changed files with 785 additions and 640 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ jobs:
# `cargo test` does not check benchmarks and `cargo test --all-targets` excludes
# documentation tests. Therefore, we need an additional docs test command here.
- run: cargo test --doc
# Check minimal build. `tests/seek.rs` fails if there are no decoders at all,
# adding one to make the tests check pass.
- run: cargo check --tests --lib --no-default-features --features mp3
cargo-publish:
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
env:
Expand Down
14 changes: 10 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,36 @@

All notable changes to this project will be documented in this file.

Migration guides for incompatible versions can be found in `UPGRADE.md` file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added
- Adds a function to write a `Source` to a `wav` file, see `output_to_wav`.
- Output audio stream buffer size can now be adjusted.
- Sources for directly generating square waves, traingle waves, square waves and
- Sources for directly generating square waves, triangle waves, square waves, and
sawtooths have been added.
- An interface for defining `SignalGenerator` patterns with an `fn`, see
`GeneratorFunction`.
- Minimal builds without `cpal` audio output are now supported.
See `README.md` for instructions. (#349)

### Changed
- Breaking: `OutputStreamBuilder` should now be used to initialize audio output stream.
- Breaking: `OutputStreamBuilder` should now be used to initialize an audio output stream.
- Breaking: `OutputStreamHandle` removed, use `OutputStream` and `OutputStream::mixer()` instead.
- Breaking: `DynamicMixerController` renamed to `Mixer`, `DynamicMixer` renamed to `MixerSource`.
- 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()`.
- The term 'frame' was renamed to 'span' in the crate and documentation.

### Fixed

- 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.
over long run times has been corrected. (#201)

# Version 0.20.1 (2024-11-08)

Expand Down
12 changes: 9 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ exclude = ["assets/**", "tests/**"]
edition = "2021"

[dependencies]
cpal = "0.15.3"
cpal = { version = "0.15.3", optional = true }
dasp_sample = "0.11.0"
claxon = { version = "0.4.2", optional = true }
hound = { version = "3.3.1", optional = true }
lewton = { version = "0.10", optional = true }
minimp3_fixed = { version = "0.5.4", 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 }

Expand All @@ -25,9 +26,10 @@ atomic_float = { version = "1.1.0", optional = true }
num-rational = "0.4.2"

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

flac = ["claxon"]
vorbis = ["lewton"]
Expand Down Expand Up @@ -74,3 +76,7 @@ required-features = ["symphonia-isomp4", "symphonia-aac"]
[[example]]
name = "noise_generator"
required-features = ["noise"]

[[example]]
name = "into_file"
required-features = ["wav", "mp3"]
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,20 @@ 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)
## Dependencies (Linux only)

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

### Minimal build

It is possible to build `rodio` without support for audio playback. In this configuration `cpal` dependency and its requirements are excluded. This configuration may be useful, for example, for decoding and processing audio in environments when the audio output is not available (e.g. in case of Linux, when ALSA is not available). See `into_file` example that works with this build.

In order to use `rodio` in this configuration disable default features and add the necessary ones. In this case the `Cargo.toml` dependency would look like:
```toml
[dependencies]
rodio = { version = "0.20.1", default-features = false, features = ["symphonia-all"] }
```

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.

# Contributing

Expand Down
19 changes: 19 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
This guide will help you update your code when upgrading from older versions of rodio.

# rodio 0.20.1 or earlier to current GitHub version

## Features
- If you use disable the rodio features with `default_features = false` in
`Cargo.toml` you need to add a new feature `playback`.

## Source implementations
- Source had a required method `current_frame_len`. In the latest version of rodio *frame* has been renamed to *span*. You will need to change every occurrence of `current_frame_len` to `current_span_len`.

## OutputStream
- The outputstream is now more configurable. Where you used `OutputStream::try_default()` you have a choice:
- *(recommended)* Get an error when the default stream could not be opened: `OutputStreamBuilder::open_default_stream()?`
- Stay close to the old behavior using:
`OutputStreamBuilder::open_stream_or_fallback()`, which tries to open the
default (audio) stream. If that fails it tries all other combinations of
device and settings. The old behavior was only trying all settings of the
default device.
2 changes: 1 addition & 1 deletion benches/conversions.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use cpal::FromSample;
use dasp_sample::FromSample;
use divan::Bencher;
use rodio::Source;

Expand Down
10 changes: 5 additions & 5 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::Source;
use rodio::{ChannelCount, SampleRate, Source};

pub struct TestSource<T> {
samples: vec::IntoIter<T>,
Expand All @@ -26,15 +26,15 @@ impl<T> ExactSizeIterator for TestSource<T> {
}

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

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

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

Expand Down Expand Up @@ -70,7 +70,7 @@ impl TestSource<i16> {
total_duration,
} = self;
let samples = samples
.map(|s| cpal::Sample::from_sample(s))
.map(|s| dasp_sample::Sample::from_sample(s))
.collect::<Vec<_>>()
.into_iter();
TestSource {
Expand Down
2 changes: 0 additions & 2 deletions examples/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ use std::error::Error;
use std::io::BufReader;
use std::thread;
use std::time::Duration;
#[cfg(feature = "tracing")]
use tracing;

fn main() -> Result<(), Box<dyn Error>> {
let stream_handle = rodio::OutputStreamBuilder::open_default_stream()?;
Expand Down
4 changes: 2 additions & 2 deletions examples/custom_config.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use cpal::traits::HostTrait;
use cpal::{BufferSize, SampleFormat, SampleRate};
use cpal::{BufferSize, SampleFormat};
use rodio::source::SineWave;
use rodio::Source;
use std::error::Error;
Expand All @@ -15,7 +15,7 @@ fn main() -> Result<(), Box<dyn Error>> {
// No need to set all parameters explicitly here,
// the defaults were set from the device's description.
.with_buffer_size(BufferSize::Fixed(256))
.with_sample_rate(SampleRate(48_000))
.with_sample_rate(48_000)
.with_sample_format(SampleFormat::F32)
// Note that the function below still tries alternative configs if the specified one fails.
// If you need to only use the exact specified configuration,
Expand Down
19 changes: 19 additions & 0 deletions examples/into_file.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use rodio::{output_to_wav, Source};
use std::error::Error;
use std::io::BufReader;

/// Converts mp3 file to a wav file.
/// This example does not use any audio devices
/// and can be used in build configurations without `cpal` feature enabled.
fn main() -> Result<(), Box<dyn Error>> {
let file = std::fs::File::open("assets/music.mp3")?;
let mut audio = rodio::Decoder::new(BufReader::new(file))?
.automatic_gain_control(1.0, 4.0, 0.005, 3.0)
.speed(0.8);

let wav_path = "music_mp3_converted.wav";
println!("Storing converted audio into {}", wav_path);
output_to_wav(&mut audio, wav_path)?;

Ok(())
}
16 changes: 6 additions & 10 deletions examples/noise_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,16 @@ fn main() -> Result<(), Box<dyn Error>> {
let noise_duration = Duration::from_millis(1000);
let interval_duration = Duration::from_millis(1500);

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

thread::sleep(interval_duration);

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

thread::sleep(interval_duration);
Expand Down
2 changes: 1 addition & 1 deletion examples/signal_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ fn main() -> Result<(), Box<dyn Error>> {

let test_signal_duration = Duration::from_millis(1000);
let interval_duration = Duration::from_millis(1500);
let sample_rate = cpal::SampleRate(48000);
let sample_rate = 48000;

println!("Playing 1000 Hz tone");
stream_handle.mixer().add(
Expand Down
21 changes: 11 additions & 10 deletions src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@
//! ```
//!
use std::time::Duration;

use crate::common::{ChannelCount, SampleRate};
use crate::source::SeekError;
use crate::{Sample, Source};
use std::time::Duration;

/// A buffer of samples treated as a source.
#[derive(Debug, Clone)]
pub struct SamplesBuffer<S> {
data: Vec<S>,
pos: usize,
channels: u16,
sample_rate: u32,
channels: ChannelCount,
sample_rate: SampleRate,
duration: Duration,
}

Expand All @@ -38,7 +38,7 @@ where
/// - Panics if the length of the buffer is larger than approximately 16 billion elements.
/// This is because the calculation of the duration would overflow.
///
pub fn new<D>(channels: u16, sample_rate: u32, data: D) -> SamplesBuffer<S>
pub fn new<D>(channels: ChannelCount, sample_rate: SampleRate, data: D) -> SamplesBuffer<S>
where
D: Into<Vec<S>>,
{
Expand Down Expand Up @@ -69,17 +69,17 @@ where
S: Sample,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
fn current_span_len(&self) -> Option<usize> {
None
}

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

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

Expand Down Expand Up @@ -174,12 +174,13 @@ mod tests {
#[cfg(test)]
mod try_seek {
use super::*;
use crate::common::{ChannelCount, SampleRate};
use std::time::Duration;

#[test]
fn channel_order_stays_correct() {
const SAMPLE_RATE: u32 = 100;
const CHANNELS: u16 = 2;
const SAMPLE_RATE: SampleRate = 100;
const CHANNELS: ChannelCount = 2;
let mut buf = SamplesBuffer::new(
CHANNELS,
SAMPLE_RATE,
Expand Down
4 changes: 2 additions & 2 deletions src/channel_bitmask.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,8 +258,8 @@ where
I: Source,
I::Item: Sample,
{
fn current_frame_len(&self) -> Option<usize> {
self.input.current_frame_len()
fn current_span_len(&self) -> Option<usize> {
self.input.current_span_len()
}

fn channels(&self) -> u16 {
Expand Down
5 changes: 5 additions & 0 deletions src/common.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// Stream sample rate (samples per second per channel).
pub type SampleRate = u32;

/// Number of channels in a stream.
pub type ChannelCount = u16;
18 changes: 8 additions & 10 deletions src/conversions/channels.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use cpal::Sample;
use crate::common::ChannelCount;
use dasp_sample::Sample;

/// Iterator that converts from a certain channel count to another.
#[derive(Clone, Debug)]
Expand All @@ -7,10 +8,10 @@ where
I: Iterator,
{
input: I,
from: cpal::ChannelCount,
to: cpal::ChannelCount,
from: ChannelCount,
to: ChannelCount,
sample_repeat: Option<I::Item>,
next_output_sample_pos: cpal::ChannelCount,
next_output_sample_pos: ChannelCount,
}

impl<I> ChannelCountConverter<I>
Expand All @@ -24,11 +25,7 @@ where
/// Panics if `from` or `to` are equal to 0.
///
#[inline]
pub fn new(
input: I,
from: cpal::ChannelCount,
to: cpal::ChannelCount,
) -> ChannelCountConverter<I> {
pub fn new(input: I, from: ChannelCount, to: ChannelCount) -> ChannelCountConverter<I> {
assert!(from >= 1);
assert!(to >= 1);

Expand Down Expand Up @@ -118,6 +115,7 @@ where
#[cfg(test)]
mod test {
use super::ChannelCountConverter;
use crate::common::ChannelCount;

#[test]
fn remove_channels() {
Expand Down Expand Up @@ -147,7 +145,7 @@ mod test {

#[test]
fn size_hint() {
fn test(input: &[i16], from: cpal::ChannelCount, to: cpal::ChannelCount) {
fn test(input: &[i16], from: ChannelCount, to: ChannelCount) {
let mut converter = ChannelCountConverter::new(input.iter().copied(), from, to);
let count = converter.clone().count();
for left_in_iter in (0..=count).rev() {
Expand Down
Loading

0 comments on commit 3c8e5b0

Please sign in to comment.