#region File Description
//-----------------------------------------------------------------------------
// SoundManager.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using RobotGameData.Camera;
#endregion
namespace RobotGameData.Sound
{
#region SoundElement
///
/// a sound structure with sound cue and emitter for 3D playback.
///
public class SoundElement
{
public Cue cue = null;
public AudioEmitter emitter = null;
public string Name
{
get { return cue.Name; }
}
}
#endregion
///
/// It reads sound data and plays sound and supports 3D positional sound.
/// When playing sound, it creates SoundElement class internally and
/// manages a sound pool.
/// In order to load sound data, the files, which have been created by
/// Microsoft Cross-Platform Audio Creation Tool (XACT), such as .xgs,
/// .xwb, and .xsb, are needed.
///
public class SoundManager
{
#region Fields
///
/// If set to false, all of the related functions get turned off.
///
bool soundOn = true;
static AudioEngine audioEngine = null;
static WaveBank waveBank = null;
static SoundBank soundBank = null;
static Dictionary audioCategories =
new Dictionary();
// Keep track of all the 3D sounds that are currently playing.
static List activeSounds = new List();
// Keep track of spare SoundObject instances, so we can reuse them.
// Otherwise we would have to allocate new instances each time
// a sound was played, which would create unnecessary garbage.
static Stack soundPool = new Stack();
// The listener describes the ear which is hearing 3D sounds.
// This is usually set to match the camera.
AudioListener listener = new AudioListener();
// The emitter describes an entity which is making a 3D sound.
AudioEmitter emitter = new AudioEmitter();
static bool pauseAll = false;
#endregion
#region Properties
public static AudioEngine AudioEngine
{
get { return audioEngine; }
}
public static WaveBank WaveBank
{
get { return waveBank; }
}
public static SoundBank SoundBank
{
get { return soundBank; }
}
public static bool PauseAll
{
get { return pauseAll; }
}
#endregion
///
/// Constructor.
///
public SoundManager()
{
audioCategories.Clear();
pauseAll = false;
}
///
/// applies 3D sound to the listener.
///
/// listener position
/// listener direction
/// listener up vector
/// listener velocity
public void ApplyEmitter(Vector3 position, Vector3 direction, Vector3 up,
Vector3 velocity)
{
// Update listener by the current camera
this.listener.Position = position;
this.listener.Forward = direction;
this.listener.Up = up;
this.listener.Velocity = velocity;
}
///
/// processes the sound pool and the 3D sound playback.
/// XNA's AudioEngine is updated here.
///
public void Update()
{
if (soundOn == false)
return;
// Loop over all the currently playing 3D sounds.
int index = 0;
while (index < activeSounds.Count)
{
SoundElement sound = activeSounds[index];
if (sound.cue.IsStopped)
{
// If the cue has stopped playing, dispose it.
//sound.cue.Dispose();
sound.cue = null;
sound.emitter = null;
// Store the SoundElement instance for future reuse.
soundPool.Push(sound);
// Remove it from the active list.
activeSounds.RemoveAt(index);
}
else
{
if (sound.emitter != null)
{
// If the cue is still playing, update its 3D settings.
Apply3D(sound);
}
index++;
}
}
// Update the XACT engine.
AudioEngine.Update();
}
///
/// stops every sound and removes every sound member.
///
public void Dispose()
{
if (soundOn == false)
return;
StopSound();
audioCategories.Clear();
SoundBank.Dispose();
WaveBank.Dispose();
AudioEngine.Dispose();
}
///
/// initialize all sound members.
/// creates and initializes the XNA's AudioEndgine.
///
///
///
///
public void Initialize( string globalSettingsFileName,
string waveBankFileName,
string soundBankFileName)
{
if (soundOn == false)
return;
// Create audio engine
audioEngine = new AudioEngine(globalSettingsFileName);
if (audioEngine == null)
{
throw new ArgumentException(
"The audio engine could not be created.");
}
// Create wave bank
waveBank = new WaveBank(audioEngine, waveBankFileName);
if (waveBank == null)
{
throw new ArgumentException(
"The wave bank could not be created.");
}
// Create sound bank
soundBank = new SoundBank(audioEngine, soundBankFileName);
if (soundBank == null)
{
throw new ArgumentException(
"The sound bank could not be created.");
}
}
///
/// adds a sound category.
///
/// name
public void AddSoundCategory(string categoryName)
{
if (soundOn == false)
return;
AudioCategory audioCategory = AudioEngine.GetCategory(categoryName);
audioCategories.Add(categoryName, audioCategory);
}
///
/// plays the sound for 3D.
///
/// entried sound name
/// 3D emitter
/// playing sound cue
public Cue PlaySound3D(string soundName, AudioEmitter emitter)
{
SoundElement sound;
if (soundPool.Count > 0)
{
// If possible, reuse an existing Cue3D instance.
sound = soundPool.Pop();
}
else
{
// Otherwise we have to allocate a new one.
sound = new SoundElement();
}
// Fill in the cue and emitter fields.
sound.cue = soundBank.GetCue(soundName);
sound.emitter = emitter;
// Set the 3D position of this cue, and then play it.
Apply3D(sound);
sound.cue.Play();
// Remember that this cue is now active.
activeSounds.Add(sound);
return sound.cue;
}
///
/// plays the sound.
///
/// entried sound name
/// playing sound cue
public static Cue PlaySound(string soundName)
{
SoundElement sound = null;
if (soundPool.Count > 0)
{
// If possible, reuse an existing Cue3D instance.
sound = soundPool.Pop();
}
else
{
// Otherwise we have to allocate a new one.
sound = new SoundElement();
}
// Fill in the cue and emitter fields.
sound.cue = soundBank.GetCue(soundName);
sound.cue.Play();
// Remember that this cue is now active.
activeSounds.Add(sound);
return sound.cue;
}
///
/// pauses the sound.
///
public void PauseSound()
{
if (soundOn == false || pauseAll )
return;
for (int i = 0; i < activeSounds.Count; i++)
PauseSound(activeSounds[i]);
pauseAll = true;
}
///
/// resumes the sound
///
public void ResumeSound()
{
if (soundOn == false || pauseAll == false)
return;
for (int i = 0; i < activeSounds.Count; i++)
ResumeSound(activeSounds[i]);
pauseAll = false;
}
///
/// stops the sound
///
/// playing sound
public bool StopSound(Cue cue)
{
if (soundOn == false || cue == null)
return false;
if (cue.IsPaused || cue.IsPlaying)
{
cue.Stop(AudioStopOptions.Immediate);
return true;
}
return false;
}
///
/// stop all sounds.
///
public void StopSound()
{
if (soundOn == false)
return;
for (int i = 0; i < activeSounds.Count; i++)
StopSound(activeSounds[i]);
}
///
/// sets volume.
///
///
///
public void SetVolume(string categoryName, float volume)
{
if (soundOn == false)
return;
if (!audioCategories.ContainsKey(categoryName))
{
throw new InvalidOperationException("Cannot find the category : "
+ categoryName);
}
audioCategories[categoryName].SetVolume(
MathHelper.Clamp(volume, 0.0f, 1.0f));
}
///
/// determines whether a sound is being played.
///
/// playing sound cue
/// true or false
public bool IsPlaying(Cue cue)
{
if (soundOn == false)
return false;
if (cue != null)
return cue.IsPlaying;
return false;
}
///
/// determines whether a sound is being played.
///
/// playing sound cue
/// true or false
public bool IsPause(Cue cue)
{
if (soundOn == false)
return false;
if (cue != null)
return cue.IsPaused;
return false;
}
///
/// applies 3D position to sound element.
///
///
private void Apply3D(SoundElement element)
{
emitter.Position = element.emitter.Position;
emitter.Forward = element.emitter.Forward;
emitter.Up = element.emitter.Up;
emitter.Velocity = element.emitter.Velocity;
element.cue.Apply3D(listener, emitter);
}
///
/// pauses the sound.
///
/// playing sound element
private bool PauseSound(SoundElement sound)
{
if (soundOn == false || sound == null || sound.cue == null)
return false;
if (sound.cue.IsPlaying)
{
sound.cue.Pause();
return true;
}
return false;
}
///
/// resumes the sound.
///
/// paused sound element
private bool ResumeSound(SoundElement sound)
{
if (soundOn == false || sound == null || sound.cue == null)
return false;
if (sound.cue.IsPaused)
{
sound.cue.Resume();
return true;
}
return false;
}
///
/// stops the sound.
///
/// playing sound element
private bool StopSound(SoundElement sound)
{
if (soundOn == false || sound == null || sound.cue == null)
return false;
if (sound.cue.IsPaused || sound.cue.IsPlaying)
{
sound.cue.Stop(AudioStopOptions.Immediate);
return true;
}
return false;
}
}
}