Syncing Sounds with TimePosition isn't reliable anymore: glitches and stuttery sounds

Hi,

I’m trying to do an audio effect and I need a way to reliably keep two Sound objects synced.

In the past, I used to use this script that relies on TimePosition, and it would reliably work every single time:

local Track1 = script.Parent:WaitForChild("Track1")
local Track2 = script.Parent:WaitForChild("Track2")

Track1:Play()
Track2:Play()

while wait() do
	Track2.TimePosition = Track1.TimePosition
end

However, sometime early this year, I noticed that this stopped working properly, even in my old projects.

In that example, Track2 is the track always having its TimePosition set to that of Track1’s. Track2 now comes out sounding stuttery and glitchy, which again it didn’t used to do, ever, until early this year.

Here’s a video example. Around 10 seconds, my drum track (Track2) starts glitching out:

I’d LOVE for this to get fixed, and for the sound on Track2 to be smooth again.

Otherwise, if there’s a better way to script this that ensure both audio tracks are synced forever and 99.9% reliably, I’d love to learn!!

Expected behavior

If everything was working correctly, the Sound object having its TimePosition set to that of the primary Sound should be playing sound smoothly and not glitching/stuttering out.

A private message is associated with this bug report

Hey @marbleycake37 – unfortunately we don’t offer any strong timing or synchronization guarantees. The audio engine runs concurrently with scripts, and scripts only get a chance to affect some change every visual frame.

In your example, there are multiple areas that can experience timing drift. As far as I can tell this wouldn’t have ever been totally reliable

In these two lines, neither of the :Play calls are guaranteed to return instantly – so you may have some timing drift

Track1:Play() -- starts playing Track1
-- some time elapses; Track1 gets ahead of Track2
Track2:Play() -- starts playing Track2 late

In the loop, this assignment

Track2.TimePosition = Track1.TimePosition

can be thought of as 2 distinct steps, which makes it easier to see where drift might occur:

local track1Time = Track1.TimePosition
-- Track1 keeps playing even after we read its TimePosition
Track2.TimePosition = track1Time -- assigning Track2's time to a stale position

More directly, the glitchy sound comes from Track2’s TimePosition jumping around. It only gets a frame or so to start playing before it has to seek to a new location.

The main workaround that comes to mind is adjusting the PlaybackSpeed to speed up/slow down Track2, instead of jumping around within the file

local threshold = 0.1
local speedAdjustment = 0.05

while wait() do
    local t1 = Track1.TimePosition
    local t2 = Track2.TimePosition
    if t2 > t1 + threshold then
        -- slow down to let Track1 catch up
        Track2.PlaybackSpeed = 1 - speedAdjustment
    elseif t2 < t1 - threshold then
        -- speed up to meet Track1
        Track2.PlaybackSpeed = 1 + speedAdjustment
    else
        -- the two are in-sync; do nothing
        Track2.PlaybackSpeed = 1
    end
end

though you’ll need to play around to find thresholds that don’t alter the perceived pitch of the file too much, while keeping the tracks in reasonable sync.

2 Likes

Hi, thank you A LOT for your insight here!

I appreciate you walking me through all the different potential points of failure on that script, as well as outlining what the main workaround for this looks like.

I’ll keep playing around with that, as I’ve had really good results so far.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.