Question

In my application I need to use a lot of short different mp3s (about 500 items one by one)

So I use SKAction playSoundFileNamed

After ~200 sounds it crashed with 'Failed to load resource - Resource s234.mp3 can not be loaded'. Memory rises to 70mb.

How to avoid this?

What I tried:

  1. recreate sound in every iteration

    SKAction *mySound=[SKAction playSoundFileNamed:aa waitForCompletion:YES];
    
  2. create the one variable in the beggining of .m

    SKAction *mySound;
    

and reuse it in iterations

    mySound=[SKAction playSoundFileNamed:aa waitForCompletion:YES];

2. load all sounds to array once at start

for (int j=0;j<500;j++){
        NSString *aa=[NSString stringWithFormat:@"s%d.mp3", j];
        [item.sounds addObject:[SKAction playSoundFileNamed:aa waitForCompletion:YES]];
    }

...but never changed - it crashes and can't load mp3.

How to clean this memory leaks?

EDITED I also tried to turn off ARC and manually dealloc it every time. Nothing changed.

Was it helpful?

Solution

This little ditty right here will allow you to use a regular old SKAction but customize the playback in code.

https://github.com/pepelkod/iOS-Examples/tree/master/PlaySoundWithVolume

+(SKAction*)playSoundFileNamed:(NSString*)fileName atVolume:(CGFloat)volume waitForCompletion:(BOOL)wait{
// setup audio
NSString*   nameOnly = [fileName stringByDeletingPathExtension];
NSString*   extension = [fileName pathExtension];
NSURL *soundPath = [[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:nameOnly ofType:extension]];
AVAudioPlayer *player = [[AVAudioPlayer alloc]initWithContentsOfURL:soundPath error:NULL];
[player setVolume:volume];
[player prepareToPlay];

SKAction*   playAction = [SKAction runBlock:^{
    [player play];
}];
if(wait == YES){
    SKAction*   waitAction = [SKAction waitForDuration:player.duration];
    SKAction* groupActions = [SKAction group:@[playAction, waitAction]];
    return groupActions;
}
return playAction;

}

OTHER TIPS

You may be better off with a "proper" sound engine. You could use AVAudioPlayer or even better, ObjectAL (already included in Kobold Kit). That way you have more control over preloading and caching the sound files, and treating streaming audio (MP3) differently from short-lived sound effects (usually CAF or WAV).

For very short sound files, say shorter than 5 seconds, MP3 isn't ideal. You should try CAF/WAV instead.

And do consider how much your sound files use in memory. Say each mp3 file is buffered into a 250 KB buffer, times 500, then that uses over 120 MB of memory. Do not look at the size of the mp3 file because it's a compressed format, and will likely be buffered uncompressed.

I think you're running out of file descriptors. What i've found is that every time playSoundFileNamed is run the sound file is opened and never closed... so the file descriptor is not released.

My testing in instruments leads me to believe there's about 250 available file descriptors. And my research tells me that not only file access uses them up but other things too. So I think your ~200 sound files sounds just about right to crash. Whenever a sound file is the next file that Xcode is trying to access I get nothing from the debugger, but when the next file is a png (I have way more art then sounds) it gives,

error = 24 (Too many open files)

I found the error while play testing and switching back to my menu scene from the game play scene every time I lost. So normally it didn't matter but running the init method for the game play scene over and over was piling up these open sound files.

I've searched and searched for a way to close these files but have come up with nothing. I'm thinking of implementing a singleton to run my sound and moving all of the playSoundFileNamed calls into it so they only ever get called once. I think this is a bug from Apple. These files should be closing with ARC. Has anyone found anything similar?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top