Let a local FMOD parameter influence an animator state

Hi guys,

I’m an audio engineer and been using FMOD for a while now. I’ve recently been coding a bit more, thanks to chatGPT and some basic understanding. I’m trying to write code that can take a local parameter’s information and based on the FMOD paramer, send a animator parameter (trigger) to a connected animator.

For visual reference: I’m creating a band that plays music. The music has certain parts where the drummer plays, other parts where he doesn’t. So I thought if I have a parameter for whether he “isPlaying” and “stopPlaying” which I control with a Command Instrument (who sets the parameter), for every band member in the arrangement.

So I think it works to set the value of the parameter in FMOD with the command instrument. Now, I only need to make a script in Unity that sends those values to a connected animator and trigger an animator state. Can anyone help me deceiver this code and make it work? One problem I already know is that the code is constantly triggering the animator controller trigger. It should do that only once (as it’s a trigger and not a boolean). Now, it’s kind of stuck on “stopPlaying”. I’m using a labeled parameter in FMOD. 0 = stopPlaying. 1 = startPlaying.

using UnityEngine;
using FMODUnity;

public class FMODAnimatorController : MonoBehaviour
{
    [SerializeField] private Animator animator;
    [SerializeField] private StudioEventEmitter fmodEvent;
    [SerializeField] private string stopPlayingParameter;
    [SerializeField] private string startPlayingParameter;
    [SerializeField] private FMOD.Studio.PARAMETER_ID localFMODParameter;


    private void Start()
    {
        fmodEvent.Play();
        Debug.Log("FMOD event has started playing.");
    }

    private void Update()
    {
        float localFMODParameterValue;
        fmodEvent.EventInstance.getParameterByID(localFMODParameter, out localFMODParameterValue);
        Debug.Log("Local FMOD parameter name: " + localFMODParameter);
        if (localFMODParameterValue == 0)
        {
            animator.SetTrigger(stopPlayingParameter);
            Debug.Log("Animator trigger for " + stopPlayingParameter + " has been set.");
        }
        else if (localFMODParameterValue == 1)
        {
            animator.SetTrigger(startPlayingParameter);
            Debug.Log("Animator trigger for " + startPlayingParameter + " has been set.");
        }
    }
}

Hi,

So there are a couple of ways you could do this.

You could check every frame if the value of localFMODParameter is different from the last frame, if so then you can change the animator’s value based on the new value.

Update()
{
    fmodEvent.EventInstance.getParameterByID(localFMODParameter, out localFMODParameterValue, 
    out localFMODParameterFinalValue);
    if (localFMODParameterFinalValue != cachedValue)
    {
        anims.SetFloat("Trigger", localFMODParameterFinalValue);
        cachedValue = localFMODParameterFinalValue;
    }
}

This is probably the easiest method code wise. You could also utilize FMOD_STUDIO_EVENT_CALLBACK (FMOD API | Studio API) in conjunction with markers (FMOD Studio | Glossary) to only trigger when the Command Instrument is activated.

Using callbacks
using System;
using System.Collections.Generic;
using UnityEngine;
using System.Runtime.InteropServices;
using System.Reflection;
using static UnityEditor.Profiling.RawFrameDataView;

[RequireComponent(typeof(FMODUnity.StudioEventEmitter))]
public class GetCurrentParamValue : MonoBehaviour
{
    public FMODUnity.StudioEventEmitter emitter;
    public Animator anims;

    private FMOD.Studio.EVENT_CALLBACK marker;
    // Start is called before the first frame update
    void Start()
    {
        marker = new FMOD.Studio.EVENT_CALLBACK(MarkerCallback);
        GCHandle animHandle = GCHandle.Alloc(anims);
        emitter.EventInstance.setUserData(GCHandle.ToIntPtr(animHandle));
        emitter.EventInstance.setCallback(marker);
    }

    [AOT.MonoPInvokeCallback(typeof(FMOD.Studio.EVENT_CALLBACK))]
    static FMOD.RESULT MarkerCallback(FMOD.Studio.EVENT_CALLBACK_TYPE type, IntPtr instancePtr, IntPtr parameterPtr)
    {
        FMOD.Studio.EventInstance inst = new FMOD.Studio.EventInstance(instancePtr);
        IntPtr animsPtr;
        inst.getUserData(out animsPtr);

        GCHandle animsHandle = GCHandle.FromIntPtr(animsPtr);
        Animator anims = animsHandle.Target as Animator;

        switch(type)
        {
            case FMOD.Studio.EVENT_CALLBACK_TYPE.TIMELINE_MARKER:
                inst.getParameterByName("IsPlaying", out float value, out float finalValue);
                SetAnimationValue(finalValue, anims);
                break;
            case FMOD.Studio.EVENT_CALLBACK_TYPE.DESTROYED:
                animsHandle.Free();
                break;
        }
        return FMOD.RESULT.OK;
    }

    private void OnDestroy()
    {
        emitter.Stop();
    }

    static void SetAnimationValue(float newValue, Animator anims)
    {
        // Set the anims value here
        Debug.Log("The new value is " + newValue);
    }
}

However, that is a lot more involved.

Hope this helps!