Random ChannelStolen errors

Hello support,

using Unity 6000.1.14f1
using FMOD Plugin Version 2.02.20

Sometimes when playing the game I am working on I get errors like this when I control my character:

[FMOD] ChannelControl::isPlaying(000000EB8A57EFA0:false) returned ERR_CHANNEL_STOLEN for CHANNELCONTROL (0x7FE0005).
UnityEngine.Debug:LogError (object)
FMODUnity.RuntimeUtils:DebugLogError (string) (at Assets/Plugins/FMOD/src/RuntimeUtils.cs:580)
FMODUnity.RuntimeManager:ERROR_CALLBACK (intptr,FMOD.SYSTEM_CALLBACK_TYPE,intptr,intptr,intptr) (at Assets/Plugins/FMOD/src/RuntimeManager.cs:142)

[FMOD] ChannelControl::isPlaying(000000EB8A57F000:false) returned ERR_CHANNEL_STOLEN for CHANNELCONTROL (0x7FE0005).
UnityEngine.Debug:LogError (object)
FMODUnity.RuntimeUtils:DebugLogError (string) (at Assets/Plugins/FMOD/src/RuntimeUtils.cs:580)
FMODUnity.RuntimeManager:ERROR_CALLBACK (intptr,FMOD.SYSTEM_CALLBACK_TYPE,intptr,intptr,intptr) (at Assets/Plugins/FMOD/src/RuntimeManager.cs:142)

[FMOD] ChannelControl::stop() returned ERR_CHANNEL_STOLEN for CHANNELCONTROL (0x7FE0005).
UnityEngine.Debug:LogError (object)
FMODUnity.RuntimeUtils:DebugLogError (string) (at Assets/Plugins/FMOD/src/RuntimeUtils.cs:580)
FMODUnity.RuntimeManager:ERROR_CALLBACK (intptr,FMOD.SYSTEM_CALLBACK_TYPE,intptr,intptr,intptr) (at Assets/Plugins/FMOD/src/RuntimeManager.cs:142)

[FMOD] ChannelControl::setDelay(99328, 119457, true) returned ERR_CHANNEL_STOLEN for CHANNELCONTROL (0x7FC0011).
UnityEngine.Debug:LogError (object)
FMODUnity.RuntimeUtils:DebugLogError (string) (at Assets/Plugins/FMOD/src/RuntimeUtils.cs:580)
FMODUnity.RuntimeManager:ERROR_CALLBACK (intptr,FMOD.SYSTEM_CALLBACK_TYPE,intptr,intptr,intptr) (at Assets/Plugins/FMOD/src/RuntimeManager.cs:142)

[FMOD] ChannelControl::setFadePointRamp(119457, 0) returned ERR_CHANNEL_STOLEN for CHANNELCONTROL (0x7FC0011).
UnityEngine.Debug:LogError (object)
FMODUnity.RuntimeUtils:DebugLogError (string) (at Assets/Plugins/FMOD/src/RuntimeUtils.cs:580)
FMODUnity.RuntimeManager:ERROR_CALLBACK (intptr,FMOD.SYSTEM_CALLBACK_TYPE,intptr,intptr,intptr) (at Assets/Plugins/FMOD/src/RuntimeManager.cs:142)

[FMOD] Channel::getLoopCount(000000EB8A57EB40:0) returned ERR_CHANNEL_STOLEN for CHANNEL (0x7FC0011).
UnityEngine.Debug:LogError (object)
FMODUnity.RuntimeUtils:DebugLogError (string) (at Assets/Plugins/FMOD/src/RuntimeUtils.cs:580)
FMODUnity.RuntimeManager:ERROR_CALLBACK (intptr,FMOD.SYSTEM_CALLBACK_TYPE,intptr,intptr,intptr) (at Assets/Plugins/FMOD/src/RuntimeManager.cs:142)

[FMOD] ChannelControl::isPlaying(000000EB8A57EFA0:false) returned ERR_CHANNEL_STOLEN for CHANNELCONTROL (0x7FC0011).
UnityEngine.Debug:LogError (object)
FMODUnity.RuntimeUtils:DebugLogError (string) (at Assets/Plugins/FMOD/src/RuntimeUtils.cs:580)
FMODUnity.RuntimeManager:ERROR_CALLBACK (intptr,FMOD.SYSTEM_CALLBACK_TYPE,intptr,intptr,intptr) (at Assets/Plugins/FMOD/src/RuntimeManager.cs:142)

[FMOD] ChannelControl::isPlaying(000000EB8A57F000:false) returned ERR_CHANNEL_STOLEN for CHANNELCONTROL (0x7FC0011).
UnityEngine.Debug:LogError (object)
FMODUnity.RuntimeUtils:DebugLogError (string) (at Assets/Plugins/FMOD/src/RuntimeUtils.cs:580)
FMODUnity.RuntimeManager:ERROR_CALLBACK (intptr,FMOD.SYSTEM_CALLBACK_TYPE,intptr,intptr,intptr) (at Assets/Plugins/FMOD/src/RuntimeManager.cs:142)

[FMOD] ChannelControl::stop() returned ERR_CHANNEL_STOLEN for CHANNELCONTROL (0x7FC0011).
UnityEngine.Debug:LogError (object)
FMODUnity.RuntimeUtils:DebugLogError (string) (at Assets/Plugins/FMOD/src/RuntimeUtils.cs:580)
FMODUnity.RuntimeManager:ERROR_CALLBACK (intptr,FMOD.SYSTEM_CALLBACK_TYPE,intptr,intptr,intptr) (at Assets/Plugins/FMOD/src/RuntimeManager.cs:142)

Now I play audio in centralised way. I have this script which basically handles registering audio events:

using System.Collections.Generic;
using FMOD.Studio;
using FMODUnity;
using Sirenix.OdinInspector;
using UnityEngine;
using EventReference = FMODUnity.EventReference;
using STOP_MODE = FMOD.Studio.STOP_MODE;

namespace BTSMOH.Game.Audio
{
    public class AudioRegistry : MonoBehaviour
    {
        [ReadOnly]
        [ShowInInspector]
        private Dictionary<EventReference, AudioEventInfo> registry = new Dictionary<EventReference, AudioEventInfo>();

        private void Awake()
        {
            registry = new Dictionary<EventReference, AudioEventInfo>();
        }

        public void Register(EventReference reference, EventReleaseType releaseType, bool canBePaused)
        {
            EventInstance  instance = RuntimeManager.CreateInstance(reference.Guid);
            AudioEventInfo info     = new AudioEventInfo(instance, releaseType, canBePaused);
            registry.TryAdd(reference, info);
        }

        public void Play(EventReference reference, bool stopBeforePlaying)
        {
            if (IsSoundEventValid(reference, out AudioEventInfo info))
            {
                if (IsEventInstanceValid(info.EventInstance))
                {
                    if (IsInstancePlaying(info.EventInstance) && stopBeforePlaying)
                    {
                        info.EventInstance.stop(STOP_MODE.IMMEDIATE);
                    }

                    info.EventInstance.start();
                    if (info.EventReleaseType == EventReleaseType.AFTERPLAY)
                    {
                        info.EventInstance.release();
                    }
                }
            }
        }

        /// <summary>
        /// PauseState determines if we pause a soundevent
        /// True -> Pause
        /// False -> Resume
        /// </summary>
        /// <param name="reference"></param>
        /// <param name="pauseState"></param>
        public void Pause(EventReference reference, bool pauseState)
        {
            if (IsSoundEventValid(reference, out AudioEventInfo info))
            {
                if (IsEventInstanceValid(info.EventInstance))
                {
                    if (info.CanBePaused)
                    {
                        info.EventInstance.setPaused(pauseState);
                    }
                }
            }
        }

        public void PauseAll()
        {
            foreach (KeyValuePair<EventReference, AudioEventInfo> kvp in registry)
            {
                Pause(kvp.Key, true);
            }
        }

        public void ResumeAll()
        {
            foreach (KeyValuePair<EventReference, AudioEventInfo> kvp in registry)
            {
                Pause(kvp.Key, false);
            }
        }

        public void Stop(EventReference reference, STOP_MODE stopMode)
        {
            if (IsSoundEventValid(reference, out AudioEventInfo info))
            {
                if (IsEventInstanceValid(info.EventInstance))
                {
                    info.EventInstance.stop(stopMode);
                    if (info.EventReleaseType == EventReleaseType.AFTERSTOP)
                    {
                        info.EventInstance.release();
                    }
                }
            }
        }


        private bool IsSoundEventValid(EventReference reference, out AudioEventInfo info)
        {
            info = new AudioEventInfo();
            if (reference.IsNull)
            {
                return false;
            }

            if (registry.TryGetValue(reference, out AudioEventInfo value))
            {
                info = value;
                return true;
            }

            return false;
        }

        private bool IsEventInstanceValid(EventInstance instance)
        {
            return instance.isValid();
        }

        public bool IsPlaying(EventReference reference)
        {
            if (IsSoundEventValid(reference, out AudioEventInfo info))
            {
                return IsInstancePlaying(info.EventInstance);
            }

            return false;
        }

        private bool IsInstancePlaying(EventInstance instance)
        {
            if (IsEventInstanceValid(instance))
            {
                PLAYBACK_STATE playbackState;
                instance.getPlaybackState(out playbackState);
                return playbackState == PLAYBACK_STATE.STARTING || playbackState == PLAYBACK_STATE.PLAYING;
            }

            return false;
        }

        public bool IsFinished(EventReference reference)
        {
            if (IsSoundEventValid(reference, out AudioEventInfo info))
            {
                return IsInstanceFinished(info.EventInstance);
            }

            return false;
        }

        private bool IsInstanceFinished(EventInstance instance)
        {
            if (IsEventInstanceValid(instance))
            {
                PLAYBACK_STATE playbackState;
                instance.getPlaybackState(out playbackState);
                return playbackState == PLAYBACK_STATE.STOPPED;
            }

            return false;
        }


        public void Unregister(EventReference reference)
        {
            if (registry.ContainsKey(reference))
            {
                if (IsSoundEventValid(reference, out AudioEventInfo info))
                {
                    if (IsEventInstanceValid(info.EventInstance))
                    {
                        info.EventInstance.stop(STOP_MODE.IMMEDIATE);
                        if (info.EventReleaseType == EventReleaseType.AFTERUNREGISTER)
                        {
                            info.EventInstance.release();
                        }

                        registry.Remove(reference);
                    }
                }
            }
        }

        public void UnregisterAll()
        {
            List<EventReference> keys = new List<EventReference>(registry.Keys);

            foreach (EventReference reference in keys)
            {
                Unregister(reference);
            }

            registry.Clear();
        }


        public void Dispose()
        {
            UnregisterAll();
        }

        private void OnDisable()
        {
            Dispose();
        }
    }
}

Like for example when I walk around with one of my characters the errors above are thrown.

This is the script that handles character audio:

using System.Collections.Generic;
using BTSMOH.Game.Audio;
using BTSMOH.Game.Entities.Animation;
using FMOD.Studio;
using FMODUnity;
using Sirenix.OdinInspector;
using UnityEngine;
using STOP_MODE = FMOD.Studio.STOP_MODE;

namespace BTSMOH.Game.Entities.Audio
{
    public class CharacterAudible : MonoBehaviour,
                                    IEntityAudible
    {
        [SerializeField]
        private EntitySoundData entitySoundData;

        [ReadOnly]
        [ShowInInspector]
        private EntityAnimationState currentAnimationState;

        private EntityStateSoundInfo currentStateSoundInfo;
        private EventReference       currentSoundEvent;
        private EventInstance        currentEventInstance;

        public EntitySoundData EntitySoundData
        {
            get => entitySoundData;
            set => entitySoundData = value;
        }

        private void Awake()
        {
            currentAnimationState = ScriptableObject.CreateInstance<EntityAnimationState>();
            foreach (KeyValuePair<EntityAnimationState, EntityStateSoundInfo> kvp in entitySoundData.SoundInfo)
            {
                if (!kvp.Value.EventReference.IsNull)
                {
                    GameHandler.AudioRegistry.Register(kvp.Value.EventReference, EventReleaseType.AFTERUNREGISTER, true);
                }
            }
        }

        public void Play(EntityAnimationState state, bool forcePlay)
        {
            // Set entityAnimation state first before play
            if (state == null)
            {
                return;
            }

            if (currentAnimationState != state)
            {
                GameHandler.AudioRegistry.Stop(currentStateSoundInfo.EventReference, STOP_MODE.IMMEDIATE);
                currentAnimationState = state;

                entitySoundData.SoundInfo.TryGetValue(currentAnimationState, out currentStateSoundInfo);
                currentSoundEvent = currentStateSoundInfo.EventReference;
            }


            if (forcePlay)
            {
                GameHandler.AudioRegistry.Play(currentSoundEvent, false);
            }
            else
            {
                if (!GameHandler.AudioRegistry.IsPlaying(currentSoundEvent))
                {
                    GameHandler.AudioRegistry.Play(currentSoundEvent, false);
                }
            }
        }

        public void Pause()
        {
            GameHandler.AudioRegistry.Pause(currentSoundEvent, true);
        }

        public void Resume()
        {
            GameHandler.AudioRegistry.Pause(currentSoundEvent, false);
        }

        public void Stop()
        {
            GameHandler.AudioRegistry.Stop(currentSoundEvent, STOP_MODE.IMMEDIATE);
        }


        private void OnDisable()
        {
            if (GameHandler.AudioRegistry != null)
            {
                foreach (KeyValuePair<EntityAnimationState, EntityStateSoundInfo> kvp in entitySoundData.SoundInfo)
                {
                    GameHandler.AudioRegistry.Unregister(kvp.Value.EventReference);
                }
            }
        }
    }
}

I read that this error specifically “ERR_CHANNEL_STOLEN for CHANNELCONTROL” means a virtual voice has its channel stolen and if there is a voice cutoff I should increase the virtual channel count int the fmod settings. But the count is set to 1024 and I feel this should suffice, especially if like just two characters are just walking around, no?
Any idea or suggestion where I should look in order to further investigate this issue?