bones_framework/audio/
audio_manager.rs1use crate::prelude::*;
4use kira;
5use kira::{
6 manager::{
7 backend::{cpal::CpalBackend, mock::MockBackend, Backend},
8 AudioManager as KiraAudioManager,
9 },
10 sound::{static_sound::StaticSoundData, SoundData},
11};
12use std::io::Cursor;
13
14#[derive(HasSchema, Deref, DerefMut)]
16#[schema(no_clone)]
17pub struct AudioManager(KiraAudioManager<CpalWithFallbackBackend>);
18
19impl Default for AudioManager {
20 fn default() -> Self {
21 Self(KiraAudioManager::<CpalWithFallbackBackend>::new(default()).unwrap())
22 }
23}
24
25#[allow(clippy::large_enum_variant)]
28pub enum CpalWithFallbackBackend {
29 Cpal(CpalBackend),
31 Dummy(MockBackend),
33}
34
35impl Backend for CpalWithFallbackBackend {
36 type Settings = <CpalBackend as Backend>::Settings;
37 type Error = <CpalBackend as Backend>::Error;
38
39 fn setup(settings: Self::Settings) -> Result<(Self, u32), Self::Error> {
40 match CpalBackend::setup(settings) {
41 Ok((back, bit)) => Ok((Self::Cpal(back), bit)),
42 Err(e) => {
43 tracing::error!("Error starting audio backend, using dummy backend instead: {e}");
44 Ok(MockBackend::setup(default())
45 .map(|(back, bit)| (Self::Dummy(back), bit))
46 .unwrap())
47 }
48 }
49 }
50
51 fn start(&mut self, renderer: kira::manager::backend::Renderer) -> Result<(), Self::Error> {
52 match self {
53 CpalWithFallbackBackend::Cpal(cpal) => cpal.start(renderer),
54 CpalWithFallbackBackend::Dummy(dummy) => {
55 dummy.start(renderer).unwrap();
56 Ok(())
57 }
58 }
59 }
60}
61
62#[derive(Clone, HasSchema, Debug, Deref, DerefMut)]
65#[schema(no_default)]
66#[type_data(asset_loader(["ogg", "mp3", "flac", "wav"], AudioLoader))]
67pub struct AudioSource(pub StaticSoundData);
68
69impl SoundData for &AudioSource {
70 type Error = <StaticSoundData as SoundData>::Error;
71 type Handle = <StaticSoundData as SoundData>::Handle;
72
73 fn into_sound(self) -> Result<(Box<dyn kira::sound::Sound>, Self::Handle), Self::Error> {
74 self.0.clone().into_sound()
75 }
76}
77
78pub struct AudioLoader;
80impl AssetLoader for AudioLoader {
81 fn load(&self, _ctx: AssetLoadCtx, bytes: &[u8]) -> BoxedFuture<anyhow::Result<SchemaBox>> {
82 let bytes = bytes.to_vec();
83 Box::pin(async move {
84 let data = StaticSoundData::from_cursor(Cursor::new(bytes))?;
85 Ok(SchemaBox::new(AudioSource(data)))
86 })
87 }
88}