This is the code I’m using now, and it’s not working (nothing happens when I press the button that calls this method). Previously, I had a property for audioPlayer and it worked (all the audioPlayers below were self.audioPlayer obviously). The problem was that when I tried to play the sound twice, it would end the first sound playing.
This is no good because I’m making a soundboard and want sounds to be able to overlap. I thought I could just make audioPlayer a local variable instead of a property and all would be ok but now the sound doesn’t work at all and I can’t figure out why. In all tutorials I’ve found for AVAudioPlayer, a property is made but no one explains why. If this can’t work, what alternatives do I have to make sounds that can overlap?
- (void)loadSound:(NSString *)sound ofType:(NSString *)type withDelegate:(BOOL)delegate {
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]
pathForResource:sound
ofType:type]];
AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
if (delegate) audioPlayer.delegate = self;
[audioPlayer prepareToPlay];
[audioPlayer play];
}
The reason you need a property or ivar is for the strong reference it provides. When using ARC any object without a strong pointer to it is fair game for deallocation, and in fact that is what you are seeing.
You are also correct that an
AVAudioPlayerstrong pointer will only allow one audio player to be referenced at a time.The solution, if you choose to continue to use
AVAudioPlayeris to use some sort of collection object to hold strong reference to all the player instances. You could use anNSMutableArrayas shown here:Edit I tweaked the code slightly so method that plays the sound takes an
NSStringsoundNameparameter.Generally an
AVAudioPlayeris overkill for an sound-effect/soundboard application. For quick sound “drops” you will likely find the audio toolbox framework, as outlined in my answer to this question.It can only play one
SystemSoundIDat a time. So for example if you have soundOne and soundTwo. You can play soundOne while soundTwo is playing, but you cannot play more than one instance of either sound at a time.Best is opinion.
If you need two instances of the same sound to play at the same time, then I would say the code posted in this answer would be the code to use. Due to the fact that each overlapping instance of the same sound requires creating a new resource, code like this with its
audioPlayerDidFinishPlaying:is much more manageable(the memory can easily be reclaimed).If overlapping instances of the same sound are not a deal-breaker then I think just using
AudioServicesCreateSystemSoundID()to create one instance of each sound is more efficient.I definitely would not try to manage the creation of and disposal of
SystemSoundIDs with each press of a button. That would go wrong in a hurry. In that instanceAVAudioPlayeris the clear winner on just maintainability alone.