using System; using System.Collections; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Audio; 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 its . /// public class SoundEffectInstanceHandler : GameComponent, IEnumerable { private readonly List playingSounds = new List(); private AudioListener[] listeners; /// /// 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. /// If has been called, all sounds will additionally be updated in 3D space. /// 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); } else { entry.TryApply3D(this.listeners); } } } /// /// Sets the collection objects that should be listening to the sounds in this handler in 3D space. /// If there are one or more listeners, this handler applies 3d effects to all sound effect instances that have been added to this handler along with an in automatically. /// public void SetListeners(params AudioListener[] listeners) { this.listeners = listeners; } /// /// Pauses all of the sound effect instances that are currently playing /// public void Pause() { foreach (var entry in this.playingSounds) entry.Instance.Pause(); } /// /// Resumes all of the sound effect instances in this handler /// public void Resume() { foreach (var entry in this.playingSounds) entry.Instance.Resume(); } /// /// 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 /// An optional audio emitter with which 3d sound can be applied /// The passed instance, for chaining public SoundEffectInstance Add(SoundEffectInstance instance, Action onStopped = null, AudioEmitter emitter = null) { var entry = new Entry(instance, onStopped, emitter); this.playingSounds.Add(entry); instance.Play(); entry.TryApply3D(this.listeners); 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 /// An optional audio emitter with which 3d sound can be applied /// The newly created public SoundEffectInstance Add(SoundEffectInfo info, Action onStopped = null, AudioEmitter emitter = null) { return this.Add(info.CreateInstance(), onStopped, emitter); } /// /// 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 /// An optional audio emitter with which 3d sound can be applied /// The newly created public SoundEffectInstance Add(SoundEffect effect, Action onStopped = null, AudioEmitter emitter = null) { return this.Add(effect.CreateInstance(), onStopped, emitter); } /// 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 readonly AudioEmitter Emitter; public Entry(SoundEffectInstance instance, Action onStopped, AudioEmitter emitter) { this.Instance = instance; this.OnStopped = onStopped; this.Emitter = emitter; } public void TryApply3D(AudioListener[] listeners) { if (listeners != null && listeners.Length > 0 && this.Emitter != null) this.Instance.Apply3D(listeners, this.Emitter); } } } }