diff --git a/MLEM/Misc/SoundEffectInfo.cs b/MLEM/Misc/SoundEffectInfo.cs index ad923ef..1a4d649 100644 --- a/MLEM/Misc/SoundEffectInfo.cs +++ b/MLEM/Misc/SoundEffectInfo.cs @@ -1,3 +1,7 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Audio; @@ -25,8 +29,6 @@ namespace MLEM.Misc { /// public float Pan; - private AudioEmitter emitter; - /// /// Creates a new sound effect info with the given values. /// @@ -49,33 +51,6 @@ namespace MLEM.Misc { return this.Sound.Play(this.Volume, this.Pitch, this.Pan); } - /// - /// Plays this info's once, with 3d positioning applied, to the given . - /// The required is automatically created and cached for future use. - /// - /// Data about the listener - /// The position to play the sound at - /// Whether to loop the sound effect instance - /// The emitter's doppler scale, defaults to 1 - /// The emitter's forward vector, defaults to - /// The emitter's up vector, defaults to - /// The emitter's velocity, defaults to - public SoundEffectInstance Play3D(AudioListener listener, Vector3 pos, bool loop = false, float? dopplerScale = null, Vector3? forward = null, Vector3? up = null, Vector3? velocity = null) { - if (this.emitter == null) - this.emitter = new AudioEmitter(); - this.emitter.Position = pos; - this.emitter.DopplerScale = dopplerScale ?? 1; - this.emitter.Forward = forward ?? Vector3.Forward; - this.emitter.Up = up ?? Vector3.Up; - this.emitter.Velocity = velocity ?? Vector3.Zero; - - var inst = this.CreateInstance(); - inst.IsLooped = loop; - inst.Apply3D(listener, this.emitter); - inst.Play(); - return inst; - } - /// /// Creates a new with this sound effect info's data. /// @@ -89,4 +64,99 @@ namespace MLEM.Misc { } } + + /// + /// A simple class that handles automatically removing and disposing objects once they are done playing to free up the audio source for new sounds. + /// Additionally, a callback can be registered that is invoked when the finishes playing. + /// Note that an object of this class can be added to a using . + /// + public class SoundEffectInstanceHandler : GameComponent, IEnumerable { + + private readonly List playingSounds = new List(); + + /// + /// Creates a new sound effect instance handler with the given settings + /// + /// The game instance + public SoundEffectInstanceHandler(Game game) : base(game) { + } + + /// + public override void Update(GameTime gameTime) { + this.Update(); + } + + /// + /// Updates this sound effect handler and manages all of the objects in it. + /// This should be called each update frame. + /// + public void Update() { + for (var i = this.playingSounds.Count - 1; i >= 0; i--) { + var entry = this.playingSounds[i]; + if (entry.Instance.IsDisposed || entry.Instance.State == SoundState.Stopped) { + entry.Instance.Stop(true); + entry.OnStopped?.Invoke(entry.Instance); + this.playingSounds.RemoveAt(i); + } + } + } + + /// + /// Adds a new to this handler. + /// This also starts playing the instance. + /// + /// The instance to add + /// The function that should be invoked when this instance stops playing, defaults to null + /// The passed instance, for chaining + public SoundEffectInstance Add(SoundEffectInstance instance, Action onStopped = null) { + this.playingSounds.Add(new Entry(instance, onStopped)); + instance.Play(); + return instance; + } + + /// + /// Adds a new to this handler. + /// This also starts playing the created instance. + /// + /// The info for which to add a + /// The function that should be invoked when this instance stops playing, defaults to null + /// The newly created + public SoundEffectInstance Add(SoundEffectInfo info, Action onStopped = null) { + return this.Add(info.CreateInstance(), onStopped); + } + + /// + /// Adds a new to this handler. + /// This also starts playing the created instance. + /// + /// The sound for which to add a + /// The function that should be invoked when this instance stops playing, defaults to null + /// The newly created + public SoundEffectInstance Add(SoundEffect effect, Action onStopped = null) { + return this.Add(effect.CreateInstance(), onStopped); + } + + /// + public IEnumerator GetEnumerator() { + foreach (var sound in this.playingSounds) + yield return sound.Instance; + } + + IEnumerator IEnumerable.GetEnumerator() { + return this.GetEnumerator(); + } + + private readonly struct Entry { + + public readonly SoundEffectInstance Instance; + public readonly Action OnStopped; + + public Entry(SoundEffectInstance instance, Action onStopped) { + this.Instance = instance; + this.OnStopped = onStopped; + } + + } + + } } \ No newline at end of file