Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - Complete inline documentation for bevy_audio #3510

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions crates/bevy_audio/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
44 changes: 31 additions & 13 deletions crates/bevy_audio/src/audio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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);
}
}
32 changes: 14 additions & 18 deletions crates/bevy_audio/src/audio_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -37,21 +37,19 @@ 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());
sink.detach();
}
}

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;
Expand All @@ -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);
};
}
22 changes: 18 additions & 4 deletions crates/bevy_audio/src/audio_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]>,
}

Expand All @@ -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(),
Expand All @@ -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()
Expand Down
37 changes: 35 additions & 2 deletions crates/bevy_audio/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -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;

Expand All @@ -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>();
}
}