love icon indicating copy to clipboard operation
love copied to clipboard

Support loop points in .wav files

Open slime73 opened this issue 6 years ago • 6 comments

Original report by Joel Schumacher (Bitbucket: dudeguy, GitHub: dudeguy).


You can specify which region to loop in a wave file and currently löve ignores this data. You can edit custom loop points using Wavosaur, but I also prepared a test file from the first two CC0 sound effects I found on opengameart (see attachments - only the second grunt sound should be looping).

They are stored in the .wav files as described here: https://sites.google.com/site/musicgapi/technical-documents/wav-file-format#cue

I would be willing to contribute the changes necessary to Wuff, if that would be helpful, but it seems to me some changes are necessary to SoundData, Sound and Decoder, which I have no real clue about.

It might also be nice to give the option of specifying those loop points via löve yourself (not as part of the .wav file).

slime73 avatar Aug 04 '18 15:08 slime73

Original comment by Mi 28 (Bitbucket: rcoaxil, ).


Loop points are not supported in the audio engine, much less in the codecs. It would not be wise to implement this just for one audio file type, and the one not commonly used to boot.

However, looping of the entire audio track is supported, and you could trivially produce looping points effect using queueable audio sources, by feeding it samples or track sections appropriately.

Since the underlying audio framework supports this, it's not difficult to implement support of generic nondescript loop points that you set up manually. But it will not play nicely with stream sources, and will not work altogether with queueable sources.

slime73 avatar Aug 05 '18 19:08 slime73

Original comment by Gabe Stilez (Bitbucket: z0r8, ).


For one, löve treats all non-queue sources the same in terms of it not caring what file format has been specified; wav, ogg, mp3 or even the module formats that libmodplug can parse and render. They are chunks of samplepoints either decoded with a Decoder object for non-static sources, or a SoundData kept in memory for static sources.

To have löve support loop points for one specific format would break the assumption that all other formats should have that support, which some simply don't have. (Module files could have their tracks toggled, for instance, but that's also not supported by the framework since they're also treated as just audio bitstreams)

The other thing is, you absolutely can make use of that information for looping, if you want! just load the file in, seek to the appropriate place in the file (wav has a neat chunk based structure, which i'm sure you know about), and load in the starting and end points, then you have two options:

  • Use a simple Source, and use :tell and :seek on it in love.update to have the playing source jump back to where it should be; disadvantages of this method is that :tell isn't that precise in terms of the update loop being called relatively slower than you'd need it to be (especially with vsync on).
  • Use a QueuableSource and a SoundData as buffer; I'd say to use a Decoder to load in the wav, then :decode chunks, and if they passed the loop end point you defined, copy over the samplepoints that are just before the loop end point, then seek the decoder back to where it should be, :decode again to get the data from the loop start point, copy over the data until the buffer's full, queue the buffer, copy in the rest, decode, repeat.

Also, @rcoaxil , i'm pretty sure the formats themselves support them, if not the codec and OpenAlSoft itself; again, software implementation of such a thing is possible; and i expanded on your second paragraph a bit, and since that's a good enough solution, your 3rd paragraph is a bit moot since nothing else has to work together nicely or not with any of the source types. Let's not confuse people too much :P

slime73 avatar Aug 06 '18 14:08 slime73

Original comment by Bart van Strien (Bitbucket: bartbes, GitHub: bartbes).


I think there are two paths to implementing this:

  • The simplest, easiest solution is to implement this at the Decoder level. Just do the looping while decoding and streaming sources just work. As far as I know, that's how loop points work for tracker formats right now. The big downside being that static sources will probably loop infinitely unless you deal with them separately.
  • The cleaner solution is to define loop points at the love.audio/(streaming) Source level. Decoders are then responsible for parsing them and passing them up. This is way more invasive, but the big advantage is that it's then trivial to provide a lua API to define your own loop points. Additionally, it may not always be easy to find loop points up front, but I'm not sure how most decoders deal with them.

slime73 avatar Aug 06 '18 17:08 slime73

Original comment by Mi 28 (Bitbucket: rcoaxil, ).


I'm pretty sure that for most file types, stream source looping will produce obnoxious clicking that's impossible to get rid of, due to waveform mismatch at the edges. In lossless files with sufficiently good codecs you may be able to work around this, but lossy files are guaranteed to have this problem. For static sources that would be fine; OpenAL supports loop points.

slime73 avatar Aug 06 '18 23:08 slime73

Original comment by Joel Schumacher (Bitbucket: dudeguy, GitHub: dudeguy).


It is true that queueable sources are able to emulate this behaviour, so there is no strict necessity for it anymore. But I have heard this question asked a couple of times on the löve Discord (how to loop specific ranges) and I think it would be nice, as bartbes suggested, to define your own loop points at the Source level.

slime73 avatar Aug 14 '18 14:08 slime73

loop_point.wav.zip

slime73 avatar Feb 22 '20 01:02 slime73