diff --git a/crates/bevy_audio/Cargo.toml b/crates/bevy_audio/Cargo.toml
index ca5c2556fae6e..d4e60d86ac777 100644
--- a/crates/bevy_audio/Cargo.toml
+++ b/crates/bevy_audio/Cargo.toml
@@ -24,6 +24,10 @@ parking_lot = "0.11.0"
[target.'cfg(target_arch = "wasm32")'.dependencies]
rodio = { version = "0.14", default-features = false, features = ["wasm-bindgen"] }
+[dev-dependencies]
+# bevy
+bevy_internal = { path = "../bevy_internal", version = "0.5.0" }
+
[features]
mp3 = ["rodio/mp3"]
flac = ["rodio/flac"]
diff --git a/crates/bevy_audio/src/audio.rs b/crates/bevy_audio/src/audio.rs
index 7a7fe12b3f99f..1a8ae32b88d9c 100644
--- a/crates/bevy_audio/src/audio.rs
+++ b/crates/bevy_audio/src/audio.rs
@@ -3,26 +3,36 @@ use bevy_asset::{Asset, Handle};
use parking_lot::RwLock;
use std::{collections::VecDeque, fmt};
-/// The external struct used to play audio
-pub struct Audio
+/// Use this resource to play audio
+///
+/// ```
+/// # use bevy_ecs::system::Res;
+/// # use bevy_asset::AssetServer;
+/// # use bevy_audio::Audio;
+/// fn play_audio_system(asset_server: Res, audio: Res) {
+/// audio.play(asset_server.load("my_sound.ogg"));
+/// }
+/// ```
+pub struct Audio
where
- P: Asset + Decodable,
+ Source: Asset + Decodable,
{
- pub queue: RwLock>>,
+ /// Queue for playing audio from asset handles
+ pub queue: RwLock>>,
}
-impl fmt::Debug for Audio
+impl fmt::Debug for Audio
where
- P: Decodable,
+ Source: Decodable,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Audio").field("queue", &self.queue).finish()
}
}
-impl Default for Audio
+impl Default for Audio
where
- P: Asset + Decodable,
+ Source: Asset + Decodable,
{
fn default() -> Self {
Self {
@@ -31,13 +41,21 @@ where
}
}
-impl Audio
+impl Audio
where
- P: Asset + Decodable,
- ::Decoder: rodio::Source + Send + Sync,
- <
::Decoder as Iterator>::Item: rodio::Sample + Send + Sync,
+ Source: Asset + Decodable,
{
- pub fn play(&self, audio_source: Handle
) {
+ /// Play audio from a [`Handle`] to the audio source
+ ///
+ /// ```
+ /// # use bevy_ecs::system::Res;
+ /// # use bevy_asset::AssetServer;
+ /// # use bevy_audio::Audio;
+ /// fn play_audio_system(asset_server: Res, audio: Res) {
+ /// audio.play(asset_server.load("my_sound.ogg"));
+ /// }
+ /// ```
+ pub fn play(&self, audio_source: Handle) {
self.queue.write().push_front(audio_source);
}
}
diff --git a/crates/bevy_audio/src/audio_output.rs b/crates/bevy_audio/src/audio_output.rs
index 404c388021a85..385b7d5fdc580 100644
--- a/crates/bevy_audio/src/audio_output.rs
+++ b/crates/bevy_audio/src/audio_output.rs
@@ -6,18 +6,18 @@ use rodio::{OutputStream, OutputStreamHandle, Sink};
use std::marker::PhantomData;
/// Used internally to play audio on the current "audio device"
-pub struct AudioOutput
+pub struct AudioOutput
where
- P: Decodable,
+ Source: Decodable,
{
_stream: Option,
stream_handle: Option,
- phantom: PhantomData,
+ phantom: PhantomData,
}
-impl Default for AudioOutput
+impl Default for AudioOutput
where
- P: Decodable,
+ Source: Decodable,
{
fn default() -> Self {
if let Ok((stream, stream_handle)) = OutputStream::try_default() {
@@ -37,13 +37,11 @@ where
}
}
-impl AudioOutput
+impl AudioOutput
where
- P: Asset + Decodable,
- ::Decoder: rodio::Source + Send + Sync,
- <
::Decoder as Iterator>::Item: rodio::Sample + Send + Sync,
+ Source: Asset + Decodable,
{
- fn play_source(&self, audio_source: &P) {
+ fn play_source(&self, audio_source: &Source) {
if let Some(stream_handle) = &self.stream_handle {
let sink = Sink::try_new(stream_handle).unwrap();
sink.append(audio_source.decoder());
@@ -51,7 +49,7 @@ where
}
}
- fn try_play_queued(&self, audio_sources: &Assets
, audio: &mut Audio
) {
+ fn try_play_queued(&self, audio_sources: &Assets, audio: &mut Audio) {
let mut queue = audio.queue.write();
let len = queue.len();
let mut i = 0;
@@ -69,17 +67,15 @@ where
}
/// Plays audio currently queued in the [`Audio`] resource through the [`AudioOutput`] resource
-pub fn play_queued_audio_system(world: &mut World)
+pub fn play_queued_audio_system(world: &mut World)
where
- P: Decodable,
- ::Decoder: rodio::Source + Send + Sync,
- <
::Decoder as Iterator>::Item: rodio::Sample + Send + Sync,
+ Source: Decodable,
{
let world = world.cell();
- let audio_output = world.get_non_send::>().unwrap();
- let mut audio = world.get_resource_mut::>().unwrap();
+ let audio_output = world.get_non_send::>().unwrap();
+ let mut audio = world.get_resource_mut::>().unwrap();
- if let Some(audio_sources) = world.get_resource::>() {
+ if let Some(audio_sources) = world.get_resource::>() {
audio_output.try_play_queued(&*audio_sources, &mut *audio);
};
}
diff --git a/crates/bevy_audio/src/audio_source.rs b/crates/bevy_audio/src/audio_source.rs
index 0aa9d95f186bc..823526046c7c3 100644
--- a/crates/bevy_audio/src/audio_source.rs
+++ b/crates/bevy_audio/src/audio_source.rs
@@ -8,6 +8,7 @@ use std::{io::Cursor, sync::Arc};
#[derive(Debug, Clone, TypeUuid)]
#[uuid = "7a14806a-672b-443b-8d16-4f18afefa463"]
pub struct AudioSource {
+ /// Raw data of the audio source
pub bytes: Arc<[u8]>,
}
@@ -17,11 +18,18 @@ impl AsRef<[u8]> for AudioSource {
}
}
-/// Loads mp3 files as [`AudioSource`] [`Assets`](bevy_asset::Assets)
+/// Loads files as [`AudioSource`] [`Assets`](bevy_asset::Assets)
+///
+/// This asset loader supports different audio formats based on the enable Bevy features.
+/// The feature `bevy/vorbis` enables loading from `.ogg` files and is enabled by default.
+/// Other file endings can be loaded from with additional features:
+/// `.mp3` with `bevy/mp3`
+/// `.flac` with `bevy/flac`
+/// `.wav` with `bevy/wav`
#[derive(Default)]
-pub struct Mp3Loader;
+pub struct AudioLoader;
-impl AssetLoader for Mp3Loader {
+impl AssetLoader for AudioLoader {
fn load(&self, bytes: &[u8], load_context: &mut LoadContext) -> BoxedFuture> {
load_context.set_default_asset(LoadedAsset::new(AudioSource {
bytes: bytes.into(),
@@ -43,14 +51,20 @@ impl AssetLoader for Mp3Loader {
}
}
+/// A type implementing this trait can be decoded as a rodio source
pub trait Decodable: Send + Sync + 'static {
- type Decoder;
+ /// The decoder that can decode the implemeting type
+ type Decoder: rodio::Source + Send + Sync + Iterator- ;
+ /// A single value given by the decoder
+ type DecoderItem: rodio::Sample + Send + Sync;
+ /// Build and return a [`Self::Decoder`] for the implementing type
fn decoder(&self) -> Self::Decoder;
}
impl Decodable for AudioSource {
type Decoder = rodio::Decoder
>;
+ type DecoderItem = > as Iterator>::Item;
fn decoder(&self) -> Self::Decoder {
rodio::Decoder::new(Cursor::new(self.clone())).unwrap()
diff --git a/crates/bevy_audio/src/lib.rs b/crates/bevy_audio/src/lib.rs
index 5f44d68f9f40b..0bf910cbba9be 100644
--- a/crates/bevy_audio/src/lib.rs
+++ b/crates/bevy_audio/src/lib.rs
@@ -1,7 +1,38 @@
+//! Audio support for the game engine Bevy
+//!
+//! ```
+//! # use bevy_ecs::{system::Res, event::EventWriter};
+//! # use bevy_audio::{Audio, AudioPlugin};
+//! # use bevy_asset::{AssetPlugin, AssetServer};
+//! # use bevy_app::{App, AppExit};
+//! # use bevy_internal::MinimalPlugins;
+//! fn main() {
+//! App::new()
+//! .add_plugins(MinimalPlugins)
+//! .add_plugin(AssetPlugin)
+//! .add_plugin(AudioPlugin)
+//! # .add_system(stop)
+//! .add_startup_system(play_background_audio)
+//! .run();
+//! }
+//!
+//! fn play_background_audio(asset_server: Res, audio: Res) {
+//! audio.play(asset_server.load("background_audio.ogg"));
+//! }
+//!
+//! # fn stop(mut events: EventWriter) {
+//! # events.send(AppExit)
+//! # }
+//! ```
+
+#![forbid(unsafe_code)]
+#![warn(missing_docs)]
+
mod audio;
mod audio_output;
mod audio_source;
+#[allow(missing_docs)]
pub mod prelude {
#[doc(hidden)]
pub use crate::{Audio, AudioOutput, AudioSource, Decodable};
@@ -15,7 +46,9 @@ use bevy_app::prelude::*;
use bevy_asset::AddAsset;
use bevy_ecs::system::IntoExclusiveSystem;
-/// Adds support for audio playback to an App
+/// Adds support for audio playback to a Bevy Application
+///
+/// Use the [`Audio`] resource to play audio.
#[derive(Default)]
pub struct AudioPlugin;
@@ -30,6 +63,6 @@ impl Plugin for AudioPlugin {
);
#[cfg(any(feature = "mp3", feature = "flac", feature = "wav", feature = "vorbis"))]
- app.init_asset_loader::();
+ app.init_asset_loader::();
}
}