use std::marker::PhantomData; use bevy::ecs::system::SystemParam; use bevy::prelude::*; use bevy_kira_audio::{AudioChannel, InstanceHandle}; use crate::utilities::{AudioSettings, CrossFadeTrack, SuppliesAudio, TrackType}; use crate::{ AmbianceAudioChannelA, AmbianceAudioChannelB, AudioCrossFade, MusicAudioChannelA, MusicAudioChannelB, SfxAudioChannel, UiSfxAudioChannel, }; #[derive(SystemParam)] pub struct AudioChannels<'w, 's> { pub music_channel_a: Res<'w, AudioChannel<MusicAudioChannelA>>, pub music_channel_b: Res<'w, AudioChannel<MusicAudioChannelB>>, pub ambiance_channel_a: Res<'w, AudioChannel<AmbianceAudioChannelA>>, pub ambiance_channel_b: Res<'w, AudioChannel<AmbianceAudioChannelB>>, 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> { pub commands: Commands<'w, 's>, pub channels: AudioChannels<'w, 's>, pub handles: Res<'w, T>, pub settings: Res<'w, AudioSettings>, pub fade_state: Res<'w, AudioCrossFade>, } pub enum MusicTrackState { Pending, Playing, FadeOut { progress: f32 }, FadeIn { progress: f32 }, CrossFade { out_progress: f32, in_progress: f32 }, } impl Default for MusicTrackState { fn default() -> Self { Self::Pending } } impl<'w, 's, T: SuppliesAudio> MusicBox<'w, 's, T> { pub fn play_looped_music<Name: ToString>(&self, name: Name) -> Option<InstanceHandle> { self.channels.music_channel_a.stop(); self.channels.music_channel_b.stop(); match self.resolve_track_name(name) { TrackType::Single(track) => match self.fade_state.music.active { CrossFadeTrack::A => Some( self.channels .music_channel_a .play_looped(track.clone_weak()), ), CrossFadeTrack::B => Some( self.channels .music_channel_b .play_looped(track.clone_weak()), ), }, TrackType::WithIntro(intro, looped) => match self.fade_state.music.active { CrossFadeTrack::A => Some( self.channels .music_channel_a .play_looped_with_intro(intro.clone_weak(), looped.clone_weak()), ), CrossFadeTrack::B => Some( self.channels .music_channel_b .play_looped_with_intro(intro.clone_weak(), looped.clone_weak()), ), }, TrackType::Missing => None, } } pub fn play_effect_once<Name: ToString>(&mut self, name: Name) -> Option<InstanceHandle> { let name = name.to_string(); match self.handles.get_audio_track(&name) { Some(track) => Some(self.channels.sfx_channel.play(track)), None => None, } } pub fn play_ambiance<Name: ToString>(&self, name: Name) -> Option<InstanceHandle> { self.channels.ambiance_channel_a.stop(); self.channels.ambiance_channel_b.stop(); match self.resolve_track_name(name) { TrackType::Single(track) => match self.fade_state.ambiance.active { CrossFadeTrack::A => Some( self.channels .ambiance_channel_a .play_looped(track.clone_weak()), ), CrossFadeTrack::B => Some( self.channels .ambiance_channel_b .play_looped(track.clone_weak()), ), }, TrackType::WithIntro(intro, looped) => match self.fade_state.ambiance.active { CrossFadeTrack::A => Some( self.channels .ambiance_channel_a .play_looped_with_intro(intro.clone_weak(), looped.clone_weak()), ), CrossFadeTrack::B => Some( self.channels .ambiance_channel_b .play_looped_with_intro(intro.clone_weak(), looped.clone_weak()), ), }, TrackType::Missing => None, } } }