Best way to sync sound with events?

I’ve went searching around and saw people either using custom waits to time it via wait() and eyeing it, some try checking the music’s position. And some use a bunch of coroutines and create new threads to get around some issues.

Though looking into it, I’m thinking I could face issues with desync by spamming custom wait()s and I want to make sure that everything is synced else it will look really off-putting. (Plus having to re-write an entire system if it doesn’t work isn’t really my forte :sweat_smile: )

I’m wondering what the best way would be to sync audio to animations/events/changes. It would also have to be able to last through many events/quick occurrences. Thanks.

3 Likes

Hello @3rdhoan123 ,

I hope I understood your question correctly, so apologies if my answer is not satisfactory. The game I work on also has a lot of events around music, and what I found to be the best method of triggering events that sync with sound is by stamping the key positions in your sound and using RunService to perfectly catch the occurences.

Let me give you an example.
Suppose we have a song, and we know there is a high energy change or music effect at times 00:03, 00:05, 00:21, 1:32 , … and so on. You might want to treat these effects differently based on their type, so what you can do is build a mapTable as such:

MapTable Example
mapTable = {
    [1] = {
        t = 3, effect = 1 
    },
    [2] = {
        t = 5, effect = 2
    },
    [3] = {
        t = 21, effect = 1
    },
    [4] = {
        t = 92, effect = 3
    },
}

Now, what you can do inside a LocalScript for example, is run a RenderStepped and hold a timer to know where you are standing within the MapTable. Based on passing the timestamps, you can then trigger your effects.

Triggering RenderStepped Example
local RunService = game:GetService("RunService")
local timer = 0
local currentIndex = 1 

function step(step)
    timer += step

    if mapTable[currentIndex] == nil then
        --Stop the RenderStep
        RunService:UnbindFromRenderStep("step")
        timer = 0
        return
    end

    if timer > mapTable[currentIndex].t then
          --Here you can implement your own functions that get triggerred.
          if mapTable[currentIndex].effect == 1 then
              runEffect1()
          elseif mapTable[currentIndex].effect == 2 then
              runEffect2()
          elseif mapTable[currentIndex].effect == 3 then
              runEffect3()
          end
          currentIndex += 1
    end
end

RunService:BindToRenderStep ("step", Enum.RenderPriority.Camera - 1, step)

This will be used in combination with your other scripts, and you have to start playing your song simultaniously with launching the RenderStepped. The sync and the event triggers should work really well. If you have any questions please let me know and once again, sorry if this wasn’t what you were looking for :stuck_out_tongue: !

Here is an example of how this looks in my game (Quite old video, game is very different now. Apologies for that) :

12 Likes

Thank you very much, this is exactly what I was looking for :+1:

1 Like