use std::marker::PhantomData; use bevy::ecs::system::SystemParam; use bevy::prelude::{Assets, Commands, Handle, Res, ResMut}; use bevy_kira_audio::{AudioChannel, AudioControl, AudioInstance, AudioSource, AudioTween}; use crate::utilities::{AudioSettings, SuppliesAudio, TrackType}; use crate::{AmbianceAudioChannel, MusicAudioChannel, SfxAudioChannel, UiSfxAudioChannel}; #[derive(SystemParam)] pub struct AudioChannels<'w, 's> { pub music_channel: Res<'w, AudioChannel<MusicAudioChannel>>, pub ambiance_channel: Res<'w, AudioChannel<AmbianceAudioChannel>>, pub sfx_channel: Res<'w, AudioChannel<SfxAudioChannel>>, pub ui_sfx_channel: Res<'w, AudioChannel<UiSfxAudioChannel>>, #[system_param(ignore)] _p: PhantomData<&'s ()>, } #[derive(SystemParam)] pub struct MusicBox<'w, 's, T: SuppliesAudio> { channels: AudioChannels<'w, 's>, handles: Res<'w, T>, settings: ResMut<'w, AudioSettings>, state: ResMut<'w, MusicBoxState>, audio_instances: ResMut<'w, Assets<AudioInstance>>, } /// Tracks the currently active audio instance singleton channels, to allow /// for transitions #[derive(Debug, Default)] pub struct MusicBoxState { pub active_music: Option<Handle<AudioInstance>>, pub active_ambiance: Option<Handle<AudioInstance>>, } impl<'w, 's, T: SuppliesAudio> MusicBox<'w, 's, T> { pub fn cross_fade_music<Name: ToString>( &mut self, name: Name, fade: AudioTween, ) -> Option<Handle<AudioInstance>> { self.fade_out_music(fade.clone()); self.fade_in_music(name, fade) } pub fn in_out_fade_music<Name: ToString>( &mut self, name: Name, in_tween: AudioTween, out_tween: AudioTween, ) -> Option<Handle<AudioInstance>> { self.fade_out_music(out_tween); self.fade_in_music(name, in_tween) } pub fn play_music<Name: ToString>(&mut self, name: Name) -> Option<Handle<AudioInstance>> { self.stop_music(); self.fade_in_music(name, AudioTween::default()) } pub fn stop_music(&mut self) { self.fade_out_music(AudioTween::default()); } pub fn fade_out_music(&mut self, fade: AudioTween) { let handle = std::mem::replace(&mut self.state.active_music, None) .and_then(|handle| self.audio_instances.get_mut(&handle)); if let Some(current) = handle { current.stop(fade); } } pub fn fade_in_music<Name: ToString>( &mut self, name: Name, fade: AudioTween, ) -> Option<Handle<AudioInstance>> { match self.map_tracks(name) { TrackType::WithIntro(_, track) | TrackType::Single(track) => { let next = self .channels .music_channel .play(track) .fade_in(fade) .looped() .handle(); self.state.active_music = Some(next.clone()); Some(next) } TrackType::Missing => None, } } pub fn cross_fade_ambiance<Name: ToString>( &mut self, name: Name, fade: AudioTween, ) -> Option<Handle<AudioInstance>> { self.fade_out_ambiance(fade.clone()); self.fade_in_ambiance(name, fade) } pub fn in_out_fade_ambiance<Name: ToString>( &mut self, name: Name, in_tween: AudioTween, out_tween: AudioTween, ) -> Option<Handle<AudioInstance>> { self.fade_out_ambiance(out_tween); self.fade_in_ambiance(name, in_tween) } pub fn play_ambiance<Name: ToString>(&mut self, name: Name) -> Option<Handle<AudioInstance>> { self.stop_ambiance(); self.fade_in_ambiance(name, AudioTween::default()) } pub fn stop_ambiance(&mut self) { self.fade_out_ambiance(AudioTween::default()); } pub fn fade_out_ambiance(&mut self, fade: AudioTween) { let handle = std::mem::replace(&mut self.state.active_ambiance, None) .and_then(|handle| self.audio_instances.get_mut(&handle)); if let Some(current) = handle { current.stop(fade); } } pub fn fade_in_ambiance<Name: ToString>( &mut self, name: Name, fade: AudioTween, ) -> Option<Handle<AudioInstance>> { match self.map_tracks(name) { TrackType::WithIntro(_, track) | TrackType::Single(track) => { let next = self .channels .ambiance_channel .play(track) .fade_in(fade) .looped() .handle(); self.state.active_ambiance = Some(next.clone()); Some(next) } TrackType::Missing => None, } } pub fn play_sfx<Name: ToString>(&mut self, name: Name) -> Option<Handle<AudioInstance>> { match self.map_tracks(name) { TrackType::WithIntro(_, track) | TrackType::Single(track) => { let instance = self.channels.sfx_channel.play(track).handle(); Some(instance) } TrackType::Missing => None, } } pub fn play_ui_sfx<Name: ToString>(&mut self, name: Name) -> Option<Handle<AudioInstance>> { match self.map_tracks(name) { TrackType::WithIntro(_, track) | TrackType::Single(track) => { let instance = self.channels.ui_sfx_channel.play(track).handle(); Some(instance) } TrackType::Missing => None, } } pub fn sync_settings(&self) { self.channels .music_channel .set_volume((self.settings.music_volume * self.settings.master_volume) as f64); self.channels .ambiance_channel .set_volume((self.settings.ambiance_volume * self.settings.master_volume) as f64); self.channels .sfx_channel .set_volume((self.settings.sfx_volume * self.settings.master_volume) as f64); self.channels .ui_sfx_channel .set_volume((self.settings.ui_volume * self.settings.master_volume) as f64); } pub fn settings(&self) -> &AudioSettings { &self.settings } pub fn settings_mut(&mut self) -> &mut AudioSettings { &mut self.settings } pub fn set_master_volume(&mut self, level: f32) { self.settings.master_volume = level; } pub fn set_music_volume(&mut self, level: f32) { self.settings.music_volume = level; } pub fn set_ambiance_volume(&mut self, level: f32) { self.settings.ambiance_volume = level; } pub fn set_sfx_volume(&mut self, level: f32) { self.settings.sfx_volume = level; } pub fn set_ui_sfx_volume(&mut self, level: f32) { self.settings.ui_volume = level; } fn map_tracks<Name: ToString>(&'w self, name: Name) -> TrackType<Handle<AudioSource>> { match self.handles.resolve_track_name(name) { TrackType::Single(name) => match self.handles.get_audio_track(name) { Some(handle) => TrackType::Single(handle), None => TrackType::Missing, }, TrackType::WithIntro(intro, looper) => match ( self.handles.get_audio_track(intro), self.handles.get_audio_track(looper), ) { (Some(intro), Some(looper)) => TrackType::WithIntro(intro, looper), _ => TrackType::Missing, }, TrackType::Missing => TrackType::Missing, } } }