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);
}
}
}
}