diff --git a/examples/audio_control.rs b/examples/audio_control.rs index da815ef..ddfdca0 100644 --- a/examples/audio_control.rs +++ b/examples/audio_control.rs @@ -32,9 +32,10 @@ struct MyMusicPlayer; fn startup(mut commands: Commands, studio: Res) { let event_description = studio.get_event("event:/Music/Level 03").unwrap(); - commands - .spawn(MyMusicPlayer) - .insert(AudioSource::new(event_description)); + commands.spawn(MyMusicPlayer).insert(AudioSource { + event_instance: event_description.create_instance().unwrap(), + despawn_stop_mode: StopMode::AllowFadeout, + }); } fn play_music(mut audio_sources: Query<&AudioSource, With>) { diff --git a/examples/minimal.rs b/examples/minimal.rs index 266b000..af22ad6 100644 --- a/examples/minimal.rs +++ b/examples/minimal.rs @@ -4,6 +4,7 @@ use bevy::prelude::*; use bevy_fmod::prelude::AudioSource; use bevy_fmod::prelude::*; +use libfmod::StopMode; fn main() { App::new() @@ -26,9 +27,10 @@ struct MyMusicPlayer; fn startup(mut commands: Commands, studio: Res) { let event_description = studio.get_event("event:/Music/Level 03").unwrap(); - commands - .spawn(MyMusicPlayer) - .insert(AudioSource::new(event_description)); + commands.spawn(MyMusicPlayer).insert(AudioSource { + event_instance: event_description.create_instance().unwrap(), + despawn_stop_mode: StopMode::AllowFadeout, + }); } fn play_music(mut audio_sources: Query<&AudioSource, With>) { diff --git a/examples/parameters.rs b/examples/parameters.rs index d90c2f4..92f0311 100644 --- a/examples/parameters.rs +++ b/examples/parameters.rs @@ -37,6 +37,7 @@ use bevy::prelude::*; use bevy_fmod::prelude::AudioSource; use bevy_fmod::prelude::*; +use libfmod::StopMode; fn main() { App::new() @@ -63,15 +64,17 @@ struct CountrySfxPlayer; fn startup(mut commands: Commands, studio: Res) { let event_description = studio.get_event("event:/Ambience/Forest").unwrap(); - commands - .spawn(ForestSfxPlayer) - .insert(AudioSource::new(event_description)); + commands.spawn(ForestSfxPlayer).insert(AudioSource { + event_instance: event_description.create_instance().unwrap(), + despawn_stop_mode: StopMode::AllowFadeout, + }); let event_description = studio.get_event("event:/Ambience/Country").unwrap(); - commands - .spawn(CountrySfxPlayer) - .insert(AudioSource::new(event_description)); + commands.spawn(CountrySfxPlayer).insert(AudioSource { + event_instance: event_description.create_instance().unwrap(), + despawn_stop_mode: StopMode::AllowFadeout, + }); } fn play_music(audio_sources: Query<&AudioSource>) { diff --git a/examples/spatial.rs b/examples/spatial.rs index 4922630..aa0356e 100644 --- a/examples/spatial.rs +++ b/examples/spatial.rs @@ -8,6 +8,7 @@ use bevy::prelude::*; use bevy_fmod::prelude::AudioSource; use bevy_fmod::prelude::*; +use libfmod::StopMode; fn main() { App::new() @@ -61,8 +62,13 @@ fn setup_scene( // Audio source: Orbiting cube let event_description = studio.get_event("event:/Music/Radio Station").unwrap(); + let audio_source = AudioSource { + event_instance: event_description.create_instance().unwrap(), + despawn_stop_mode: StopMode::AllowFadeout, + }; + commands - .spawn(SpatialAudioBundle::new(event_description)) + .spawn(SpatialAudioBundle::from(audio_source)) .insert(PbrBundle { mesh: meshes.add(Cuboid::default()), material: materials.add(Color::srgb(0.8, 0.7, 0.6)), diff --git a/src/components/audio_source.rs b/src/components/audio_source.rs index a8902a5..0488b05 100644 --- a/src/components/audio_source.rs +++ b/src/components/audio_source.rs @@ -1,20 +1,22 @@ -use bevy::math::Vec3; -use bevy::prelude::{Component, Deref, DerefMut, GlobalTransform, Query}; -use libfmod::StopMode::Immediate; -use libfmod::{EventDescription, EventInstance}; - use crate::attributes_3d::attributes3d; use crate::components::velocity::Velocity; +use bevy::math::Vec3; +use bevy::prelude::{Component, Deref, DerefMut, GlobalTransform, Query}; +use libfmod::{EventInstance, StopMode}; /// See the [`Velocity`] component for information on enabling the Doppler effect. #[derive(Component, Deref, DerefMut)] -pub struct AudioSource(pub EventInstance); +pub struct AudioSource { + /// The [EventInstance] that is playing the audio. Create an instance from an + /// [EventDescription](libfmod::EventDescription) using + /// [EventDescription::create_instance](libfmod::EventDescription::create_instance). + #[deref] + pub event_instance: EventInstance, + /// The [StopMode] to use when the entity despawns. + pub despawn_stop_mode: StopMode, +} impl AudioSource { - pub fn new(event_description: EventDescription) -> Self { - Self(event_description.create_instance().unwrap()) - } - pub(crate) fn update_3d_attributes( mut query: Query<(&AudioSource, &GlobalTransform, Option<&Velocity>)>, ) { @@ -37,9 +39,7 @@ impl AudioSource { .unwrap(); }); } -} -impl AudioSource { #[deprecated = "Use `AudioSource::get_volume` instead."] pub fn volume(&self) -> f32 { self.get_volume().unwrap().0 @@ -47,7 +47,7 @@ impl AudioSource { #[deprecated = "Use `AudioSource::set_volume` instead."] pub fn set_volume(&self, volume: f32) { - self.0.set_volume(volume).unwrap(); + self.event_instance.set_volume(volume).unwrap(); } #[deprecated = "Use `AudioSource::get_pitch` instead."] @@ -80,13 +80,8 @@ impl AudioSource { } pub fn toggle(&self) { - self.0.set_paused(!self.0.get_paused().unwrap()).unwrap(); - } -} - -impl Drop for AudioSource { - fn drop(&mut self) { - self.0.stop(Immediate).unwrap(); - self.release().unwrap(); + self.event_instance + .set_paused(!self.event_instance.get_paused().unwrap()) + .unwrap(); } } diff --git a/src/components/bundles.rs b/src/components/bundles.rs index 02f09ae..2a15d9b 100644 --- a/src/components/bundles.rs +++ b/src/components/bundles.rs @@ -1,6 +1,6 @@ use crate::prelude::{AudioListener, AudioSource, Velocity}; use bevy::prelude::{Bundle, TransformBundle}; -use libfmod::EventDescription; +use libfmod::{EventDescription, StopMode}; #[derive(Bundle)] pub struct SpatialAudioBundle { @@ -10,9 +10,23 @@ pub struct SpatialAudioBundle { } impl SpatialAudioBundle { + #[deprecated = "Use `AudioSource::from` instead."] pub fn new(event_description: EventDescription) -> Self { SpatialAudioBundle { - audio_source: AudioSource::new(event_description), + audio_source: AudioSource { + event_instance: event_description.create_instance().unwrap(), + despawn_stop_mode: StopMode::AllowFadeout, + }, + velocity: Velocity::default(), + transform: TransformBundle::default(), + } + } +} + +impl From for SpatialAudioBundle { + fn from(value: AudioSource) -> Self { + SpatialAudioBundle { + audio_source: value, velocity: Velocity::default(), transform: TransformBundle::default(), } diff --git a/src/fmod_plugin.rs b/src/fmod_plugin.rs index b286875..02ad1cf 100644 --- a/src/fmod_plugin.rs +++ b/src/fmod_plugin.rs @@ -1,4 +1,5 @@ -use bevy::prelude::{App, Plugin, PostUpdate, Res, Update}; +use bevy::app::PreStartup; +use bevy::prelude::{App, Plugin, PostUpdate, Res, Update, World}; use bevy_mod_sysfail::sysfail; use crate::components::audio_listener::AudioListener; @@ -19,6 +20,7 @@ impl Plugin for FmodPlugin { fn build(&self, app: &mut App) { app.add_plugins(VelocityPlugin) .insert_resource(FmodStudio::new(self.audio_banks_paths, self.plugin_paths)) + .add_systems(PreStartup, register_component_hooks) .add_systems( Update, ( @@ -33,10 +35,11 @@ impl Plugin for FmodPlugin { impl FmodPlugin { #[sysfail(log(level = "error"))] fn update(studio: Res) -> anyhow::Result<()> { - studio.0.update()?; + studio.update()?; Ok(()) } + #[must_use] pub fn new(audio_banks_paths: &'static [&'static str]) -> Self { FmodPlugin { audio_banks_paths, @@ -44,3 +47,16 @@ impl FmodPlugin { } } } + +fn register_component_hooks(world: &mut World) { + world + .register_component_hooks::() + .on_remove(|mut world, entity, _| { + let mut entity_mut = world.entity_mut(entity); + let audio_source = entity_mut.get_mut::().unwrap(); + let event_instance = audio_source.event_instance; + + event_instance.stop(audio_source.despawn_stop_mode).unwrap(); + event_instance.release().unwrap(); + }); +}