How do I play audio perfectly in sync?

Suppose that I have two Sounds. They contain music which is the exact same speed and starts at exactly the same time. I play them at the same time from a script:

soundOne:Play()
soundTwo:Play()

For some reason, they don’t line up exactly (anywhere up to 100ms off) which makes the entire thing sound bad. I’ve tried setting the SoundPosition of one equal to the other but that doesn’t make it better.

They are 120 bpm, ogg format, not same length. They do not need to loop.

Are there any tricks to make this work?

1 Like

If you are running this from the server, it might be possible that there is some inconsistent network delay to the client for playing the two sounds. Have you tried doing this clientside?

4 Likes

If what @Defaultio said doesn’t fix it. Then in the extreme case you can always call the functions on different threads so that it get called simultaneously .

In the extreme case of course.

1 Like

I’m running this in a local script, so that doesn’t really apply.

This actually worked!

spawn(function() soundOne:Play() end)
spawn(function() soundTwo:Play() end)

It’s kinda strange tho, since it shouldn’t yield or anything like that. Should this be documented?

2 Likes

It’s just that the code is being ran synchronously. That is one Play() gets called before the other. From top to bottom, since they aren’t getting called at the same time you get those few milliseconds of trouble.

I would expect a small delay between the two, but considering that the Roblox Lua interpreter can do somewhere around 15 million instructions per second on my computer, I wouldn’t expect it to be as bad as 100ms?

By the way, I got that 15 million number from this code (I actually got 16058135 but I reduced it a bit):

local counter = 0
local start = tick()
while tick() - start < 1 do
    counter = counter + 1
end
print(counter)

Same, that’s why I considered the two threads an extreme case.

Are you sure it’s not the sound files causing the issues? There might be a few milliseconds-seconds of silence before your file actually has audio playing. This is especially true for MP3s, which have a brief moment of silence before the audio starts. Try making the audio FLACs, and if you need to convert the MP3s, make sure you cut out the silence in the beginning.

They’re oggs, and it shouldn’t be an issue anyway since it’s working extremely consistently after moving the calls to different threads. It’s likely something strange in Roblox itself I would guess.

edit: if it’s at all relevant, I’m currently wearing Bluetooth headphones, with Bluetooth keyboard and mouse also connected, so I do get some latency or glitches occasionally which might throw off some timing in the Roblox system?

1 Like

If it’s working that’s great. Just giving a possible solution. :slight_smile:

Also, Oggs aren’t necessarily safe. If someone converted an MP3 to an Ogg somewhere down the line, the Ogg will have silence and other MP3-specific artifacts. Food for thought in case anyone needs it in the future.

1 Like

Yea definitely. I can export MP3 directly from my DAW but I’ve stopped doing that in favour of exporting to WAV and converting to OGG to get perfect loops and better audio quality.

1 Like

Make use of this property of sounds.

Load the sounds, wait until they’re both loaded, and then play them. Something like this:

local sound1 = workspace.Sound1
local sound2 = workspace.Sound2
repeat
    game:GetService"RunService".RunService:Wait() -- make sure this code is on the client!
until sound1.IsLoaded and sound2.IsLoaded

sound1:Play()
sound1:Play()

Alternatively,

local sound1 = workspace.Sound1
local sound2 = workspace.Sound2

if not sound1.IsLoaded then
    sound1.Loaded:Wait()
end
if not sound2.IsLoaded then
    sound2.Loaded:Wait()
end
sound1:Play()
sound2:Play()

Let me know if this works.

I already preload them using ContentProvider, so it won’t be a loading issue. It’s something to do with the way that :Play() is called as seen above.

1 Like

I know this thread is a bit old but it comes up a lot on google searches so I thought I would add my 2 pence worth.

I have been experimenting with creating immersive audio environments by placing audio loops belonging to a larger composition in various base parts so that the music/atmosphere changes as the player moves around their environment.

Starting sounds in sequence like the example below works but is subject to drift over time at best or can start completely out of sync at worst.

However, if you throw Bindable Events into the mix you get a lot more stability.
In this basic example, I have a bindable event called “AudioSync” and a couple of sounds with a script inside each of them called “PlayScript”. Finally, I have a server script called “BeVentAudioTest” placed in ServerScriptService.

Also, note that “AudioSync” is in a folder called BeVents which is in the workspace.

BeVentAudioTest

local BeVents = workspace.BeVents
local AudioSync = BeVents:WaitForChild("AudioSync")
--wait 20 seconds
wait(20)
--Play
AudioSync:Fire("play")

PlayScript

local BeVents = workspace.BeVents
local AudioSync = BeVents:WaitForChild("AudioSync")

local sound = script.Parent


AudioSync.Event:Connect(function ()
	sound:Play()
end)

This is the most basic example but it can be used to build your own library for synchronised playback.

One thing to note is that all of the sound clips should be the same length and of course the same tempo. I have not tried it with clips of different lengths ie:16 bars, 8 bars and 4 bars. It should work in theory but it would complicate things and possibly start to drift more quickly seeing as this is still not a sample-accurate solution and these loops will generally be playing as long as the server is running.

3 Likes

Thanks, I had a similar issue (I needed to sync some frames to a sound but it was delayed) and this helped a lot!

1 Like

This is quite an old thread, but, does anyone know if this has been fixed or not yet? I’ll be adding dynamic layered music to my game shortly and the idea of being forced to spawn new threads in just seems like a fairly janky solution to what shouldn’t really be a problem to begin with…

Surely, there is a better solution?

If by ‘fix’ you mean roblox adding the ability to play sounds in sync, no.

1 Like

I’ve tryed a lot of things and what I currently have is a system that can somewhat detect desych using playback loudness and replaying the songs if they aren’t in sync
It’s pretty jank and doesn’t always work but that’s the best I managed to do

Sorry to revive this dead topic, but has this issue been fixed? I worked on a track layering system for my game’s music sometime in 2021, and this desynchronizing issue was present back then. I’m revisiting the problem now, and as far as I can tell, this issue no longer occurs?

All I’m doing so far is simply playing two audio tracks (in a localscript, obviously) after ensuring both tracks have been properly preloaded. I’ve been testing this repeatedly in both Studio and live servers, and so far I have not encountered desyncing on any level (if there was even the smallest desync, it would be very apparent to the ear).

If anyone else has worked on this problem and can tell me their experiences on the matter, it would help massively! Also, if there’s a chance anyone has tackled stitching audio tracks to play one after the other (imagine you segment your song into an intro and a main loop, and you want them to connect seamlessly), please let me know how you accomplished that. Last I tried this in 2021, it was a very hacky solution to get it to play seamlessly.

[edit]: Apparently Sounds have received some silent upgrades in the past year! There’s a new feature called LoopRegion (I looked it up and there seems to have been no official announcement, it was silently added in late 2022), so perhaps with newfound attention towards Sounds, these playback delay problems have been fixed?

I still have desync issues. Lets say I have multiple speakers playing the same song, they have some distance between each other but can still be all heard at the same time. I noticed that when I existed the range in which I could hear the song and went back in, desync happened, and the songs were just playing normally.