Skip to content

Commit a80470f

Browse files
mnmaitadependabot[bot]rparrett
authored
Update Rodio to 0.22 (#20323)
# Objective - Closes #19672 ## Solution - Updated both `cpal` and `rodio` to their latest versions. - Updated code to address `rodio`'s breaking changes. - Reworked audio related feature flags. NOTE: `symphonia` will only be the default backend for formats with no alternative fallback. - Added `audio-all-formats` feature collection to easily enable all the available audio formats using their default backends. - Replaced `aarch64-apple-ios-sim` target with `arm64-apple-ios-simulator`. ## Testing - Tested audio related examples. - CI checks passing. --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Rob Parrett <robparrett@gmail.com>
1 parent 87e716f commit a80470f

19 files changed

Lines changed: 175 additions & 177 deletions

File tree

.github/workflows/validation-jobs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ jobs:
9999
run: cargo install --force cargo-ndk
100100

101101
- name: Build .so file
102-
run: cargo ndk -t arm64-v8a -o android_example/app/src/main/jniLibs build --package bevy_mobile_example
102+
run: cargo ndk -t arm64-v8a -P 26 -o android_example/app/src/main/jniLibs build --package bevy_mobile_example
103103

104104
- name: Build app for Android
105105
run: cd examples/mobile/android_example && chmod +x gradlew && ./gradlew build

Cargo.toml

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,9 @@ dev = [
174174
# COLLECTION: Features used to build audio Bevy apps.
175175
audio = ["bevy_audio", "vorbis"]
176176

177+
# COLLECTION: Enables audio features and all supported formats.
178+
audio-all-formats = ["bevy_audio", "aac", "flac", "mp3", "mp4", "vorbis", "wav"]
179+
177180
# COLLECTION: Features used to compose Bevy scenes.
178181
scene = ["bevy_scene"]
179182

@@ -196,7 +199,6 @@ default_app = [
196199
default_platform = [
197200
"std",
198201
"android-game-activity",
199-
"android_shared_stdcxx",
200202
"bevy_gilrs",
201203
"bevy_winit",
202204
"default_font",
@@ -497,35 +499,32 @@ zstd_rust = ["bevy_internal/zstd_rust"]
497499
# For KTX2 Zstandard decompression using [zstd](https://crates.io/crates/zstd). This is a faster backend, but uses unsafe C bindings. For the safe option, stick to the default backend with "zstd_rust".
498500
zstd_c = ["bevy_internal/zstd_c"]
499501

500-
# FLAC audio format support
501-
flac = ["bevy_internal/flac"]
502-
503-
# MP3 audio format support
504-
mp3 = ["bevy_internal/mp3"]
502+
# FLAC audio format support (through `symphonia`)
503+
symphonia-flac = ["bevy_internal/symphonia-flac"]
505504

506-
# OGG/VORBIS audio format support
507-
vorbis = ["bevy_internal/vorbis"]
505+
# OGG/VORBIS audio format support (through `symphonia`)
506+
symphonia-vorbis = ["bevy_internal/symphonia-vorbis"]
508507

509-
# WAV audio format support
510-
wav = ["bevy_internal/wav"]
508+
# WAV audio format support (through `symphonia`)
509+
symphonia-wav = ["bevy_internal/symphonia-wav"]
511510

512-
# AAC audio format support (through symphonia)
513-
symphonia-aac = ["bevy_internal/symphonia-aac"]
511+
# AAC audio format support (through `symphonia`)
512+
aac = ["bevy_internal/aac"]
514513

515-
# AAC, FLAC, MP3, MP4, OGG/VORBIS, and WAV audio formats support (through symphonia)
516-
symphonia-all = ["bevy_internal/symphonia-all"]
514+
# FLAC audio format support (through `claxon`)
515+
flac = ["bevy_internal/flac"]
517516

518-
# FLAC audio format support (through symphonia)
519-
symphonia-flac = ["bevy_internal/symphonia-flac"]
517+
# MP4 audio format support (through `symphonia`). It also enables AAC support.
518+
mp4 = ["bevy_internal/mp4"]
520519

521-
# MP4 audio format support (through symphonia)
522-
symphonia-isomp4 = ["bevy_internal/symphonia-isomp4"]
520+
# MP3 audio format support (through `symphonia`)
521+
mp3 = ["bevy_internal/mp3"]
523522

524-
# OGG/VORBIS audio format support (through symphonia)
525-
symphonia-vorbis = ["bevy_internal/symphonia-vorbis"]
523+
# OGG/VORBIS audio format support (through `lewton`)
524+
vorbis = ["bevy_internal/vorbis"]
526525

527-
# WAV audio format support (through symphonia)
528-
symphonia-wav = ["bevy_internal/symphonia-wav"]
526+
# WAV audio format support (through `hound`)
527+
wav = ["bevy_internal/wav"]
529528

530529
# Enable serialization support through serde
531530
serialize = ["bevy_internal/serialize"]
@@ -560,9 +559,6 @@ morph = ["bevy_internal/morph"]
560559
# Enables bevy_mesh and bevy_animation morph weight support
561560
morph_animation = ["bevy_internal/morph_animation"]
562561

563-
# Enable using a shared stdlib for cxx on Android
564-
android_shared_stdcxx = ["bevy_internal/android_shared_stdcxx"]
565-
566562
# Enable detailed trace event logging. These trace events are expensive even when off, thus they require compile time opt-in
567563
detailed_trace = ["bevy_internal/detailed_trace"]
568564

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
title: "Rodio 0.22 Update"
3+
pull_requests: [20323]
4+
---
5+
6+
`rodio` was updated to `0.22` and `cpal` to `0.17`. The following sections will guide you through the necessary changes to ensure compatibility.
7+
8+
## Audio Feature Flags
9+
10+
Audio format related features were reworked with this update.
11+
12+
By default, Bevy will enable the `vorbis` feature, which supports OGG/VORBIS files through `lewton`.
13+
14+
If you are not using Bevy's default features, here's a list you can use for reference:
15+
16+
- `vorbis`: OGG/VORBIS audio format support (through `lewton`).
17+
- `wav`: WAV audio format support (through `hound`).
18+
- `mp3`: MP3 audio format support (through `symphonia`).
19+
- `mp4`: MP4 audio format support (through `symphonia`). It also enables AAC support.
20+
- `flac`: FLAC audio format support (through `claxon`).
21+
- `aac`: AAC audio format support (through `symphonia`).
22+
23+
There are also specific `symphonia` backend flags you can use for certain formats instead of the default flags:
24+
25+
- `symphonia-flac`
26+
- `symphonia-vorbis`
27+
- `symphonia-wav`
28+
29+
Notice that OGG/VORBIS support through `symphonia` is currently subject to issues with buffering, reverb, looping and spatial audio. Check the following issues/PRs for additional context:
30+
31+
- <https://github.com/RustAudio/rodio/issues/775>
32+
- <https://github.com/RustAudio/rodio/pull/786>
33+
34+
The `audio-all-formats` feature collection was added for convenience. It will enable `bevy_audio` and all the available audio formats through their default backends.
35+
36+
## Audio Traits
37+
38+
`type DecoderItem` was removed from the `Decodable` trait. Now `rodio::Sample` is an alias for `f32`.
39+
40+
## Android Related Features
41+
42+
The `android_shared_stdcxx` feature was removed, as `cpal`'s `oboe-shared-stdcxx` feature was also removed in favor of Android NDK audio APIs.
43+
44+
Keep in mind that if you are using `bevy_audio` the minimum supported Android API version is now 26 (Android 8/Oreo).

crates/bevy_audio/Cargo.toml

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,17 @@ bevy_reflect = { path = "../bevy_reflect", version = "0.19.0-dev" }
1818
bevy_transform = { path = "../bevy_transform", version = "0.19.0-dev" }
1919

2020
# other
21-
# TODO: Remove `coreaudio-sys` dep below when updating `cpal`.
22-
rodio = { version = "0.20", default-features = false }
21+
rodio = { version = "0.22", default-features = false, features = ["playback"] }
2322
tracing = { version = "0.1", default-features = false, features = ["std"] }
2423

2524
[target.'cfg(target_os = "android")'.dependencies]
26-
cpal = { version = "0.15", optional = true }
27-
28-
[target.'cfg(target_vendor = "apple")'.dependencies]
29-
# NOTE: Explicitly depend on this patch version to fix:
30-
# https://github.com/bevyengine/bevy/issues/18893
31-
coreaudio-sys = { version = "0.2.17", default-features = false }
25+
cpal = { version = "0.17", optional = true }
3226

3327
[target.'cfg(target_arch = "wasm32")'.dependencies]
3428
# TODO: Assuming all wasm builds are for the browser. Require `no_std` support to break assumption.
35-
rodio = { version = "0.20", default-features = false, features = [
29+
rodio = { version = "0.22", default-features = false, features = [
3630
"wasm-bindgen",
31+
"playback",
3732
] }
3833
bevy_app = { path = "../bevy_app", version = "0.19.0-dev", default-features = false, features = [
3934
"web",
@@ -43,18 +38,15 @@ bevy_reflect = { path = "../bevy_reflect", version = "0.19.0-dev", default-featu
4338
] }
4439

4540
[features]
41+
aac = ["rodio/symphonia-aac"]
42+
flac = ["rodio/claxon"]
4643
mp3 = ["rodio/mp3"]
47-
flac = ["rodio/flac"]
48-
wav = ["rodio/wav"]
49-
vorbis = ["rodio/vorbis"]
50-
symphonia-aac = ["rodio/symphonia-aac"]
51-
symphonia-all = ["rodio/symphonia-all"]
52-
symphonia-flac = ["rodio/symphonia-flac"]
53-
symphonia-isomp4 = ["rodio/symphonia-isomp4"]
54-
symphonia-vorbis = ["rodio/symphonia-vorbis"]
55-
symphonia-wav = ["rodio/symphonia-wav"]
56-
# Enable using a shared stdlib for cxx on Android.
57-
android_shared_stdcxx = ["cpal/oboe-shared-stdcxx"]
44+
mp4 = ["rodio/mp4"]
45+
vorbis = ["rodio/lewton"]
46+
wav = ["rodio/hound"]
47+
symphonia-flac = ["rodio/flac"]
48+
symphonia-vorbis = ["rodio/vorbis"]
49+
symphonia-wav = ["rodio/wav"]
5850

5951
[lints]
6052
workspace = true

crates/bevy_audio/src/audio_output.rs

Lines changed: 16 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -6,42 +6,25 @@ use bevy_asset::{Asset, Assets};
66
use bevy_ecs::{prelude::*, system::SystemParam};
77
use bevy_math::Vec3;
88
use bevy_transform::prelude::GlobalTransform;
9-
use rodio::{OutputStream, OutputStreamHandle, Sink, Source, SpatialSink};
9+
use rodio::{DeviceSinkBuilder, MixerDeviceSink, Player, Source, SpatialPlayer};
1010
use tracing::warn;
1111

1212
use crate::{AudioSink, AudioSinkPlayback};
1313

1414
/// Used internally to play audio on the current "audio device"
15-
///
16-
/// ## Note
17-
///
18-
/// Initializing this resource will leak [`OutputStream`]
19-
/// using [`std::mem::forget`].
20-
/// This is done to avoid storing this in the struct (and making this `!Send`)
21-
/// while preventing it from dropping (to avoid halting of audio).
22-
///
23-
/// This is fine when initializing this once (as is default when adding this plugin),
24-
/// since the memory cost will be the same.
25-
/// However, repeatedly inserting this resource into the app will **leak more memory**.
2615
#[derive(Resource)]
2716
pub(crate) struct AudioOutput {
28-
stream_handle: Option<OutputStreamHandle>,
17+
stream: Option<MixerDeviceSink>,
2918
}
3019

3120
impl Default for AudioOutput {
3221
fn default() -> Self {
33-
if let Ok((stream, stream_handle)) = OutputStream::try_default() {
34-
// We leak `OutputStream` to prevent the audio from stopping.
35-
core::mem::forget(stream);
36-
Self {
37-
stream_handle: Some(stream_handle),
38-
}
39-
} else {
40-
warn!("No audio device found.");
41-
Self {
42-
stream_handle: None,
43-
}
44-
}
22+
let stream = DeviceSinkBuilder::open_default_sink()
23+
.inspect_err(|_err| {
24+
warn!("No audio device found.");
25+
})
26+
.ok();
27+
Self { stream }
4528
}
4629
}
4730

@@ -111,12 +94,13 @@ pub(crate) fn play_queued_audio_system<Source: Asset + Decodable>(
11194
default_spatial_scale: Res<DefaultSpatialScale>,
11295
mut commands: Commands,
11396
) where
114-
f32: rodio::cpal::FromSample<Source::DecoderItem>,
97+
f32: rodio::cpal::FromSample<rodio::Sample>,
11598
{
116-
let Some(stream_handle) = audio_output.stream_handle.as_ref() else {
99+
let Some(stream) = audio_output.stream.as_ref() else {
117100
// audio output unavailable; cannot play sound
118101
return;
119102
};
103+
let mixer = stream.mixer();
120104

121105
for (entity, source_handle, settings, maybe_emitter_transform) in &query_nonplaying {
122106
let Some(audio_source) = audio_sources.get(&source_handle.0) else {
@@ -144,18 +128,12 @@ pub(crate) fn play_queued_audio_system<Source: Asset + Decodable>(
144128
Vec3::ZERO.into()
145129
};
146130

147-
let sink = match SpatialSink::try_new(
148-
stream_handle,
131+
let sink = SpatialPlayer::connect_new(
132+
mixer,
149133
emitter_translation,
150134
(left_ear * scale).into(),
151135
(right_ear * scale).into(),
152-
) {
153-
Ok(sink) => sink,
154-
Err(err) => {
155-
warn!("Error creating spatial sink: {err:?}");
156-
continue;
157-
}
158-
};
136+
);
159137

160138
let decoder = audio_source.decoder();
161139

@@ -226,14 +204,7 @@ pub(crate) fn play_queued_audio_system<Source: Asset + Decodable>(
226204
.insert((sink, PlaybackRemoveMarker)),
227205
};
228206
} else {
229-
let sink = match Sink::try_new(stream_handle) {
230-
Ok(sink) => sink,
231-
Err(err) => {
232-
warn!("Error creating sink: {err:?}");
233-
continue;
234-
}
235-
};
236-
207+
let sink = Player::connect_new(mixer);
237208
let decoder = audio_source.decoder();
238209

239210
match settings.mode {
@@ -359,7 +330,7 @@ pub(crate) fn cleanup_finished_audio<T: Decodable + Asset>(
359330

360331
/// Run Condition to only play audio if the audio output is available
361332
pub(crate) fn audio_output_available(audio_output: Res<AudioOutput>) -> bool {
362-
audio_output.stream_handle.is_some()
333+
audio_output.stream.is_some()
363334
}
364335

365336
/// Updates spatial audio sinks when emitter positions change.

crates/bevy_audio/src/audio_source.rs

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,11 @@ impl AsRef<[u8]> for AudioSource {
3030
///
3131
/// This asset loader supports different audio formats based on the enable Bevy features.
3232
/// The feature `bevy/vorbis` enables loading from `.ogg` files and is enabled by default.
33-
/// Other file endings can be loaded from with additional features:
33+
/// Other file extensions can be loaded from with additional features:
3434
/// `.mp3` with `bevy/mp3`
35-
/// `.flac` with `bevy/flac`
36-
/// `.wav` with `bevy/wav`
35+
/// `.flac` with `bevy/flac` or `bevy/symphonia-flac`
36+
/// `.wav` with `bevy/wav` or `bevy/symphonia-wav`
37+
/// The `bevy/audio-all-formats` feature collection will enable all supported audio formats.
3738
#[derive(Default, TypePath)]
3839
pub struct AudioLoader;
3940

@@ -59,15 +60,15 @@ impl AssetLoader for AudioLoader {
5960
&[
6061
#[cfg(feature = "mp3")]
6162
"mp3",
62-
#[cfg(feature = "flac")]
63+
#[cfg(any(feature = "flac", feature = "symphonia-flac"))]
6364
"flac",
64-
#[cfg(feature = "wav")]
65+
#[cfg(any(feature = "wav", feature = "symphonia-wav"))]
6566
"wav",
66-
#[cfg(feature = "vorbis")]
67+
#[cfg(any(feature = "vorbis", feature = "symphonia-vorbis"))]
6768
"oga",
68-
#[cfg(feature = "vorbis")]
69+
#[cfg(any(feature = "vorbis", feature = "symphonia-vorbis"))]
6970
"ogg",
70-
#[cfg(feature = "vorbis")]
71+
#[cfg(any(feature = "vorbis", feature = "symphonia-vorbis"))]
7172
"spx",
7273
]
7374
}
@@ -80,22 +81,16 @@ impl AssetLoader for AudioLoader {
8081
/// This trait is implemented for [`AudioSource`].
8182
/// Check the example [`decodable`](https://github.com/bevyengine/bevy/blob/latest/examples/audio/decodable.rs) for how to implement this trait on a custom type.
8283
pub trait Decodable: Send + Sync + 'static {
83-
/// The type of the audio samples.
84-
/// Usually a [`u16`], [`i16`] or [`f32`], as those implement [`rodio::Sample`].
85-
/// Other types can implement the [`rodio::Sample`] trait as well.
86-
type DecoderItem: rodio::Sample + Send + Sync;
87-
8884
/// The type of the iterator of the audio samples,
89-
/// which iterates over samples of type [`Self::DecoderItem`].
85+
/// which iterates over samples of type [`rodio::Sample`].
9086
/// Must be a [`rodio::Source`] so that it can provide information on the audio it is iterating over.
91-
type Decoder: rodio::Source + Send + Iterator<Item = Self::DecoderItem>;
87+
type Decoder: rodio::Source + Send + Iterator<Item = rodio::Sample>;
9288

9389
/// Build and return a [`Self::Decoder`] of the implementing type
9490
fn decoder(&self) -> Self::Decoder;
9591
}
9692

9793
impl Decodable for AudioSource {
98-
type DecoderItem = <rodio::Decoder<Cursor<AudioSource>> as Iterator>::Item;
9994
type Decoder = rodio::Decoder<Cursor<AudioSource>>;
10095

10196
fn decoder(&self) -> Self::Decoder {
@@ -115,5 +110,5 @@ pub trait AddAudioSource {
115110
fn add_audio_source<T>(&mut self) -> &mut Self
116111
where
117112
T: Decodable + Asset,
118-
f32: rodio::cpal::FromSample<T::DecoderItem>;
113+
f32: rodio::cpal::FromSample<rodio::Sample>;
119114
}

crates/bevy_audio/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ pub use audio_source::*;
5252
pub use pitch::*;
5353
pub use volume::*;
5454

55-
pub use rodio::{cpal::Sample as CpalSample, source::Source, Sample};
55+
pub use rodio::{cpal::Sample as CpalSample, source::Source, ChannelCount, Sample, SampleRate};
5656
pub use sinks::*;
5757

5858
use bevy_app::prelude::*;
@@ -108,7 +108,7 @@ impl AddAudioSource for App {
108108
fn add_audio_source<T>(&mut self) -> &mut Self
109109
where
110110
T: Decodable + Asset,
111-
f32: rodio::cpal::FromSample<T::DecoderItem>,
111+
f32: rodio::cpal::FromSample<Sample>,
112112
{
113113
self.init_asset::<T>().add_systems(
114114
PostUpdate,

crates/bevy_audio/src/pitch.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ impl Pitch {
2626
}
2727

2828
impl Decodable for Pitch {
29-
type DecoderItem = <SineWave as Iterator>::Item;
3029
type Decoder = TakeDuration<SineWave>;
3130

3231
fn decoder(&self) -> Self::Decoder {

0 commit comments

Comments
 (0)