Greg Heo

Simple iOS audio playback

Making noise with Audio Services and AVAudioPlayer.

So you want some sound playback in your iOS app? Just something simple, like an alert sound or two…

Note: This post has been slightly tweaked to be ARC-friendly.

Audio Services

The (arguably) simplest way to play a sound is with Audio Services. You can play uncompressed audio files with a maximum of 30 seconds in caf, aif, or wav format. Only one sound can be played at a time.

Here’s the setup code to load the sound file:

// need this include file and the AudioToolbox framework
#import <AudioToolbox/AudioToolbox.h>

// also need an instance variable like so:
// SystemSoundID sound1;

NSURL *soundURL = [[NSBundle mainBundle] URLForResource:@"sample"
                                          withExtension:@"caf"];
AudioServicesCreateSystemSoundID((CFURLRef)soundURL, &sound1);

If you like, you can add a callback function for when the sound starts playing:

AudioServicesAddSystemSoundCompletion(sound1,
                                      NULL,
                                      NULL,
                                      systemAudioCallback,
                                      NULL);

AudioServicesPlaySystemSound will play a specified sound; AudioServicesPlayAlertSound will play the sound and vibrate the device, if applicable:

AudioServicesPlaySystemSound(sound1); // or...
AudioServicesPlayAlertSound(sound1);

Note that these functions will return immediately regardless of how long the sound is. You need to have set up the callback and registered it in advance. Here’s what the callback looks like:

void systemAudioCallback(SystemSoundID soundId, void *clientData)
{
    NSLog(@"System sound finished playing!");
}

Finally, the cleanup:

AudioServicesRemoveSystemSoundCompletion(sound1);    
AudioServicesDisposeSystemSoundID(sound1);

I say Audio Services is arguably simple because it exposes a C API. People who like objects and square brackets may be mystified by the toll-free bridging and Core Foundation use.

Why to use it: It’s simple!

Why not to use it: It’s too simple! And based on C/Core Foundation.

AVAudioPlayer

Next is AVAudioPlayer. AVAudioPlayer offers a friendly Objective-C layer where each sound gets its own AVAudioPlayer instance. Audio files can be a wider variety of types, such as compressed caf or mp3.

// need this include file and the AVFoundation framework
#import <AVFoundation/AVFoundation.h>

// also need an instance variable like so:
// AVAudioPlayer *avSound

NSURL *soundURL = [[NSBundle mainBundle] URLForResource:@"sample"
                                          withExtension:@"caf"];
avSound = [[AVAudioPlayer alloc] 
           initWithContentsOfURL:soundURL error:nil];

[avSound play];

// later...

if ([avSound isPlaying])
    [avSound stop];

The all-important cleanup, if you’re not using ARC:

[avSound release];

With AVAudioPlayer, you can set up multiple instances and have playback control over each sound individually. There’s also much more control over playback settings (volume, panning) and control (play, pause, seek to a point in the audio file).

AVAudioPlayer has a set of delegate methods to notify you when playback has finished or in the case of interruptions. These follow the usual delegate/protocol patterns found elsewhere in Cocoa.

You’ll also get a bit of a delay the first time you play a sound since iOS does a little lazy loading. You can send the prepareToPlay message to an AVAudioPlayer instance to speed up the eventual play call.

Why to use it: It’s a simple yet powerful object-based system.

Why not to use it: Latency.

Next

There’s still some pretty noticeable latency when using AVAudioPlayer; it might be OK for simple sound effects, but there’s always OpenAL for more precise and timely playback.