use std::fmt::Debug;
use std::fmt::Formatter;
use bevy::prelude::*;
use kira::sound::PlaybackState;
use crate::DynamicSoundHandle;
use crate::KiraPlayable;
pub use crate::sound::sound_types::KiraPlayingSound;
use crate::sound::sound_types::KiraTrackHandle;
use kira::sound::static_sound::StaticSoundHandle;
use crate::KiraContext;
#[derive(Component, Default, Reflect)]
pub struct KiraPlayingSounds(#[reflect(ignore)] pub(crate) Vec<KiraPlayingSound>);
impl KiraPlayingSounds {
pub fn static_handles(&self) -> impl Iterator<Item = &StaticSoundHandle> {
self.0.iter().filter_map(|sound| match sound {
KiraPlayingSound::Static(sound) => Some(sound),
KiraPlayingSound::Dynamic(_) => None,
})
}
pub fn dynamic_handles<T>(&self) -> impl Iterator<Item = &T>
where
T: DynamicSoundHandle + 'static,
{
self.0.iter().filter_map(|sound| match sound {
KiraPlayingSound::Static(_) => None,
KiraPlayingSound::Dynamic(dyn_handle) => dyn_handle.as_any().downcast_ref::<T>(),
})
}
}
#[derive(Event)]
pub struct KiraPlaySoundEvent {
pub(super) entity: Entity,
pub(super) track_entity: Option<Entity>,
pub(super) sound: Box<dyn KiraPlayable>,
}
impl KiraPlaySoundEvent {
pub fn new(entity: Entity, track_entity: Option<Entity>, sound: impl KiraPlayable) -> Self {
Self {
entity,
track_entity,
sound: Box::new(sound),
}
}
}
impl Debug for KiraPlayingSounds {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("KiraActiveSounds")
.field("len", &self.0.len())
.finish()
}
}
pub(super) fn do_play_sys(
mut commands: Commands,
mut kira: NonSendMut<KiraContext>,
mut query: Query<(Entity, Option<&mut KiraPlayingSounds>)>,
mut track_query: Query<&mut KiraTrackHandle>,
mut ev_play: ResMut<Events<KiraPlaySoundEvent>>,
) -> Result<(), BevyError> {
for event in ev_play.drain() {
let mut opt_track = if let Some(track_entity) = event.track_entity {
let res = track_query.get_mut(track_entity)?;
Some(res)
} else {
None
};
let sound_handle = match kira.play(event.sound, opt_track.as_deref_mut()) {
Ok(s) => s,
Err(e) => {
error!("Error playing sound: {}", e);
continue;
}
};
if let Ok((eid, active_sounds)) = query.get_mut(event.entity) {
match active_sounds {
Some(mut sounds) => {
sounds.0.push(sound_handle);
}
None => {
commands
.entity(eid)
.insert(KiraPlayingSounds(vec![sound_handle]));
}
};
} else {
error!(
"Failed to associate playing sound handle with entity: {:?}. \
The handle will be dropped.",
event.entity
);
}
}
Ok(())
}
pub(super) fn cleanup_inactive_sounds_sys(
mut commands: Commands,
mut query: Query<(Entity, &mut KiraPlayingSounds)>,
) {
for (eid, mut sounds) in query.iter_mut() {
let needs_cleanup = sounds.0.iter().any(|sound| match &sound {
KiraPlayingSound::Static(sound) => sound.state() == PlaybackState::Stopped,
KiraPlayingSound::Dynamic(sound) => sound.state() == PlaybackState::Stopped,
});
if needs_cleanup {
sounds.0.retain(|sound| match &sound {
KiraPlayingSound::Static(sound) => sound.state() != PlaybackState::Stopped,
KiraPlayingSound::Dynamic(sound) => sound.state() != PlaybackState::Stopped,
});
}
if sounds.0.is_empty() {
commands.entity(eid).remove::<KiraPlayingSounds>();
}
}
}