Looping in queueable sources

General discussion about LÖVE, Lua, game development, puns, and unicorns.
Post Reply
User avatar
pgimeno
Party member
Posts: 3656
Joined: Sun Oct 18, 2015 2:58 pm

Looping in queueable sources

Post by pgimeno »

I'm opening this new thread to avoid derailing the thread where it originated.
zorg wrote: Wed Apr 04, 2018 10:56 am
pgimeno wrote: Wed Apr 04, 2018 9:01 am One inconvenient of this usage is that if the sounds are not encoded losslessly (i.e. WAV), the transitions/loop may have audible artifacts.

I'm not finding a way to loop one of the samples. Maybe it's not possible with this API.
I'd contest that claim since 1. SoundData objects always use a samplepoint based format, and 2. the issue would lie sorely in the Decoder's :decode method; looping is supported in a manual fashion.
Which of both claims exactly? The artifacts one or the API one?

If you mean the artifacts one, it's a consequence of lossy compression, not Löve's fault (nor did I imply it was).

If you mean the API one, can you provide an example for looping a sample in a QueueableSource? It requires SoundData, but SoundData can't be set to loop, and a QueueableSource itself can't be set to loop either. At best you can queue the same sound again and again until it needs to stop; I couldn't get rid of the one-loop latency at stop time when doing this. Example attached. Artifacts are audible at the start/loop transition and at the loop/end transition, because they don't stitch together well, but that's the fault of the sounds.
Attachments
queue_loop.love
(54.7 KiB) Downloaded 122 times
User avatar
raidho36
Party member
Posts: 2063
Joined: Mon Jun 17, 2013 12:00 pm

Re: Looping in queueable sources

Post by raidho36 »

Technically it's possible to specify per-sample looping points as well as looping mechanism (ping-pong, wrap, etc.) but this functionality is not implemented. I don't think you'd need it since it's basically just a harder way of using separate individual sound clips.
User avatar
pgimeno
Party member
Posts: 3656
Joined: Sun Oct 18, 2015 2:58 pm

Re: Looping in queueable sources

Post by pgimeno »

raidho36 wrote: Wed Apr 04, 2018 9:24 pm Technically it's possible to specify per-sample looping points as well as looping mechanism (ping-pong, wrap, etc.) but this functionality is not implemented. I don't think you'd need it since it's basically just a harder way of using separate individual sound clips.
What do you mean about using separate individual sound clips?

This is what I am doing in T2R for lack of such a feature. The loop is not always smooth. https://github.com/pgimeno/Thrust-II-re ... e.lua#L963
User avatar
zorg
Party member
Posts: 3465
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Looping in queueable sources

Post by zorg »

Okay, so first off, i do wanted to apologize, because i wrote that reply on the bus home from a ~32 hour work shift + training session, so it was a bit terse and i didn't elaborate on what i meant; and then i fell asleep, waking up just a few minutes ago.

You have artefactsissues with looping mp3 and similar formats, because they aren't exactly stored in a per-samplepoint fashion, hence in most cases the end of those bitstreams will contain some hiccups. mp3 artefacting usually refers more to the bubbling quality that arises from low bitrates not containing the higher frequencies correctly, thanks to the encoding scheme the format uses (filtering out information based on a psycho-acoustic model).

(Also, i just noticed you actually stating this, i was under the impression you meant artefacting as bad looping, and not the bubbling thing; this is moot then since we agree :P)

If you wanted to loop an mp3 with QSources (do permit me not to write out the silent vowels in that word), then you could do a similar thing to what you are doing with music:tell() and music:seek() in your project's source that you posted, but a bit differently:
(This is what i was referring to when i previously wrote that you'd need to do some things "manually")

Code: Select all

mSource = love.sound.newDecoder('path/to/music/song.mp3', 2048) -- Default, but this should be smaller, imo.
qSource = love.audio.newQueueableSource(mSource:getSampleRate(), mSource:getBitDepth(), mSource:getChannelCount(), 8) -- default
smpCounter = 0
yourLoopPoint = 6942804

-- in update
if qSource:getFreeBufferCount() then
    local Buffer = mSource:decode() -- You'll get a 2048 (by default) sized SoundData
    if yourLoopPoint == smpCounter + Buffer:getSampleCount() then
        -- queue and seek
        qSource:queue(Buffer)
        mSource:seek(0) -- or whatever your starting point would be
        smpCounter = 0
    if yourLoopPoint > smpCounter+Buffer:getSampleCount() then
        -- just queue the whole buffer
        qSource:queue(Buffer)
        smpCounter = smpCounter + Buffer:getSampleCount()
    elseif yourLoopPoint > smpCounter then
        -- only queue part of the buffer, and then seek to the starting point (or to the loop start if you have such a thing) with the Decoder.
        local temp = love.sound.newSoundData(yourLoopPoint - smpCounter, mSource:getSampleRate(), mSource:getBitDepth(), 
    mSource:getChannelCount())
        for i=0, (yourLoopPoint - smpCounter - 1) do
            temp:setSample(i, Buffer:getSample(i))
        end
        qSource:queue(temp)
        mSource:seek(0) -- or whatever your starting point would be
        smpCounter = 0
    end
end
Fair note, Decoders have decode and seek methods, but they aren't mentioned in the changelogs, much less have entries on the wiki.
Also, i'm not 100% sure what unit :seek uses, if it's seconds, then anything other than seek(0) won't be samplepoint-accurate.
Also also, not sure whether :decode creates a new SoundData each time it's called or not, if they do, that's both sad and unoptimized, hence this kind of thing may cost a fair amount of cycles more than it should. (the temp buffer thing is amortized though, since it's only done when the loop end point is reached... then again, it still bumps up the processing time when it happens)
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
User avatar
raidho36
Party member
Posts: 2063
Joined: Mon Jun 17, 2013 12:00 pm

Re: Looping in queueable sources

Post by raidho36 »

pgimeno wrote: Wed Apr 04, 2018 9:55 pm What do you mean about using separate individual sound clips?

This is what I am doing in T2R for lack of such a feature. The loop is not always smooth. https://github.com/pgimeno/Thrust-II-re ... e.lua#L963
Instead of using individual sound clips you're trying to play specific parts of a bigger sound clip. Like a sound atlas. Except for graphics APIs using atlases makes sense because that saves GPU draw calls which are expensive, whereas for audio it does absolutely nothing.

Also, sample-based looping points are not supported for stream and queue sources anyway - for obvious reasons.
User avatar
zorg
Party member
Posts: 3465
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Looping in queueable sources

Post by zorg »

raidho36 wrote: Thu Apr 05, 2018 11:37 am Also, sample-based looping points are not supported for stream and queue sources anyway - for obvious reasons.
Unless you do it manually, like how i detailed it in my post, that is.
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
User avatar
pgimeno
Party member
Posts: 3656
Joined: Sun Oct 18, 2015 2:58 pm

Re: Looping in queueable sources

Post by pgimeno »

@zorg:
zorg wrote: Thu Apr 05, 2018 2:43 am Okay, so first off, i do wanted to apologize, because i wrote that reply on the bus home from a ~32 hour work shift + training session, so it was a bit terse and i didn't elaborate on what i meant; and then i fell asleep, waking up just a few minutes ago.
No problem, glad you could rest!
zorg wrote: Thu Apr 05, 2018 2:43 am [...]
(Also, i just noticed you actually stating this, i was under the impression you meant artefacting as bad looping, and not the bubbling thing; this is moot then since we agree :P)
To clarify, I meant artifacts in a more general sense of unwanted effects. Like in the demo I posted. What I meant is that the compression generates a waveform that differs from the original, and if you adjust the loop for the original, that loop may have a click in the lossy-compressed-and-uncompressed version because the level at the start and end of the loop may be different, even if it matched in the original.
zorg wrote: Thu Apr 05, 2018 2:43 am If you wanted to loop an mp3 with QSources (do permit me not to write out the silent vowels in that word), then you could do a similar thing to what you are doing with music:tell() and music:seek() in your project's source that you posted, but a bit differently:
(This is what i was referring to when i previously wrote that you'd need to do some things "manually")
I think I get the general idea, but that code isn't too clear. It seems to assume that the sound file's last sample is the last in the loop, but I think I have a rough idea of how to adapt it for the kind of usage I need.
zorg wrote: Thu Apr 05, 2018 2:43 am Fair note, Decoders have decode and seek methods, but they aren't mentioned in the changelogs, much less have entries on the wiki.
Also, i'm not 100% sure what unit :seek uses, if it's seconds, then anything other than seek(0) won't be samplepoint-accurate.
Hopefully the same as source:seek? (i.e. default to seconds but allow specifying the unit as an optional 2nd parameter). I haven't tried.

*****

@raidho: Got you, thanks. For the reason above, it's not a very good idea to use separate compressed streams for the intro and the loop.
User avatar
raidho36
Party member
Posts: 2063
Joined: Mon Jun 17, 2013 12:00 pm

Re: Looping in queueable sources

Post by raidho36 »

If you can't use tracker formats, which you should for this purpose, and you don't want to use a lossless compression, you can always do a crossfade on the edges. Just pad your sound atlas a little bit with the looped data, the same way as you pad graphics atlases.

Also I think I remember that decoders don't support sample-perfect seeking - for technical reasons. The decoder class is the same thing that's used internally for playing stream sources.
Post Reply

Who is online

Users browsing this forum: No registered users and 4 guests