Reputation: 8138
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 there is that about 2 seconds freeze on first play when application starts?
Upvotes: 3
Views: 9842
Reputation: 2273
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.
Upvotes: 1
Reputation: 4185
This sometimes happens in the simulator for me too. Everything seems to work fine on the device. Have you tested on actual hardware?
Upvotes: 0
Reputation: 2049
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.
Upvotes: 2
Reputation: 5117
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.
Upvotes: 2