Migrate to rodio 0.12 using thread local resources (#692)
Migrate to rodio 0.12 using thread local resources
This commit is contained in:
parent
67f87e1d2b
commit
0dbba3efff
@ -15,6 +15,9 @@
|
|||||||
- New methods `Color::rgb_linear` and `Color::rgba_linear` will accept colors already in linear sRGB (the old behavior)
|
- New methods `Color::rgb_linear` and `Color::rgba_linear` will accept colors already in linear sRGB (the old behavior)
|
||||||
- Individual color-components must now be accessed through setters and getters: `.r`, `.g`, `.b`, `.a`, `.set_r`, `.set_g`, `.set_b`, `.set_a`, and the corresponding methods with the `*_linear` suffix.
|
- Individual color-components must now be accessed through setters and getters: `.r`, `.g`, `.b`, `.a`, `.set_r`, `.set_g`, `.set_b`, `.set_a`, and the corresponding methods with the `*_linear` suffix.
|
||||||
- Despawning an entity multiple times causes a debug-level log message to be emitted instead of a panic [649] [651]
|
- Despawning an entity multiple times causes a debug-level log message to be emitted instead of a panic [649] [651]
|
||||||
|
- Breaking Change: Migrated to rodio 0.12, this means:
|
||||||
|
- Playing an mp3 no longer sometimes panics in debug mode
|
||||||
|
- New method of playing audio can be found in the audio example (an intermediary `Audio` struct is used instead of `AudioOutput` directly)
|
||||||
|
|
||||||
[696]: https://github.com/bevyengine/bevy/pull/696
|
[696]: https://github.com/bevyengine/bevy/pull/696
|
||||||
[689]: https://github.com/bevyengine/bevy/pull/689
|
[689]: https://github.com/bevyengine/bevy/pull/689
|
||||||
|
@ -22,7 +22,7 @@ bevy_utils = { path = "../bevy_utils", version = "0.2.1" }
|
|||||||
|
|
||||||
# other
|
# other
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
rodio = { version = "0.11", default-features = false }
|
rodio = { version = "0.12", default-features = false }
|
||||||
parking_lot = "0.11.0"
|
parking_lot = "0.11.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
43
crates/bevy_audio/src/audio.rs
Normal file
43
crates/bevy_audio/src/audio.rs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
use crate::{AudioSource, Decodable};
|
||||||
|
use bevy_asset::Handle;
|
||||||
|
use parking_lot::RwLock;
|
||||||
|
use std::{collections::VecDeque, fmt};
|
||||||
|
|
||||||
|
/// The external struct used to play audio
|
||||||
|
pub struct Audio<P = AudioSource>
|
||||||
|
where
|
||||||
|
P: Decodable,
|
||||||
|
{
|
||||||
|
pub queue: RwLock<VecDeque<Handle<P>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P> fmt::Debug for Audio<P>
|
||||||
|
where
|
||||||
|
P: Decodable,
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
f.debug_struct("Audio").field("queue", &self.queue).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P> Default for Audio<P>
|
||||||
|
where
|
||||||
|
P: Decodable,
|
||||||
|
{
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
queue: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P> Audio<P>
|
||||||
|
where
|
||||||
|
P: Decodable,
|
||||||
|
<P as Decodable>::Decoder: rodio::Source + Send + Sync,
|
||||||
|
<<P as Decodable>::Decoder as Iterator>::Item: rodio::Sample + Send + Sync,
|
||||||
|
{
|
||||||
|
pub fn play(&self, audio_source: Handle<P>) {
|
||||||
|
self.queue.write().push_front(audio_source);
|
||||||
|
}
|
||||||
|
}
|
@ -1,28 +1,17 @@
|
|||||||
use crate::{AudioSource, Decodable};
|
use crate::{Audio, AudioSource, Decodable};
|
||||||
use bevy_asset::{Asset, Assets, Handle};
|
use bevy_asset::{Asset, Assets};
|
||||||
use bevy_ecs::Res;
|
use bevy_ecs::{Resources, World};
|
||||||
use parking_lot::RwLock;
|
use rodio::{OutputStream, OutputStreamHandle, Sink};
|
||||||
use rodio::{Device, Sink};
|
use std::marker::PhantomData;
|
||||||
use std::{collections::VecDeque, fmt};
|
|
||||||
|
|
||||||
/// Used to play audio on the current "audio device"
|
/// Used internally to play audio on the current "audio device"
|
||||||
pub struct AudioOutput<P = AudioSource>
|
pub struct AudioOutput<P = AudioSource>
|
||||||
where
|
where
|
||||||
P: Decodable,
|
P: Decodable,
|
||||||
{
|
{
|
||||||
device: Device,
|
_stream: OutputStream,
|
||||||
queue: RwLock<VecDeque<Handle<P>>>,
|
stream_handle: OutputStreamHandle,
|
||||||
}
|
phantom: PhantomData<P>,
|
||||||
|
|
||||||
impl<P> fmt::Debug for AudioOutput<P>
|
|
||||||
where
|
|
||||||
P: Decodable,
|
|
||||||
{
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
f.debug_struct("AudioOutput")
|
|
||||||
.field("queue", &self.queue)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P> Default for AudioOutput<P>
|
impl<P> Default for AudioOutput<P>
|
||||||
@ -30,9 +19,12 @@ where
|
|||||||
P: Decodable,
|
P: Decodable,
|
||||||
{
|
{
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
let (stream, stream_handle) = OutputStream::try_default().unwrap();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
device: rodio::default_output_device().unwrap(),
|
_stream: stream,
|
||||||
queue: Default::default(),
|
stream_handle,
|
||||||
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -43,18 +35,14 @@ where
|
|||||||
<P as Decodable>::Decoder: rodio::Source + Send + Sync,
|
<P as Decodable>::Decoder: rodio::Source + Send + Sync,
|
||||||
<<P as Decodable>::Decoder as Iterator>::Item: rodio::Sample + Send + Sync,
|
<<P as Decodable>::Decoder as Iterator>::Item: rodio::Sample + Send + Sync,
|
||||||
{
|
{
|
||||||
pub fn play_source(&self, audio_source: &P) {
|
fn play_source(&self, audio_source: &P) {
|
||||||
let sink = Sink::new(&self.device);
|
let sink = Sink::try_new(&self.stream_handle).unwrap();
|
||||||
sink.append(audio_source.decoder());
|
sink.append(audio_source.decoder());
|
||||||
sink.detach();
|
sink.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn play(&self, audio_source: Handle<P>) {
|
fn try_play_queued(&self, audio_sources: &Assets<P>, audio: &mut Audio<P>) {
|
||||||
self.queue.write().push_front(audio_source);
|
let mut queue = audio.queue.write();
|
||||||
}
|
|
||||||
|
|
||||||
pub fn try_play_queued(&self, audio_sources: &Assets<P>) {
|
|
||||||
let mut queue = self.queue.write();
|
|
||||||
let len = queue.len();
|
let len = queue.len();
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
while i < len {
|
while i < len {
|
||||||
@ -70,14 +58,17 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Plays audio currently queued in the [AudioOutput] resource
|
/// Plays audio currently queued in the [Audio] resource through the [AudioOutput] resource
|
||||||
pub fn play_queued_audio_system<P: Asset>(
|
pub fn play_queued_audio_system<P: Asset>(_world: &mut World, resources: &mut Resources)
|
||||||
audio_sources: Res<Assets<P>>,
|
where
|
||||||
audio_output: Res<AudioOutput<P>>,
|
|
||||||
) where
|
|
||||||
P: Decodable,
|
P: Decodable,
|
||||||
<P as Decodable>::Decoder: rodio::Source + Send + Sync,
|
<P as Decodable>::Decoder: rodio::Source + Send + Sync,
|
||||||
<<P as Decodable>::Decoder as Iterator>::Item: rodio::Sample + Send + Sync,
|
<<P as Decodable>::Decoder as Iterator>::Item: rodio::Sample + Send + Sync,
|
||||||
{
|
{
|
||||||
audio_output.try_play_queued(&audio_sources);
|
let audio_output = resources.get_thread_local::<AudioOutput<P>>().unwrap();
|
||||||
|
let mut audio = resources.get_mut::<Audio<P>>().unwrap();
|
||||||
|
|
||||||
|
if let Some(audio_sources) = resources.get::<Assets<P>>() {
|
||||||
|
audio_output.try_play_queued(&*audio_sources, &mut *audio);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
|
mod audio;
|
||||||
mod audio_output;
|
mod audio_output;
|
||||||
mod audio_source;
|
mod audio_source;
|
||||||
|
|
||||||
|
pub use audio::*;
|
||||||
pub use audio_output::*;
|
pub use audio_output::*;
|
||||||
pub use audio_source::*;
|
pub use audio_source::*;
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::{AudioOutput, AudioSource, Decodable};
|
pub use crate::{Audio, AudioOutput, AudioSource, Decodable};
|
||||||
}
|
}
|
||||||
|
|
||||||
use bevy_app::prelude::*;
|
use bevy_app::prelude::*;
|
||||||
use bevy_asset::AddAsset;
|
use bevy_asset::AddAsset;
|
||||||
use bevy_ecs::IntoQuerySystem;
|
use bevy_ecs::IntoThreadLocalSystem;
|
||||||
|
|
||||||
/// Adds support for audio playback to an App
|
/// Adds support for audio playback to an App
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -18,12 +20,13 @@ pub struct AudioPlugin;
|
|||||||
|
|
||||||
impl Plugin for AudioPlugin {
|
impl Plugin for AudioPlugin {
|
||||||
fn build(&self, app: &mut AppBuilder) {
|
fn build(&self, app: &mut AppBuilder) {
|
||||||
app.init_resource::<AudioOutput<AudioSource>>()
|
app.init_thread_local_resource::<AudioOutput<AudioSource>>()
|
||||||
.add_asset::<AudioSource>()
|
.add_asset::<AudioSource>()
|
||||||
.init_asset_loader::<Mp3Loader>()
|
.init_asset_loader::<Mp3Loader>()
|
||||||
|
.init_resource::<Audio<AudioSource>>()
|
||||||
.add_system_to_stage(
|
.add_system_to_stage(
|
||||||
stage::POST_UPDATE,
|
stage::POST_UPDATE,
|
||||||
play_queued_audio_system::<AudioSource>.system(),
|
play_queued_audio_system::<AudioSource>.thread_local_system(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ fn main() {
|
|||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup(asset_server: Res<AssetServer>, audio_output: Res<AudioOutput>) {
|
fn setup(asset_server: Res<AssetServer>, audio: Res<Audio>) {
|
||||||
let music = asset_server.load("sounds/Windless Slopes.mp3");
|
let music = asset_server.load("sounds/Windless Slopes.mp3");
|
||||||
audio_output.play(music);
|
audio.play(music);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user