iPhone AVAudioPlayer app freeze on first play
I'm using AVAudioPlayer from iOS SDK for playing short sounds on each click in tableView rows.
I have manually made @selector
on button in each row that fires up method playSound:(id)receiver {}
. From receiver I get sound url so I can play it up.
This method looks like that:
- (void)playSound:(id)sender {
[audioPlayer prepareToPlay];
UIButton *audioButton = (UIButton *)sender;
[audioButton setImage:[UIImage imageNamed:@"sound_preview.png"] forState:UIControlStateNormal];
NSString *soundUrl = [[listOfItems objectForKey:[NSString stringWithFormat:@"%i",currentPlayingIndex]] objectForKey:@"sound_url"];
//here I get mp3 file from http url via NSRequest in NSData
NSData *soundData = [sharedAppSettingsController getSoundUrl:defaultDictionaryID uri:soundUrl];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc] initWithData:soundData error:&error];
audioPlayer.numberOfLoops = 0;
if (error) {
NSLog(@"Error: %@",[error description]);
}
else {
audioPlayer.delegate = self;
[audioPlayer play];
}
}
Everything works fine except for the first play of some sound. The application freezes for about 2 seconds and than sound is played. Second and every other sound play works just right after click on sound button.
I wonder why the开发者_如何学JAVAre is that about 2 seconds freeze on first play when application starts?
From your code snippet, audioPlayer
must be an ivar, right?
At the top of the method, you invoke -prepareToPlay
on the existing audioPlayer
instance (which could be nil, at least on the first pass).
Later in the method, you replace this existing audio player with a brand new AVAudioPlayer instance. The previous -prepareToPlay
is wasted. And you're leaking memory with each new AVAudioPlayer.
Instead of caching sound data or URLs, I would try creating a cache of AVAudioPlayer objects, one for each sound. In your -playSound:
method, get a reference to the appropriate audio player for the table row, and -play
.
You can use -tableView:cellForRowAtIndexPath:
as the appropriate point to get the AVAudioPlayer instance for that row, maybe lazily creating the instance and caching it there as well.
You can try -tableView:willDisplayCell:forRowAtIndexPath:
as the point where you would invoke -prepareToPlay
on the row's AVAudioPlayer instance.
Or you could just do the prepare in -tableView:cellForRowAtIndexPath:
. Experiment and see which works best.
Check whether you are getting data asynchronously in function..
NSData *soundData = [sharedAppSettingsController getSoundUrl:defaultDictionaryID uri:soundUrl];
If you are getting asynchronously the execution will be blocked until it will get data.
If your audio is less than 30 seconds long in length and is in linear PCM or IMA4 format, and is packaged as a .caf, .wav, or .aiff you can use system sounds:
Import the AudioToolbox Framework
In your .h file create this variable:
SystemSoundID mySound;
In your .m file implement it in your init method:
-(id)init{
if (self) {
//Get path of VICTORY.WAV <-- the sound file in your bundle
NSString* soundPath = [[NSBundle mainBundle] pathForResource:@"VICTORY" ofType:@"WAV"];
//If the file is in the bundle
if (soundPath) {
//Create a file URL with this path
NSURL* soundURL = [NSURL fileURLWithPath:soundPath];
//Register sound file located at that URL as a system sound
OSStatus err = AudioServicesCreateSystemSoundID((CFURLRef)soundURL, &mySound);
if (err != kAudioServicesNoError) {
NSLog(@"Could not load %@, error code: %ld", soundURL, err);
}
}
}
return self;
}
In your IBAction method you call the sound with this:
AudioServicesPlaySystemSound(mySound);
This works for me, plays the sound pretty damn close to when the button is pressed. Hope this helps you.
This sometimes happens in the simulator for me too. Everything seems to work fine on the device. Have you tested on actual hardware?
精彩评论