Crash in FMOD5_Thread_SetAttributes

I’m using a test script to test music playback for my game, and I frequently am getting a crash on FMOD::System::playSound on either subsequent playbacks, or when removing the script or gameobject its attached to, or simply stopping the game in the editor. I dunno if I’m releasing things when I shouldn’t, or not releasing things when I should.

here’s the Unity Editor stacktrace: FMOD Ex Unity stacktrace - Pastebin.com
and here’s the script in question: FMOD Ex Unity script - Pastebin.com

I have hit a segfault by commenting back in baseSound.release(); on line 145- that would be because of calling release before trying to access the subsounds.
I couldn’t get any crashes otherwise, but I am seeing errors when playing and stopping the scene:

[FMOD] Sound::release() returned ERR_INVALID_HANDLE for SOUND (0x1B224743028).

Firstly, you do not need to release subsounds when using fsbs, secondly, you are double releasing all of your handles by calling OnDisable() and OnDestroy(). I suggest something like this instead:

    void OnDisable()
    {
        ReloadSound(false);
    }

    void ReloadSound(bool runAwake)
    {
        if (hasLoadedAtLeastOnce)
        {
            ReleaseAll();

            hasInitializedMainSound = false;
            hasInitializedSound2 = false;
            hasInitializedSubSounds = false;
        }
        else
        { 
            hasLoadedAtLeastOnce = true;
        }
        if (runAwake)
            LoadTrack();
    }

    void ReleaseAll()
    {
        dsp01.release();
        dsp02.release();
        dsp01.release();
        dsp02.release();
        //sound01.release();
        //sound02.release();
        baseSound.release();
        baseSound02.release();
    }

I am currently still getting crashes if I disable and then reenable the script with this code. I also still occasionally get freezing when exiting play mode (although that’s much more rare now). Have I missed something again?

using FMOD;
using UnityEngine;

public class fmodlowleveltest : MonoBehaviour
{
    [Range(1, 6)] public int stereoPairs = 6;
    private int stereoPairsPlayback = 6;

    [Range(0.0f, 1.0f)] public float baseVolume = 0.75f;

    [Range(0.0f, 1.0f)] public float channel1Volume = 1.0f;
    [Range(0.0f, 1.0f)] public float channel2Volume;
    [Range(0.0f, 1.0f)] public float channel3Volume;
    [Range(0.0f, 1.0f)] public float channel4Volume;
    [Range(0.0f, 1.0f)] public float channel5Volume;
    [Range(0.0f, 1.0f)] public float channel6Volume;

    private FMOD.System SystemEX;
    private Sound baseSound;
    private Sound baseSound02;
    private Sound sound01;
    private Channel channel01;
    private Sound sound02;
    private Channel channel02;
    private ChannelGroup channelGroup;
    bool hasInitializedMainSound = false;
    bool hasInitializedSubSounds = false;
	bool hasInitializedSound2 = false;
    public string bank = "i_pod.fsb";
    public uint loopStart = 0;
    public bool reloadSound = false;
	private bool hasLoadedAtLeastOnce;
    private bool gotBus = false;
    FMOD.Studio.Bus bus;


    public static bool ERRCHECK(FMOD.RESULT result)
    {
        if (result != FMOD.RESULT.OK)
        {
            Debug.LogError("FMOD Error (" + result.ToString() + "): " + FMOD.Error.String(result));
        }
        
        return (result == FMOD.RESULT.OK);
    }

    void Start()
	{
        SystemEX = FMODUnity.RuntimeManager.CoreSystem;
        stereoPairsPlayback = stereoPairs;
    }

    // Start is called before the first frame update
    void LoadTrack()
    {
        RESULT result;
        stereoPairsPlayback = stereoPairs;
        Debug.Log("set up parameters");

        string bankpath = string.Format("{0}/audio/{1}", Application.streamingAssetsPath, bank);
        MODE modes = MODE.LOOP_NORMAL | MODE._2D | MODE.CREATESTREAM | MODE.NONBLOCKING;

        result = SystemEX.createStream(bankpath, modes, out baseSound);
        ERRCHECK(result);
        if (stereoPairsPlayback > 3)
        {
            result = SystemEX.createStream(bankpath, modes, out baseSound02);
            ERRCHECK(result);
            Debug.Log("Loaded IntMusic with more than 3 stereo pairs!");
        }
    }

    void setVolumes(float track1, float track2, float track3, float track4, float track5, float track6)
	{
        float[] vol = new float[3] { track1 * baseVolume, track2 * baseVolume, track3 * baseVolume };
        float[] vol02 = new float[3] { track4 * baseVolume, track5 * baseVolume, track6 * baseVolume };

        float[] matrix01_1 = new float[System.Math.Min(stereoPairsPlayback, 3) * 2];
        float[] matrix01_2 = new float[System.Math.Min(stereoPairsPlayback, 3) * 2];
        for (int i = 0; i < System.Math.Min(stereoPairsPlayback, 3); i++)
		{
            matrix01_1[i * 2] = vol[i];
            matrix01_2[i * 2] = 0;
            matrix01_1[(i * 2) + 1] = 0;
            matrix01_2[(i * 2) + 1] = vol[i];
        }
        float[] matrix01 = new float[matrix01_1.Length + matrix01_2.Length];
        matrix01_1.CopyTo(matrix01, 0);
        matrix01_2.CopyTo(matrix01, matrix01_1.Length);
        if (hasInitializedSubSounds)
            channel01.setMixMatrix(matrix01, 2, 6, System.Math.Min(stereoPairsPlayback, 3) * 2);
        if (stereoPairsPlayback > 3)
        {
            float[] matrix02_1 = new float[(stereoPairsPlayback - 3) * 2];
            float[] matrix02_2 = new float[(stereoPairsPlayback - 3) * 2];
            for (int i = 0; i < (stereoPairsPlayback - 3); i++)
            {
                matrix02_1[i * 2] = vol02[i];
                matrix02_2[i * 2] = 0;
                matrix02_1[(i * 2) + 1] = 0;
                matrix02_2[(i * 2) + 1] = vol02[i];
            }
            float[] matrix02 = new float[matrix01_1.Length + matrix01_2.Length];
            matrix02_1.CopyTo(matrix02, 0);
            matrix02_2.CopyTo(matrix02, matrix02_1.Length);
            if (hasInitializedSubSounds)
                channel02.setMixMatrix(matrix02, 2, 6, (stereoPairsPlayback - 3) * 2);
        }
    }

    // Update is called once per frame
    void Update()
    {
        RESULT result;
        if (hasLoadedAtLeastOnce)
        {
            OPENSTATE openstate;
            result = baseSound.getOpenState(out openstate, out _, out _, out _);
            ERRCHECK(result);

            if (openstate == FMOD.OPENSTATE.READY && !hasInitializedMainSound)
            {
                hasInitializedMainSound = true;
                result = baseSound.getSubSound(0, out sound01);
                ERRCHECK(result);
                Debug.Log("get sound 1");
            }

            OPENSTATE openstate01 = 0;
            if (stereoPairsPlayback > 3)
            {
                result = baseSound02.getOpenState(out openstate01, out _, out _, out _);
                ERRCHECK(result);
            }

            if (openstate01 == FMOD.OPENSTATE.READY && !hasInitializedSound2 && stereoPairsPlayback > 3)
            {
                hasInitializedSound2 = true;
                result = baseSound02.getSubSound(1, out sound02);
                ERRCHECK(result);
                Debug.Log("get sound 2");
            }

            OPENSTATE openstate02 = 0;
            if (stereoPairsPlayback > 3 && openstate01 == FMOD.OPENSTATE.READY)
            {
                result = sound02.getOpenState(out openstate02, out _, out _, out _);
                ERRCHECK(result);
            }

            if (openstate01 == FMOD.OPENSTATE.READY && (openstate02 == FMOD.OPENSTATE.READY || stereoPairsPlayback <= 3) && !hasInitializedSubSounds)
            {
                hasInitializedSubSounds = true;
                //baseSound.release();                
                if (!gotBus)
                {
                    bus = FMODUnity.RuntimeManager.GetBus("bus:/master/music");
                    ERRCHECK(result);
                    ERRCHECK(bus.lockChannelGroup());
                    ERRCHECK(FMODUnity.RuntimeManager.StudioSystem.flushCommands());
                    ERRCHECK(bus.getChannelGroup(out channelGroup));
                    gotBus = true;
                }

                Debug.Log("get channel group");

                uint sound01loopend;
                sound01.getLoopPoints(out _, TIMEUNIT.MS, out sound01loopend, TIMEUNIT.PCM);
                sound01.setLoopPoints(loopStart, TIMEUNIT.MS, sound01loopend, TIMEUNIT.PCM);

                if (stereoPairsPlayback > 3)
                {
                    uint sound02loopend;
                    sound02.getLoopPoints(out _, TIMEUNIT.MS, out sound02loopend, TIMEUNIT.PCM);
                    sound02.setLoopPoints(loopStart, TIMEUNIT.MS, sound02loopend, TIMEUNIT.PCM);
                    SystemEX.playSound(sound01, channelGroup, false, out channel01);
                    SystemEX.playSound(sound02, channelGroup, false, out channel02);
                }
                else
                {
                    SystemEX.playSound(sound01, channelGroup, false, out channel01);
                }
                setVolumes(channel1Volume, channel2Volume, channel3Volume, channel4Volume, channel5Volume, channel6Volume);
                string name01;
                sound01.getName(out name01, 256);
                if (stereoPairsPlayback > 3)
                {
                    string name02;
                    sound02.getName(out name02, 256);
                    Debug.LogFormat("Now playing {0} and {1}", name01, name02);
                }
                else
                {
                    Debug.LogFormat("Now playing {0}", name01);
                }
            }
        }
        if (reloadSound && FMODUnity.RuntimeManager.IsInitialized)
		{
            reloadSound = false;
            ReloadSound(true);
        }
        setVolumes(channel1Volume, channel2Volume, channel3Volume, channel4Volume, channel5Volume, channel6Volume);
    }

	void OnDisable()
	{
        ReloadSound(false);
    }

    void ReloadSound(bool runAwake)
	{
        if (hasLoadedAtLeastOnce)
        {
            //OnDestroy();
            Reload();
            hasInitializedMainSound = false;
            hasInitializedSound2 = false;
            hasInitializedSubSounds = false;
        }
        else if(runAwake)
		{
            hasLoadedAtLeastOnce = true;
        }
        if (runAwake)
            LoadTrack();
    }

    void Reload()
	{
        if (hasInitializedSubSounds)
        {
            baseSound.release();
            if (stereoPairsPlayback > 3)
                baseSound02.release();
        }
    }

	void OnDestroy()
    {
        //Reload();
        if (gotBus)
        {
            channelGroup.release();
            bus.unlockChannelGroup();
        }
        //SystemEX.release();
    }
}

the crash occurs at line 176

by the way, commented out lines were commented out on purpose due to them no longer being needed in the current iteration of the script, and really old stuff that stayed in a now strange-looking place

In this case it’s because the state when the script starts isn’t the same as when the script is started, disabled and then re-enabled. I suggest you reinitialize some of your variables in OnEnable():

private void OnEnable()
{
    hasLoadedAtLeastOnce = false;
}

exiting playmode still is unstable though. sometimes it works, sometimes it freezes the editor, and sometimes it outright crashes it. The crash log seems to point to releasing the sounds in the Reload() function.

looking at the logs, it’s the Studio API crashing on release while for some reason trying to set a VCA’s volume

0x00000000771E85B8 (ntdll) RtlRaiseStatus
0x00000000771A162B (ntdll) longjmp
0x000000007714D261 (ntdll) RtlEnterCriticalSection
0x000007FEBDBD9C3A (fmodstudiol) FMOD_Thread_SetAttributes
0x000007FEBDC2547C (fmodstudiol) FMOD::SystemI::createMemoryFile
0x000007FEBDC06F24 (fmodstudiol) FMOD::SystemI::setInternalCallback
0x000007FEBDAC6239 (fmodstudiol) FMOD_Studio_VCA_SetVolume
  ERROR: SymGetSymFromAddr64, GetLastError: 'Attempt to access invalid address.' (Address: 000007FEBD9CBA82)
0x000007FEBD9CBA82 (fmodstudiol) (function-name not available)
  ERROR: SymGetSymFromAddr64, GetLastError: 'Attempt to access invalid address.' (Address: 000007FEBDA05352)
0x000007FEBDA05352 (fmodstudiol) (function-name not available)
0x000007FEBDA6E027 (fmodstudiol) FMOD::Studio::System::stopCommandCapture
0x000007FEBDA67D47 (fmodstudiol) FMOD::Studio::System::release
0x00000000471D49EA (Mono JIT Code) (wrapper managed-to-native) FMOD.Studio.System:FMOD_Studio_System_Release (intptr)
0x00000000471D48AB (Mono JIT Code) [...\Assets\Plugins\FMOD\src\Runtime\wrapper\fmod_studio.cs:421] FMOD.Studio.System:release () 
0x00000000471D4663 (Mono JIT Code) [...\Assets\Plugins\FMOD\src\Runtime\RuntimeManager.cs:620] FMODUnity.RuntimeManager:Destroy () 
0x0000000046D89C33 (Mono JIT Code) [...\Assets\Plugins\FMOD\src\Runtime\RuntimeManager.cs:638] FMODUnity.RuntimeManager:HandlePlayModeStateChange (UnityEditor.PlayModeStateChange) 

funnily enough this only happens in my test scene
ingame, where FMOD Studio events are actually used, this doesn’t seem to occur

strange

Ok so here’s the situation right now, the script works, but I need a helper script to run FMODUnity.RuntimeManager.StudioSystem.getCoreSystem() and call release on it in OnDestroy to even have a chance of the editor not completely freezing with high CPU usage

this is the only thing in the log after stopping playback, no errors or anything like that

Refresh: detecting if any assets need to be imported or removed ...

Refresh Completed time: 0.071120s
	Asset Scan time: 0.067765s
	Asset Hashing: 0.000000s [0 B, 0.000000 mb/s]
	Asset Import (Scripting) time: 0.000000s (count: 0)
	Post Processs Assets (Scripting) time: 0.000000s
	Asset Rehashing: 0.000000s [0 B, 0.000000 mb/s]
	Asset Import (Non Scripting) time: 0.000000s (count: 0)
	Post Process Assets (Non Scripting) time: 0.000000s
	Dependent Assets to Import Queue time: 0.000000s

Load scene 'Temp/__Backupscenes/0.backup' time: 0.773826 ms 
Unloading 3 Unused Serialized files (Serialized files now loaded: 0)
System memory in use before: 371.4 MB.
System memory in use after: 348.4 MB.

Unloading 1448 unused Assets to reduce memory usage. Loaded Objects now: 2728.
Total: 14.069682 ms (FindLiveObjects: 0.653955 ms CreateObjectMapping: 0.140808 ms MarkObjects: 10.246939 ms  DeleteObjects: 3.025928 ms)

actually not even that helps anymore

weird

There should be no need to call release on the RuntimeManager.StudioSystem object yourself, that is handled by the runtime manager. Perhaps you should try using the ERRCHECK method on the result of all of your FMOD API calls and see if anything is failing to load, release etc.

I’ve had a few minor issues but they’re not the cause of the hang

The only other thing I can think of is an infinite loop somewhere. You can try doing the following to figure out if this is the case:

  1. Attach the Visual Studio debugger
  2. Wait until you get a hang
  3. Break All (hit the pause button)
  4. Look through the threads menu for some code you have access to
  5. Continue (hit the play button)
  6. Repeat from step 3 until you identify some code that is getting hit repeatedly

the loop seems to be in fmodstudioL.dll, and breaking usually ends up at some point in SleepEx in KernelBase or NtDelayExecution in ntdll

all I know is that said loop occurs in the FMOD Studio DLL and that the script in question causes it (defining DEBUG_OFF makes it not hang)

using FMOD;
using UnityEngine;

public static class TrimEndThing
{
    public static string TrimEnd(this string source, string value)
    {
        return source.EndsWith(value) ? source.Remove(source.LastIndexOf(value, System.StringComparison.Ordinal)) : source;
    }
    public static string TrimStart(this string source, string value)
    {
        return source.StartsWith(value) ? source.Substring(value.Length) : source;
    }
}

public class FMODLowLevelMusicHandler : MonoBehaviour
{
    
    public enum FadeState
	{
        NoFade,
        FadeIn,
        FadeOut,
	};

    public enum CrossfadeMode
	{
        FadeOutOnly,
        FadeOutThenIn,
        NoCrossfade
	}

    private FadeState fadeState = FadeState.FadeIn;
    public CrossfadeMode crossfadeMode = CrossfadeMode.FadeOutOnly;
    float[] fadeLevel = new float[6];

    private float channel1Volume;
    private float channel2Volume;
    private float channel3Volume;
    private float channel4Volume;
    private float channel5Volume;
    private float channel6Volume;

    [Range(0.0f, 1.0f)] public float[] sliderTargetVolumes = { 0, 0, 0, 0, 0, 0 };

    //private FMOD.System SystemEx;
    private Sound baseSound;
    private Sound baseSound02;
    private Sound sound01;
    private Channel channel01;
    private Sound sound02;
    private Channel channel02;
    private ChannelGroup channelGroup;
    bool hasInitializedMainSound = false;
    bool hasInitializedSubSounds = false;
	bool hasInitializedSound2 = false;
    public string bank = "";
    public uint loopStart = 0;
    public bool reloadSound = false;
	private bool hasLoadedAtLeastOnce;
    private bool gotBus = false;
    FMOD.Studio.Bus bus;
	private bool loadTrack;
    bool isApplicationQuitting = false;
    public static FMODLowLevelMusicHandler Instance;

    [System.Serializable]
    public class StereoPair {
        public string MusicStemTranslatedTag = "";
        public int SliderIndex = 0;
        public float Fade = 2.0f;
        public float FadeIn = 2.0f;
        public float FadeOut = 1.0f;
        public float ForwardSend = 1.0f;
        public float RearSend = 0.0f;
        public bool Trigger = false;
    };

    [System.Serializable]
    public class TrackData
	{
        public float MasterVolume = 1.0f;
        public string TrackTranslatedTag = "";
        public bool IsCopyrighted = false;
        public bool IsSting = false;
        public StereoPair[] StereoPairs = new StereoPair[1];
    };

    [SerializeField]
    public TrackData currentTrack;

    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    static void AutoRun()
    {
        GameObject obj = new GameObject("MusicHandlerEx", typeof(FMODLowLevelMusicHandler));
        Instance = obj.GetComponent<FMODLowLevelMusicHandler>();
        DontDestroyOnLoad(obj);
    }

    bool ERRCHECK(FMOD.RESULT result)
    {
        #if !DEBUG_OFF
        if (result != FMOD.RESULT.OK)
        {
            Debug.LogError("FMOD Error (" + result.ToString() + "): " + FMOD.Error.String(result));
            isQuitting = true;
            ReloadSound(false);
            reloadSound = false;
            this.enabled = false;
        }
        
        return (result == FMOD.RESULT.OK);
        #else
        return true;
        #endif
    }

    void Start()
	{
        //SystemEx = FMODUnity.RuntimeManager.CoreSystem;
        fadeLevel = new float[6];
    }

	private void OnEnable()
	{
        isQuitting = false;
        //ReloadSound(false);
        fadeLevel = new float[6];
        loadTrack = false;
    }

    public void SetFadeState(FadeState state)
	{
        fadeState = state;
    }
    
    bool hasPlayedAtLeastOneThing = false;

    public void Play(string bank, float[] volumes)
    {
        Play(bank, volumes, 0);
    }

    public void Play(string bank, float[] volumes, int loopStart = 0)
    {
        Play(bank, volumes, (uint)loopStart);
    }

    public void Play(int bank, float[] volumes)
    {
        Play(bank, volumes, 0);
    }

    public void Play(int bank, float[] volumes, int loopStart = 0)
    {
        Play(bank, volumes, (uint)loopStart);
    }

    private string musFileNotFoundError = "Mus file not found for {0}";

    public void Play(int bank, float[] volumes, uint loopStart = 0)
    {
        string bankString = null;
        bankString = blurayguids.GetPathFromGuid(bank).TrimStart("audio/");
        if (bankString != null)
            Play(bankString, volumes, loopStart);
        else
            Debug.LogErrorFormat(musFileNotFoundError, blurayguids.guidToString(bank));
    }

    public void Play(string bank, float[] volumes, uint loopStart = 0) {
        #if !DEBUG_OFF
		if (bank != this.bank || !hasPlayedAtLeastOneThing) {
            hasPlayedAtLeastOneThing = true;
            this.bank = bank;
            reloadSound = true;
            setSlidersInstant(volumes);
            this.loopStart = loopStart;
        } else {
            setSliders(volumes);
        }
        #endif
    }

	// Start is called before the first frame update
	void LoadTrack()
    {
        #if !DEBUG_OFF
        RESULT result;

        var musFile = Resources.Load<TextAsset>(string.Format("audio/{0}", bank.TrimEnd("_prev")));

        if (musFile == null)
		{
            ReloadSound(false);
            reloadSound = false;
            this.enabled = false;
            Debug.LogErrorFormat(musFileNotFoundError, bank);
            return;
        }

        ParseMusText(musFile.text);

        //Debug.Log("set up parameters");

        string bankpath = string.Format("{0}/audio/{1}.fsb", Application.streamingAssetsPath, bank);
        MODE modes = MODE.LOOP_NORMAL | MODE._2D | MODE.CREATESTREAM | MODE.NONBLOCKING;

        result = FMODUnity.RuntimeManager.CoreSystem.createStream(bankpath, modes, out baseSound);
        ERRCHECK(result);
        if (currentTrack.StereoPairs.Length > 3)
        {
            result = FMODUnity.RuntimeManager.CoreSystem.createStream(bankpath, modes, out baseSound02);
            ERRCHECK(result);
            //Debug.Log("Loaded IntMusic with more than 3 stereo pairs!");
        }
        loadTrack = false;
        #endif
    }
    
    public void setSliders(float[] sliderValues)
	{
        sliderTargetVolumes = new float[6] { 0, 0, 0, 0, 0, 0 };
        for (int i = 0; i < sliderValues.Length && i < 6; i++)
        {
            sliderTargetVolumes[i] = sliderValues[i];
        }
    }

    public void setSlidersInstant(float[] sliderValues)
    {
        setSliders(sliderValues);
        channel1Volume = sliderTargetVolumes[0];
        channel2Volume = sliderTargetVolumes[1];
        channel3Volume = sliderTargetVolumes[2];
        channel4Volume = sliderTargetVolumes[3];
        channel5Volume = sliderTargetVolumes[4];
        channel6Volume = sliderTargetVolumes[5];
    }
    void setVolumes(float track1, float track2, float track3, float track4, float track5, float track6)
    {
        setVolumes(new float[6] { track1, track2, track3, track4, track5, track6 });
    }

    void setVolumes(float[] sliderValues)
	{
        float[] forwardSend = new float[6];
        float[] rearSend = new float[6];
        float[] fade = new float[6] { 1, 1, 1, 1, 1, 1 };
        int[] sliderIndices = new int[6] { 0, 1, 2, 3, 4, 5 };

        if (currentTrack == null || currentTrack.StereoPairs == null)
		{
            return;
		}

        for (int i = 0; i < currentTrack.StereoPairs.Length; i++)
		{
            forwardSend[i] = currentTrack.StereoPairs[i].ForwardSend;
            rearSend[i] = currentTrack.StereoPairs[i].RearSend;
            sliderIndices[i] = currentTrack.StereoPairs[i].SliderIndex;
            fade[i] = currentTrack.StereoPairs[i].Fade;
            if (fadeState == FadeState.FadeOut)
            {
                fadeLevel[i] = Mathf.Max(0, fadeLevel[i] - (Time.deltaTime/currentTrack.StereoPairs[i].FadeOut));
                //if (fadeLevel[i] == 0)
                //    fadeState = FadeState.NoFade;
            }
            else if (fadeState == FadeState.FadeIn)
            {
                fadeLevel[i] = Mathf.Min(1, fadeLevel[i] + (Time.deltaTime/currentTrack.StereoPairs[i].FadeIn * 2.6f));
                //if (fadeLevel == 1)
                //    fadeState = FadeState.NoFade;
            }
        }

        for (int i = 0; i < 6-currentTrack.StereoPairs.Length; i++)
        {
            fadeLevel[currentTrack.StereoPairs.Length + i] = 0.0f;
        }

        if (fadeState != FadeState.FadeOut)
        {
            channel1Volume = Mathf.MoveTowards(channel1Volume, sliderTargetVolumes[0], Time.deltaTime/fade[sliderIndices[0]] * (channel1Volume > sliderTargetVolumes[0] ? 0.6f : 1.6f));
            channel2Volume = Mathf.MoveTowards(channel2Volume, sliderTargetVolumes[1], Time.deltaTime/fade[sliderIndices[1]] * (channel2Volume > sliderTargetVolumes[1] ? 0.6f : 1.6f));
            channel3Volume = Mathf.MoveTowards(channel3Volume, sliderTargetVolumes[2], Time.deltaTime/fade[sliderIndices[2]] * (channel3Volume > sliderTargetVolumes[2] ? 0.6f : 1.6f));
            channel4Volume = Mathf.MoveTowards(channel4Volume, sliderTargetVolumes[3], Time.deltaTime/fade[sliderIndices[3]] * (channel4Volume > sliderTargetVolumes[3] ? 0.6f : 1.6f));
            channel5Volume = Mathf.MoveTowards(channel5Volume, sliderTargetVolumes[4], Time.deltaTime/fade[sliderIndices[4]] * (channel5Volume > sliderTargetVolumes[4] ? 0.6f : 1.6f));
            channel6Volume = Mathf.MoveTowards(channel6Volume, sliderTargetVolumes[5], Time.deltaTime/fade[sliderIndices[5]] * (channel6Volume > sliderTargetVolumes[5] ? 0.6f : 1.6f));
        }
        if ((fadeLevel[0] + fadeLevel[1] + fadeLevel[2] + fadeLevel[3] + fadeLevel[4] + fadeLevel[5]) <= (0 * currentTrack.StereoPairs.Length) && fadeState == FadeState.FadeOut)
            fadeState = FadeState.NoFade;

        if ((fadeLevel[0] + fadeLevel[1] + fadeLevel[2] + fadeLevel[3] + fadeLevel[4] + fadeLevel[5]) >= (1 * currentTrack.StereoPairs.Length) && fadeState == FadeState.FadeIn)
            fadeState = FadeState.NoFade;

        float volumePower = 1.5f;

        float[] vol01 = new float[3] { sliderValues[sliderIndices[0]] * currentTrack.MasterVolume * fadeLevel[0], sliderValues[sliderIndices[1]] * currentTrack.MasterVolume * fadeLevel[1], sliderValues[sliderIndices[2]] * currentTrack.MasterVolume * fadeLevel[2] };
        float[] vol02 = new float[3] { sliderValues[sliderIndices[3]] * currentTrack.MasterVolume * fadeLevel[3], sliderValues[sliderIndices[4]] * currentTrack.MasterVolume * fadeLevel[4], sliderValues[sliderIndices[5]] * currentTrack.MasterVolume * fadeLevel[5] };

        for(int i=0; i<3; i++)
		{
            vol01[i] = Mathf.Pow(vol01[i], volumePower);
            vol02[i] = Mathf.Pow(vol02[i], volumePower);
        }

        float[] matrix01_FL = new float[6];
        float[] matrix01_FR = new float[6];
        float[] matrix01_C = new float[6];
        float[] matrix01_LFE = new float[6];
        float[] matrix01_RL = new float[6];
        float[] matrix01_RR = new float[6];
        for (int i = 0; i < 3; i++)
		{
            matrix01_FL[i * 2] = vol01[i] * forwardSend[i];
            matrix01_FR[i * 2] = 0;
            matrix01_FL[(i * 2) + 1] = 0;
            matrix01_FR[(i * 2) + 1] = vol01[i] * forwardSend[i];
            matrix01_LFE[i * 2] = 0;// vol01[i] * forwardSend[i];
            matrix01_LFE[(i * 2) + 1] = 0;// vol01[i] * forwardSend[i];
            matrix01_RL[i * 2] = vol01[i] * rearSend[i];
            matrix01_RR[i * 2] = 0;
            matrix01_RL[(i * 2) + 1] = 0;
            matrix01_RR[(i * 2) + 1] = vol01[i] * rearSend[i];
        }
        float[] matrix01 = new float[6*6];
        matrix01_FL.CopyTo(matrix01, 6*0);
        matrix01_FR.CopyTo(matrix01, 6*1);
        matrix01_C.CopyTo(matrix01, 6*2);
        matrix01_LFE.CopyTo(matrix01, 6*3);
        matrix01_RL.CopyTo(matrix01, 6*4);
        matrix01_RR.CopyTo(matrix01, 6*5);

        if (hasInitializedSubSounds)
            channel01.setMixMatrix(matrix01, 6, 6, 6);

        if (currentTrack.StereoPairs.Length > 3)
        {
            float[] matrix02_FL = new float[6];
            float[] matrix02_FR = new float[6];
            float[] matrix02_C = new float[6];
            float[] matrix02_LFE = new float[6];
            float[] matrix02_RL = new float[6];
            float[] matrix02_RR = new float[6];
            for (int i = 0; i < 3; i++)
            {
                matrix02_FL[i * 2] = vol02[i] * forwardSend[i+3];
                matrix02_FR[i * 2] = 0;
                matrix02_FL[(i * 2) + 1] = 0;
                matrix02_FR[(i * 2) + 1] = vol02[i] * forwardSend[i + 3];
                matrix02_LFE[i * 2] = 0;// vol02[i] * forwardSend[i + 3];
                matrix02_LFE[(i * 2) + 1] = 0;// vol02[i] * forwardSend[i + 3];
                matrix02_RL[i * 2] = vol02[i] * rearSend[i + 3];
                matrix02_RR[i * 2] = 0;
                matrix02_RL[(i * 2) + 1] = 0;
                matrix02_RR[(i * 2) + 1] = vol02[i] * rearSend[i + 3];
            }
            float[] matrix02 = new float[6*6];
            matrix02_FL.CopyTo(matrix02, 6 * 0);
            matrix02_FR.CopyTo(matrix02, 6 * 1);
            matrix02_C.CopyTo(matrix02, 6 * 2);
            matrix02_LFE.CopyTo(matrix02, 6 * 3);
            matrix02_RL.CopyTo(matrix02, 6 * 4);
            matrix02_RR.CopyTo(matrix02, 6 * 5);

            if (hasInitializedSubSounds)
                channel02.setMixMatrix(matrix02, 6, 6, 6);
        }
    }

    // Update is called once per frame
    void LateUpdate()
    {
        #if !DEBUG_OFF
        if (isQuitting)
            return;
        RESULT result;
        if (loadTrack && fadeState != FadeState.FadeOut)
        {
            setSlidersInstant(sliderTargetVolumes);
            Reload();
            hasInitializedMainSound = false;
            hasInitializedSound2 = false;
            hasInitializedSubSounds = false;
            LoadTrack();
        }
        if (hasInitializedSubSounds)
        {
            setVolumes(new float[6] { channel1Volume, channel2Volume, channel3Volume, channel4Volume, channel5Volume, channel6Volume });
            if (currentTrack.StereoPairs.Length > 3)
            {
                uint pos01ms;
                uint pos02ms;
                uint lengthms;
                uint loopstartms;
                channel01.getPosition(out pos01ms, TIMEUNIT.MS);
                channel02.getPosition(out pos02ms, TIMEUNIT.MS);
                sound01.getLength(out lengthms, TIMEUNIT.MS); // hopefully fixes jank regarding looping
                sound01.getLoopPoints(out loopstartms, TIMEUNIT.MS, out _, TIMEUNIT.MS);
                if (Mathf.Abs(pos01ms - pos02ms) > 2.0f/0.03f && 
                    Mathf.Min(Mathf.Abs(pos02ms - (pos01ms + loopstartms + lengthms)), Mathf.Abs(pos01ms - (pos02ms+loopstartms+lengthms))) > 2.0f / 0.03f &&
                    Mathf.Abs(pos01ms - loopstartms) > 2.0/0.03f &&
                    Mathf.Abs(pos02ms - loopstartms) > 2.0 / 0.03f &&
                    Mathf.Abs(pos01ms - lengthms) > 2.0 / 0.03f &&
                    Mathf.Abs(pos02ms - lengthms) > 2.0 / 0.03f
                    )
                {
                    uint pos01;
                    uint pos02;
                    channel01.getPosition(out pos01, TIMEUNIT.PCM);
                    channel02.getPosition(out pos02, TIMEUNIT.PCM);
                    uint pos = (uint)Mathf.Max(pos01, pos02);
                    FMODUnity.RuntimeManager.CoreSystem.lockDSP(); // ensure synchronization
                    channel01.setPosition(pos, TIMEUNIT.PCM);
                    channel02.setPosition(pos, TIMEUNIT.PCM);
                    FMODUnity.RuntimeManager.CoreSystem.unlockDSP();
                }
            }
        }
        if (hasLoadedAtLeastOnce && fadeState != FadeState.FadeOut)
        {
            OPENSTATE openstate;
            result = baseSound.getOpenState(out openstate, out _, out _, out _);
            ERRCHECK(result);

            if (openstate == FMOD.OPENSTATE.READY && !hasInitializedMainSound)
            {
                hasInitializedMainSound = true;
                result = baseSound.getSubSound(0, out sound01);
                ERRCHECK(result);
                //Debug.Log("get sound 1");
            }

            OPENSTATE openstate01 = 0;
            if (currentTrack.StereoPairs.Length > 3)
            {
                result = baseSound02.getOpenState(out openstate01, out _, out _, out _);
                ERRCHECK(result);
            }

            if (openstate01 == FMOD.OPENSTATE.READY && !hasInitializedSound2 && currentTrack.StereoPairs.Length > 3)
            {
                hasInitializedSound2 = true;
                result = baseSound02.getSubSound(1, out sound02);
                ERRCHECK(result);
                //Debug.Log("get sound 2");
            }

            OPENSTATE openstate02 = 0;
            if (currentTrack.StereoPairs.Length > 3 && openstate01 == FMOD.OPENSTATE.READY)
            {
                result = sound02.getOpenState(out openstate02, out _, out _, out _);
                ERRCHECK(result);
            }

            if (openstate == FMOD.OPENSTATE.READY && ((openstate01 == FMOD.OPENSTATE.READY && openstate02 == FMOD.OPENSTATE.READY) || currentTrack.StereoPairs.Length <= 3) && !hasInitializedSubSounds)
            {
                hasInitializedSubSounds = true;
                if (!gotBus)
                {
                    bus = FMODUnity.RuntimeManager.GetBus("bus:/master/music");
                    ERRCHECK(result);
                    ERRCHECK(bus.lockChannelGroup());
                    ERRCHECK(FMODUnity.RuntimeManager.StudioSystem.flushCommands());
                    ERRCHECK(bus.getChannelGroup(out channelGroup));
                    gotBus = true;
                }

                //Debug.Log("get channel group");

                uint sound01loopend;
                sound01.getLoopPoints(out _, TIMEUNIT.PCM, out sound01loopend, TIMEUNIT.PCM);
                sound01.setLoopPoints(loopStart, TIMEUNIT.PCM, sound01loopend, TIMEUNIT.PCM);

                if (currentTrack.StereoPairs.Length > 3)
                {
                    uint sound02loopend;
                    sound02.getLoopPoints(out _, TIMEUNIT.PCM, out sound02loopend, TIMEUNIT.PCM);
                    sound02.setLoopPoints(loopStart, TIMEUNIT.PCM, sound02loopend, TIMEUNIT.PCM);
                    FMODUnity.RuntimeManager.CoreSystem.lockDSP(); // ensure synchronization
                    FMODUnity.RuntimeManager.CoreSystem.playSound(sound01, channelGroup, false, out channel01);
                    FMODUnity.RuntimeManager.CoreSystem.playSound(sound02, channelGroup, false, out channel02);
                    FMODUnity.RuntimeManager.CoreSystem.unlockDSP();
                }
                else
                {
                    FMODUnity.RuntimeManager.CoreSystem.playSound(sound01, channelGroup, false, out channel01);
                }
                fadeState = crossfadeMode == CrossfadeMode.FadeOutThenIn ? FadeState.FadeIn : FadeState.NoFade;
                if (crossfadeMode != CrossfadeMode.FadeOutThenIn)
				{
                    for(int i = 0; i < fadeLevel.Length; i++)
					{
                        fadeLevel[i] = 1.0f;
					}
				}
                setVolumes(new float[6] { channel1Volume, channel2Volume, channel3Volume, channel4Volume, channel5Volume, channel6Volume });
                string name01;
                sound01.getName(out name01, 256);
                if (currentTrack.StereoPairs.Length > 3)
                {
                    string name02;
                    sound02.getName(out name02, 256);
                    //Debug.LogFormat("Now playing {0} and {1}", name01, name02);
                }
                else
                {
                    //Debug.LogFormat("Now playing {0}", name01);
                }
            } else if (!hasInitializedSubSounds) {
                LoadingIndicator.ShowLoadingIndicator();
            }
        }
        if (reloadSound && FMODUnity.RuntimeManager.IsInitialized)
		{
            reloadSound = false;
            ReloadSoundQueue();
        }
        #endif
    }

    private bool isQuitting;

    void OnApplicationQuit()
    {
        #if !DEBUG_OFF
        setSlidersInstant(sliderTargetVolumes);
        isQuitting = true;
        ReloadSound(false);
        //SystemEx.release();
        isApplicationQuitting = true;
        #endif
    }

    void OnDisable()
	{
        #if !DEBUG_OFF
        if (isApplicationQuitting)
        {
            isQuitting = true;
            try
            {
                //ERRCHECK(sound01.release());
                //sound01.clearHandle();
                ERRCHECK(baseSound.release());
                baseSound.clearHandle();
                if (currentTrack.StereoPairs.Length > 3)
                {
                    //ERRCHECK(sound02.release());
                    //sound02.clearHandle();
                    Debug.Log("releasing baseSound02");
                    ERRCHECK(baseSound02.release());
                    baseSound02.clearHandle();
                }
            }
            catch (System.Exception e)
            {
                Debug.LogError(e.Message);
            }
            return;
        }
        else
        {
            hasLoadedAtLeastOnce = false;
            ReloadSound(false);
        }
        #endif
    }

    void ReloadSoundQueue()
    {
        #if !DEBUG_OFF
        if (hasLoadedAtLeastOnce)
        {
            fadeState = crossfadeMode != CrossfadeMode.NoCrossfade ? FadeState.FadeOut : FadeState.NoFade;
            loadTrack = true;
        }
        else
        {
            ReloadSound(true);
        }
        #endif   
    }

    void ReloadSound(bool runAwake)
	{
        #if !DEBUG_OFF
        if (hasLoadedAtLeastOnce || !runAwake)
        {
            if (!isQuitting)
                Reload();
            hasInitializedMainSound = false;
            hasInitializedSound2 = false;
            hasInitializedSubSounds = false;
        }
        else if(runAwake)
		{
            hasLoadedAtLeastOnce = true;
        }
        if (runAwake)
            loadTrack = true;
        #endif
    }

    void Reload()
	{
        #if !DEBUG_OFF
        //Debug.Log("Reloading!");
        if (hasInitializedSubSounds)
        {
            try
            {
                //ERRCHECK(sound01.release());
                ERRCHECK(baseSound.release());
                if (currentTrack.StereoPairs.Length > 3) {
                    //ERRCHECK(sound02.release());
                    Debug.Log("releasing baseSound02");
                    ERRCHECK(baseSound02.release());
                }
            }
            catch (System.Exception e)
            {
                Debug.LogError(e.Message);
            }
        }
        #endif
    }

	void OnDestroy()
    {
        #if !DEBUG_OFF
        isQuitting = true;
        if (gotBus)
        {
            try
            {
                channelGroup.release();
                bus.unlockChannelGroup();
            } catch (System.Exception e)
			{
                Debug.LogError(e.Message);
			}
        }
        #endif
    }

    void ParseMusText(string mus)
	{
        string[] splitMus = mus.Split('\n');
        if (splitMus[0] != "MUSt")
		{
            Debug.LogError("No MUSt header found!");
            return;
		}
        int currentPair = 0;
        currentTrack = new TrackData();
        for(int line = 0; line < splitMus.Length; line++)
		{
            string[] splitLine = splitMus[line].Split('#')[0].Split((char[])null, System.StringSplitOptions.RemoveEmptyEntries);
            if (splitLine != null && splitLine.Length != 0)
            {
                switch (splitLine[0])
                {
                    case "MasterVolume":
                        currentTrack.MasterVolume = float.Parse(splitLine[1]);
                        break;
                    case "TrackTranslatedTag":
                        currentTrack.TrackTranslatedTag = string.Join(" ", splitLine, 1, splitLine.Length - 1).Trim('"'); ;
                        break;
                    case "IsCopyrighted":
                        currentTrack.IsCopyrighted = splitLine[1] == "true";
                        break;
                    case "IsSting":
                        currentTrack.IsSting = splitLine[1] == "true";
                        break;
                    case "StereoPairs":
                        currentTrack.StereoPairs = new StereoPair[int.Parse(splitLine[1])];
                        for (int i = 0; i < currentTrack.StereoPairs.Length; i++)
                            currentTrack.StereoPairs[i] = new StereoPair();
                        break;
                    case "[0]":
                        currentPair = 0;
                        break;
                    case "[1]":
                        currentPair = 1;
                        break;
                    case "[2]":
                        currentPair = 2;
                        break;
                    case "[3]":
                        currentPair = 3;
                        break;
                    case "[4]":
                        currentPair = 4;
                        break;
                    case "[5]":
                        currentPair = 5;
                        break;
                    case "MusicStemTranslatedTag":
                        currentTrack.StereoPairs[currentPair].MusicStemTranslatedTag = string.Join(" ", splitLine, 1, splitLine.Length-1).Trim('"');
                        break;
                    case "SliderIndex":
                        currentTrack.StereoPairs[currentPair].SliderIndex = int.Parse(splitLine[1]);
                        break;
                    case "Fade":
                        currentTrack.StereoPairs[currentPair].Fade = float.Parse(splitLine[1]);
                        break;
                    case "FadeIn":
                        currentTrack.StereoPairs[currentPair].FadeIn = float.Parse(splitLine[1]);
                        break;
                    case "FadeOut":
                        currentTrack.StereoPairs[currentPair].FadeOut = float.Parse(splitLine[1]);
                        break;
                    case "ForwardSend":
                        currentTrack.StereoPairs[currentPair].ForwardSend = float.Parse(splitLine[1]);
                        break;
                    case "RearSend":
                        currentTrack.StereoPairs[currentPair].RearSend = float.Parse(splitLine[1]);
                        break;
                    case "Trigger":
                        currentTrack.StereoPairs[currentPair].Trigger = splitLine[1] == "true";
                        break;
                }
            }
        }
	}
}

ok so I’ve made the gameobject only initialize if something gets the instance and now it no longer freezes, strange

it seems it was crashing because I was accessing it too early?

1 Like

That is strange, I wouldn’t have thought the initialization time of a singleton that accesses the RuntimeManager could cause a hang when exiting play. Glad to know you got to the bottom of it, and good to know that lazy loading works around the issue- thank you for sharing your solution!

actually I was wrong, it only fixes the issue in the editor for some reason

player still hangs

I think I am going to need a repro to provide any more advice here. If you upload a stripped down version of your project to the Uploads section of your Profile I’ll take a look and see what the underlying issue is.

alright so due to my project’s circumstances I’ve had to move back to using FMOD Ex (as the goal of the project is to effectively be a fanmade expansion that slots over the original game to avoid copyright-related issues, and the original game uses Ex and FMOD 4 soundbanks), and the editor straight up crashes unless I release the subsounds first, which throws an error (ERR_SUBSOUND_CANT_MOVE)

I guess the error can just be safely ignored for now? (and not checked in the code to not spam the log)