use alloc::sync::Arc;1use bevy_asset::{io::Reader, Asset, AssetLoader, LoadContext};2use bevy_reflect::TypePath;3use std::io::Cursor;45/// A source of audio data6#[derive(Asset, Debug, Clone, TypePath)]7pub struct AudioSource {8/// Raw data of the audio source.9///10/// The data must be one of the file formats supported by Bevy (`wav`, `ogg`, `flac`, or `mp3`).11/// However, support for these file formats is not part of Bevy's [`default feature set`](https://docs.rs/bevy/latest/bevy/index.html#default-features).12/// In order to be able to use these file formats, you will have to enable the appropriate [`optional features`](https://docs.rs/bevy/latest/bevy/index.html#optional-features).13///14/// It is decoded using [`rodio::decoder::Decoder`](https://docs.rs/rodio/latest/rodio/decoder/struct.Decoder.html).15/// The decoder has conditionally compiled methods16/// depending on the features enabled.17/// If the format used is not enabled,18/// then this will panic with an `UnrecognizedFormat` error.19pub bytes: Arc<[u8]>,20}2122impl AsRef<[u8]> for AudioSource {23fn as_ref(&self) -> &[u8] {24&self.bytes25}26}2728/// Loads files as [`AudioSource`] [`Assets`](bevy_asset::Assets)29///30/// This asset loader supports different audio formats based on the enable Bevy features.31/// The feature `bevy/vorbis` enables loading from `.ogg` files and is enabled by default.32/// Other file endings can be loaded from with additional features:33/// `.mp3` with `bevy/mp3`34/// `.flac` with `bevy/flac`35/// `.wav` with `bevy/wav`36#[derive(Default)]37pub struct AudioLoader;3839impl AssetLoader for AudioLoader {40type Asset = AudioSource;41type Settings = ();42type Error = std::io::Error;4344async fn load(45&self,46reader: &mut dyn Reader,47_settings: &Self::Settings,48_load_context: &mut LoadContext<'_>,49) -> Result<AudioSource, Self::Error> {50let mut bytes = Vec::new();51reader.read_to_end(&mut bytes).await?;52Ok(AudioSource {53bytes: bytes.into(),54})55}5657fn extensions(&self) -> &[&str] {58&[59#[cfg(feature = "mp3")]60"mp3",61#[cfg(feature = "flac")]62"flac",63#[cfg(feature = "wav")]64"wav",65#[cfg(feature = "vorbis")]66"oga",67#[cfg(feature = "vorbis")]68"ogg",69#[cfg(feature = "vorbis")]70"spx",71]72}73}7475/// A type implementing this trait can be converted to a [`rodio::Source`] type.76///77/// It must be [`Send`] and [`Sync`] in order to be registered.78/// Types that implement this trait usually contain raw sound data that can be converted into an iterator of samples.79/// This trait is implemented for [`AudioSource`].80/// Check the example [`decodable`](https://github.com/bevyengine/bevy/blob/latest/examples/audio/decodable.rs) for how to implement this trait on a custom type.81pub trait Decodable: Send + Sync + 'static {82/// The type of the audio samples.83/// Usually a [`u16`], [`i16`] or [`f32`], as those implement [`rodio::Sample`].84/// Other types can implement the [`rodio::Sample`] trait as well.85type DecoderItem: rodio::Sample + Send + Sync;8687/// The type of the iterator of the audio samples,88/// which iterates over samples of type [`Self::DecoderItem`].89/// Must be a [`rodio::Source`] so that it can provide information on the audio it is iterating over.90type Decoder: rodio::Source + Send + Iterator<Item = Self::DecoderItem>;9192/// Build and return a [`Self::Decoder`] of the implementing type93fn decoder(&self) -> Self::Decoder;94}9596impl Decodable for AudioSource {97type DecoderItem = <rodio::Decoder<Cursor<AudioSource>> as Iterator>::Item;98type Decoder = rodio::Decoder<Cursor<AudioSource>>;99100fn decoder(&self) -> Self::Decoder {101rodio::Decoder::new(Cursor::new(self.clone())).unwrap()102}103}104105/// A trait that allows adding a custom audio source to the object.106/// This is implemented for [`App`][bevy_app::App] to allow registering custom [`Decodable`] types.107pub trait AddAudioSource {108/// Registers an audio source.109/// The type must implement [`Decodable`],110/// so that it can be converted to a [`rodio::Source`] type,111/// and [`Asset`], so that it can be registered as an asset.112/// To use this method on [`App`][bevy_app::App],113/// the [audio][super::AudioPlugin] and [asset][bevy_asset::AssetPlugin] plugins must be added first.114fn add_audio_source<T>(&mut self) -> &mut Self115where116T: Decodable + Asset,117f32: rodio::cpal::FromSample<T::DecoderItem>;118}119120121