audio.play() leaking memory

My game is about ready for release (your basic space shooter) and I’ve spent the better part of the last 2-3 weeks trying to find my memory leaks.

Today just on a whim, I turned off my sound effects and I can play my game for levels without seeing a blip of a leak.

Now my audio isn’t that complex.

I have my own playSound function

local function playSound(sound)  
 if soundOn then  
 audio.play( sound )  
 end  
end  

which just wraps a check around audio.play so I will return if the player has turned off effects. All of these sounds are loaded at the beginning via handle = audio.loadSound(“sound.wav”) at the end of my main.lua file before firing up any event listeners and calling my starting function.

I load up about 15 sounds this way (booms, weapons firing, shields getting hit). Many of these fire every round and I don’t leak. But when I die or kill a boss, if I have my sound effects on, I get 512 byte leaks coming from the Audio Mixer.

Just before I load up my sounds, I call this block of code:

audio.reserveChannels(5)  
local masterVolume = audio.getVolume()  
-- print("volume " .. masterVolume)  
audio.setVolume(0.80, { channel = 1 }) -- music track  
audio.setVolume(0.66, { channel = 2 }) -- boss sound  
audio.setVolume(1.0, { channel = 3 }) -- voice overs  
audio.setVolume(1.0, { channel = 4 }) -- alien voice  
audio.setVolume(0.25, { channel = 5 }) -- weak explosion  

I use audio.loadStream() to load in one of 4 sound tracks that I rotate each level and play on reserved channel 1. Initially I thought this could be the source of my leak, but I let players turn sound and music off separately and while testing it, I don’t leak while music is playing, only with sound effects are.

While the boss is on the screen, I loop some evil mood music. I commented it out and it didn’t have any effect on the leaks.

Like my playSound() function, I have a playVoice() which plays audio handles (preloaded like the SFX) but on a specific channel so I can tweek the volume of the sounds.

Channel 4 is currently not being used. The alien voice tracks added a lot of weight to the package and really didn’t add to the game.

Channel 5 is there to take one of my explosion sounds and make it be much quieter. I should find another sound I know. But I commented it out too.

I eventually got down to a point of only commenting out the audio.play function in the playSound() function above. When its not commented out, I leak. When its commented out or I have my sfx turned off, I don’t leak.

The weird thing is I fire off a lot of sounds each round and it only seems to leak after a level change and then I get spotty leaks afterwards.

Has anyone else seen leaks coming from the Audio subssytem (and I get a leak while I’m still loading which I got without my main.lua file empty that is charged to the Audio Mixer)

There has to be something in my code but when commenting out audio.play stops the leaks… I would think that others would be reporting these leaks if it were in Corona SDK (build 484).

Thoughts? I really want to release the game this week, but I’m afraid that Apple won’t approve it with these rampant leaks.

Thanks
Rob
[import]uid: 19626 topic_id: 9783 reply_id: 309783[/import]

I had a leak from the audio on my game which I got rid of by removing the audio used in each scene. Whenever switching scenes, I included this for each audio instance:

audio.play( tapSound, { channel=2, onComplete=audio.stop( 2 ) } )  
audio.dispose( 2 )  
tapSound = nil  
director:changeScene("menu")  

Hope that helps. [import]uid: 14032 topic_id: 9783 reply_id: 35656[/import]

I know Corona suggests unloading sounds when you’re done and in a game that has different sound needs for each level I can see doing that, but I use the same sounds over and over and in fact I could start playing a sound before I done with an earlier instance so I’m not sure I could use an onComplete anyway.

I’ll see if I can make it happen.
[import]uid: 19626 topic_id: 9783 reply_id: 35657[/import]

I put in some debug prints to find out what channel the sound was playing on and frequently I’m in the high 20’s and even saw it hit a channel #32. I wonder if this could be the source of my leaks?
[import]uid: 19626 topic_id: 9783 reply_id: 35778[/import]

Well I seem to have beat this demon. I leaked 300 bytes of memory in 15 minutes of play where as I was leaking in the 5-10k previously.

My game at its max could play around 17 sounds on non-reserved channels and thats if all possible enemies were on screen and they all blew up at the same time, very unlikely. Remember I reserved 5. So the only way I should ever see it hit channels above 22 would be if sounds were still playing when more queued up and it was taking too long to actually finish.

So there were two fixes. Instead of letting audio.play() just do its thing I did this instead:

local availableChannel = audio.findFreeChannel(6)  
if availableChannel \> 0 then  
 local channel = audio.play( sound , { channel=availableChannel })  
end  

It’s possible that audio.play() thinks it has 32 channels to work with when it doesn’t because of the 5 reserved channels.

The other step was to edit all my sounds and trim them to remove any dead (or too quiet) space from the ends that made the sounds play longer than they should. I also converted everything to mono 11Khz to make the smaller and lighter. Everything is also now a WAV files (except the soundtracks) and they are now local to the main.lua instead of being global variables.

Maybe this will help others in the future. [import]uid: 19626 topic_id: 9783 reply_id: 35908[/import]

Nice, thanks for sharing Rob!

Omni Blaster is really coming along. [import]uid: 14598 topic_id: 9783 reply_id: 35919[/import]

How are you determining if there is a leak?
If you submit a simple reproducible test case, we can try to track down any leak.

[import]uid: 7563 topic_id: 9783 reply_id: 37729[/import]

I was using Instruments “Leaks” tool to watch for leaks. At seemingly unpredictable times, I would get a leak. They tended to happen towards the end of a level but sometimes at the beginning of a wave I’d see leaks. Leaks would frequently report “Audio Mixer” as the source of the leak.

I turned off my sound effects, but left my music playing and ran it against Instruments and there were ZERO leaks other than the initial leak that I can get by compiling an empty main.lua file! I turned my sound effects back on, started leaking.

Eventually I got to a point where the only thing I commented out was audio.play(). Commented out, no leaks, executing, leaks.

But this confused me because I have a lot of sound effects going on and if there was a toolkit leak I’d see it more often.

I put in a print to print the channel number and saw I was getting non-consecutive numbers back and frequently channels 25+ being returned which is excessively high.

I do have 5 reserved channels and audio.play for the most part started at channel 6 as expected. When I put in code to get a channel number and not let audio.play() determine it and if I get a channel number over 25 I throw the sound away. My leaks ended.

So it seems, though I can’t prove it at the moment, that when you have reserved channels, audio.play may still be trying to get up to 32 channels instead of 32 - NumberOfReservedChannels or perhaps there is a fence post error that’s causing the leak.

I’m working on my Windows Box tonight on a photo project, so I’ll try and whip up a test program that leaks and submit it, but I bet if you reserved X channels, and then queue up 50 sounds to play you will see the leak.

Rob [import]uid: 19626 topic_id: 9783 reply_id: 37731[/import]

Here ya go!

--  
-- Audio Test  
-- Copyright © 2011 Omnigeek Media. All Rights Reserveed.  
-- Rob Miracle  
-- http://omnigeekmedia.com  
--  
  
local sounds = {}  
sounds[1] = audio.loadSound("explosion2.wav")  
sounds[2] = audio.loadSound("missile.wav")  
sounds[3] = audio.loadSound("scifi048.wav")  
  
--  
-- Does NOT Leak (well there is an inital leak that I get with an empty main.lua file....)  
--  
local function playSound2(sound)  
 local availableChannel = audio.findFreeChannel(6)  
 print("availableChannel = " .. availableChannel)  
 if availableChannel \> 0 then  
 local channel = audio.play( sound , { channel=availableChannel })  
 print("Sound played on channel " .. channel)  
 else  
 print("Skipped sound")  
 end  
end  
  
--  
-- Leaks like a siv  
--  
local function playSound(sound)  
 local channel = audio.play( sound )  
 print("Channel: " .. channel)  
end  
  
local function randomSound()  
 local i = math.random(3)  
 print("Sound: " .. i)  
 -- Leaks  
 playSound(sounds[i])  
 -- Doesn't leak  
 -- playSound2(sounds[i])  
end  
  
local function blockOfSounds()  
 local i  
 for i = 1, 100 do  
 local delay = math.random(5000)  
 print("playing sound #" .. i .. " in " .. delay .. " milliseconds")  
 timer.performWithDelay(delay,randomSound)  
 end  
end  
  
audio.reserveChannels(5)  
local masterVolume = audio.getVolume()  
-- print("volume " .. masterVolume)  
audio.setVolume(0.80, { channel = 1 }) -- music track  
audio.setVolume(0.66, { channel = 2 }) -- boss sound  
audio.setVolume(1.0, { channel = 3 }) -- voice overs  
audio.setVolume(1.0, { channel = 4 }) -- alien voice  
audio.setVolume(0.25, { channel = 5 }) -- weak explosion  
  
local i  
for i = 1, 50 do  
 local delay = (i - 1) \* 5000  
 timer.performWithDelay(delay,blockOfSounds)  
end  

I can try and get you the sound files, but I suspect that any short 1-4 second sound will work.
[import]uid: 19626 topic_id: 9783 reply_id: 37803[/import]

Okay, there seem to be 2 different leaks, one ours, one Apple’s. I filed bug 5780 in our system. (In the future, please feel free to submit to our bug reporter directly so we make sure we get all the relevant information.) I’m assuming you only see our bug in the iOS version and not the Mac because the bug I found is specific to only our on-device code.

I just fixed our leak and should be in the next daily build. There was a leak when creating internal error strings. This is why you only hit it when you overflowed the number of channels.

There seems to be a different initialization leak that is in the “Audio Toolbox” inside AU3DMixerEmbeddedInputElement::Initialize(). Audio Toolbox is Apple’s library. It is my belief that this is an Apple memory leak caused when we create an OpenAL context. This isn’t something we can really do anything about and Apple needs to fix. It seems to happen only once at startup, so it probably isn’t terrible. I filed a bug with Apple rdar://9487538 and mirrored at Open Radar:
http://openradar.appspot.com/radar?id=1197409

I recommend everybody else concerned about this should file duplicate bugs with Apple. (They measure how many people are affected by it to prioritize bugs.) Just refer to my original bug. You can say you use Corona which uses ALmixer.

[import]uid: 7563 topic_id: 9783 reply_id: 37825[/import]

Glad I was able to help!!!

As for the one leak, I had heard that was Apple’s problem and since it only happens once, its not a real problem. Leaks are only a problem when they continue to leak over time.

Now to figure out how to get more people to know more about my game…
[import]uid: 19626 topic_id: 9783 reply_id: 37827[/import]