Why do I get an exception when playing multiple sound instances?
- by Boreal
Right now, I'm adding a rudimentary sound engine to my game. So far, I am able to load in a WAV file and play it once, then free up the memory when I close the game. However, the game crashes with a nice ArgumentOutOfBoundsException when I try to play another sound instance.
Specified argument was out of the range of valid values.
Parameter name: readLength
I'm following this tutorial pretty much exactly, but I still keep getting the aforementioned error. Here's my sound-related code.
/// <summary>
/// Manages all sound instances.
/// </summary>
public static class Audio
{
static XAudio2 device;
static MasteringVoice master;
static List<SoundInstance> instances;
/// <summary>
/// The XAudio2 device.
/// </summary>
internal static XAudio2 Device
{
get { return device; }
}
/// <summary>
/// Initializes the audio device and master track.
/// </summary>
internal static void Initialize()
{
device = new XAudio2();
master = new MasteringVoice(device);
instances = new List<SoundInstance>();
}
/// <summary>
/// Releases all XA2 resources.
/// </summary>
internal static void Shutdown()
{
foreach(SoundInstance i in instances)
i.Dispose();
master.Dispose();
device.Dispose();
}
/// <summary>
/// Registers a sound instance with the system.
/// </summary>
/// <param name="instance">Sound instance</param>
internal static void AddInstance(SoundInstance instance)
{
instances.Add(instance);
}
/// <summary>
/// Disposes any sound instance that has stopped playing.
/// </summary>
internal static void Update()
{
List<SoundInstance> temp = new List<SoundInstance>(instances);
foreach(SoundInstance i in temp)
if(!i.Playing)
{
i.Dispose();
instances.Remove(i);
}
}
}
/// <summary>
/// Loads sounds from various files.
/// </summary>
internal class SoundLoader
{
/// <summary>
/// Loads a .wav sound file.
/// </summary>
/// <param name="format">The decoded format will be sent here</param>
/// <param name="buffer">The data will be sent here</param>
/// <param name="soundName">The path to the WAV file</param>
internal static void LoadWAV(out WaveFormat format, out AudioBuffer buffer, string soundName)
{
WaveStream wave = new WaveStream(soundName);
format = wave.Format;
buffer = new AudioBuffer();
buffer.AudioData = wave;
buffer.AudioBytes = (int)wave.Length;
buffer.Flags = BufferFlags.EndOfStream;
}
}
/// <summary>
/// Manages the data for a single sound.
/// </summary>
public class Sound : IAsset
{
WaveFormat format;
AudioBuffer buffer;
/// <summary>
/// Loads a sound from a file.
/// </summary>
/// <param name="soundName">The path to the sound file</param>
/// <returns>Whether the sound loaded successfully</returns>
public bool Load(string soundName)
{
if(soundName.EndsWith(".wav"))
SoundLoader.LoadWAV(out format, out buffer, soundName);
else
return false;
return true;
}
/// <summary>
/// Plays the sound.
/// </summary>
public void Play()
{
Audio.AddInstance(new SoundInstance(format, buffer));
}
/// <summary>
/// Unloads the sound from memory.
/// </summary>
public void Unload()
{
buffer.Dispose();
}
}
/// <summary>
/// Manages a single sound instance.
/// </summary>
public class SoundInstance
{
SourceVoice source;
bool playing;
/// <summary>
/// Whether the sound is currently playing.
/// </summary>
public bool Playing
{
get { return playing; }
}
/// <summary>
/// Starts a new instance of a sound.
/// </summary>
/// <param name="format">Format of the sound</param>
/// <param name="buffer">Buffer holding sound data</param>
internal SoundInstance(WaveFormat format, AudioBuffer buffer)
{
source = new SourceVoice(Audio.Device, format);
source.BufferEnd += (s, e) => playing = false;
source.Start();
source.SubmitSourceBuffer(buffer); // THIS IS WHERE THE EXCEPTION IS THROWN
playing = true;
}
/// <summary>
/// Releases memory used by the instance.
/// </summary>
internal void Dispose()
{
source.Dispose();
}
}
The exception occurs on line 156 when I am playing the sound:
source.SubmitSourceBuffer(buffer);