Task.wait() is very inconsistent

I may be a bit off here but, from working with many languages there is a unwritten rule more or less not to call 0 time waits … wait(), wait(0) or the task.wait version. Roblox uses this in some templates so I assume your 0 time calls are not truly 0 time. If I need to go low, I will always use 0.33 or even 0.033. these are old assembler tricks on faking one FPS wait. The fact you are finding a 0 wait not consistent isn’t shocking. Many languages would just lock up with a call like that.

Edit: It would be interesting to see your test using this technique. I got a feeling it will pass with flying colors with a task.wait …

Like this? The wording was a little confusing.
Either way, the issue persists.

local SoundService = game:GetService("SoundService")
local Samples = SoundService.Samples

local offset

while true do
	local c = Samples.loop_mika:Clone()
	c.Parent = Samples
	c:Play() -- doesnt seem to make a difference if its before or after

	if not c.IsLoaded then c.Loaded:Wait() end
	
	local nextOffset = c.TimePosition
	print(nextOffset) -- still returns 0
	if offset then c.TimePosition += offset end

	task.wait(math.max(0.5 - nextOffset, 0))
	offset = nextOffset
end

Removing the 0 waits doesn’t seem to change anything.

local SoundService = game:GetService("SoundService")

local Samples = SoundService.Samples

local expectedInterval = 0.5
local lastTime = tick()

while true do
	local currentTime = tick()
	local elapsedTime = currentTime - lastTime
	lastTime = currentTime

	local waitTime = expectedInterval - elapsedTime
	lastElapsed = (waitTime > 0 and task.wait(waitTime) or 0.5)

	local c = Samples.loop_mika:Clone()
	c.Parent = SoundService
	c:Destroy()
end

If you read the documentation, task.wait will wait until the heartbeat after the time is up. It also says that task.wait() will default to task.wait(0) and that it is equivalent to RunService.Heartbeat:Wait()

Yes. In the top post, I was asking for any possible alternatives to task.wait, or any way to mitigate its desync well enough, to where it doesn’t sound awful.

I didn’t say remove it. Anyways you’re using this to find an expectedInterval/elapsedTime. Why do that when you can just read the real position of the music being played.

I figured that is what it was doing. You call a real wait 0, you’re heading for a crash … lol

Because it is being destroyed in that code example.
Using an alternative version where it isn’t being destroyed, the result (unless I did it wrong) is also the same issue.

local SoundService = game:GetService("SoundService")
local Samples = SoundService.Samples

local offset

while true do
	local c = Samples.loop_mika:Clone()
	c.Name = "-"
	c.Parent = Samples
	c:Play()

	if not c.IsLoaded then c.Loaded:Wait() end

	local nextOffset = c.TimePosition
	print(nextOffset)
	if offset then c.TimePosition += offset end
	
	local calc = 0.5 - nextOffset
	if calc > 0 then task.wait(calc) end
	offset = nextOffset
end

I was just pointing out, I could see how a wait(0) could be inconstant. There is no such a call … It’s like dividing by 0 … one of the things you just don’t do in a program or it crashes.

Going with your post … how does this work out?

local SoundService = game:GetService("SoundService")
local RunService = game:GetService("RunService")

local Samples = SoundService.Samples
local expectedInterval = 0.5
local lastTime = tick()

local function createSound()
	local c = Samples.loop_mika:Clone()
	c.Parent = Samples
	c:Play()
	lastTime = tick()
end

RunService.Heartbeat:Connect(function()
	if tick() - lastTime >= expectedInterval then
		createSound()
	end
end)

createSound()

Sounds are unreliable, make sure they’re completely loaded before playing
you can use a while true do loop to check if enough time has passed constantly, and it runs faster than task.wait(). The loop will unfortunately unintentionally cap FPS unless you use a parallel desynchronized thread, however using that may bring on another onset of delay issues.

Perhaps the Roblox engine simply isn’t cut for the job.

It’s not very inconsistent. The absolute minimum time it allows you to wait is 29ms.

Unfortunately, no. The issue still remains.

It is very inconsistent for my use case, as humans are much more perceptive to minor differences in audio delay, than most other things, which is why this issue even exists, and has no already known solution in sight. It’s a very niche use.

I think it’s the sounds, not the wait method. There’s a miniscule difference between these.

I agree … let’s try something totally different.

local SoundService = game:GetService("SoundService")
local TweenService = game:GetService("TweenService")

local Samples = SoundService.Samples
local expectedInterval = 0.5

local function createSound()
	local c = Samples.loop_mika:Clone()
	c.Parent = Samples
	c:Play()

	local tweenInfo = TweenInfo.new(expectedInterval)
	local tween = TweenService:Create(c, tweenInfo, {})
	tween:Play()
	tween.Completed:Connect(function()
		createSound()
	end)
end

createSound()

Actually, wait() with negative numbers works. I just tried it and it says it on the documentation. task.wait doesn’t. It went back in time 2 seconds when I did wait(-2).

Again, no.

You could actually experiment with the exact same setup yourself, as I’ve linked the original audio file for download above (since we’re already clogging up the thread replies fairly quickly):

It definitely doesn’t go back in time 2 seconds. It just seems to clamp back down to 0 (aka the next heartbeat)

local i = 0 while i < 50 do i += 1 print(i) task.wait(-2) end

Yeah you need to use wait() not task.wait() trust me bro

No? It just clamps to the wait default (0.03), instead of the next heartbeat.