Slow start for AVAudioPlayer the first time a sound is played
The delay seems to be related to instantiating AVAudioPlayer for the first time. If I load any audio, run [audioPlayer prepareToPlay] and then immediately release it, the load times for all of my other audio is very close to imperceptible. So now I'm doing that in applicationDidFinishLaunching and everything else runs well.
I can't find anything about this in the docs, but it certainly seems to be the case.
Here's what I've done (in a separate thread):
[audioplayer start]
[audioplayer stop]
self.audioplayer = audioplayer
[audioplayer prepareToPlay] seems to be an asynchronous method, so you can't be sure when it returns if the audio is in fact ready to play.
In my case I call start to actually start playing - this appears to be synchronous. Then I stop it immediately. In the simulator anyway I don't hear any sound coming out from this activity. Now that the sound is "really" ready to play, I assign the local variable to a member variable so code outside the thread has access to it.
I must say I find it somewhat surprising that even on iOS 4 it takes some 2 seconds just to load an audio file that is only 4 seconds in length....
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.
I've taken an alternative approach that works for me. I've seen this technique mentioned elsewhere (tho I don't recall at the moment where that was).
In short, preflight the sound system by loading and playing a short, "blank" sound before you do anything else. The code looks like this for a short mp3 file I preload in my view controller'sviewDidLoad
method that's of .1 second duration:
NSError* error = nil;
NSString* soundfilePath = [[NSBundle mainBundle] pathForResource:@"point1sec" ofType:@"mp3"];
NSURL* soundfileURL = [NSURL fileURLWithPath:soundfilePath];
AVAudioPlayer* player = [[[AVAudioPlayer alloc] initWithContentsOfURL:soundfileURL error:&error] autorelease];
[player play];
You can create your own blank mp3 if you want, or do a google search on "blank mp3s" to find and download one already constructed by somebody else.
I wrote a simple wrapper for AVAudioPlayer that provides a prepareToPlay method that actually works:
https://github.com/nicklockwood/SoundManager
It basically just scans your app for sound files, picks one and plays it at zero volume. That initialises the audio hardware and allows the next sound to play instantly.