This issue keeps cropping up for me, where after a relatively short time playing the game on android it will crash throwing an InstancePlayLimitException. I tracked it it down to the OpenALSoundController class, line 417, where it runs out of audio sources. I’m a bit confused by this as the SoundInstancePool class shows the playingInstances list isn’t full, and goes down to zero in scene transitions where no sounds are playing. What else counts as a audio source? I thought the limit only applied to sound effect instances, perhaps does it include music as well?
I did a custom build of monogame so I could trace the error and if I keep replaying a level I can see the available sources count slowly drop over time, so after a while it runs down to zero and throws this error. I must be missing something here. I can share my audio manager class, its short and simple.
There are quite a lot of sounds in game, its a racing game with trails of pickups on the track, so if you boost through a line of them it does play quite a few sounds (a bit like sonic when you run through a load of rings), but it shouldn’t hit anywhere near the 32 limit on android.
Here’s the class. The one shot method deliberately uses an audio instance at the moment because I want to trigger this error for debugging.
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Media;
using System.Collections.Generic;
namespace P3
{
public static class Audio
{
private static readonly List<SoundEffectInstance> playingInstances = new List<SoundEffectInstance>();
private const int MAX_INSTANCES = 32;
public enum MuteType { All, Sound, Music, VO }
private static int tick = 0;
private struct MuteConfig
{
public bool all;
public bool sound;
public bool music;
public bool vo;
public MuteConfig(bool all = false, bool sound = false, bool music = false, bool vo = false)
{
this.all = all;
this.sound = sound;
this.music = music;
this.vo = vo;
}
}
private static SoundEffectInstance vo;
private static MuteConfig mute = new MuteConfig();
private static bool playingVO = false;
public static Song PlayMusic(string name, bool loop = true)
{
var music = Game.Instance.Assets.GetMusic(name);
Microsoft.Xna.Framework.Media.MediaPlayer.IsRepeating = loop;
Microsoft.Xna.Framework.Media.MediaPlayer.Play(music);
return music;
}
public static void Update()
{
/*
//debug stuff
var instances = SoundEffectInstancePool.playingInstances;
if (instances.Count > 30)
{
System.Diagnostics.Debug.WriteLine("over 30 sound effect instances in pool");
}
if (tick++ % 10 == 0)
{
System.Diagnostics.Debug.WriteLine(instances.Count);
}
*/
for (int i = 0; i < playingInstances.Count; i++)
{
var instance = playingInstances[i];
if (instance.IsDisposed || instance.State == SoundState.Stopped)
{
if (instance == vo)
playingVO = false;
instance.Stop(true);
playingInstances.Remove(instance);
if (!instance.IsDisposed)
instance.Dispose();
instance = null;
}
}
}
public static void StopMusic()
{
Microsoft.Xna.Framework.Media.MediaPlayer.Stop();
}
public static SoundEffectInstance PlaySound(string name, bool loop = false)
{
//if we exceed max instances return the last sound played - is this wise?
if (playingInstances.Count >= MAX_INSTANCES - 1)
return playingInstances[playingInstances.Count - 1];
var sound = Game.Instance.Assets.GetSound(name);
var instance = sound.CreateInstance();
instance.IsLooped = loop;
instance.Play();
playingInstances.Add(instance);
return instance;
}
public static SoundEffectInstance PlaySound(string[] names, bool loop = false)
{
//if we exceed max instances return the last sound played - is this wise?
if (playingInstances.Count >= MAX_INSTANCES - 1)
return playingInstances[playingInstances.Count - 1];
var sound = Game.Instance.Assets.GetSound(Utils.SelectRandom(names));
var instance = sound.CreateInstance();
instance.IsLooped = loop;
instance.Play();
playingInstances.Add(instance);
return instance;
}
public static SoundEffectInstance PlayOneShot(string name)
{
//if we exceed max instances return the last sound played - is this wise?
if (playingInstances.Count >= MAX_INSTANCES)
return playingInstances[playingInstances.Count - 1];
var sound = Game.Instance.Assets.GetSound(name);
var instance = sound.CreateInstance();
instance.Play();
playingInstances.Add(instance);
return instance;
}
public static SoundEffectInstance PlayOneShot(string[] names)
{
var sound = Game.Instance.Assets.GetSound(Utils.SelectRandom(names));
var instance = sound.CreateInstance();
sound.Play();
playingInstances.Add(instance);
return instance;
}
public static SoundEffectInstance PlayVO(string name, float volume = 1f, bool force = false)
{
if ((playingVO && !force) || playingInstances.Count >= MAX_INSTANCES - 1)
return vo;
StopVO();
var sound = Game.Instance.Assets.GetSound(name);
vo = sound.CreateInstance();
vo.Volume = volume;
vo.Play();
playingVO = true;
playingInstances.Add(vo);
return vo;
}
public static SoundEffectInstance PlayVO(string[] names, float volume = 1f, bool force = false)
{
if ((playingVO && !force) || playingInstances.Count >= MAX_INSTANCES - 1)
return vo;
StopVO();
var sound = Game.Instance.Assets.GetSound(Utils.SelectRandom(names));
vo = sound.CreateInstance();
vo.Volume = volume;
vo.Play();
playingVO = true;
playingInstances.Add(vo);
return vo;
}
public static void StopVO()
{
if (vo != null && !vo.IsDisposed)
{
vo.Stop(true);
vo.Dispose();
vo = null;
}
}
public static void Mute(MuteType type = MuteType.All)
{
switch (type)
{
case MuteType.All:
mute.all = true;
break;
case MuteType.Sound:
mute.sound = true;
break;
case MuteType.Music:
mute.music = true;
break;
case MuteType.VO:
mute.vo = true;
break;
}
if (type == MuteType.All)
{
SoundEffect.MasterVolume = 0f;
Microsoft.Xna.Framework.Media.MediaPlayer.IsMuted = true;
}
else if (type == MuteType.Sound)
{
SoundEffect.MasterVolume = 0f;
}
else if (type == MuteType.Music)
{
Microsoft.Xna.Framework.Media.MediaPlayer.IsMuted = true;
}
}
public static void UnMute(MuteType type = MuteType.All)
{
switch (type)
{
case MuteType.All:
mute.all = false;
break;
case MuteType.Sound:
mute.sound = false;
break;
case MuteType.Music:
mute.music = false;
break;
case MuteType.VO:
mute.vo = false;
break;
}
if (type == MuteType.All)
{
SoundEffect.MasterVolume = 1f;
Microsoft.Xna.Framework.Media.MediaPlayer.IsMuted = false;
}
else if (type == MuteType.Sound)
{
SoundEffect.MasterVolume = 1f;
}
else if (type == MuteType.Music)
{
Microsoft.Xna.Framework.Media.MediaPlayer.IsMuted = false;
}
}
public static bool IsMute(MuteType type = MuteType.All)
{
switch (type)
{
case MuteType.All: return mute.all;
case MuteType.Sound: return mute.sound;
case MuteType.Music: return mute.music;
case MuteType.VO: return mute.vo;
}
return false;
}
}
}