First things first: If you're targeting XBLIG and Windows desktop, you're using XACT, right? Just making sure. Unless you have a compelling reason not to, you should be using XACT. It's got warts, but it solves too many problems for it not be used. That is, unless you're looking to also compile with MonoGame or deploy to Windows Phone. Then it's not so useful.
Does it make sense to carry out all audio (music, sound effects, etc.) in a separate thread?
Many audio playback libraries start a background thread to load/stream audio into memory and write to the output buffer. You adding another thread on top of this wouldn't do much without a lot of main loop tweaking and tuning (which is probably futile if you aren't specifically targeting XBLIG).
In your experience, how large would you expect the performance impact of playing some MP3 music and 10-30 sound effects to be?
Yes, there's an impact. How much that impact matters depends on the minimum hardware you want to target and what else is happening in the game at that time. But, before you run into performance problems, you'll run into ear fatigue problems with that many sound effects if you don't do some clever mixing.
Could performance be an issue with starting to play many different sound effects (e.g. in the same frame)?
You'll probably get a framerate dip if there's a ton of sounds starting up in one frame.
If it really becomes a performance problem, consider staggering their playback across frames. Players' ears won't notice the difference, and you give the CPU some extra milliseconds to start mixing the samples.
Rough crappy example that uses SoundEffect:
public class AudioManager
{
private const MaxSoundTriggersPerFrame = 15;
private struct PlayInfo
{
public SoundEffect sfx;
public float volume;
public float pitch;
public float pan;
}
Queue<SoundEffect> queuedSFX = new Queue<SoundEffect>();
PlayOneShot(SoundEffect sfx, float volume, float pitch, float pan)
{
queuedSFX.Add(new PlayInfo() {
sfx = sfx,
volume = volume,
pitch = pitch,
pan = pan
});
}
// Assuming this is called every update frame from Game1.cs or whatever
public void Update()
{
int playCount = 0;
while (queuedSFX.Count > 0 && playCount <= MaxSoundTriggersPerFrame) {
var playInfo = queuedSFX.Dequeue();
playInfo.sfx.Play(playInfo.volume, playInfo.pitch, playInfo.pan);
}
}
}
It would be neat to pre-populate some metadata about a particular sound effect. In-memory sounds could have their own limit, and sounds that have to be streamed and decoded could have a separate limit.
This also uses a .NET queue, and if you're concerned about performance maybe look into how this is implemented (I don't know off the top of my head). You want it to behave like a List<T>
, such that internally it stores an array of PlayInfo
objects and not a linked list of them.
But again: If you're thinking at this level, twenty bucks says you have too many sounds playing at once to be pleasant or useful.