sound-moduleFollowing up on last week’s “Goodbye Globals!” tutorial, today we’ll discuss how to manage audio files between scenes in a non-global method. In most apps, certain audio files are needed throughout the app while other audio files are only required in a specific scene. You may also encounter an instance where a particular audio file is loaded in one scene but “overlaps” into another scene, which can complicate the cleanup process.

So how do you manage this? Well, you could load your common sounds in main.lua and provide global handles to them, allowing them to be played from any scene. However, we always encourage you to avoid globals, and fortunately you can use the same method discussed last week to create your own “global” space that isn’t actually global.


Creating the “sfx.lua” Module

The first step is creating a module named sfx.lua to handle your audio. The first few lines are basic enough: we’ll require the myData.lua module as discussed last week (which stores some settings and other data), and then create a namespace to hold some variables.

Keep in mind that the first time a module is require‘d by Lua, its main chunk is executed only once. Things that you want to set/configure at the beginning can be done in the core code, for example:

Here, you’re simply creating handles for the sounds and telling the audio API to load each sound and store the handle in the sfx table. Since Lua lets you index these tables either in dot syntax or bracket syntax — sfx.boomSound or sfx[“boomSound”] — this lets you use the handle names to reference the sounds later. For example, if you require sfx.lua within a scene, you can play an audio file as follows:

Furthermore, you can extend this method to provide additional features. Let’s say you want to set up some additional audio parameters like these:

You can easily create an init function that you can call from the sfx table:

Now, masterVolume is stored in our sfx table. And, if you want to defer these actions until later, you can.

Another interesting trick is to make our own version of audio.play that will honor your app’s sound on/off settings. For example, if you find yourself coding many checks like this…

…we can avoid it by creating our own function called sfx.play. It will work similarly to audio.play but it will honor your sound settings:


Loading Scene-Specific Sounds

Using audio along with Storyboard creates a few challenging situations, including:

  • Loading sounds in the scene’s “main chunk” will cause problems if the sounds are disposed of — which they should be! — because the only way to completely “reload” them is to remove and recreate the scene.
  • The createScene event doesn’t necessarily load every time the scene loads, and loading large sounds may delay transitions.
  • The enterScene event will fire every time, but sounds won’t load until the scene is completely on the screen.

Given that the enterScene and exitScene events happen in pairs, enterScene is probably the best place to load scene-specific sounds. You can then dispose of them using audio.dispose() in the exitScene event (don’t forget to nil out the handle!).

But wait… what if your sound is still playing (and can’t just be abruptly stopped) when your app tries to go to a new scene? In the next scene, you no longer have access to the audio handle, so proper cleanup and disposal is tricky. Fortunately, this can be solved by loading the sound into the sfx table and using an anonymous function to dispose it on the onComplete phase. Consider this code:

Since Lua lets you write “anonymous functions” that onComplete events can call, you can use this method to dispose of an audio file after it’s finished. Notice that we still need to forward-declare the sound handle in the sfx table before it actually gets used.

You of course would play the sound where appropriate for your app.


In Summary…

Hopefully you can see the benefits of handling audio in a module, especially if you’re using Storyboard or another scene manager utility. We can’t emphasize enough that audio must be carefully and properly managed — and disposed of! — to prevent memory leaks and other erratic behavior. Using the module method to “isolate” your audio is one technique of doing exactly this.

Continued here:

Tutorial: Handling Cross-Scene Audio