Skip to content

Commit

Permalink
Merge pull request #667 from PetrGlad/optional-cpal
Browse files Browse the repository at this point in the history
Make CPAL depenrency optional
  • Loading branch information
dvdsk authored Dec 31, 2024
2 parents 46a13e6 + c30421e commit 1d01704
Show file tree
Hide file tree
Showing 69 changed files with 423 additions and 362 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
13 changes: 8 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,25 @@

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`
- 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, triangle 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.
Expand All @@ -25,10 +29,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- 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
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
8 changes: 4 additions & 4 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 Down Expand Up @@ -30,11 +30,11 @@ impl<T: rodio::Sample> Source for TestSource<T> {
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
19 changes: 10 additions & 9 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 @@ -74,12 +74,12 @@ where
}

#[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
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
6 changes: 3 additions & 3 deletions src/conversions/sample.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use cpal::{FromSample, Sample as CpalSample};
use dasp_sample::{FromSample, Sample as DaspSample};
use std::marker::PhantomData;

/// Converts the samples data type to `O`.
Expand Down Expand Up @@ -41,7 +41,7 @@ where

#[inline]
fn next(&mut self) -> Option<O> {
self.input.next().map(|s| CpalSample::from_sample(s))
self.input.next().map(|s| DaspSample::from_sample(s))
}

#[inline]
Expand Down Expand Up @@ -71,7 +71,7 @@ where
///
/// You can implement this trait on your own type as well if you wish so.
///
pub trait Sample: CpalSample {
pub trait Sample: DaspSample {
/// Linear interpolation between two samples.
///
/// The result should be equivalent to
Expand Down
Loading

0 comments on commit 1d01704

Please sign in to comment.