namespace DarkWynterEngine.Audio
{
#region Using Statements
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using System.Xml;
#endregion
using Globals;
using Physics;
using GameObjects;
///
/// Contains the neccessary information to us a 3D sound cue.
///
public class Cue3D
{
///
/// Cue contatining sound used in 3D audio.
///
public Cue cue;
///
/// A sound generator.
///
public AudioEmitter emitter = new AudioEmitter();
///
/// A sound receiver.
///
public AudioListener listener = new AudioListener();
}
///
/// Sounds specific to particles
///
public class ParticleSounds
{
///
/// Earth Attack sound.
///
public Cue3D earthAttack = new Cue3D();
///
/// Water Attack sound.
///
public Cue3D waterAttack = new Cue3D();
///
/// Air Attack sound.
///
public Cue3D airAttack = new Cue3D();
///
/// Constructor for particle sounds.
///
public ParticleSounds()
{
earthAttack.cue = Audio.soundBank.GetCue("EarthBurst");
waterAttack.cue = Audio.soundBank.GetCue("WaterBurst");
airAttack.cue = Audio.soundBank.GetCue("AirBurst");
}
}
///
/// Sounds specific to players
///
public class PlayerSounds
{
///
/// 3D Cue used with jump sound.
///
public Cue3D jump = new Cue3D();
///
/// 3D Cue used with hit sound.
///
public Cue3D smackDownHit = new Cue3D();
///
/// 3D Cue used with walk sound.
///
public Cue3D walking = new Cue3D();
///
/// 3D Cue used with terrainMod sound.
///
public Cue3D terrainMod = new Cue3D();
///
/// 3D Cue used with fire attack sound.
///
public Cue3D fireAttack = new Cue3D();
///
/// Delay before playing another walk cue.
/// Used to stop too many sounds from being played at once.
///
public Stopwatch walkingSoundTimer = new Stopwatch();
///
/// Delay before playing another terrainMod cue.
/// Used to stop too many sounds from being played at once.
///
public Stopwatch terrainModSoundTimer = new Stopwatch();
///
/// Player sounds constructor.
///
public PlayerSounds()
{
jump.cue = Audio.soundBank.GetCue("Jump");
smackDownHit.cue = Audio.soundBank.GetCue("Hit");
walking.cue = Audio.soundBank.GetCue("Walking");
terrainMod.cue = Audio.soundBank.GetCue("TerrainMod");
fireAttack.cue = Audio.soundBank.GetCue("FireBurst");
walkingSoundTimer.Start();
terrainModSoundTimer.Start();
}
}
///
/// Supports 2D and 3D audio.
/// Functions are named after the effect they use in simple functions that
/// wrap the audio file name, making it easy to change sounds that are
/// scattered throughout the engine by changing a single reference.
///
public static class Audio
{
///
/// XNA audio engine API
///
static AudioEngine audioEngine;
///
/// Background Music
///
static WaveBank music;
///
/// Sound Effects
///
static WaveBank effects;
///
/// Provides access to WaveBank entries
///
public static SoundBank soundBank;
///
/// Holds the xml branch contatining music data.
///
static XmlNode orchestration;
///
/// Background music queue. Effects don't need queues b/c they aren't looped.
///
static List cues;
///
/// True if background music is playing.
///
static bool musicPlaying = true;
///
/// Used for 3D sound.. don't play if out of range.. wastes proccesses
///
public static float SOUND_CUTOFF_DISTANCE = 1500;
///
/// Loads audioEngine, soundBank, music waveBank, and effects waveBank.
///
public static void Initialize()
{
// Load Audio Engine Files
audioEngine = new AudioEngine("Content/_audio/Elemental.xgs");
soundBank = new SoundBank(audioEngine, "Content/_audio/Sound Bank.xsb");
music = new WaveBank(audioEngine, "Content/_audio/Music.xwb");
effects = new WaveBank(audioEngine, "Content/_audio/Effects.xwb");
if (orchestration != null)
{
orchestration.RemoveAll();
}
// Clear music cue list
cues = new List();
}
///
/// Loads xml data for audio tag and play's level song.
///
/// Audio xml node.
public static void LoadXML(XmlNode musicNode)
{
// Save for later
orchestration = musicNode;
if (orchestration.Name == "music")
{
try
{
PlayLevelSong(orchestration.Attributes["background"].Value);
}
catch
{
System.Diagnostics.Debug.WriteLine("Error reading music filename");
return;
}
}
}
///
/// Play's a song in the background.
///
/// Song Name contained in the loaded SoundBank.
public static void PlayLevelSong(string songName)
{
// Kill everthing
for (int i = 0; i < cues.Count; i++)
{
cues[i].Stop(AudioStopOptions.Immediate);
}
// Unload all the cues
cues.Clear();
// Start new theme
Cue playCue = soundBank.GetCue(songName);
musicPlaying = true;
cues.Add(playCue);
if (Statics.SystemSettings.music)
{
playCue.Play();
}
}
///
/// Update XNA audio engine.
/// See if user has requested to mute or unmute music.
///
public static void Update()
{
if (Statics.SystemSettings.music && !musicPlaying)
{
for (int i = 0; i < cues.Count; i++)
{
cues[i].Resume();
musicPlaying = true;
}
}
else if (!Statics.SystemSettings.music && musicPlaying)
{
for (int i = 0; i < cues.Count; i++)
{
cues[i].Pause();
musicPlaying = false;
}
}
audioEngine.Update();
}
#region 3D Sounds
///
/// Sound coming from a particle on the board, used in attacks.
/// Sound is attached to the particle itself.
///
/// Sound wrapper containing particle-based sounds.
/// Particle emmitting sound.
/// Player listening to sounds.
/// Used to cut off sound after a given time.
public static void Attack(ParticleSounds pSound, Particle particle, Mass listener, float timestep)
{
//check the distance first!
if (Vector3.Distance(particle.mass.currentPosition, listener.currentPosition) > SOUND_CUTOFF_DISTANCE)
{return;}
if (timestep < 1000){timestep = 1.0f;}
else{timestep /= 1000.0f;}
pSound.earthAttack.emitter.Position = particle.mass.currentPosition * timestep;
pSound.earthAttack.emitter.Velocity = particle.mass.velocity;
pSound.earthAttack.listener.Position = listener.currentPosition * timestep;
pSound.earthAttack.listener.Velocity = listener.velocity;
pSound.earthAttack.cue.Apply3D(pSound.earthAttack.listener, pSound.earthAttack.emitter);
if (!pSound.earthAttack.cue.IsPlaying)
{
pSound.earthAttack.cue = Audio.soundBank.GetCue("EarthBurst");
pSound.earthAttack.cue.Apply3D(pSound.earthAttack.listener, pSound.earthAttack.emitter);
pSound.earthAttack.cue.Play();
}
}
///
/// Kills all Attack sounds.
///
/// Wrapper contatining attack cues.
public static void StopAttack(ParticleSounds pSound)
{
// Stop Sound
if (pSound.earthAttack.cue != null)
{
pSound.earthAttack.cue.Stop(AudioStopOptions.Immediate);
}
if (pSound.waterAttack.cue != null)
{
pSound.waterAttack.cue.Stop(AudioStopOptions.Immediate);
}
if (pSound.airAttack.cue != null)
{
pSound.airAttack.cue.Stop(AudioStopOptions.Immediate);
}
}
///
/// Sound used in Fire attacks.
/// Not based on particles, unlike Attack.
///
/// Class containing cues for Player-based sounds.
/// Location of the Attacker.
/// Location of the Listener.
public static void FireAttack(PlayerSounds pSound, Vector3 emitterPosition, Vector3 listenerPosition)
{
//check the distance first!
if (Vector3.Distance(emitterPosition, listenerPosition) > SOUND_CUTOFF_DISTANCE)
{
return;
}
pSound.fireAttack.emitter.Position = emitterPosition;
pSound.fireAttack.listener.Position = listenerPosition;
pSound.fireAttack.cue.Apply3D(pSound.fireAttack.listener, pSound.fireAttack.emitter);
if (!pSound.fireAttack.cue.IsPlaying)
{
pSound.fireAttack.cue = Audio.soundBank.GetCue("FireBurst");
pSound.fireAttack.cue.Apply3D(pSound.fireAttack.listener, pSound.fireAttack.emitter);
pSound.fireAttack.cue.Play();
}
}
///
/// Sound used when Player jumps.
///
/// Class containing cues for Player-based sounds.
/// Location of the Attacker.
/// Location of the Listener.
public static void Jump(PlayerSounds pSound, Vector3 emitterPosition, Vector3 listenerPosition)
{
//check the distance first!
if (Vector3.Distance(emitterPosition, listenerPosition) > SOUND_CUTOFF_DISTANCE)
{
return;
}
pSound.jump.cue = soundBank.GetCue("Jump");
pSound.jump.emitter.Position = emitterPosition;
pSound.jump.listener.Position = listenerPosition;
pSound.jump.cue.Apply3D(pSound.jump.listener, pSound.jump.emitter);
pSound.jump.cue.Play();
}
///
/// Sound used when Player gets hit.
///
/// Class containing cues for Player-based sounds.
/// Location of the Attacker.
/// Location of the Listener.
public static void Hit(PlayerSounds pSound, Vector3 emitterPosition, Vector3 listenerPosition)
{
//check the distance first!
if (Vector3.Distance(emitterPosition, listenerPosition) > SOUND_CUTOFF_DISTANCE)
{
return;
}
pSound.smackDownHit.cue = soundBank.GetCue("Hit");
pSound.smackDownHit.emitter.Position = emitterPosition;
pSound.smackDownHit.listener.Position = listenerPosition;
pSound.smackDownHit.cue.Apply3D(pSound.smackDownHit.listener, pSound.smackDownHit.emitter);
pSound.smackDownHit.cue.Play();
}
///
/// Sound used when Player is walking.
///
/// Class containing cues for Player-based sounds.
/// Location of the Attacker.
/// Location of the Listener.
public static void Walking(PlayerSounds pSound, Vector3 emitterPosition, Vector3 listenerPosition)
{
//check the distance first!
if (Vector3.Distance(emitterPosition, listenerPosition) > SOUND_CUTOFF_DISTANCE)
{
return;
}
//pSound.walking.emitter.Position = emitterPosition;
//pSound.walking.listener.Position = listenerPosition;
//pSound.walking.cue.Apply3D(pSound.walking.listener, pSound.walking.emitter);
//if (pSound.walkingSoundTimer.ElapsedMilliseconds > 100)
//{
// pSound.walking.cue = soundBank.GetCue("Walking");
// pSound.walking.cue.Apply3D(pSound.walking.listener, pSound.walking.emitter);
// pSound.walking.cue.Play();
// pSound.walkingSoundTimer = Stopwatch.StartNew();
//}
}
///
/// Sound used when Player terrainMods.
///
/// Class containing cues for Player-based sounds.
/// Location of the Attacker.
/// Location of the Listener.
public static void TerrainMod(PlayerSounds pSound, Vector3 emitterPosition, Vector3 listenerPosition)
{
//check the distance first!
if (Vector3.Distance(emitterPosition, listenerPosition) > SOUND_CUTOFF_DISTANCE)
{
return;
}
pSound.terrainMod.emitter.Position = emitterPosition;
pSound.terrainMod.listener.Position = listenerPosition;
pSound.terrainMod.cue.Apply3D(pSound.terrainMod.listener, pSound.terrainMod.emitter);
if (pSound.terrainModSoundTimer.ElapsedMilliseconds > 900)
{
pSound.terrainMod.cue = soundBank.GetCue("TerrainMod");
pSound.terrainMod.cue.Apply3D(pSound.terrainMod.listener, pSound.terrainMod.emitter);
pSound.terrainMod.cue.Play();
pSound.terrainModSoundTimer = Stopwatch.StartNew();
}
}
#endregion
#region 2D Sounds
///
/// Base function called by all 2D sounds.
///
/// Name of cue to play
public static void PlayCue2D(string cueName)
{
if (Statics.SystemSettings.soundFX)
{
soundBank.PlayCue(cueName);
}
}
///
/// Sound played when player finds a Key.
///
public static void KeyFound() { PlayCue2D("KeyFound"); }
///
/// Sound played when Start button is pressed in menu system.
///
public static void MenuStart() { PlayCue2D("Tin"); }
///
/// Sound played when Back button is pressed in menu system.
///
public static void MenuBack() { PlayCue2D("Tin"); }
///
/// Sound played when Up button is pressed in menu system.
///
public static void MenuUp() { PlayCue2D("Tin"); }
///
/// Sound played when Down button is pressed in menu system.
///
public static void MenuDown() { PlayCue2D("Tin"); }
///
/// Sound played when Left button is pressed in menu system.
///
public static void MenuLeft() { PlayCue2D("Tin"); }
///
/// Sound played when Right button is pressed in menu system.
///
public static void MenuRight() { PlayCue2D("Tin"); }
///
/// Sound played when Player selection is changed in menu system.
///
public static void MenuPlayerShuffleRight() { PlayCue2D("Tin"); }
///
/// Sound played when Player selection is changed in menu system.
///
public static void MenuPlayerShuffleLeft() { PlayCue2D("Tin"); }
///
/// Sound played when Dpad button is pressed in Game.
///
public static void MenuDPad() { PlayCue2D("Tin"); }
#endregion
}
}