Complete inline documentation for bevy_audio (#3510)
# Objective Part of #3492 - Complete inline documentation of `bevy_audio` ## Solution - Added inline documentation to all public parts of `bevy_audio` - Added a few inline examples at important places - Some renaming for clarity (e.g. `AudioLoader` and generics) - added `#![warn(missing_docs)]` and `#![forbid(unsafe_code)]` to `bevy_audio` I also tried adding support for the other vorbis file endings `.oga` and `.spx` to the `AudioLoader` (see `file endings` at https://tools.ietf.org/html/rfc5334#section-10.3), but the `rodio` decoder does not seem to support those.
This commit is contained in:
parent
251514017f
commit
f9b51ca602
@ -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"]
|
||||
|
@ -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<P = AudioSource>
|
||||
/// 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<AssetServer>, audio: Res<Audio>) {
|
||||
/// audio.play(asset_server.load("my_sound.ogg"));
|
||||
/// }
|
||||
/// ```
|
||||
pub struct Audio<Source = AudioSource>
|
||||
where
|
||||
P: Asset + Decodable,
|
||||
Source: Asset + Decodable,
|
||||
{
|
||||
pub queue: RwLock<VecDeque<Handle<P>>>,
|
||||
/// Queue for playing audio from asset handles
|
||||
pub queue: RwLock<VecDeque<Handle<Source>>>,
|
||||
}
|
||||
|
||||
impl<P: Asset> fmt::Debug for Audio<P>
|
||||
impl<Source: Asset> fmt::Debug for Audio<Source>
|
||||
where
|
||||
P: Decodable,
|
||||
Source: 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>
|
||||
impl<Source> Default for Audio<Source>
|
||||
where
|
||||
P: Asset + Decodable,
|
||||
Source: Asset + Decodable,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
@ -31,13 +41,21 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Audio<P>
|
||||
impl<Source> Audio<Source>
|
||||
where
|
||||
P: Asset + Decodable,
|
||||
<P as Decodable>::Decoder: rodio::Source + Send + Sync,
|
||||
<<P as Decodable>::Decoder as Iterator>::Item: rodio::Sample + Send + Sync,
|
||||
Source: Asset + Decodable,
|
||||
{
|
||||
pub fn play(&self, audio_source: Handle<P>) {
|
||||
/// 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<AssetServer>, audio: Res<Audio>) {
|
||||
/// audio.play(asset_server.load("my_sound.ogg"));
|
||||
/// }
|
||||
/// ```
|
||||
pub fn play(&self, audio_source: Handle<Source>) {
|
||||
self.queue.write().push_front(audio_source);
|
||||
}
|
||||
}
|
||||
|
@ -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<P = AudioSource>
|
||||
pub struct AudioOutput<Source = AudioSource>
|
||||
where
|
||||
P: Decodable,
|
||||
Source: Decodable,
|
||||
{
|
||||
_stream: Option<OutputStream>,
|
||||
stream_handle: Option<OutputStreamHandle>,
|
||||
phantom: PhantomData<P>,
|
||||
phantom: PhantomData<Source>,
|
||||
}
|
||||
|
||||
impl<P> Default for AudioOutput<P>
|
||||
impl<Source> Default for AudioOutput<Source>
|
||||
where
|
||||
P: Decodable,
|
||||
Source: Decodable,
|
||||
{
|
||||
fn default() -> Self {
|
||||
if let Ok((stream, stream_handle)) = OutputStream::try_default() {
|
||||
@ -37,13 +37,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> AudioOutput<P>
|
||||
impl<Source> AudioOutput<Source>
|
||||
where
|
||||
P: Asset + Decodable,
|
||||
<P as Decodable>::Decoder: rodio::Source + Send + Sync,
|
||||
<<P as Decodable>::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<P>, audio: &mut Audio<P>) {
|
||||
fn try_play_queued(&self, audio_sources: &Assets<Source>, audio: &mut Audio<Source>) {
|
||||
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<P: Asset>(world: &mut World)
|
||||
pub fn play_queued_audio_system<Source: Asset>(world: &mut World)
|
||||
where
|
||||
P: Decodable,
|
||||
<P as Decodable>::Decoder: rodio::Source + Send + Sync,
|
||||
<<P as Decodable>::Decoder as Iterator>::Item: rodio::Sample + Send + Sync,
|
||||
Source: Decodable,
|
||||
{
|
||||
let world = world.cell();
|
||||
let audio_output = world.get_non_send::<AudioOutput<P>>().unwrap();
|
||||
let mut audio = world.get_resource_mut::<Audio<P>>().unwrap();
|
||||
let audio_output = world.get_non_send::<AudioOutput<Source>>().unwrap();
|
||||
let mut audio = world.get_resource_mut::<Audio<Source>>().unwrap();
|
||||
|
||||
if let Some(audio_sources) = world.get_resource::<Assets<P>>() {
|
||||
if let Some(audio_sources) = world.get_resource::<Assets<Source>>() {
|
||||
audio_output.try_play_queued(&*audio_sources, &mut *audio);
|
||||
};
|
||||
}
|
||||
|
@ -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<Result<()>> {
|
||||
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<Item = Self::DecoderItem>;
|
||||
/// 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<Cursor<AudioSource>>;
|
||||
type DecoderItem = <rodio::Decoder<Cursor<AudioSource>> as Iterator>::Item;
|
||||
|
||||
fn decoder(&self) -> Self::Decoder {
|
||||
rodio::Decoder::new(Cursor::new(self.clone())).unwrap()
|
||||
|
@ -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<AssetServer>, audio: Res<Audio>) {
|
||||
//! audio.play(asset_server.load("background_audio.ogg"));
|
||||
//! }
|
||||
//!
|
||||
//! # fn stop(mut events: EventWriter<AppExit>) {
|
||||
//! # 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::<Mp3Loader>();
|
||||
app.init_asset_loader::<AudioLoader>();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user