Page 1 of 1

Queueable sources don't always free buffers

Posted: Sat Dec 05, 2020 11:52 pm
by myQwil
I'm looking to create a queueable audio source with the least amount of delay that I can possibly get away with before stuttering occurs.
This is my main.lua:

Code: Select all

size ,samplerate ,bitdepth ,channels ,buffers =
128  ,48000      ,16       ,1        ,8

bufs_per_ms = (samplerate / 1000) / size

function love.load()
	sdata  = love.sound.newSoundData(size, samplerate, bitdepth, channels)
	source = love.audio.newQueueableSource(samplerate, bitdepth, channels, buffers)
end

function love.update(dt)
	dt = dt * 1000
	local bufs  = dt * bufs_per_ms
	local count = source:getFreeBufferCount()
	print(
		string.format('%.2f', dt)..' ms'
		..'   expected: '..string.format('%.2f', bufs)
		..'   got: '..(count>0 and count or 'none')
	)
	for _=1,count do
		source:queue(sdata)
		source:play()
	end
end
And this is an excerpt from the print output:

Code: Select all

17.00 ms   expected: 6.37   got: 8
16.43 ms   expected: 6.16   got: 8
16.60 ms   expected: 6.22   got: none
16.67 ms   expected: 6.25   got: 8
16.83 ms   expected: 6.31   got: 8
16.62 ms   expected: 6.23   got: 8
16.66 ms   expected: 6.25   got: none
16.63 ms   expected: 6.24   got: 8
16.40 ms   expected: 6.15   got: 8
16.98 ms   expected: 6.37   got: 8
16.60 ms   expected: 6.22   got: none
16.48 ms   expected: 6.18   got: 8
Assuming I haven't screwed up the math, there should be, according to the delta time, roughly 6 or 7 buffers used and freed up at any given update call, but what we see instead is that either all of the buffers are free or none of them are. Can anyone explain to me what the reason is behind that behavior? Also, is there any way to have it distribute the work more evenly between update calls?

Re: Queueable sources don't always free buffers

Posted: Sun Dec 06, 2020 2:15 am
by ReFreezed
I'm just guessing here, but I think this is just an effect caused by multiple audio-related things simply not being in sync. Your code always prints either 7, 8 or none for me. Disabling vsync or increasing 'buffers' doesn't make any difference - it seems that the buffers in the source get "freed" in chunks of 7 or 8. With vsync off, I increased 'buffers' to 32 and wrapped the loop in 'if count == buffers then ... end' and got this:

Code: Select all

0.99 ms   expected: 0.37   got: 15
1.02 ms   expected: 0.38   got: 22
(... lots of "got: 22" ...)
0.99 ms   expected: 0.37   got: 22
1.04 ms   expected: 0.39   got: 30
(... lots of "got: 30" ...)
1.02 ms   expected: 0.38   got: 30
1.00 ms   expected: 0.38   got: 32
0.98 ms   expected: 0.37   got: none
1.01 ms   expected: 0.38   got: none
Increasing 'size' does seem to decrease the amount of buffers in each freed "chunk". Setting 'size' to 512 made the chunk size 1 or 2 for me, so maybe the size you're using is just too small for things to work properly.

Re: Queueable sources don't always free buffers

Posted: Sun Dec 06, 2020 3:21 am
by myQwil
One thing I hadn't taken into account was my JACK audio server. It was running at 1024 frames/period with 2 buffers, resulting in an additional 42.7 ms of delay, so I was able to lower that to 16 ms, which makes a considerable difference.

Re: Queueable sources don't always free buffers

Posted: Mon Dec 07, 2020 12:04 pm
by zorg
OpenALSoft allegedly has a horrid internal latency as well (50ms iirc - by default), but i did manage to get about a (3x)11ms calculated latency with only 3 "internal" buffers (that you set for the Qsource, as opposed to using multiple SoundData objects), 44100 Hz sampling rate, mono and a buffer size of 512 samplepoints where my ad-hoc sine signal still sounded good... caveat: on my machine, might not sound good on yours. :3

I modified the OP's posted script a bit, gonna attach the love file; it also actually plays sound so one can hear if it underruns or not.
test.love
(167.67 KiB) Downloaded 153 times

Oh, i also took vsync out, i prefer it that way when rendering audio. (do it in another thread also, less chance of interference from the scheduler or anything)

Still, i do feel like comparing the audio time offset with deltatime is not that useful, since you shouldn't rely on that anyway; just queue up the data if there are free buffers, the backend/soundcard will do its job at the correct pace.