use bevy::{asset::AssetLoader, prelude::*};
use firewheel::{collector::ArcGc, sample_resource::SampleResource};
use std::num::NonZeroU32;
use std::sync::Arc;
#[derive(Asset, TypePath, Clone)]
pub struct Sample(ArcGc<dyn SampleResource>);
impl Sample {
pub fn new<S: SampleResource>(sample: S) -> Self {
Self(ArcGc::new_unsized(|| Arc::new(sample) as _))
}
pub fn get(&self) -> ArcGc<dyn SampleResource> {
self.0.clone()
}
}
impl core::fmt::Debug for Sample {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Sample").finish_non_exhaustive()
}
}
#[derive(Debug)]
pub struct SampleLoader {
pub sample_rate: NonZeroU32,
}
#[derive(Debug)]
pub enum SampleLoaderError {
StdIo(std::io::Error),
Symphonium(String),
}
impl From<std::io::Error> for SampleLoaderError {
fn from(value: std::io::Error) -> Self {
Self::StdIo(value)
}
}
impl From<symphonium::error::LoadError> for SampleLoaderError {
fn from(value: symphonium::error::LoadError) -> Self {
Self::Symphonium(value.to_string())
}
}
impl std::error::Error for SampleLoaderError {}
impl std::fmt::Display for SampleLoaderError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::StdIo(stdio) => stdio.fmt(f),
Self::Symphonium(sy) => f.write_str(sy),
}
}
}
impl AssetLoader for SampleLoader {
type Asset = Sample;
type Settings = ();
type Error = SampleLoaderError;
async fn load(
&self,
reader: &mut dyn bevy::asset::io::Reader,
_settings: &Self::Settings,
load_context: &mut bevy::asset::LoadContext<'_>,
) -> Result<Self::Asset, Self::Error> {
let mut bytes = Vec::new();
reader.read_to_end(&mut bytes).await?;
let mut hint = symphonia::core::probe::Hint::new();
hint.with_extension(&load_context.path().to_string_lossy());
let mut loader = symphonium::SymphoniumLoader::new();
let source = firewheel::load_audio_file_from_source(
&mut loader,
Box::new(std::io::Cursor::new(bytes)),
Some(hint),
self.sample_rate,
Default::default(),
)?;
Ok(Sample(ArcGc::new_unsized(|| {
Arc::new(source) as Arc<dyn SampleResource>
})))
}
fn extensions(&self) -> &[&str] {
&[
#[cfg(feature = "wav")]
"wav",
#[cfg(feature = "ogg")]
"ogg",
#[cfg(feature = "mp3")]
"mp3",
#[cfg(feature = "flac")]
"flac",
#[cfg(feature = "mkv")]
"mkv",
]
}
}