How to use setTimelinePosition?

Is there any example about how to use setTimelinePosition?

I’m trying to add setTimelinePosition to StudioEventEmitter what I want to achieve is to completely control the playback of an sfx.

Call play to only instantiate the event without playing and using setTimelinePosition to control it (like for example, a cassette tape, controlling its speed etc).

Added this lines to StudioEventEmitter

        public int GetLenght()
        {
            int lenght = 0;

            if (instance.isValid())
                eventDescription.getLength(out lenght);

            return lenght;
        }

        public int GetTimelinePos()
        {
            int pos = 0;

            if (instance.isValid())
                instance.getTimelinePosition(out pos);

            return pos;
        }

        public void SetTimelinePos(int pos)
        {
            if (instance.isValid())
            {
                instance.setTimelinePosition(pos);
            }
        }

and tried to seek using this test code

    public StudioEventEmitter emitter;
    private int timelinePos = 0;


    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.J))
        {
            PlaySound();
        }
        if (Input.GetKey(KeyCode.L))
        {
            if (emitter.IsPaused())
                emitter.Pause(false);

            timelinePos += 1;
            emitter.SetTimelinePos(timelinePos);

            Debug.Log(timelinePos);
        }
        if (Input.GetKeyUp(KeyCode.L))
        {
            if (!emitter.IsPaused())
                emitter.Pause(true);
        }
    }

    void PlaySound()
    {
        emitter.Play();

        timelinePos = 0;
        emitter.SetTimelinePos(timelinePos);

        emitter.Pause(true);
    }

But things aren’t working as expected feels like the sound is playing a thousand times.

I would advise against editing the StudioEventEmitter.cs file directly as that can lead to unexpected behaviours, especially if you update the integration at some point. It is much better to use the timeline functions on a custom script to avoid this.

In your custom script, you can use something similar to this code:

FMOD.Studio.EventInstance eventInstance;
    int currentPosition = 0;

    // Start is called before the first frame update
    void Start()
    {
        GetComponent<FMODUnity.StudioEventEmitter>().Play();
        eventInstance = GetComponent<FMODUnity.StudioEventEmitter>().EventInstance;
    }

    // Update is called once per frame
    void Update()
    {
        eventInstance.getTimelinePosition(out currentPosition);

        if (Input.GetKeyDown(KeyCode.UpArrow))
        {
            eventInstance.setTimelinePosition(0);
        }

        Debug.Log("Current position: " + currentPosition);
    }

It is important to get the event instance after playing the event emitter in order to grab the event instance. Otherwise, a new event instance is spawned and the rest of the code will not reflect what you are expecting.

Thank you for your answer, and what about advancing in the timeline position while stiil being able to hear the sound (cassette tape behaviour)?

There’s no feature that lets you interpolate between timeline positions, so this would require some designing.

You can have a setup such as:

  1. Pause the event
  2. Play a new event that plays a cassette fast-forward sound
  3. Seek to the required timeline position
  4. Unpause event

Another possibility is to have a function that advances in chunks from the current position to the new position whilst a similar “fast forward” sound effect plays.

You could even use EventInstance::setPitch and increase the pitch (and speed) of the event until you reach the needed timeline position. This cannot work backwards, however.

https://www.fmod.com/resources/documentation-api?version=2.02&page=studio-api-eventinstance.html#studio_eventinstance_setpitch

Alternatively, a more complicated solution would be to use the Core API to play the audio file you want and use Channel::setFrequency to speed up or play backwards as needed and Channel::getPosition to know when you reach the necessary timeline position.

Sorry for themisunderstanding I don’t need the actual cassette effect but rather to play a sound being able to advance it’s playback via code. No need to any pitch change or anything like that.

Then you would just need to adjust the event’s pitch to speed the event up at the same time as adjusting a pitch shift to bring it back to its original pitch. You can add a pitch shift effect to the event’s master track and automate it along with the event’s pitch with a parameter.

Wait, got me lost here.

This is how I changed my code

using UnityEngine;
using FMODUnity;
using FMOD.Studio;

public class TestSetTimelinePos : MonoBehaviour
{
    public StudioEventEmitter emitter;

    EventInstance eventInstance;
    int currentPosition = 0;

    // Start is called before the first frame update
    void Start()
    {
        emitter.Play();
        eventInstance = emitter.EventInstance;
    }

    // Update is called once per frame
    void Update()
    {
        eventInstance.getTimelinePosition(out currentPosition);

        if (Input.GetKeyDown(KeyCode.UpArrow))
        {
            eventInstance.setTimelinePosition(0);
        }
        if (Input.GetKey(KeyCode.DownArrow))
        {
            currentPosition += 1;
            eventInstance.setTimelinePosition(currentPosition);
        }

        Debug.Log("Current position: " + currentPosition);
    }
}

Now obviously no sound is played when I change the current position but think we’re getting on the right track.
Now since you adviced me to not change the event emitter code, but how can I get the total timeline lenght so that I can do my % calc to compare to the ingame animations I’m using without accessing the protected eventDescription?

I’m not certain what you mean by “protected eventDescription”. I’m able to get the event instance being played and access its event description to get the length of the event without issue. You should be able to do it like this:

public class TestSetTimelinePos : MonoBehaviour
{
    public StudioEventEmitter emitter;

    EventInstance eventInstance;
    EventDescription eventDescription;
    int eventLength;
    int currentPosition = 0;

    // Start is called before the first frame update
    void Start()
    {
        emitter.Play();
        eventInstance = emitter.EventInstance;
        eventInstance.getDescription(out eventDescription);
        eventDescription.getLength(out eventLength);
    }

}

Oh sorry just feel dumb.
I’m almost there, I just need to understand how to resume from a playback after changing the timeline position. Everytime I start the sound it start from pos 0.

    void Update()
    {
        eventInstance.getTimelinePosition(out currentPosition);

        if (Input.GetKeyDown(KeyCode.UpArrow))
        {
            eventInstance.setTimelinePosition(0);
        }
        if (Input.GetKey(KeyCode.DownArrow))
        {
            currentPosition += 1;
            eventInstance.setTimelinePosition(currentPosition);
        }

        if (Input.GetKey(KeyCode.LeftArrow))
        {
            eventInstance.setPaused(false);
            eventInstance.start();
        }
        if (Input.GetKey(KeyCode.RightArrow))
        {
            eventInstance.setPaused(true);
        }

        Debug.Log("Current position: " + currentPosition);
    }

The problem is here - when you start() an event instance it will play from the beginning. You can just setPause(false) and it will unpause, playing from where you last paused.

Yep you’re right.
But what I want to achieve is
1: play the sound on start to get all reference (no one will hear it)
2: set timeline pos to 0 just to be sure
3: move timeline position to whatever I want
4: play from the position I choose
5: pause the sound
6: change position back and forth and resume from there

  1. You can play then immediately pause the event instance
  2. setTimelinePosition(0) will do that
  3. setTimelinePosition(x) will do that
  4. setPaused(False) will unpause and make the event play from the new position set in step 3
  5. setPause(True) will pause the event
  6. I’m not sure what you mean by changing the position back & forth but you can use the functions from steps 3 and 4/5 to move the timeline around