views:

82

answers:

3

I have a library of 16 short sound clips I need to be able to play in quick succession. I realized that creating and preparing AVAudioPlayer objects in real time was too much to ask of the iPhone.

So instead, during my app's initialization, I am pre-creating a series of AVAudioPlayers so that each one is basically pre-loaded with one of my 16 sounds, so they're ready to be played at any time.

The problem is, to keep this clean, I would like to store the references for these 16 AVAudioPlayers in an NSMutableArray, so I can easily get at them just by knowing their array location. However, the way I'm doing it is crashing the simulator w/no error messages in the log:

Here is how I'm currently setting up the array of AVAudioPlayer references:

// (soundPlayers is an NSMutableArray instance var)
soundPlayers = [NSMutableArray arrayWithCapacity:(NSUInteger)16];                                       

for ( int i = 0; i < 16; i++ ) {
    NSString *soundName = [NSString stringWithFormat:@"sound-%d", i];
    NSString *soundPath = [[NSBundle mainBundle] pathForResource:soundName ofType:@"mp3"];
    NSURL *soundFile = [[NSURL alloc] initFileURLWithPath:soundPath];   
    AVAudioPlayer *p = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFile error:nil];
    [soundFile release];
    [p prepareToPlay];
    [soundPlayers addObject:(id)p];
    [p release];
}

Then later I try to load, say, sound #8 and play it back:

// (soundPlayer is an AVAudioPlayer instance var)
self.soundPlayer = [soundPlayers objectAtIndex:(NSUInteger)8];
[soundPlayer play]; 

Any ideas? Also does anyone know if any of the slick debugging tools that come with XCode would be useful for this type of problem?

A: 

I'm just guessing here, but there might be a problem with the capacity of the array.

Try: soundPlayers = [[NSMutableArray alloc] init]; instead.

This isn't likely to be a cause of the crash, but try NSLogging self.soundPlayer before calling -play on it to make sure that its not nil. And another minor little thing: you don't need to typecast p when adding it to the array. Just call [soundPlayers addObject:p] without (id).

I'm not sure of the exact cause of your crash, just going through the code and pointing out what doesn't look right. Hope this helps!

macatomy
NSMutableArray's grow as needed, capacity isn't the issue.
progrmr
Right- I don't think the capacity is the problem, but I switched to the above initialization code just in case. Also got rid of the typecasting, thanks.
todd412
This did it! It turned out I did need to initialize the array the way you suggested here for it to work. The other critical change turned out to be in the line where I pull in the AVAudioPlayer reference from the array-- it needed an extra 'self.' I believe to force it to use the synthesized 'getter' method, as in: self.soundPlayer = [self.soundPlayers objectAtIndex:(NSUInteger)8]; - Thanks to everyone for your help!
todd412
A: 

It looks OK to me except perhaps you shouldn't call prepareToPlay before you put them into the array.

It's probably better to do this when you actually are going to play them. The description of that method says it preloads buffers and acquires the audio hardware for playback. Doing it 16 times at once may be too much.

EDIT - you should also add error checking, your missing results that might tell you something is wrong, initWithContentsOfURL:outError: returns an error message. Take advantage of that:

NSError* error = nil;
AVAudioPlayer *p = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFile error:&error];
if (error) {
    NSLog(@"ERROR: %@, URL=%@", error.localizedDescription, soundFile);
}
progrmr
I think preparing them just before playing does make more sense, I changed that.I also implemented your error checking suggestion- thanks for that. However, it didn't show any errors, so it appears the AVAudioPlayers are getting created successfully.
todd412
+1  A: 

It is most likely the case that your soundPlayers mutable array is being released at some point since you aren't retaining it. I would ensure your property for the array is set to retain and do what Macatomy previously stated:

self.soundPlayers = [[NSMutableArray alloc] init];

Or even initWithCapacity:

raidfive
Good point. I added the following code to the header file to make sure the arrray is getting retained, but still no luck. @property (nonatomic, retain) NSMutableArray *soundPlayers;
todd412