Destroy Programmer Instrument

Hi there!
I am trying to figure out how to destroy a looping programmer instrument when loading the next scene. Right now, the programmer instrument is fully destroyed (event killed and memory unpin) when exiting player mode. However, loading into the next scene doesn’t kill the instrument.

I am uncertain how may I trigger the “Event_Callback_Type. Destroy_Programmer_Sound” case and “Event_Callback_Type. Destroyed” in the switch in the script below. I realized I can call “recSound.Release()”, as recSound is the FMOD.Sound I created to record into. However, that only trigger the “Destory_Programmer_Sound” but not the release memory part. Would be grateful to have some help here!

using System;
using System.Collections.Generic;
using UnityEngine;
using System.Runtime.InteropServices;
using FMOD.Studio;
using System.Collections;

public class ProgrammingInstrument : MonoBehaviour
{


    /// <summary>
    /// Recording into a FMOD.Sound, then assign it to a progammer instrument
    /// </summary>

    FMOD.Studio.EVENT_CALLBACK dialogueCallback;
    public FMODUnity.EventReference EventName;
    EVENT_CALLBACK_TYPE type;

//#if UNITY_EDITOR
//    void Reset()
//    {
//        EventName = FMODUnity.EventReference.Find("event:/Record");
//    }
//#endif

    //Recording varaibles

    private uint samplesRecorded, samplesPlayed = 0;
    private int nativeRate, nativeChannels = 0;
    private uint recSoundLength = 0;

    //FMOD Sound variables
    private FMOD.CREATESOUNDEXINFO exInfo = new FMOD.CREATESOUNDEXINFO();
    public FMOD.Sound recSound;
    private FMOD.Channel channel;

    //Programmer instrument
    public IntPtr TempInstPtr;
    public IntPtr TempParaPtr;


    void Start()
    {
        //recording
        /*
            Determine latency in samples.
        */
        FMODUnity.RuntimeManager.CoreSystem.getRecordDriverInfo(0, out _, 0, out _, out nativeRate, out _, out nativeChannels, out _);

        /*
            Create user sound to record into, then start recording.
        */
        exInfo.cbsize = Marshal.SizeOf(typeof(FMOD.CREATESOUNDEXINFO));
        exInfo.numchannels = nativeChannels;
        exInfo.format = FMOD.SOUND_FORMAT.PCM24;
        exInfo.defaultfrequency = nativeRate;
        exInfo.length = (uint)(nativeRate/3);

        //Create recSound as an FMOD Sound and record into it 
        FMODUnity.RuntimeManager.CoreSystem.createSound("Record", FMOD.MODE.LOOP_NORMAL|FMOD.MODE.OPENUSER, ref exInfo, out recSound);
        FMODUnity.RuntimeManager.CoreSystem.recordStart(0, recSound, true);
        recSound.getLength(out recSoundLength, FMOD.TIMEUNIT.PCM);

        //FMODUnity.RuntimeManager.CoreSystem.getMasterChannelGroup(out FMOD.ChannelGroup mCG);
        //FMODUnity.RuntimeManager.CoreSystem.playSound(recSound, mCG, false, out channel);

        /// <summary>
        /// Create switch for programmer instrument
        /// </summary>


        // Explicitly create the delegate object and assign it to a member so it doesn't get freed
        // by the garbage collected while it's being used
        dialogueCallback = new FMOD.Studio.EVENT_CALLBACK(DialogueEventCallback);
        TempParaPtr = System.IntPtr.Zero;


        [AOT.MonoPInvokeCallback(typeof(FMOD.Studio.EVENT_CALLBACK))]
        FMOD.RESULT DialogueEventCallback(FMOD.Studio.EVENT_CALLBACK_TYPE type, IntPtr instancePtr, IntPtr parameterPtr)
        {
            FMOD.Studio.EventInstance instance = new FMOD.Studio.EventInstance(instancePtr);

            // Retrieve the user data
            IntPtr stringPtr;
            instance.getUserData(out stringPtr);

            // Get the string object
            GCHandle stringHandle = GCHandle.FromIntPtr(stringPtr);
            String key = stringHandle.Target as String;

            TempInstPtr = instancePtr;
            TempParaPtr = parameterPtr;

            switch (type)
            {
                case FMOD.Studio.EVENT_CALLBACK_TYPE.CREATE_PROGRAMMER_SOUND:
                    {
                        var parameter = (FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES)Marshal.PtrToStructure(parameterPtr, typeof(FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES));
                      
                        // We now need to pass recSound to the programmer instrument
                        parameter.sound = recSound.handle;
                        Marshal.StructureToPtr(parameter, parameterPtr, false);
                        Debug.Log("Programmer Instrument Created");
                        break;
                    }
                case FMOD.Studio.EVENT_CALLBACK_TYPE.DESTROY_PROGRAMMER_SOUND:
                    {
                        var parameter = (FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES)Marshal.PtrToStructure(parameterPtr, typeof(FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES));
                        var sound = new FMOD.Sound(parameter.sound);
                        sound.release();
                        // change from sound..release() to recSound.release
                        Debug.Log("Destroy Programme Instruemnt");
                        break;
                    }
                case FMOD.Studio.EVENT_CALLBACK_TYPE.DESTROYED:
                    {
                        // Now the event has been destroyed, unpin the string memory so it can be garbage collected
                        stringHandle.Free();
                        Debug.Log("destroyed");
                        break;
                    }
            }
            return FMOD.RESULT.OK;
        }

    }


    void PlayDialogue()
    {
        //create event instance to play
        var dialogueInstance = FMODUnity.RuntimeManager.CreateInstance(EventName);
  
        // Pin the key string in memory and pass a pointer through the user data
        GCHandle stringHandle2 = GCHandle.Alloc(recSound);
        IntPtr pointer = GCHandle.ToIntPtr(stringHandle2);
        dialogueInstance.setUserData(pointer);
        dialogueInstance.setCallback(dialogueCallback);
        dialogueInstance.start();
        dialogueInstance.release();
        Debug.Log("release dialogue instance");
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.P))
        {
            PlayDialogue();
        }
    }

}

Hi,

Thank you for the code.

Could I grab some info:

  1. What version of the FMOD are you using?
  2. How are you triggering loading the new scene?

Calling release only tags the instance to be cleaned up, it does not start the process. More info can be found here: FMOD Engine | Studio API Reference - EventInstance::release(). To stop the event instance before the end of this timeline you can call: FMOD Engine | Studio API Reference - EventInstance::stop(). This could be called when loading into the new scene.

Hope this helps.