mirror of
https://github.com/bevyengine/bevy
synced 2024-11-21 20:23:28 +00:00
Add AddAudioSource
trait and improve Decodable
docs (#6649)
# Objective - Fixes #6361 - Fixes #6362 - Fixes #6364 ## Solution - Added an example for creating a custom `Decodable` type - Clarified the documentation on `Decodable` - Added an `AddAudioSource` trait and implemented it for `App` Co-authored-by: dis-da-moe <84386186+dis-da-moe@users.noreply.github.com>
This commit is contained in:
parent
7d0edbc4d6
commit
0df67cdaae
5 changed files with 155 additions and 7 deletions
10
Cargo.toml
10
Cargo.toml
|
@ -763,6 +763,16 @@ description = "Shows how to load and play an audio file, and control how it's pl
|
|||
category = "Audio"
|
||||
wasm = true
|
||||
|
||||
[[example]]
|
||||
name = "decodable"
|
||||
path = "examples/audio/decodable.rs"
|
||||
|
||||
[package.metadata.example.decodable]
|
||||
name = "Decodable"
|
||||
description = "Shows how to create and register a custom audio source by implementing the `Decodable` type."
|
||||
category = "Audio"
|
||||
wasm = true
|
||||
|
||||
# Diagnostics
|
||||
[[example]]
|
||||
name = "log_diagnostics"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use anyhow::Result;
|
||||
use bevy_asset::{AssetLoader, LoadContext, LoadedAsset};
|
||||
use bevy_asset::{Asset, AssetLoader, LoadContext, LoadedAsset};
|
||||
use bevy_reflect::TypeUuid;
|
||||
use bevy_utils::BoxedFuture;
|
||||
use std::{io::Cursor, sync::Arc};
|
||||
|
@ -63,14 +63,24 @@ impl AssetLoader for AudioLoader {
|
|||
}
|
||||
}
|
||||
|
||||
/// A type implementing this trait can be decoded as a rodio source
|
||||
/// A type implementing this trait can be converted to a [`rodio::Source`] type.
|
||||
/// It must be [`Send`] and [`Sync`], and usually implements [`Asset`] so needs to be [`TypeUuid`],
|
||||
/// in order to be registered.
|
||||
/// Types that implement this trait usually contain raw sound data that can be converted into an iterator of samples.
|
||||
/// This trait is implemented for [`AudioSource`].
|
||||
/// Check the example `audio/decodable` for how to implement this trait on a custom type.
|
||||
pub trait Decodable: Send + Sync + 'static {
|
||||
/// The decoder that can decode the implementing type
|
||||
type Decoder: rodio::Source + Send + Iterator<Item = Self::DecoderItem>;
|
||||
/// A single value given by the decoder
|
||||
/// The type of the audio samples.
|
||||
/// Usually a [`u16`], [`i16`] or [`f32`], as those implement [`rodio::Sample`].
|
||||
/// Other types can implement the [`rodio::Sample`] trait as well.
|
||||
type DecoderItem: rodio::Sample + Send + Sync;
|
||||
|
||||
/// Build and return a [`Self::Decoder`] for the implementing type
|
||||
/// The type of the iterator of the audio samples,
|
||||
/// which iterates over samples of type [`Self::DecoderItem`].
|
||||
/// Must be a [`rodio::Source`] so that it can provide information on the audio it is iterating over.
|
||||
type Decoder: rodio::Source + Send + Iterator<Item = Self::DecoderItem>;
|
||||
|
||||
/// Build and return a [`Self::Decoder`] of the implementing type
|
||||
fn decoder(&self) -> Self::Decoder;
|
||||
}
|
||||
|
||||
|
@ -82,3 +92,17 @@ impl Decodable for AudioSource {
|
|||
rodio::Decoder::new(Cursor::new(self.clone())).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait that allows adding a custom audio source to the object.
|
||||
/// This is implemented for [`App`][bevy_app::App] to allow registering custom [`Decodable`] types.
|
||||
pub trait AddAudioSource {
|
||||
/// Registers an audio source.
|
||||
/// The type must implement [`Decodable`],
|
||||
/// so that it can be converted to a [`rodio::Source`] type,
|
||||
/// and [`Asset`], so that it can be registered as an asset.
|
||||
/// To use this method on [`App`][bevy_app::App],
|
||||
/// the [audio][super::AudioPlugin] and [asset][bevy_asset::AssetPlugin] plugins must be added first.
|
||||
fn add_audio_source<T>(&mut self) -> &mut Self
|
||||
where
|
||||
T: Decodable + Asset;
|
||||
}
|
||||
|
|
|
@ -35,12 +35,13 @@ pub mod prelude {
|
|||
pub use audio::*;
|
||||
pub use audio_output::*;
|
||||
pub use audio_source::*;
|
||||
|
||||
pub use rodio::cpal::Sample as CpalSample;
|
||||
pub use rodio::source::Source;
|
||||
pub use rodio::Sample;
|
||||
|
||||
use bevy_app::prelude::*;
|
||||
use bevy_asset::AddAsset;
|
||||
use bevy_asset::{AddAsset, Asset};
|
||||
|
||||
/// Adds support for audio playback to a Bevy Application
|
||||
///
|
||||
|
@ -63,3 +64,15 @@ impl Plugin for AudioPlugin {
|
|||
app.init_asset_loader::<AudioLoader>();
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAudioSource for App {
|
||||
fn add_audio_source<T>(&mut self) -> &mut Self
|
||||
where
|
||||
T: Decodable + Asset,
|
||||
{
|
||||
self.add_asset::<T>()
|
||||
.init_resource::<Audio<T>>()
|
||||
.init_non_send_resource::<AudioOutput<T>>()
|
||||
.add_system_to_stage(CoreStage::PostUpdate, play_queued_audio_system::<T>)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -178,6 +178,7 @@ Example | Description
|
|||
--- | ---
|
||||
[Audio](../examples/audio/audio.rs) | Shows how to load and play an audio file
|
||||
[Audio Control](../examples/audio/audio_control.rs) | Shows how to load and play an audio file, and control how it's played
|
||||
[Decodable](../examples/audio/decodable.rs) | Shows how to create and register a custom audio source by implementing the `Decodable` type.
|
||||
|
||||
## Diagnostics
|
||||
|
||||
|
|
100
examples/audio/decodable.rs
Normal file
100
examples/audio/decodable.rs
Normal file
|
@ -0,0 +1,100 @@
|
|||
//! Shows how to create a custom `Decodable` type by implementing a Sine wave.
|
||||
//! ***WARNING THIS EXAMPLE IS VERY LOUD.*** Turn your volume down.
|
||||
use bevy::audio::AddAudioSource;
|
||||
use bevy::audio::Source;
|
||||
use bevy::prelude::*;
|
||||
use bevy::reflect::TypeUuid;
|
||||
use bevy::utils::Duration;
|
||||
|
||||
// This struct usually contains the data for the audio being played.
|
||||
// This is where data read from an audio file would be stored, for example.
|
||||
// Implementing `TypeUuid` will automatically implement `Asset`.
|
||||
// This allows the type to be registered as an asset.
|
||||
#[derive(TypeUuid)]
|
||||
#[uuid = "c2090c23-78fd-44f1-8508-c89b1f3cec29"]
|
||||
struct SineAudio {
|
||||
frequency: f32,
|
||||
}
|
||||
// This decoder is responsible for playing the audio,
|
||||
// and so stores data about the audio being played.
|
||||
struct SineDecoder {
|
||||
// how far along one period the wave is (between 0 and 1)
|
||||
current_progress: f32,
|
||||
// how much we move along the period every frame
|
||||
progress_per_frame: f32,
|
||||
// how long a period is
|
||||
period: f32,
|
||||
sample_rate: u32,
|
||||
}
|
||||
|
||||
impl SineDecoder {
|
||||
fn new(frequency: f32) -> Self {
|
||||
// standard sample rate for most recordings
|
||||
let sample_rate = 44_100;
|
||||
SineDecoder {
|
||||
current_progress: 0.,
|
||||
progress_per_frame: frequency / sample_rate as f32,
|
||||
period: std::f32::consts::PI * 2.,
|
||||
sample_rate,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The decoder must implement iterator so that it can implement `Decodable`.
|
||||
impl Iterator for SineDecoder {
|
||||
type Item = f32;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.current_progress += self.progress_per_frame;
|
||||
// we loop back round to 0 to avoid floating point inaccuracies
|
||||
self.current_progress %= 1.;
|
||||
Some(f32::sin(self.period * self.current_progress))
|
||||
}
|
||||
}
|
||||
// `Source` is what allows the audio source to be played by bevy.
|
||||
// This trait provides information on the audio.
|
||||
impl Source for SineDecoder {
|
||||
fn current_frame_len(&self) -> Option<usize> {
|
||||
None
|
||||
}
|
||||
|
||||
fn channels(&self) -> u16 {
|
||||
1
|
||||
}
|
||||
|
||||
fn sample_rate(&self) -> u32 {
|
||||
self.sample_rate
|
||||
}
|
||||
|
||||
fn total_duration(&self) -> Option<Duration> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// Finally `Decodable` can be implemented for our `SineAudio`.
|
||||
impl Decodable for SineAudio {
|
||||
type Decoder = SineDecoder;
|
||||
|
||||
type DecoderItem = <SineDecoder as Iterator>::Item;
|
||||
|
||||
fn decoder(&self) -> Self::Decoder {
|
||||
SineDecoder::new(self.frequency)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut app = App::new();
|
||||
// register the audio source so that it can be used
|
||||
app.add_plugins(DefaultPlugins)
|
||||
.add_audio_source::<SineAudio>()
|
||||
.add_startup_system(setup)
|
||||
.run();
|
||||
}
|
||||
|
||||
fn setup(mut assets: ResMut<Assets<SineAudio>>, audio: Res<Audio<SineAudio>>) {
|
||||
// add a `SineAudio` to the asset server so that it can be played
|
||||
let audio_handle = assets.add(SineAudio {
|
||||
frequency: 440., //this is the frequency of A4
|
||||
});
|
||||
audio.play(audio_handle);
|
||||
}
|
Loading…
Reference in a new issue