Support hacky user-assigned music through Programmer Instruments

Hi,

Thanks for the photos.

This is definitely possible! I have slightly modified our scripting example: Unity Integration | Examples Programmer Sounds. When we assign the event callback (FMOD Engine | Studio Api Eventinstance - Fmod::Studio::Event::Callback::Type) CREATE_PROGRAMMER_SOUND this will trigger both times the programmer instruments are played. I made an event similar to yours:

Where the fader of each audio track is controlled via a parameter.

I then use the following script to play and assigned the audio files for both the programmer instruments:

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.InputSystem;

[Serializable]
public class EventData
{
    // Paths of the audio files on disk to be played
    public List<string> paths = new List<string>();

    // Used increment the file paths used when triggering the programmer instruments
    private int currentProgInstrument = 0;
    public int CurrentProgInstrument
    {
        get
        {
            return currentProgInstrument;
        }
        set
        {
            currentProgInstrument = value;
        }
    }
}

public class TriggerTwoProgInstruments : MonoBehaviour
{
    public FMODUnity.EventReference eventRef;
    private FMOD.Studio.EventInstance eventInst;

    public EventData data = new EventData();

    void Start()
    {
        // Create our event instance
        eventInst = FMODUnity.RuntimeManager.CreateInstance(eventRef);

        // Set our callback that will only trigger when the programmer instruments are created and destroyed
        eventInst.setCallback(EventCallback, FMOD.Studio.EVENT_CALLBACK_TYPE.CREATE_PROGRAMMER_SOUND | FMOD.Studio.EVENT_CALLBACK_TYPE.DESTROY_PROGRAMMER_SOUND);

        // Convert our EventData to an IntPtr so it can be passed to our event instance
        GCHandle handle = GCHandle.Alloc(data);
        eventInst.setUserData(GCHandle.ToIntPtr(handle));

        // Start our instance
        eventInst.start();
    }

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

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

        // Get the data from our pointer
        GCHandle dataHandle = GCHandle.FromIntPtr(dataPtr);
        EventData data = dataHandle.Target as EventData;

        switch (type)
        {
            case FMOD.Studio.EVENT_CALLBACK_TYPE.CREATE_PROGRAMMER_SOUND:
            {
                FMOD.MODE soundMode = FMOD.MODE.LOOP_NORMAL | FMOD.MODE.CREATECOMPRESSEDSAMPLE | FMOD.MODE.NONBLOCKING;
                var parameter = (FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES)Marshal.PtrToStructure(parameterPtr, typeof(FMOD.Studio.PROGRAMMER_SOUND_PROPERTIES));

                FMOD.Sound sound;
                // We will use the `CurrentProgInstrument` to retrieve the audio files paths from our list
                var soundResult = FMODUnity.RuntimeManager.CoreSystem.createSound(data.paths[data.CurrentProgInstrument], soundMode, out sound);
                if (soundResult == FMOD.RESULT.OK)
                {
                    parameter.sound = sound.handle;
                    parameter.subsoundIndex = -1;
                    Marshal.StructureToPtr(parameter, parameterPtr, false);
                }

                // Increment the index to use the next audio file path
                data.CurrentProgInstrument++;

                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();

                break;
            }
            case FMOD.Studio.EVENT_CALLBACK_TYPE.DESTROYED:
            {
                // Now the event has been destroyed, unpin the string memory so it can be garbage collected
                dataHandle.Free();

                break;
            }
        }
        return FMOD.RESULT.OK;
    }
}

I haven’t implemented a way to set the parameter value but that is easily done with: FMOD Engine | Studio Api Eventinstance - Studio::Eventinstance::Setparameterbyname.

Please note, this script should only be used for testing.

Hope this helps!