1. Anders Ruud
  2. love
  3. Issues


Issue #647 wontfix

Simplifying the API for controlling the playback of Sources

created an issue

The current API for the playback (so not including the volume, pitch and spatial settings) of Sources does some stuff which I think is kind of "policy-y" and not "mechanism-y", which isn't what Lua or LÖVE really do. (http://lua-users.org/wiki/MechanismNotPolicy)

LÖVE does provide "policy-y" stuff, but really only in the form of "it would be too slow to do otherwise" stuff (love.physics and ParticleSystems). There's also "redundancy" in the API with helpful "shortcuts", stuff like love.graphics.getWidth, Canvas:renderTo, love.graphics.setNewFont, etc.

But, I think that currently Sources are slightly like the animation system which used to be in LÖVE. And even though Sources do some useful stuff, like keeping track of the currently playing Sources so they can all be controlled at once, it doesn't do other useful stuff, like allowing more than one instance of a Source to play at one time.

Let's get back to "first principles":

A Source can be either:

  • Making sound and moving forward
  • Not making sound

It also has the state of the current playblack position, and also a rule which dictates what it does when it gets to the end, either:

  • Setting the playback position to the beginning and stopping making sound
  • Setting the playback position to the beginning (and continuing to make sound, i.e. looping)

Changing these states could be controlled by methods like these, but with better names (I've chosen different names than the current names in an attempt to make things less confusing):


(There'd be getters too, of course! :P)

And, I think that might just be all the mechanisms needed to control the playback of Sources.

The most basic stuff is still easy, like playing looping music:

music = love.audio.newSource('music.ogg')

Or firing off a sound effect, which might already be playing:

if something_happens then

The pattern of source:setLocation(0) effect:startMakingSound() could be really common, just like source:stop() source:play() is really common in the current API, so there could be a shortcut method for doing this:

if something_happens then

What else might people want to do with Sources? Well, maybe a lot of stuff, I'd predict some of the most common things would be:

  • Wanting to play more than one instance of a sound at the same time.
  • A system for stopping all of the sounds when the game is paused or sent back the main menu, and resuming the sounds when the game is unpaused.

Playing more than one sound at once can't be done with Sources currently, but it's possible to do something like SLAM does:

function newAudio(from)
    return {
        sounddata = love.sound.newSoundData(from),
        sources = {}

function play(audio)
    local source = love.audio.newSource(audio.sounddata)
    table.insert(audio.sources, source)

-- etc.

And here's a recreation of what Sources can do currently using the proposed API:

function newAudioContainer(from)
    return {
        source = love.audio.newSource(from),
        paused = false

function pause(s)
    s.paused = true

function resume(s)
    if s.paused then
        s.paused = false

function stop(s)

function rewind(s)

audio_table = {}

function newAudio(from)
    local audio = newAudioContainer(from)
    table.insert(audio, audio_table)
    return audio

function pauseAll()
    for _, container in pairs(audio_table) do

-- And it's the same sort of thing as "pauseAll" for resuming, stopping,
-- rewinding, positioning, velocity, etc.

So, you might be thinking "So you're proposing that pausing be removed? Pausing works perfectly fine now, and it's useful!"

Edit: I should clarify, "pausing" and "resuming" are basically what the functions I propose do, but there is no "paused" state. This could be approximated by seeing if the Source isn't playing and if the playback location is greater than 0, but this misses the case where a Source was "paused" before it progressed in time.

To which I might respond, if I could read your thoughts, "Yes! Pausing sources is very useful, so is controlling all of the sources at once, so is playing more than one instance of a source at once, and so are animations! However, these things aren't mechanisms, they don't seem to be things that LÖVE would do, unless they would be super slow otherwise."

love.audio.getSourceCount, if it's a useful function, might have to remain because the amount of Sources which can play at one time is externally limited.

I think it's cool how the current API has controls similar to a media player, and it certainly would have some benefits, but I'm not really sure if "being like something familiar" is the best design principle to follow here.

In summary, I think that Sources should provide only mechanisms, so I propose that the API be simplified by replacing all of the playback functionality with methods which...

  • Start the sound progressing through time from its current position.
  • Stop the sound progressing through time and keep its current position.
  • Set the position.
  • Set whether or not to loop when it gets to the end.
  • A shortcut method for setting the playback position to the beginning and starting the sound.

I think this would have the benefit of...

  • Simplifying the API.
  • Being more consistent with the philosophy of Lua and LÖVE.
  • It's still as easy as it is currently to play looping music, and actually easier to play sound effects with the "set to the beginning and start" shortcut.

Comments (4)

  1. Bart van Strien

    'startMakingSound', like it can start doing anything else, that name is overly verbose, 'start' is appropriate, but then, start implies a process that goes from start to end, and this is not necessarily the case, hence 'play' is probably better-suited. 'replay' also doesn't match 'start', but in which of your "shortcut" categories does it even fall?

    Regarding setLocation, as mentioned in another ticket, seek/tell are standard names, why not obey to those?

    One point I do see is that the "stopped" state may not need to exist, I mean, you could simply pause it, but then, we'd deviate from standard terminology again, I mean, have you never seen the difference between pause and stop in a media player?

    Now, as for the functions affecting all sources, this can not be trivially reimplemented, because of threading, and other code beyond your control.

    Did I miss anything?

  2. hahawoo reporter

    Oh sorry, startMakingSound etc. were just silly names to make sure they're different from the current names to avoid confusion with them, yeah, something like play would be a lot better. :D

    With "replay" (again, no thought went into that name), I just thought that setting a sound back the start and playing it would be something people would want to do all the time, so there could be a method for that.

    I don't know anything about... anything, but maybe not having a stopped state is actually not so good because of how things work internally? Like, if you pause a Source, it seems to be still active in that getSourceCount includes it.

  3. Log in to comment