mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
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
6 changed files with 83 additions and 43 deletions
|
@ -15,6 +15,9 @@
|
|||
- 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.
|
||||
- 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
|
||||
[689]: https://github.com/bevyengine/bevy/pull/689
|
||||
|
|
|
@ -22,7 +22,7 @@ bevy_utils = { path = "../bevy_utils", version = "0.2.1" }
|
|||
|
||||
# other
|
||||
anyhow = "1.0"
|
||||
rodio = { version = "0.11", default-features = false }
|
||||
rodio = { version = "0.12", default-features = false }
|
||||
parking_lot = "0.11.0"
|
||||
|
||||
[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 bevy_asset::{Asset, Assets, Handle};
|
||||
use bevy_ecs::Res;
|
||||
use parking_lot::RwLock;
|
||||
use rodio::{Device, Sink};
|
||||
use std::{collections::VecDeque, fmt};
|
||||
use crate::{Audio, AudioSource, Decodable};
|
||||
use bevy_asset::{Asset, Assets};
|
||||
use bevy_ecs::{Resources, World};
|
||||
use rodio::{OutputStream, OutputStreamHandle, Sink};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// Used to play audio on the current "audio device"
|
||||
/// Used internally to play audio on the current "audio device"
|
||||
pub struct AudioOutput<P = AudioSource>
|
||||
where
|
||||
P: Decodable,
|
||||
{
|
||||
device: Device,
|
||||
queue: RwLock<VecDeque<Handle<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()
|
||||
}
|
||||
_stream: OutputStream,
|
||||
stream_handle: OutputStreamHandle,
|
||||
phantom: PhantomData<P>,
|
||||
}
|
||||
|
||||
impl<P> Default for AudioOutput<P>
|
||||
|
@ -30,9 +19,12 @@ where
|
|||
P: Decodable,
|
||||
{
|
||||
fn default() -> Self {
|
||||
let (stream, stream_handle) = OutputStream::try_default().unwrap();
|
||||
|
||||
Self {
|
||||
device: rodio::default_output_device().unwrap(),
|
||||
queue: Default::default(),
|
||||
_stream: stream,
|
||||
stream_handle,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,18 +35,14 @@ where
|
|||
<P as Decodable>::Decoder: rodio::Source + Send + Sync,
|
||||
<<P as Decodable>::Decoder as Iterator>::Item: rodio::Sample + Send + Sync,
|
||||
{
|
||||
pub fn play_source(&self, audio_source: &P) {
|
||||
let sink = Sink::new(&self.device);
|
||||
fn play_source(&self, audio_source: &P) {
|
||||
let sink = Sink::try_new(&self.stream_handle).unwrap();
|
||||
sink.append(audio_source.decoder());
|
||||
sink.detach();
|
||||
}
|
||||
|
||||
pub fn play(&self, audio_source: Handle<P>) {
|
||||
self.queue.write().push_front(audio_source);
|
||||
}
|
||||
|
||||
pub fn try_play_queued(&self, audio_sources: &Assets<P>) {
|
||||
let mut queue = self.queue.write();
|
||||
fn try_play_queued(&self, audio_sources: &Assets<P>, audio: &mut Audio<P>) {
|
||||
let mut queue = audio.queue.write();
|
||||
let len = queue.len();
|
||||
let mut i = 0;
|
||||
while i < len {
|
||||
|
@ -70,14 +58,17 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Plays audio currently queued in the [AudioOutput] resource
|
||||
pub fn play_queued_audio_system<P: Asset>(
|
||||
audio_sources: Res<Assets<P>>,
|
||||
audio_output: Res<AudioOutput<P>>,
|
||||
) where
|
||||
/// Plays audio currently queued in the [Audio] resource through the [AudioOutput] resource
|
||||
pub fn play_queued_audio_system<P: Asset>(_world: &mut World, resources: &mut Resources)
|
||||
where
|
||||
P: Decodable,
|
||||
<P as Decodable>::Decoder: rodio::Source + 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_source;
|
||||
|
||||
pub use audio::*;
|
||||
pub use audio_output::*;
|
||||
pub use audio_source::*;
|
||||
|
||||
pub mod prelude {
|
||||
pub use crate::{AudioOutput, AudioSource, Decodable};
|
||||
pub use crate::{Audio, AudioOutput, AudioSource, Decodable};
|
||||
}
|
||||
|
||||
use bevy_app::prelude::*;
|
||||
use bevy_asset::AddAsset;
|
||||
use bevy_ecs::IntoQuerySystem;
|
||||
use bevy_ecs::IntoThreadLocalSystem;
|
||||
|
||||
/// Adds support for audio playback to an App
|
||||
#[derive(Default)]
|
||||
|
@ -18,12 +20,13 @@ pub struct AudioPlugin;
|
|||
|
||||
impl Plugin for AudioPlugin {
|
||||
fn build(&self, app: &mut AppBuilder) {
|
||||
app.init_resource::<AudioOutput<AudioSource>>()
|
||||
app.init_thread_local_resource::<AudioOutput<AudioSource>>()
|
||||
.add_asset::<AudioSource>()
|
||||
.init_asset_loader::<Mp3Loader>()
|
||||
.init_resource::<Audio<AudioSource>>()
|
||||
.add_system_to_stage(
|
||||
stage::POST_UPDATE,
|
||||
play_queued_audio_system::<AudioSource>.system(),
|
||||
play_queued_audio_system::<AudioSource>.thread_local_system(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ fn main() {
|
|||
.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");
|
||||
audio_output.play(music);
|
||||
audio.play(music);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue