Grab Length of AudioClip

Hello, so currently I am working on implementing voice lines and want to have my coroutine last only as long as the fmod.studio audio clip and I’m at a complete loss on how to go about that!
currently, I’m using c# in unity if that’s any consolation.
Please Help

Hi Keil,

If you are looking to get the length of the event instance, you can use EventDescription::getLength(). This is the length of the entire event from 0 to the last instrument or marker.

https://www.fmod.com/resources/documentation-api?page=content/generated/FMOD_Studio_EventDescription_GetLength.html#/

However, if you are looking for the length of the audio file being referenced in an instrument that is playing, you will need to set up a callback using FMOD_STUDIO_EVENT_CALLBACK to grab the FMOD_STUDIO_EVENT_CALLBACK_SOUND_PLAYED and then using Sound::getLength().

https://www.fmod.com/resources/documentation-api?page=content/generated/FMOD_STUDIO_EVENT_CALLBACK.html#/
https://www.fmod.com/resources/documentation-api?page=content/generated/FMOD_STUDIO_EVENT_CALLBACK_TYPE.html
https://www.fmod.com/resources/documentation-api?page=content/generated/FMOD_Sound_GetLength.html#/

Thanks,
Richard

What’s the method of doing this in 2.0 and later? Seems like there is no EventDescription::getLength() anymore.

EventDescription::getLength() is still available for 2.0 and later.

https://www.fmod.com/resources/documentation-api?version=2.1&page=studio-api-eventdescription.html#studio_eventdescription_getlength

Hi Richard. To be honest I think there si a big issue with callback’s documentation as it is really hard for people coming from a non programmer background to learn how to use them. In my case, I also need to grab the lenght of an audio clip played in my event instance. But I don’t understand where to implement the exemple code provided by the documentation in my own code, but I feel that it’d be a lot easier with more context

I am aware of the timeline callback scripting examples available in the documentation, but it feels very specific to a type of usage and thus, becomes confusing by providing a lot of information where it is hard to know what I really need to use in my context.

To explain my goal briefly. I have a “ambWind3D” event which triggers a wind whistle sfx. I want this event to follow a random shaped parabola around the listener each time the event is called, and I want the duration how the parabola animation to be equal to the duration of the audio clip contained by the event instance that is played.

So following your message, I should create a new callback, grab the FMOD_STUDIO_EVENT_CALLBACK_SOUND_PLAYED and then use the getLength function.

My problem is that I am stuck on the first step. I’m trying to get the infos from the event Instance but for some reason, I get the following error while trying to replicate the scripting example in the documentation : The type or namespace name 'IntPtr' could not be found (are you missing a using directive or an assembly reference?)

here is my code :

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

public class RS_3DWind : MonoBehaviour
{
  private GameObject m_camera;
  private GameObject m_3DWind;
  private ParabolaController m_parabolaController;

  class TimelineInfo
  {
    public int currentMusicBar = 0;
    public FMOD.StringWrapper lastMarker = new FMOD.StringWrapper();
  }


  TimelineInfo timelineInfo;
  GCHandle timelineHandle;
  private FMOD.Studio.EVENT_CALLBACK m_3DWindCallback;
  [SerializeField] EventReference m_ambWind3DA;
  private FMOD.Studio.EventInstance m_ambWind3DAInstance = default;

  private void Awake()
  {
    m_camera = GameObject.Find("RS_Camera");
    m_parabolaController = GetComponent<ParabolaController>();
  }
  
  private void Start()
  {
    // Set ambWind3DA&B Event on repeat
    InvokeRepeating("CallAmbWind3DA", 3.0f, 3.0f);
  }
  private void CallAmbWind3DA ()
  {
      StartCoroutine(CallAmbWind3DACoroutine());

      m_ambWind3DAInstance = RuntimeManager.CreateInstance(m_ambWind3DA);
      m_ambWind3DAInstance.set3DAttributes(RuntimeUtils.To3DAttributes(this.transform));
      m_ambWind3DAInstance.start();
      m_ambWind3DAInstance.release();

      timelineInfo = new TimelineInfo();
      m_3DWindCallback = new FMOD.Studio.EVENT_CALLBACK(m_3DAWindEventCallback);
      timelineHandle = GCHandle.Alloc(timelineInfo);
      m_ambWind3DAInstance.setUserData(GCHandle.ToIntPtr(timelineHandle));
      m_ambWind3DAInstance.setCallback(m_3DWindCallback, FMOD.Studio.EVENT_CALLBACK_TYPE.SOUND_PLAYED);

      m_parabolaController.FollowParabola();
  }

  private IEnumerator CallAmbWind3DACoroutine ()
  {
      int randomWait = Random.Range(0, 7);
      yield return new WaitForSeconds(randomWait);
  }

  static FMOD.RESULT m_3DAWindEventCallback(FMOD.Studio.EVENT_CALLBACK_TYPE type, IntPtr instancePtr, IntPtr parameterPtr)
  {
    FMOD.Studio.EventInstance instance = new FMOD.Studio.EventInstance(instancePtr);
    IntPtr timelineInfoPtr;
    FMOD.RESULT result = instance.getUserData(out timelineInfoPtr);
    if (result != FMOD.RESULT.OK)
    {
      Debug.LogError("Timeline Callback error: " + result);
    }
    else if (timelineInfoPtr != IntPtr.Zero)
    {
      Debug.Log("yep");
    }
  }
  void OnDestroy()
  {
    m_ambWind3DAInstance.setUserData(IntPtr.Zero);
    timelineHandle.Free();
  }
}

For your error, you need to add using System; at the top of the script alongside the other using statements. This will give you access to the IntPtr namespace. After that there are a few more namespace issues to take care of like Random.Range needing to specify which namespace you’re using. Here’s your script with those issues taken care of (I’ve not got access to ParabolaController so I can’t verify if that part of the code works):

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

public class RS_3DWind : MonoBehaviour
{
    private GameObject m_camera;
    private GameObject m_3DWind;
    private ParabolaController m_parabolaController;

    class TimelineInfo
    {
        public int currentMusicBar = 0;
        public FMOD.StringWrapper lastMarker = new FMOD.StringWrapper();
    }


    TimelineInfo timelineInfo;
    GCHandle timelineHandle;
    private FMOD.Studio.EVENT_CALLBACK m_3DWindCallback;
    [SerializeField] EventReference m_ambWind3DA;
    private FMOD.Studio.EventInstance m_ambWind3DAInstance = default;

    private void Awake()
    {
        m_camera = GameObject.Find("RS_Camera");
        m_parabolaController = GetComponent<ParabolaController>();
    }

    private void Start()
    {
        // Set ambWind3DA&B Event on repeat
        InvokeRepeating("CallAmbWind3DA", 3.0f, 3.0f);
    }
    private void CallAmbWind3DA()
    {
        StartCoroutine(CallAmbWind3DACoroutine());

        m_ambWind3DAInstance = RuntimeManager.CreateInstance(m_ambWind3DA);
        m_ambWind3DAInstance.set3DAttributes(RuntimeUtils.To3DAttributes(this.transform));
        m_ambWind3DAInstance.start();
        m_ambWind3DAInstance.release();

        timelineInfo = new TimelineInfo();
        m_3DWindCallback = new FMOD.Studio.EVENT_CALLBACK(m_3DAWindEventCallback);
        timelineHandle = GCHandle.Alloc(timelineInfo);
        m_ambWind3DAInstance.setUserData(GCHandle.ToIntPtr(timelineHandle));
        m_ambWind3DAInstance.setCallback(m_3DWindCallback, FMOD.Studio.EVENT_CALLBACK_TYPE.SOUND_PLAYED);

        m_parabolaController.FollowParabola();
    }

    private IEnumerator CallAmbWind3DACoroutine()
    {
        int randomWait = UnityEngine.Random.Range(0, 7);
        yield return new WaitForSeconds(randomWait);
    }

    static FMOD.RESULT m_3DAWindEventCallback(FMOD.Studio.EVENT_CALLBACK_TYPE type, IntPtr instancePtr, IntPtr parameterPtr)
    {
        FMOD.Studio.EventInstance instance = new FMOD.Studio.EventInstance(instancePtr);
        IntPtr timelineInfoPtr;
        FMOD.RESULT result = instance.getUserData(out timelineInfoPtr);
        if (result != FMOD.RESULT.OK)
        {
            Debug.LogError("Timeline Callback error: " + result);
        }
        else if (timelineInfoPtr != IntPtr.Zero)
        {
            Debug.Log("yep");
        }
        return FMOD.RESULT.OK;
    }
    void OnDestroy()
    {
        m_ambWind3DAInstance.setUserData(IntPtr.Zero);
        timelineHandle.Free();
    }
}

It’s understandable that using programmer sounds is difficult. It is a very advanced technique with FMOD that is more geared towards a programmer than a sound designer or composer. We will try to improve our documentation to give more general information on how to set these up.

1 Like

Thank you Richard, it Worked.
So, from my understanding what I have to do next is to use the m_3DAWindEventCallback(), specifying the CALLBACK_TYPE, and the instance I want to get my information from.

So I tried this in m_3DAWindEventCallback() :

static FMOD.RESULT m_3DAWindEventCallback(FMOD.Studio.EVENT_CALLBACK_TYPE type, IntPtr instancePtr, IntPtr parameterPtr)
    {
        FMOD.Studio.EventInstance instance = new FMOD.Studio.EventInstance(instancePtr);
        m_soundPlayed = false;
        IntPtr timelineInfoPtr;
        FMOD.RESULT result = instance.getUserData(out timelineInfoPtr);
        if (result != FMOD.RESULT.OK)
        {
            Debug.LogError("Timeline Callback error: " + result);
        }
        else if (timelineInfoPtr != IntPtr.Zero)
        {
            Debug.Log("yep");
        }
        if(type == FMOD.Studio.EVENT_CALLBACK_TYPE.SOUND_PLAYED)
        {
          m_soundPlayed = true;
        }
        else
          m_soundPlayed = false;
        return FMOD.RESULT.OK;
    }

and this in CallAmbWind3DA() :

m_3DAWindEventCallback(FMOD.Studio.EVENT_CALLBACK_TYPE.SOUND_PLAYED, "?", "?");
      if (m_soundPlayed = true)
      {
        // use getLength
      }

My issue is that I don’t understand the proper way to use getLength. Also, I don’t know what value I should pass in the m_3DAWindEventCallback() function in order to get get a reference of my instance. I slightly understood that the IntPtr are representing a physical space in memory. I assume that I want to find the place where my fmod event’s info is stored, but I don’t have any idea about how to acess it ?

By the way, thank you for considering to make the documentation more accessible for neophytes.

EDIT : I tried to take a second look at some scripting exemple I found on the documentation and on the forum and I have come to the conclusion that i didn’t need to call m_3DAWindEventCallback() in my coroutine in order to access its results. So I tried to use getLentgh() the best I could but I feel like gropping for a correct way to do it, and I must say that I don’t really know what I’m doing :

static FMOD.RESULT m_3DAWindEventCallback(FMOD.Studio.EVENT_CALLBACK_TYPE type, IntPtr instancePtr, IntPtr parameterPtr)
{
instance = new FMOD.Studio.EventInstance(instancePtr);
m_soundPlayed = false;
int length;
IntPtr timelineInfoPtr;
FMOD.RESULT result = instance.getUserData(out timelineInfoPtr);
FMOD.Studio.EventDescription eventDescription = new FMOD.Studio.EventDescription(timelineInfoPtr);
FMOD.RESULT lengthResult = eventDescription.getLength(out length);
if (result != FMOD.RESULT.OK)
{
Debug.LogError("Timeline Callback error: " + result);
}
else if (timelineInfoPtr != IntPtr.Zero)
{
// Debug.Log(“yep”);
}
if(type == FMOD.Studio.EVENT_CALLBACK_TYPE.SOUND_PLAYED)
{
m_soundPlayed = true;
Debug.Log(m_soundPlayed);
if(lengthResult != null);
Debug.Log(lengthResult);
}
else
m_soundPlayed = false;
return FMOD.RESULT.OK;
}

Right now getLentgh returns “ERR_INVALID_HANDLE”, I assume that It comes from my EventDescription, but I don’t really know how to set it up.

The ERR_INVALID_HANDLE you are seeing is because you are trying to check if lengthResult is null. Since lengthResult is an FMOD.RESULT it can never be null so this check will always be true. Instead you should try if(lengthResult != FMOD.RESULT.OK).

However, you are still trying to get the length of the FMOD event which is different from the length of the actual audio file you are playing. If the event you are getting the length of has nothing on the timeline (instead has instruments on a parameter sheet) it will return 0. To get the length of the sound files being played, you need to add the following into the if (type == FMOD.Studio.EVENT_CALLBACK_TYPE.SOUND_PLAYED) statement:

if (type == FMOD.Studio.EVENT_CALLBACK_TYPE.SOUND_PLAYED)
{
    m_soundPlayed = true;
    Debug.Log(m_soundPlayed);
    uint len;

    FMOD.Sound sound = new FMOD.Sound(parameterPtr);
    sound.getLength(out len, FMOD.TIMEUNIT.PCM);

    Debug.Log("SOUND LENGTH: " + len);
}
1 Like

It worked perfectly thank you so much !