diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 47ff92c0..fb9fa117 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,11 +45,11 @@ jobs: - run: cargo test --all-targets - run: cargo test --lib --bins --tests --benches --features=experimental - - run: cargo test --all-targets --features=symphonia-all + - run: cargo test --all-targets --features=symphonia-all,wav_output # `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. - run: cargo check --tests --lib --no-default-features # Check alternative decoders. - - run: cargo check --tests --lib --no-default-features --features minimp3 + - run: cargo check --tests --lib --no-default-features --features claxon,hound,minimp3,lewton diff --git a/CHANGELOG.md b/CHANGELOG.md index 30ecd0de..4efd4b0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added `Source::amplify_decibel()` method to control volume by decibels. - Added `Source::amplify_normalized()` method to perceptually modify volume. -- 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, enable the `wav_output` feature and see + `output_to_wav`. - Output audio stream buffer size can now be adjusted. - Sources for directly generating square waves, triangle waves, square waves, and sawtooths have been added. @@ -48,6 +49,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 functions from `dasp_sample` crate. For example `DaspSample::from_sample(sample)`. - `OutputStreamConfig` is now public. - Update `cpal` to [0.16](https://github.com/RustAudio/cpal/blob/master/CHANGELOG.md#version-0160-2025-06-07). +- The default decoders have changed to Symphonia. The previous decoders are still available as + optional features: use `claxon` for FLAC, `lewton` for Vorbis, and `hound` for WAV. +- Support for decoding MP4 containers with AAC audio is now enabled by default. +- Breaking: As optional features are now available: CAF and MKV containers, MP1/MP2 and ADPCM + decoders. Previously, the ADPCM decoder was enabled when `symphonia-wav` was. ### Fixed - `ChannelVolume` no longer clips/overflows when converting from many channels to diff --git a/Cargo.lock b/Cargo.lock index 101fb5a1..10ea65bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -66,6 +66,24 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "bindgen" +version = "0.72.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f72209734318d0b619a5e0f5129918b848c416e122a3c4ce054e03cb87b726f" +dependencies = [ + "bitflags 2.9.0", + "cexpr", + "clang-sys", + "itertools", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -108,6 +126,8 @@ version = "1.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "525046617d8376e3db1deffb079e91cef90a89fc3ca5c185bbf8c9ecdd15cd5c" dependencies = [ + "jobserver", + "libc", "shlex", ] @@ -117,12 +137,32 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" version = "4.5.35" @@ -171,27 +211,40 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf0a07a401f374238ab8e2f11a104d2851bf9ce711ec69804834de8af45c7af" +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "coreaudio-rs" -version = "0.13.0" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aae284fbaf7d27aa0e292f7677dfbe26503b0d555026f702940805a630eac17" +checksum = "321077172d79c662f64f5071a03120748d5bb652f5231570141be24cfcd2bace" dependencies = [ "bitflags 1.3.2", - "libc", - "objc2-audio-toolbox", - "objc2-core-audio", - "objc2-core-audio-types", - "objc2-core-foundation", + "core-foundation-sys", + "coreaudio-sys", +] + +[[package]] +name = "coreaudio-sys" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ceec7a6067e62d6f931a2baf6f3a751f4a892595bcec1461a3c94ef9949864b6" +dependencies = [ + "bindgen", ] [[package]] name = "cpal" -version = "0.16.0" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbd307f43cc2a697e2d1f8bc7a1d824b5269e052209e28883e5bc04d095aaa3f" +checksum = "873dab07c8f743075e57f524c583985fbaf745602acbe916a01539364369a779" dependencies = [ "alsa", + "core-foundation-sys", "coreaudio-rs", "dasp_sample", "jni", @@ -200,11 +253,7 @@ dependencies = [ "mach2", "ndk", "ndk-context", - "num-derive", - "num-traits", - "objc2-audio-toolbox", - "objc2-core-audio", - "objc2-core-audio-types", + "oboe", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -232,16 +281,6 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" -[[package]] -name = "dispatch2" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" -dependencies = [ - "bitflags 2.9.0", - "objc2", -] - [[package]] name = "divan" version = "0.1.18" @@ -267,6 +306,12 @@ dependencies = [ "syn", ] +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + [[package]] name = "encoding_rs" version = "0.8.35" @@ -454,6 +499,15 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "jni" version = "0.21.1" @@ -476,6 +530,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +[[package]] +name = "jobserver" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +dependencies = [ + "getrandom 0.3.2", + "libc", +] + [[package]] name = "js-sys" version = "0.3.77" @@ -509,6 +573,16 @@ version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +[[package]] +name = "libloading" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +dependencies = [ + "cfg-if", + "windows-targets 0.52.6", +] + [[package]] name = "linux-raw-sys" version = "0.9.3" @@ -536,6 +610,12 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "minimp3-sys" version = "0.3.2" @@ -558,9 +638,9 @@ dependencies = [ [[package]] name = "ndk" -version = "0.9.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" dependencies = [ "bitflags 2.9.0", "jni-sys", @@ -578,13 +658,23 @@ checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" [[package]] name = "ndk-sys" -version = "0.6.0+11769913" +version = "0.5.0+25.2.9519653" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" dependencies = [ "jni-sys", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -657,75 +747,26 @@ dependencies = [ ] [[package]] -name = "objc2" +name = "oboe" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551" -dependencies = [ - "objc2-encode", -] - -[[package]] -name = "objc2-audio-toolbox" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10cbe18d879e20a4aea544f8befe38bcf52255eb63d3f23eca2842f3319e4c07" -dependencies = [ - "bitflags 2.9.0", - "libc", - "objc2", - "objc2-core-audio", - "objc2-core-audio-types", - "objc2-core-foundation", - "objc2-foundation", -] - -[[package]] -name = "objc2-core-audio" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca44961e888e19313b808f23497073e3f6b3c22bb485056674c8b49f3b025c82" -dependencies = [ - "dispatch2", - "objc2", - "objc2-core-audio-types", - "objc2-core-foundation", -] - -[[package]] -name = "objc2-core-audio-types" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0f1cc99bb07ad2ddb6527ddf83db6a15271bb036b3eb94b801cd44fdc666ee1" -dependencies = [ - "bitflags 2.9.0", - "objc2", -] - -[[package]] -name = "objc2-core-foundation" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" +checksum = "e8b61bebd49e5d43f5f8cc7ee2891c16e0f41ec7954d36bcb6c14c5e0de867fb" dependencies = [ - "bitflags 2.9.0", - "dispatch2", - "objc2", + "jni", + "ndk", + "ndk-context", + "num-derive", + "num-traits", + "oboe-sys", ] [[package]] -name = "objc2-encode" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" - -[[package]] -name = "objc2-foundation" -version = "0.3.1" +name = "oboe-sys" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" +checksum = "6c8bb09a4a2b1d668170cfe0a7d5bc103f8999fb316c98099b6a9939c9f2e79d" dependencies = [ - "objc2", + "cc", ] [[package]] @@ -979,6 +1020,12 @@ dependencies = [ "syn", ] +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + [[package]] name = "rustc_version" version = "0.4.1" @@ -1063,7 +1110,9 @@ dependencies = [ "symphonia-codec-pcm", "symphonia-codec-vorbis", "symphonia-core", + "symphonia-format-caf", "symphonia-format-isomp4", + "symphonia-format-mkv", "symphonia-format-ogg", "symphonia-format-riff", "symphonia-metadata", @@ -1158,6 +1207,17 @@ dependencies = [ "log", ] +[[package]] +name = "symphonia-format-caf" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e43c99c696a388295a29fe71b133079f5d8b18041cf734c5459c35ad9097af50" +dependencies = [ + "log", + "symphonia-core", + "symphonia-metadata", +] + [[package]] name = "symphonia-format-isomp4" version = "0.5.4" @@ -1171,6 +1231,19 @@ dependencies = [ "symphonia-utils-xiph", ] +[[package]] +name = "symphonia-format-mkv" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb43471a100f7882dc9937395bd5ebee8329298e766250b15b3875652fe3d6f" +dependencies = [ + "lazy_static", + "log", + "symphonia-core", + "symphonia-metadata", + "symphonia-utils-xiph", +] + [[package]] name = "symphonia-format-ogg" version = "0.5.4" diff --git a/Cargo.toml b/Cargo.toml index a92f2984..667b39c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,8 +9,87 @@ documentation = "https://docs.rs/rodio" exclude = ["assets/**", "tests/**"] edition = "2021" +[features] +# Default feature set provides audio playback and common format support +default = ["playback", "flac", "mp3", "mp4", "vorbis", "wav"] + +# Core functionality features +# +# Enable audio playback +playback = ["dep:cpal"] +# Enable writing audio to WAV files +wav_output = ["dep:hound"] +# Enable structured observability and instrumentation +tracing = ["dep:tracing"] +# Experimental features using atomic floating-point operations +experimental = ["dep:atomic_float"] + +# Audio generation features +# +# Enable noise generation (white noise, pink noise, etc.) +noise = ["rand"] + +# Platform-specific features +# +# Enable WebAssembly support for web browsers +wasm-bindgen = ["cpal/wasm-bindgen"] +# Use shared C++ stdlib on Android (reduces APK size, fixes linking issues) +cpal-shared-stdcxx = ["cpal/oboe-shared-stdcxx"] + +# To decode an audio source with Rodio, you need to enable the appropriate features for *both* the +# demuxer and the decoder. +# +# Audio files consist of a demuxer (container format) and a decoder (audio codec), for example: +# - .mp3 is an MPEG-1 Audio Layer III file, which is a container format that uses the MP3 codec +# - .mp4 is an MPEG-4 container, typically (but not always) with an AAC-encoded audio stream +# - .ogg is an Ogg container with a Vorbis-encoded audio stream +# +# A reasonable set of audio demuxers and decoders for most applications. +flac = ["symphonia-flac"] +mp3 = ["symphonia-mp3"] +mp4 = ["symphonia-isomp4", "symphonia-aac"] +vorbis = ["symphonia-ogg", "symphonia-vorbis"] +wav = ["symphonia-wav", "symphonia-pcm"] + +# The following features are combinations of demuxers and decoders provided by Symphonia. +# Unless you are developing a generic audio player, this is probably overkill. +symphonia-all = ["symphonia/all-formats", "symphonia/all-codecs"] + +# Combination of decoder and native demuxer provided by Symphonia +symphonia-flac = ["symphonia/flac"] +symphonia-mp1 = ["symphonia/mp1"] # MPEG-1 Audio Layer I +symphonia-mp2 = ["symphonia/mp2"] # MPEG-1 Audio Layer II +symphonia-mp3 = ["symphonia/mp3"] # MPEG-1 Audio Layer III + +# Combination of all MPEG-1 audio demuxers and decoders provided by Symphonia +symphonia-mpa = ["symphonia/mpa"] + +# Formats (demuxers) provided by Symphonia +symphonia-aiff = ["symphonia/aiff"] +symphonia-caf = ["symphonia/caf"] +symphonia-isomp4 = ["symphonia/isomp4"] +symphonia-mkv = ["symphonia/mkv"] +symphonia-ogg = ["symphonia/ogg"] + +# Codecs (decoders) provided by Symphonia +symphonia-aac = ["symphonia/aac"] +symphonia-adpcm = ["symphonia/adpcm"] +symphonia-alac = ["symphonia/alac"] +symphonia-pcm = ["symphonia/pcm"] +symphonia-vorbis = ["symphonia/vorbis"] +symphonia-wav = ["symphonia/wav"] + +# Alternative decoders and demuxers +claxon = ["dep:claxon"] # FLAC +hound = ["dep:hound"] # WAV +minimp3 = ["dep:minimp3_fixed"] # MP3 +lewton = ["dep:lewton"] # Ogg Vorbis + +[package.metadata.docs.rs] +all-features = true + [dependencies] -cpal = { version = "0.16.0", optional = true } +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 } @@ -28,42 +107,6 @@ tracing = { version = "0.1.40", optional = true } atomic_float = { version = "1.1.0", optional = true } num-rational = "0.4.2" -[features] -default = ["playback", "flac", "vorbis", "wav", "mp3"] -tracing = ["dep:tracing"] -experimental = ["dep:atomic_float"] -playback = ["dep:cpal"] - -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-ogg", - "symphonia-vorbis", - "symphonia-wav", -] -symphonia-flac = ["symphonia/flac"] -symphonia-isomp4 = ["symphonia/isomp4"] -symphonia-mp3 = ["symphonia/mp3"] -symphonia-ogg = ["symphonia/ogg"] -symphonia-vorbis = ["symphonia/vorbis"] -symphonia-wav = ["symphonia/wav", "symphonia/pcm", "symphonia/adpcm"] -symphonia-alac = ["symphonia/isomp4", "symphonia/alac"] -symphonia-aiff = ["symphonia/aiff", "symphonia/pcm"] - [dev-dependencies] quickcheck = "1" rstest = "0.18.2" @@ -130,7 +173,7 @@ required-features = ["playback"] [[example]] name = "into_file" -required-features = ["mp3", "wav"] +required-features = ["mp3", "wav_output"] [[example]] name = "limit_wav" @@ -150,7 +193,7 @@ required-features = ["playback", "flac"] [[example]] name = "music_m4a" -required-features = ["playback", "symphonia-isomp4", "symphonia-aac"] +required-features = ["playback", "mp4"] [[example]] name = "music_mp3" diff --git a/README.md b/README.md index 6e9f8047..b026b250 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,14 @@ Rust playback library. -Playback is handled by [cpal](https://github.com/RustAudio/cpal). Format decoding can be handled either by [Symphonia](https://github.com/pdeljanov/Symphonia), or by format-specific decoders: +Playback is handled by [cpal](https://github.com/RustAudio/cpal). Format decoding is handled by [Symphonia](https://github.com/pdeljanov/Symphonia) by default, or by optional format-specific decoders: - - MP3 by [minimp3](https://github.com/lieff/minimp3) (but defaults to [Symphonia](https://github.com/pdeljanov/Symphonia)). - - WAV by [hound](https://github.com/ruud-v-a/hound). - - Vorbis by [lewton](https://github.com/est31/lewton). - FLAC by [claxon](https://github.com/ruuda/claxon). - - MP4 and AAC (both disabled by default) are handled only by [Symphonia](https://github.com/pdeljanov/Symphonia). + - MP3 by [minimp3](https://github.com/lieff/minimp3). + - Vorbis by [lewton](https://github.com/est31/lewton). + - WAV by [hound](https://github.com/ruud-v-a/hound). -See [the docs](https://docs.rs/rodio/latest/rodio/#alternative-decoder-backends) for more details on backends. +See [the feature flags](https://docs.rs/crate/rodio/latest/features) for more details on formats, and other features. # [Documentation](http://docs.rs/rodio) @@ -51,7 +50,7 @@ Through cpal rodio depends on the alsa library (libasound & libasound-dev), this - Install crossbuild-essential-arm64: `sudo apt-get install crossbuild-essential-arm64 clang` - Add the aarch64 target for rust: `rustup target add aarch64-unknown-linux-gnu` - Add the architecture arm64 to apt using: `sudo dpkg --add-architecture arm64` -- Install the [multi-arch](https://wiki.debian.org/Multiarch/HOWTO) version of libasound2-dev for arm64 using: `sudo apt install libasound2-dev:arm64` +- Install the [multi-arch](https://wiki.debian.org/Multiarch/HOWTO) version of libasound2-dev for arm64 using: `sudo apt install libasound2-dev:arm64` - Build with the pkg config sysroot set to /usr/aarch64-linux-gnu and aarch64-linux-gnu as linker: `PKG_CONFIG_SYSROOT_DIR=/usr/aarch64-linux-gnu RUSTFLAGS="-C linker=aarch64-linux-gnu-gcc" cargo build --target aarch64-unknown-linux-gnu` This will work for other Linux targets too if you change the architecture in the diff --git a/UPGRADE.md b/UPGRADE.md index 0e6f39f3..2fbb3e09 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -3,14 +3,16 @@ This guide will help you update your code when upgrading from older versions of # 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`. +- If you use disable the rodio features with `default_features = false` in `Cargo.toml` you need to + add a new feature `playback`. +- The default decoders have changed to Symphonia, which itself is licensed under MPL. If you want + to revert to the old decoders, you need to enable the `claxon`, `hound` and `lewton` features in `Cargo.toml` for respectively FLAC, WAV and Ogg Vorbis. ## 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: +- The output stream 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 diff --git a/src/decoder/builder.rs b/src/decoder/builder.rs index b6a7dcc8..8d6dc247 100644 --- a/src/decoder/builder.rs +++ b/src/decoder/builder.rs @@ -267,18 +267,18 @@ impl DecoderBuilder { fn build_impl(self) -> Result<(DecoderImpl, Settings), DecoderError> { let data = self.data.ok_or(DecoderError::UnrecognizedFormat)?; - #[cfg(all(feature = "wav", not(feature = "symphonia-wav")))] + #[cfg(all(feature = "hound", not(feature = "symphonia-wav")))] let data = match wav::WavDecoder::new(data) { Ok(decoder) => return Ok((DecoderImpl::Wav(decoder), self.settings)), Err(data) => data, }; - #[cfg(all(feature = "flac", not(feature = "symphonia-flac")))] + #[cfg(all(feature = "claxon", not(feature = "symphonia-flac")))] let data = match flac::FlacDecoder::new(data) { Ok(decoder) => return Ok((DecoderImpl::Flac(decoder), self.settings)), Err(data) => data, }; - #[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))] + #[cfg(all(feature = "lewton", not(feature = "symphonia-vorbis")))] let data = match vorbis::VorbisDecoder::new(data) { Ok(decoder) => return Ok((DecoderImpl::Vorbis(decoder), self.settings)), Err(data) => data, diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index ac0b4fc7..d2f792d1 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -65,7 +65,7 @@ use crate::{ pub mod builder; pub use builder::{DecoderBuilder, Settings}; -#[cfg(all(feature = "flac", not(feature = "symphonia-flac")))] +#[cfg(all(feature = "claxon", not(feature = "symphonia-flac")))] mod flac; #[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))] mod mp3; @@ -74,9 +74,9 @@ mod read_seek_source; #[cfg(feature = "symphonia")] /// Symphonia decoders types pub mod symphonia; -#[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))] +#[cfg(all(feature = "lewton", not(feature = "symphonia-vorbis")))] mod vorbis; -#[cfg(all(feature = "wav", not(feature = "symphonia-wav")))] +#[cfg(all(feature = "hound", not(feature = "symphonia-wav")))] mod wav; /// Source of audio samples decoded from an input stream. @@ -109,11 +109,11 @@ pub struct LoopedDecoder { // arrays just a lot of struct fields. #[allow(clippy::large_enum_variant)] enum DecoderImpl { - #[cfg(all(feature = "wav", not(feature = "symphonia-wav")))] + #[cfg(all(feature = "hound", not(feature = "symphonia-wav")))] Wav(wav::WavDecoder), - #[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))] + #[cfg(all(feature = "lewton", not(feature = "symphonia-vorbis")))] Vorbis(vorbis::VorbisDecoder), - #[cfg(all(feature = "flac", not(feature = "symphonia-flac")))] + #[cfg(all(feature = "claxon", not(feature = "symphonia-flac")))] Flac(flac::FlacDecoder), #[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))] Mp3(mp3::Mp3Decoder), @@ -131,11 +131,11 @@ impl DecoderImpl { #[inline] fn next(&mut self) -> Option { match self { - #[cfg(all(feature = "wav", not(feature = "symphonia-wav")))] + #[cfg(all(feature = "hound", not(feature = "symphonia-wav")))] DecoderImpl::Wav(source) => source.next(), - #[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))] + #[cfg(all(feature = "lewton", not(feature = "symphonia-vorbis")))] DecoderImpl::Vorbis(source) => source.next(), - #[cfg(all(feature = "flac", not(feature = "symphonia-flac")))] + #[cfg(all(feature = "claxon", not(feature = "symphonia-flac")))] DecoderImpl::Flac(source) => source.next(), #[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))] DecoderImpl::Mp3(source) => source.next(), @@ -148,11 +148,11 @@ impl DecoderImpl { #[inline] fn size_hint(&self) -> (usize, Option) { match self { - #[cfg(all(feature = "wav", not(feature = "symphonia-wav")))] + #[cfg(all(feature = "hound", not(feature = "symphonia-wav")))] DecoderImpl::Wav(source) => source.size_hint(), - #[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))] + #[cfg(all(feature = "lewton", not(feature = "symphonia-vorbis")))] DecoderImpl::Vorbis(source) => source.size_hint(), - #[cfg(all(feature = "flac", not(feature = "symphonia-flac")))] + #[cfg(all(feature = "claxon", not(feature = "symphonia-flac")))] DecoderImpl::Flac(source) => source.size_hint(), #[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))] DecoderImpl::Mp3(source) => source.size_hint(), @@ -165,11 +165,11 @@ impl DecoderImpl { #[inline] fn current_span_len(&self) -> Option { match self { - #[cfg(all(feature = "wav", not(feature = "symphonia-wav")))] + #[cfg(all(feature = "hound", not(feature = "symphonia-wav")))] DecoderImpl::Wav(source) => source.current_span_len(), - #[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))] + #[cfg(all(feature = "lewton", not(feature = "symphonia-vorbis")))] DecoderImpl::Vorbis(source) => source.current_span_len(), - #[cfg(all(feature = "flac", not(feature = "symphonia-flac")))] + #[cfg(all(feature = "claxon", not(feature = "symphonia-flac")))] DecoderImpl::Flac(source) => source.current_span_len(), #[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))] DecoderImpl::Mp3(source) => source.current_span_len(), @@ -182,11 +182,11 @@ impl DecoderImpl { #[inline] fn channels(&self) -> ChannelCount { match self { - #[cfg(all(feature = "wav", not(feature = "symphonia-wav")))] + #[cfg(all(feature = "hound", not(feature = "symphonia-wav")))] DecoderImpl::Wav(source) => source.channels(), - #[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))] + #[cfg(all(feature = "lewton", not(feature = "symphonia-vorbis")))] DecoderImpl::Vorbis(source) => source.channels(), - #[cfg(all(feature = "flac", not(feature = "symphonia-flac")))] + #[cfg(all(feature = "claxon", not(feature = "symphonia-flac")))] DecoderImpl::Flac(source) => source.channels(), #[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))] DecoderImpl::Mp3(source) => source.channels(), @@ -199,11 +199,11 @@ impl DecoderImpl { #[inline] fn sample_rate(&self) -> SampleRate { match self { - #[cfg(all(feature = "wav", not(feature = "symphonia-wav")))] + #[cfg(all(feature = "hound", not(feature = "symphonia-wav")))] DecoderImpl::Wav(source) => source.sample_rate(), - #[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))] + #[cfg(all(feature = "lewton", not(feature = "symphonia-vorbis")))] DecoderImpl::Vorbis(source) => source.sample_rate(), - #[cfg(all(feature = "flac", not(feature = "symphonia-flac")))] + #[cfg(all(feature = "claxon", not(feature = "symphonia-flac")))] DecoderImpl::Flac(source) => source.sample_rate(), #[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))] DecoderImpl::Mp3(source) => source.sample_rate(), @@ -222,11 +222,11 @@ impl DecoderImpl { #[inline] fn total_duration(&self) -> Option { match self { - #[cfg(all(feature = "wav", not(feature = "symphonia-wav")))] + #[cfg(all(feature = "hound", not(feature = "symphonia-wav")))] DecoderImpl::Wav(source) => source.total_duration(), - #[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))] + #[cfg(all(feature = "lewton", not(feature = "symphonia-vorbis")))] DecoderImpl::Vorbis(source) => source.total_duration(), - #[cfg(all(feature = "flac", not(feature = "symphonia-flac")))] + #[cfg(all(feature = "claxon", not(feature = "symphonia-flac")))] DecoderImpl::Flac(source) => source.total_duration(), #[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))] DecoderImpl::Mp3(source) => source.total_duration(), @@ -239,11 +239,11 @@ impl DecoderImpl { #[inline] fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> { match self { - #[cfg(all(feature = "wav", not(feature = "symphonia-wav")))] + #[cfg(all(feature = "hound", not(feature = "symphonia-wav")))] DecoderImpl::Wav(source) => source.try_seek(pos), - #[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))] + #[cfg(all(feature = "lewton", not(feature = "symphonia-vorbis")))] DecoderImpl::Vorbis(source) => source.try_seek(pos), - #[cfg(all(feature = "flac", not(feature = "symphonia-flac")))] + #[cfg(all(feature = "claxon", not(feature = "symphonia-flac")))] DecoderImpl::Flac(source) => source.try_seek(pos), #[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))] DecoderImpl::Mp3(source) => source.try_seek(pos), @@ -418,7 +418,7 @@ impl Decoder { /// let file = File::open("audio.wav").unwrap(); /// let decoder = Decoder::new_wav(file).unwrap(); /// ``` - #[cfg(any(feature = "wav", feature = "symphonia-wav"))] + #[cfg(any(feature = "hound", feature = "symphonia-wav"))] pub fn new_wav(data: R) -> Result { DecoderBuilder::new() .with_data(data) @@ -444,7 +444,7 @@ impl Decoder { /// let file = File::open("audio.flac").unwrap(); /// let decoder = Decoder::new_flac(file).unwrap(); /// ``` - #[cfg(any(feature = "flac", feature = "symphonia-flac"))] + #[cfg(any(feature = "claxon", feature = "symphonia-flac"))] pub fn new_flac(data: R) -> Result { DecoderBuilder::new() .with_data(data) @@ -470,7 +470,7 @@ impl Decoder { /// let file = File::open("audio.ogg").unwrap(); /// let decoder = Decoder::new_vorbis(file).unwrap(); /// ``` - #[cfg(any(feature = "vorbis", feature = "symphonia-vorbis"))] + #[cfg(any(feature = "lewton", feature = "symphonia-vorbis"))] pub fn new_vorbis(data: R) -> Result { DecoderBuilder::new() .with_data(data) @@ -623,7 +623,7 @@ where // Take ownership of the decoder to reset it let decoder = self.inner.take()?; let (new_decoder, sample) = match decoder { - #[cfg(all(feature = "wav", not(feature = "symphonia-wav")))] + #[cfg(all(feature = "hound", not(feature = "symphonia-wav")))] DecoderImpl::Wav(source) => { let mut reader = source.into_inner(); reader.seek(SeekFrom::Start(0)).ok()?; @@ -631,7 +631,7 @@ where let sample = source.next(); (DecoderImpl::Wav(source), sample) } - #[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))] + #[cfg(all(feature = "lewton", not(feature = "symphonia-vorbis")))] DecoderImpl::Vorbis(source) => { use lewton::inside_ogg::OggStreamReader; let mut reader = source.into_inner().into_inner(); @@ -642,7 +642,7 @@ where let sample = source.next(); (DecoderImpl::Vorbis(source), sample) } - #[cfg(all(feature = "flac", not(feature = "symphonia-flac")))] + #[cfg(all(feature = "claxon", not(feature = "symphonia-flac")))] DecoderImpl::Flac(source) => { let mut reader = source.into_inner(); reader.seek(SeekFrom::Start(0)).ok()?; diff --git a/src/lib.rs b/src/lib.rs index 4cab8f00..d27103d2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -171,7 +171,7 @@ mod sink; mod spatial_sink; #[cfg(feature = "playback")] pub mod stream; -#[cfg(feature = "wav")] +#[cfg(feature = "wav_output")] mod wav_output; pub mod buffer; @@ -190,5 +190,5 @@ pub use crate::source::Source; pub use crate::spatial_sink::SpatialSink; #[cfg(feature = "playback")] pub use crate::stream::{play, OutputStream, OutputStreamBuilder, PlayError, StreamError}; -#[cfg(feature = "wav")] +#[cfg(feature = "wav_output")] pub use crate::wav_output::output_to_wav; diff --git a/src/source/mod.rs b/src/source/mod.rs index dfb1388e..97bed40b 100644 --- a/src/source/mod.rs +++ b/src/source/mod.rs @@ -686,7 +686,7 @@ pub enum SeekError { #[cfg(feature = "symphonia")] /// The symphonia decoder ran into an issue SymphoniaDecoder(crate::decoder::symphonia::SeekError), - #[cfg(feature = "wav")] + #[cfg(feature = "hound")] /// The hound (wav) decoder ran into an issue HoundDecoder(std::io::Error), // Prefer adding an enum variant to using this. It's meant for end users their @@ -706,7 +706,7 @@ impl fmt::Display for SeekError { } #[cfg(feature = "symphonia")] SeekError::SymphoniaDecoder(err) => write!(f, "Error seeking: {}", err), - #[cfg(feature = "wav")] + #[cfg(feature = "hound")] SeekError::HoundDecoder(err) => write!(f, "Error seeking in wav source: {}", err), SeekError::Other(_) => write!(f, "An error occurred"), } @@ -718,7 +718,7 @@ impl std::error::Error for SeekError { SeekError::NotSupported { .. } => None, #[cfg(feature = "symphonia")] SeekError::SymphoniaDecoder(err) => Some(err), - #[cfg(feature = "wav")] + #[cfg(feature = "hound")] SeekError::HoundDecoder(err) => Some(err), SeekError::Other(err) => Some(err.as_ref()), } @@ -740,7 +740,7 @@ impl SeekError { SeekError::NotSupported { .. } => true, #[cfg(feature = "symphonia")] SeekError::SymphoniaDecoder(_) => false, - #[cfg(feature = "wav")] + #[cfg(feature = "hound")] SeekError::HoundDecoder(_) => false, SeekError::Other(_) => false, } diff --git a/tests/flac_test.rs b/tests/flac_test.rs index be61bd75..e17602a6 100644 --- a/tests/flac_test.rs +++ b/tests/flac_test.rs @@ -1,9 +1,9 @@ -#[cfg(any(feature = "flac", feature = "symphonia-flac"))] +#[cfg(any(feature = "claxon", feature = "symphonia-flac"))] use rodio::Source; -#[cfg(any(feature = "flac", feature = "symphonia-flac"))] +#[cfg(any(feature = "claxon", feature = "symphonia-flac"))] use std::time::Duration; -#[cfg(any(feature = "flac", feature = "symphonia-flac"))] +#[cfg(any(feature = "claxon", feature = "symphonia-flac"))] #[test] fn test_flac_encodings() { // 16 bit FLAC file exported from Audacity (2 channels, compression level 5) diff --git a/tests/seek.rs b/tests/seek.rs index 3b9113b9..6743865e 100644 --- a/tests/seek.rs +++ b/tests/seek.rs @@ -11,7 +11,7 @@ use std::path::Path; use std::time::Duration; #[cfg(any( - feature = "flac", + feature = "claxon", feature = "minimp3", feature = "symphonia-aac", feature = "symphonia-flac", @@ -19,7 +19,7 @@ use std::time::Duration; feature = "symphonia-isomp4", feature = "symphonia-ogg", feature = "symphonia-wav", - feature = "wav", + feature = "hound", ))] #[template] #[rstest] @@ -32,11 +32,11 @@ use std::time::Duration; case("mp3", false, "minimp3") )] #[cfg_attr( - all(feature = "wav", not(feature = "symphonia-wav")), + all(feature = "hound", not(feature = "symphonia-wav")), case("wav", true, "hound") )] #[cfg_attr( - all(feature = "flac", not(feature = "symphonia-flac")), + all(feature = "claxon", not(feature = "symphonia-flac")), case("flac", false, "claxon") )] #[cfg_attr(feature = "symphonia-mp3", case("mp3", true, "symphonia"))] @@ -59,7 +59,7 @@ fn all_decoders( feature = "symphonia-isomp4", feature = "symphonia-ogg", feature = "symphonia-wav", - feature = "wav", + feature = "hound", ))] #[template] #[rstest] @@ -68,7 +68,7 @@ fn all_decoders( case("ogg", "symphonia") )] #[cfg_attr( - all(feature = "wav", not(feature = "symphonia-wav")), + all(feature = "hound", not(feature = "symphonia-wav")), case("wav", "hound") )] #[cfg_attr(feature = "symphonia-mp3", case("mp3", "symphonia"))] @@ -81,14 +81,14 @@ fn all_decoders( fn supported_decoders(#[case] format: &'static str, #[case] decoder_name: &'static str) {} #[cfg(any( - feature = "flac", + feature = "claxon", feature = "minimp3", feature = "symphonia-flac", feature = "symphonia-mp3", feature = "symphonia-isomp4", feature = "symphonia-ogg", feature = "symphonia-wav", - feature = "wav", + feature = "hound", ))] #[apply(all_decoders)] #[trace] @@ -108,7 +108,7 @@ fn seek_returns_err_if_unsupported( feature = "symphonia-isomp4", feature = "symphonia-ogg", feature = "symphonia-wav", - feature = "wav", + feature = "hound", ))] #[apply(supported_decoders)] #[trace] @@ -127,7 +127,7 @@ fn seek_beyond_end_saturates(#[case] format: &'static str, #[case] decoder_name: feature = "symphonia-isomp4", feature = "symphonia-ogg", feature = "symphonia-wav", - feature = "wav", + feature = "hound", ))] #[apply(supported_decoders)] #[trace] @@ -163,7 +163,7 @@ fn seek_results_in_correct_remaining_playtime( feature = "symphonia-isomp4", feature = "symphonia-ogg", feature = "symphonia-wav", - feature = "wav", + feature = "hound", ))] #[apply(supported_decoders)] #[trace] @@ -185,7 +185,7 @@ fn seek_possible_after_exausting_source( feature = "symphonia-isomp4", feature = "symphonia-ogg", feature = "symphonia-wav", - feature = "wav", + feature = "hound", ))] #[apply(supported_decoders)] #[trace] diff --git a/tests/total_duration.rs b/tests/total_duration.rs index 0738c86d..8d56a8c4 100644 --- a/tests/total_duration.rs +++ b/tests/total_duration.rs @@ -11,7 +11,7 @@ use rstest::rstest; use rstest_reuse::{self, *}; #[cfg(any( - feature = "flac", + feature = "claxon", feature = "minimp3", feature = "symphonia-aac", feature = "symphonia-flac", @@ -19,7 +19,7 @@ use rstest_reuse::{self, *}; feature = "symphonia-isomp4", feature = "symphonia-ogg", feature = "symphonia-wav", - feature = "wav", + feature = "hound", ))] #[template] #[rstest] @@ -32,11 +32,11 @@ use rstest_reuse::{self, *}; case("mp3", Duration::ZERO, "minimp3") )] #[cfg_attr( - all(feature = "wav", not(feature = "symphonia-wav")), + all(feature = "hound", not(feature = "symphonia-wav")), case("wav", Duration::from_secs_f64(10.143469387), "hound") )] #[cfg_attr( - all(feature = "flac", not(feature = "symphonia-flac")), + all(feature = "claxon", not(feature = "symphonia-flac")), case("flac", Duration::from_secs_f64(10.152380952), "claxon") )] #[cfg_attr( @@ -76,14 +76,14 @@ fn get_music(format: &str) -> Decoder { } #[cfg(any( - feature = "flac", + feature = "claxon", feature = "minimp3", feature = "symphonia-flac", feature = "symphonia-mp3", feature = "symphonia-isomp4", feature = "symphonia-ogg", feature = "symphonia-wav", - feature = "wav", + feature = "hound", ))] #[apply(all_decoders)] #[trace] diff --git a/tests/wav_test.rs b/tests/wav_test.rs index c7b396f9..585eadda 100644 --- a/tests/wav_test.rs +++ b/tests/wav_test.rs @@ -1,4 +1,4 @@ -#[cfg(feature = "wav")] +#[cfg(any(feature = "hound", feature = "wav"))] #[test] fn test_wav_encodings() { // 16 bit wav file exported from Audacity (1 channel)