Reputation: 1889
Every once in a while, AVAudioPlayer
crashes when I call its play
method.
My app is a game, with background music and many sound effects. Originally, AVAudioPlayer
was causing it to run very slowly. However, as it seems to be the simplest sound API to work with, (especially for a 2D game like mine) I simply decided to play the sounds concurrently using dispatch_async
.
This resulted in a considerable improvement, with no noticeable performance toll taken on the main thread of the game. But it crashes the game. Every 5 or 10 minutes, while playing, an exception appears.
I don't understand why this is happening. Is something being released internally within the player before it can be run concurrently? Or is it something else altogether?
EDIT:
This is a snippet of code I use to play sounds when the player collects coins in game. I use 4 AVAudioPlayers
so the sounds can overlap:
if([coinCollectPlayer isPlaying]){
if([coinCollectPlayer2 isPlaying]){
if([coinCollectPlayer3 isPlaying]){
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
[coinCollectPlayer4 play];});
}
else dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
[coinCollectPlayer3 play];});
}
else dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
[coinCollectPlayer2 play];});
}
else
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){[coinCollectPlayer play];});
Upvotes: 1
Views: 332
Reputation: 535138
The reason you're crashing occasionally is that this structure is unsound:
if([coinCollectPlayer isPlaying]){
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
[coinCollectPlayer play];
});
}
You are checking isPlaying
on the main thread. So how do you know that between then and when you say play
on a background thread, you haven't already said play
on a background thread? You don't. Think of it like this:
if([coinCollectPlayer isPlaying]){
// GREAT BIG HOLE HERE YOU CAN DRIVE A TRUCK THROUGH
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
[coinCollectPlayer play];
});
}
You need to use a consistent thread for all communication with your audio players, and either you must use locking / synchronize to enforce coherency or you must use a serial queue (which DISPATCH_QUEUE_PRIORITY_DEFAULT
is not).
Having said all that, I think what you're doing is just wrong. I don't believe that AVAudioPlayer needs to or should be used on a background thread.
What I suggest you try is to use every AVAudioPlayer once. In other words, to play a sound, make an AVAudioPlayer, play a sound, and throw the whole thing away.
Upvotes: 1