Each section in this chapter provides example code for implementing a common audio session task. Typically, you would place most of your audio session code in an iPhone view controller class, as is done in the AddMusic sample.
To understand how these tasks relate to each other and to your application’s life cycle, see the previous chapter, “Configuring the Audio Session.”
In production code, always check for errors when making API calls. For an example, see Listing 7-4. Most examples in this chapter do not illustrate error checking.
If you use AV Foundation delegate methods for handling audio interruptions, you can rely on implicit audio session initialization as described in “Initializing the Audio Session.”
You can instead write a C callback function to handle audio interruptions. To attach that code to the audio session, you must initialize the session explicitly using the AudioSessionInitialize
function. Do this just once, during application launch.
Note: Your application does not instantiate its audio session. Instead, iOS provides a singleton audio session object to your application, which is available when your application has finished launching.
Listing 7-1 illustrates how to initialize your audio session and register your interruption listener callback function.
Listing 7-1 Initializing a session and registering an interruption callback
AudioSessionInitialize ( |
NULL, // 1 |
NULL, // 2 |
interruptionListenerCallback, // 3 |
userData // 4 |
); |
Here’s how this code works:
Use NULL
to use the default (main) run loop.
Use NULL
to use the default run loop mode.
A reference to your interruption listener callback function. See “Responding to Audio Session Interruptions” for a description of how to write and use an interruption callback function.
Data you intend to be passed to your interruption listener callback function when the audio session object invokes it.
Activate your audio session as shown in Listing 7-2.
Listing 7-2 Activating an audio session using the AV Foundation framework
NSError *activationError = nil; |
[[AVAudioSession sharedInstance] setActive: YES error: &activationError]; |
In the rare instance that you want to deactivate your audio session, pass NO
to the setActive parameter.
If you prefer to use the C-based Audio Session Services interface, activate your audio session by calling the AudioSessionSetActive
function. Listing 7-3 shows how.
Listing 7-3 Activating an audio session using Audio Session Services
OSStatus activationResult = NULL; |
result = AudioSessionSetActive (true); |
In the rare instance that you want to deactivate your audio session, pass false
to the AudioSessionSetActive
function.
To set the audio session category, call the setCategory:error:
method as shown in Listing 7-4. For descriptions of all the categories, refer to “Audio Session Categories.”
Listing 7-4 Setting the audio session category using the AV Foundation framework
NSError *setCategoryError = nil; |
[[AVAudioSession sharedInstance] |
setCategory: AVAudioSessionCategoryAmbient |
error: &setCategoryError]; |
if (setCategoryError) { /* handle the error condition */ } |
Listing 7-5 shows the equivalent operation using the C-based Audio Session Services interface.
Listing 7-5 Setting the audio session category using Audio Session Services
UInt32 sessionCategory = kAudioSessionCategory_AmbientSound; // 1 |
AudioSessionSetProperty ( |
kAudioSessionProperty_AudioCategory, // 2 |
sizeof (sessionCategory), // 3 |
&sessionCategory // 4 |
); |
Here’s how this code works:
Defines a new variable of type UInt32
and initializes it with the identifier for the category you want to apply to the audio session.
The identifier, or key, for the audio session property you want to set. Other audio session properties are described in Audio Session Services Property Identifiers
.
The size, in bytes, of the property value that you are applying.
The category you want to apply to the audio session.
When a user launches your app, sound may already be playing on the device. This is especially salient if your app is a game. For example, the iPod may be playing music, or Safari may be streaming audio. In such a case, “Using Sound” in iPhone Human Interface Guidelines advises you to assume that the user expects the other audio to continue as they play your game.
In such a case, use the Ambient category, which supports mixing with other audio; and do not play your usual background soundtrack.
Listing 7-6 shows how to check whether or not other audio is playing, and to then appropriately set the category for a typical game app. Perform this check after initializing the audio session.
Listing 7-6 Checking if other audio is playing
UInt32 otherAudioIsPlaying; // 1 |
UInt32 propertySize = sizeof (otherAudioIsPlaying); |
AudioSessionGetProperty ( // 2 |
kAudioSessionProperty_OtherAudioIsPlaying, |
&propertySize, |
&otherAudioIsPlaying |
); |
if (otherAudioIsPlaying) { // 3 |
[[AVAudioSession sharedInstance] |
setCategory: AVAudioSessionCategoryAmbient |
error: nil]; |
} else { |
[[AVAudioSession sharedInstance] |
setCategory: AVAudioSessionCategorySoloAmbient |
error: nil]; |
} |
Here’s how this code works:
Defines a variable to hold the value of the audio session’s kAudioSessionProperty_OtherAudioIsPlaying
property.
Obtains the value of the kAudioSessionProperty_OtherAudioIsPlaying
property, assigning it to the otherAudioIsPlaying
variable.
If other audio is playing when your application launches, assigns the AVAudioSessionCategoryAmbient
category to your audio session; otherwise, assigns the AVAudioSessionCategorySoloAmbient
category.
Two playback categories that normally silence other audio on the device can be modified to support mixing. These are the AVAudioSessionCategoryPlayback
(or the equivalent kAudioSessionCategory_MediaPlayback
) category, and the AVAudioSessionCategoryPlayAndRecord
(or the equivalent kAudioSessionCategory_PlayAndRecord
) category. To allow mixing, you set a particular audio session property. Listing 7-7 shows how.
Listing 7-7 Overriding audio mixing behavior
OSStatus propertySetError = 0; |
UInt32 allowMixing = true; |
propertySetError = AudioSessionSetProperty ( |
kAudioSessionProperty_OverrideCategoryMixWithOthers, // 1 |
sizeof (allowMixing), // 2 |
&allowMixing // 3 |
); |
Here’s how this code works:
The identifier, or key, for the audio session property you want to set. Other audio session properties are described in Audio Session Services Property Identifiers
.
The size, in bytes, of the property value that you are applying.
The value to apply to the property.
This property has a value of false
(0) by default. When the audio session category changes, such as during an interruption, the value of this property reverts to false
. To regain mixing behavior you must then set this property again.
Always check to see if setting this property succeeds or fails, and react appropriately; behavior may change in future releases of iOS.
If you want to ensure that your audio continues when the screen locks and when the Ring/Silent switch is in the “silent” position, you need to assign an appropriate category to your audio session. You may also need to specially configure audio units, if using them for playback, as described in “Configuring Audio Units to Continue Playing When the Screen Locks.”
You cannot rely on the default audio session, whose category (starting in iOS 2.2) is AVAudioSessionCategorySoloAmbient
(or equivalently, kAudioSessionCategory_SoloAmbientSound
).
Use the AVAudioSessionCategoryPlayback
(or the equivalent kAudioSessionCategory_MediaPlayback
) category. Assign this category to your audio session as described in “Setting the Category.” For a complete list of audio session categories and their characteristics, see “Audio Session Categories.”
If you are using audio units for playback and want your audio to continue when the screen locks, you may need to perform some additional configuration to support that. This section briefly explains why, and shows how to do it.
If the system’s default audio unit latency of 23 ms (for 44.1 kHz audio) works well for your application, you can take advantage of the lower playback power consumption that is available when the screen locks. To do so, you must perform some configuration on the audio units you are using, as described in “Optimizing for Minimal Power Consumption.”
If your application demands shorter audio latency, you instead perform some configuration using the audio session object, as described in “Optimizing for Low Latency.”
Apple recommends that you optimize for minimal power consumption, unless your application has special needs that demand lower latency, such as VOIP.
To optimize for minimal audio playback power consumption, you must adjust the kAudioUnitProperty_MaximumFramesPerSlice
property value in the audio units you’re using, as described here.
By default, the system invokes the I/O unit’s render callback with a slice size of 1,024 frames. (See “slice” in Core Audio Glossary, and the kAudioUnitProperty_MaximumFramesPerSlice
property, to learn about slices.) This corresponds to a latency of about 23 ms for 44.1 kHz audio. A latency of 0.02 seconds—while not extremely short—works very well for most audio uses, such as media playback, Internet streaming, and games.
When the screen locks, the system conserves power by increasing the I/O unit’s slice size to 4096 frames—equivalent to about 93 ms for 44.1 kHz audio. Specifically, with a larger slice size, the system invokes the I/O unit’s render callback less frequently, conserving power. Longer latency while the screen is locked is not a problem, because there is no user interaction. The system manages the slice size for I/O units. Do not set I/O unit slice size yourself.
The system does not, however, adjust the maximum slice size property for other audio units involved in playback. If you are feeding audio to an I/O unit from one or more other audio units, it is up to you to ensure those audio units can handle the larger slice size that is used on screen lock. If you don’t do this, audio stops or stutters on screen lock.
For each audio unit involved in playback—other than the I/O unit—set the kAudioUnitProperty_MaximumFramesPerSlice
property to a value of 4096. The time to do this is when configuring your audio units—before initializing them. If you are using an audio processing graph, do this when configuring the graph—prior to initializing it.
Important: When optimizing for minimal power consumption, do not set a preferred I/O hardware buffer duration—for if you do, playback cannot go into a lower-power mode on screen lock.
Listing 7-8 shows how to support lower-power mode by setting an audio unit’s kAudioUnitProperty_MaximumFramesPerSlice
property, using the AudioUnitSetProperty
function.
Listing 7-8 Setting an audio unit’s maximum-frames-per-slice property
UInt32 maximumFramesPerSlice = 4096; |
AudioUnitSetProperty ( |
mixerUnit, |
kAudioUnitProperty_MaximumFramesPerSlice, |
kAudioUnitScope_Global, |
0, // global scope always uses element 0 |
&maximumFramesPerSlice, |
sizeof (maximumFramesPerSlice) |
); |
For an example of how to find, instantiate, and configure audio units, see the iPhoneMultichannelMixerTest sample.
The system-provided render latency of 23 ms (for 44.1 kHz audio) is too long for certain time-critical applications, such as voice over IP (VOIP). You can reduce latency by setting a preferred I/O hardware buffer duration—see “Optimizing for Device Hardware” and “Specifying Preferred Hardware I/O Buffer Duration.” However, after you set the I/O buffer duration, the system uses that value until you explicitly change it. In other words, audio playback will not change to a low-power mode upon screen lock.
When using the “play and record” category—which indicates your intention to use both audio input and output—output audio normally goes to the receiver. You can redirect this audio to the speaker at the bottom of the phone in two ways:
Using a category routing override. An override reverts to the receiver upon interruption and when a user changes the audio route such as by plugging in or unplugging a headset. If you want to continue using the speaker, you must again invoke the override.
Changing the default output route. The new output route remains in effect unless you change the audio session category. This option is available starting in iOS 3.1.
Listing 7-9 shows how to perform a category routing override. Before executing this code, you would set the audio session category to AVAudioSessionCategoryRecord
(or to the equivalent kAudioSessionCategory_PlayAndRecord
), as described in “Setting the Category.”
Listing 7-9 Overriding the output audio route
UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker; // 1 |
AudioSessionSetProperty ( |
kAudioSessionProperty_OverrideAudioRoute, // 2 |
sizeof (audioRouteOverride), // 3 |
&audioRouteOverride // 4 |
); |
Here’s how this code works:
The identifiers you can use for redirecting output audio are described in Audio Session Category Route Overrides
.
The identifier, or key, for the audio session property you want to set.
The size, in bytes, of the property value that you are applying.
The audio route override that you want to apply to the audio session’s category.
If you want to redirect output audio in a more permanent fashion, change the default output as shown in Listing 7-10. Again, you would first set the audio session category to “play and record.
Listing 7-10 Changing the default output audio route
UInt32 doChangeDefaultRoute = 1; |
AudioSessionSetProperty ( |
kAudioSessionProperty_OverrideCategoryDefaultToSpeaker, |
sizeof (doChangeDefaultRoute), |
&doChangeDefaultRoute |
); |
Starting in iOS 3.1, you can modify the recording audio session categories to allow audio input from a paired Bluetooth device. Listing 7-11 shows how.
Listing 7-11 Modifying a recording category to support Bluetooth input
// First, set an audio session category that allows input. Use the |
// AVAudioSessionCategoryRecord or the equivalent kAudioSessionCategory_Record |
// category, or the AVAudioSessionCategoryPlayAndRecord or the equivalent |
// kAudioSessionCategory_PlayAndRecord category. Then proceed as shown here: |
UInt32 allowBluetoothInput = 1; |
AudioSessionSetProperty ( |
kAudioSessionProperty_OverrideCategoryEnableBluetoothInput, |
sizeof (allowBluetoothInput), |
&allowBluetoothInput |
); |
The technique is parallel to the previous code example, “Redirecting Output Audio.” Before executing the code in Listing 7-11, you would set an audio session category that allows input. Use the AVAudioSessionCategoryRecord
(or the equivalent kAudioSessionCategory_RecordAudio
) category, or the AVAudioSessionCategoryPlayAndRecord
(or the equivalent kAudioSessionCategory_PlayAndRecord
) category.
You can use the AV Foundation framework to respond to interruptions. To do so, implement an “interruption started” and an “interruption ended” delegate method. Alternatively, you can use a C callback function, but doing so is more involved. Using a callback, you:
Define methods to be invoked upon interruption
Define an interruption listener callback function
Register your callback with the audio session
This section describes using the AVAudioSessionDelegate
protocol, suitable for when you are doing audio work with Audio Queue Services, OpenAL, or the I/O audio unit. The next section describes how to instead use the interruption delegate methods provided directly by audio players (see AVAudioPlayerDelegate Protocol Reference) and audio recorders (see AVAudioRecorderDelegate Protocol Reference).
Listing 7-12 shows simple implementations of the “begin” and “end” interruption methods from the AVAudioSessionDelegate
protocol, described in AVAudioSessionDelegate Protocol Reference. When your “begin” delegate gets called, your audio has already been stopped and your audio session has already been deactivated.
Listing 7-12 Implementing AVAudioSessionDelegate interruption methods
- (void) beginInterruption { |
if (playing) { |
playing = NO; |
interruptedWhilePlaying = YES; |
[self updateUserInterface]; |
} |
} |
NSError *activationError = nil; |
- (void) endInterruption { |
if (interruptedWhilePlaying) { |
[[AVAudioSession sharedInstance] setActive: YES error: &activationError]; |
[player play]; |
playing = YES; |
interruptedWhilePlaying = NO; |
[self updateUserInterface]; |
} |
} |
The beginInterruption
method updates application state and user interface.
The endInterruption
method reactivates the audio session, resumes playback, and updates application state and user interface.
The AVAudioPlayer
class, first available in iOS 2.2, provides its own interruption delegate methods, described in AVAudioPlayerDelegate Protocol Reference. Likewise, the AVAudioRecorder
class, first available in iOS 3.0, provides its own interruption delegate methods, described AVAudioRecorderDelegate Protocol Reference. This section describes how to handle interruptions with the AVAudioPlayer
class. You use a very similar approach for the AVAudioRecorder
class.
When your interruption-started delegate gets called, your audio player has already been paused and your audio session has already been deactivated. You can provide an implementation such as that shown in Listing 7-13.
Listing 7-13 An interruption-started delegate method for an audio player
- (void) audioPlayerBeginInterruption: (AVAudioPlayer *) player { |
if (playing) { |
playing = NO; |
interruptedOnPlayback = YES; |
[self updateUserInterface]; |
} |
} |
After checking to ensure that the audio player was indeed playing when the interruption arrived, this method updates your application’s state and then updates the user interface. (The system automatically pauses the audio player.)
If a user ignores a phone call, the system automatically reactivates your audio session and your interruption-ended delegate method gets invoked. Listing 7-14 shows a simple implementation of this method.
Listing 7-14 An interruption-ended delegate method for an audio player
- (void) audioPlayerEndInterruption: (AVAudioPlayer *) player { |
if (interruptedOnPlayback) { |
[player prepareToPlay]; |
[player play]; |
playing = YES; |
interruptedOnPlayback = NO; |
} |
} |
Note: The remainder of this section on responding to audio interruptions applies if you are handling playback or recording interruptions with a C-language interruption listener callback function.
To respond effectively to audio interruptions, your application needs methods that take appropriate action upon interruption-begin and interruption-end. Some of these actions—such as to stop recording—are ones you define anyway for your application. Others—such as to save playback state—may be ones that you invoke only from the interruption listener callback function.
Methods to be invoked only by a callback do not have associated user interface. Otherwise, they are like other control methods. For examples of audio control methods with and without a user interface, see the recordOrStop:
, playOrStop:
, pausePlayback:
, and resumePlayback:
methods in the AudioViewController.m
class file in the SpeakHere sample code project. The resumePlayback:
method, which is not directly invoked by a user interface element, is reproduced in Listing 7-15.
Listing 7-15 An interruption method
- (void) resumePlayback { |
UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback; // 1 |
AudioSessionSetProperty ( // 2 |
kAudioSessionProperty_AudioCategory, // 3 |
sizeof (sessionCategory), // 4 |
&sessionCategory // 5 |
); |
AudioSessionSetActive (true); // 6 |
[self.audioPlayer resume]; // 7 |
} |
Here’s how this code works:
Defines a variable to represent the audio session category and initializes it to the “media playback” category identifier. This category indicates the intent that other audio in the phone should be silenced when your audio session activates. You set the category prior to activating the audio session, which you do in line 6.
The AudioSessionSetProperty
function assigns a value to a designated property in your audio session object.
The audio session property you want to set a value for—here, the “audio category” property.
The size of the value you are assigning to the property.
The address of the variable containing the category identifier.
Activates the audio session, which you do just before resuming playback.
Invokes the resume
method of the audioPlayer
object.
You define your interruption listener callback function to respond to the two possible interruption states, identified by the audio session constants kAudioSessionBeginInterruption
and kAudioSessionEndInterruption
. Listing 7-16 illustrates a basic implementation.
Listing 7-16 An interruption listener callback function
void interruptionListenerCallback ( |
void *inUserData, // 1 |
UInt32 interruptionState // 2 |
) { |
AudioViewController *controller = |
(AudioViewController *) inUserData; // 3 |
if (interruptionState == kAudioSessionBeginInterruption) { // 4 |
if (controller.audioRecorder) { |
[controller recordOrStop: (id) controller]; // 5 |
} else if (controller.audioPlayer) { |
[controller pausePlayback]; // 6 |
controller.interruptedOnPlayback = YES; // 7 |
} |
} else if ((interruptionState == kAudioSessionEndInterruption) && |
controller.interruptedOnPlayback) { // 8 |
[controller resumePlayback]; |
controller.interruptedOnPlayback = NO; |
} |
} |
Here’s how this code works:
A pointer to data that you provide when initializing your audio session.
In an Objective-C class file, such as a view controller class, you place the interruption listener callback function outside of the class implementation block. Because of this, the callback needs a reference to the controller object to be able to send messages to it. You provide this reference when initializing your audio session, as described in Listing 7-1.
The interruption state, which is a constant from the Audio Session Interruption States
enumeration.
Initializes an AudioViewController
object instance to the reference passed in by the inUserData parameter.
If true, an audio session interruption has just begun. Your recording or playback has been stopped.
If currently recording, flushes recording buffers and updates the user interface to show the stopped condition.
If currently playing, saves playback state and updates the user interface to show the stopped (or, effectively, paused) condition.
Sets a flag to indicate that playback was interrupted. This flag gets used if the callback is invoked again with an “end interruption” state.
If the interruption was removed, and the app had been playing, resumes playback.
Your audio session needs to be told about your interruption listener callback function so that it can send it messages. You register your callback during session initialization, as described in “Initializing an Audio Session.”
When using OpenAL for audio playback, you must manage the OpenAL context , as shown in Listing 7-17. Register this callback function with the audio session as described in “Initializing an Audio Session.”
Listing 7-17 Managing the OpenAL context during an audio interruption
void openALInterruptionListener ( |
void *inClientData, |
UInt32 inInterruptionState |
) { |
if (inInterruptionState == kAudioSessionBeginInterruption) { |
alcMakeContextCurrent (NULL); |
} else if (inInterruptionState == kAudioSessionEndInterruption) { |
alcMakeContextCurrent (myContext); |
} |
// other interruption-listener handling code |
} |
To make your code compatible with older versions of iOS, conditionalize it as shown in Listing 7-18.
Listing 7-18 Making OpenAL interruption code compatible with older versions of iOS
- (NSInteger) getMajorOSVersion { |
NSString *versionString = [[UIDevice currentDevice] systemVersion]; |
return [[[versionString componentsSeparateByString:@"."] objectAtIndex:0] intValue]; |
} |
void platformIndependentOpenALInterruptionListener ( |
void *inClientData, |
UInt32 inInterruptionState |
) { |
if ([self getMajorOSVersion] >= 3) { |
// 3.0 OS mechanism -- use alcMakeContextCurrent |
} else { |
if (inInterruptionState == kAudioSessionBeginInterruption) { |
// Save state of OpenAL context and related sources |
StoreMyContextState (myContext); |
alcDestroyContext (myContext); |
} if (inInterruptionState == kAudioSessionEndInterruption) { |
ALCcontext *myContext = alcCreateContext (myOpenALDevice, NULL); |
// Restore OALContext State and related OALSources |
RestoreMyContextState (myContext); |
} |
} |
// other interruption-listener handling code |
} |
To respond to route changes, you employ a property listener callback function. The system invokes the callback when a user plugs in or unplugs a headset, or docks or undocks the device—thereby adding or removing an audio connection. The system also invokes the property listener callback when a Bluetooth device connects or disconnects.
The example code in this section includes configuring and presenting an alert that lets a user choose how to proceed when audio playback is paused following a route change.
To present an alert that provides a resume-or-stop choice, implement a UIAlertView
delegate method as shown in Listing 7-19. You can employ this method in the property listener callback function as shown in “Defining a Property Listener Callback Function.” (Refer to UIAlertViewDelegate Protocol Reference for the alertView:clickedButtonAtIndex:
method’s description.) Code similar to this is used in the AddMusic sample.
Listing 7-19 A UIAlertView delegate method
- (void) alertView: routeChangeAlertView |
clickedButtonAtIndex: buttonIndex { // 1 |
if ((NSInteger) buttonIndex == 1) { |
[self resumePlayback]; // 2 |
} else { |
[self playOrStop: self]; // 3 |
} |
[routeChangeAlertView release]; // 4 |
} |
Here’s how this code works:
The buttonIndex parameter contains the zero-indexed integer corresponding to the button the user tapped. The left-most button in the alert has index 0.
Invokes the resumePlayback
method in the current object when the user taps the rightmost button.
Responds to the user tapping the leftmost of the two buttons in the alert. Invokes the playOrStop:
method in the current object.
Releases the UIAlertView
object, which was allocated earlier in the property listener callback function (see “Defining a Property Listener Callback Function”).
To respond to a route change, a property listener callback function must:
Identify the nature of the route change
Branch, depending on the specific route change and the current audio context (for example, recording, playback, or stopped)
Take or invoke appropriate action
Listing 7-20 shows one way to write a callback that responds to route changes. To see a similar callback function in context, refer to the AddMusic sample code project. With appropriate modifications, you can use a callback like this to respond to the other audio session state changes, which include changes to hardware audio output volume and whether audio input is available.
Listing 7-20 A property listener callback function for route changes
void audioRouteChangeListenerCallback ( |
void *inUserData, // 1 |
AudioSessionPropertyID inPropertyID, // 2 |
UInt32 inPropertyValueSize, // 3 |
const void *inPropertyValue // 4 |
) { |
if (inPropertyID != kAudioSessionProperty_AudioRouteChange) return; // 5 |
MainViewController *controller = (MainViewController *) inUserData; // 6 |
if (controller.appSoundPlayer.playing == 0 ) { // 7 |
return; |
} else { |
CFDictionaryRef routeChangeDictionary = inPropertyValue; // 8 |
CFNumberRef routeChangeReasonRef = |
CFDictionaryGetValue ( |
routeChangeDictionary, |
CFSTR (kAudioSession_AudioRouteChangeKey_Reason) |
); |
SInt32 routeChangeReason; |
CFNumberGetValue ( |
routeChangeReasonRef, kCFNumberSInt32Type, &routeChangeReason |
); |
if (routeChangeReason == |
kAudioSessionRouteChangeReason_OldDeviceUnavailable) { // 9 |
[controller.appSoundPlayer pause]; |
UIAlertView *routeChangeAlertView = |
[[UIAlertView alloc] |
initWithTitle: @"Playback Paused" |
message: @"Audio output was changed." |
delegate: controller |
cancelButtonTitle: @"Stop" |
otherButtonTitles: @"Play", nil]; |
[routeChangeAlertView show]; |
} |
} |
} |
Here’s how this code works:
A pointer to data that you provide when initializing your audio session.
In an Objective-C class file, such as a view controller class, you place the property listener callback function outside of the class implementation block. Because of this, the callback needs a reference to the controller object to be able to send messages to it. You provide this reference when initializing your audio session, as described in Listing 7-21.
The identifier for the property that this callback function gets notified about.
The size, in bytes, of the data in the inPropertyValue parameter.
The current value of the property that this callback function is monitoring. Because the property being monitored is the kAudioSessionProperty_AudioRouteChange
property, this value is a CFDictionary
object.
Ensures that the callback was invoked for the correct audio session property change.
Initializes a MainViewController
object instance to the reference passed in by the inUserData parameter. This allows the callback to send messages to your view controller object—typically defined in the same file that implements the callback.
If application sound is not playing, there's nothing to do, so return.
This line and the next several lines determine the reason for the route change. The one route change of interest in this playback-only example is that an output device, such as a headset, was removed.
If an output device was indeed removed, then pause playback and display an alert that allows the user to stop or resume playback. Release of the UIAlertView
object takes place in the method shown in Listing 7-19.
Your application can listen for hardware and route change events by way of the property mechanism in Audio Session Services. For example, to listen for route change events, you register a callback function with your audio session object, as shown in Listing 7-21.
Listing 7-21 Registering a property listener callback in Audio Session Services
AudioSessionPropertyID routeChangeID = |
kAudioSessionProperty_AudioRouteChange; // 1 |
AudioSessionAddPropertyListener ( // 2 |
routeChangeID, // 3 |
propertyListenerCallback, // 4 |
userData // 5 |
); |
Here’s how this code works:
Declares and initializes a variable to the identifier for the property you want to monitor.
Registers your property listener callback function with your initialized audio session.
The identifier for the property you want to monitor.
A reference to your hardware listener callback function.
Data you want the audio session to pass back to your callback function.
Hardware characteristics of an iPhone can change while your application is running, and can differ from device to device. When using the built-in microphone for an original iPhone, for example, recording sample rate is limited to 8 kHz; attaching a headset and using the headset microphone provides a higher sample rate. Newer iPhone models support higher hardware sample rates for the built-in microphone.
Before you specify preferred hardware characteristics, ensure that the audio session is inactive. After establishing your preferences, activate the session and then query it to determine the actual characteristics. This final step is important because in some cases, the system is unable to provide what you ask for.
You can specify preferred hardware sample rate and preferred hardware I/O buffer duration using the AVAudioSession
class or by using Audio Session Services. Listing 7-22 shows how to set a preferred I/O buffer duration using the AVAudioSession
class. To set preferred sample rate you’d use similar code.
Listing 7-22 Specifying preferred I/O buffer duration using the AVAudioSession class
NSError *setPreferenceError = nil; |
NSTimeInterval preferredBufferDuration = 0.005; |
[[AVAudiosession sharedInstance] |
setPreferredIOBufferDuration: preferredBufferDuration |
error: &setPreferenceError]; |
Listing 7-23 shows how to accomplish the same thing using the C-based Audio Session Services. To set preferred sample rate you’d use similar code.
Listing 7-23 Specifying a preferred I/O buffer duration using Audio Session Services
Float32 preferredBufferDuration = 0.005; // 1 |
AudioSessionSetProperty ( // 2 |
kAudioSessionProperty_PreferredHardwareIOBufferDuration, |
sizeof (preferredBufferDuration), |
&preferredBufferDuration |
); |
Here’s how this code works:
Declares and initializes a value for I/O buffer duration, in seconds.
Sets the preferred I/O buffer duration.
After establishing a hardware preference, always ask the hardware for the actual value, as shown in Listing 7-25; the system may not be able to provide what you ask for.
As part of setting up for recording audio, you need to obtain the current audio hardware sample rate and apply it to your audio data format. Listing 7-24 shows how. You would typically place this code in the implementation file for a recording class. Use similar code to obtain other hardware properties including the input and output number of channels.
Before you query the audio session for current hardware characteristics, ensure that the session is active.
Listing 7-24 Obtaining the current audio hardware sample rate using the AVAudioSession class
double sampleRate; |
sampleRate = [[AVAudiosession sharedInstance] currentHardwareSampleRate]; |
You can accomplish the same thing using the C-based Audio Session Services, as shown in Listing 7-25.
Listing 7-25 Obtaining the current audio hardware sample rate using Audio Session Services
UInt32 propertySize = sizeof (self.hardwareSampleRate); // 1 |
AudioSessionGetProperty ( // 2 |
kAudioSessionProperty_CurrentHardwareSampleRate, |
&propertySize, |
&hardwareSampleRate |
); |
Here’s how this code works:
Declares and initializes a UInt32
variable to the size, in bytes, of the hardwareSampleRate
instance variable—the instance variable belongs to the object that defines this method. This instance variable is declared (in a header file) as a Float64
value. For a complete example, refer to the SpeakHere sample code project.
Obtains the current audio hardware sample rate by querying the audio session object using the kAudioSessionProperty_CurrentHardwareSampleRate
property identifier. The sample rate is stored in the hardwareSampleRate
instance variable.
If your application is running on an iPhone, recording is supported. If running on an iPod touch (2nd generation), however, recording is possible only when appropriate accessory hardware is attached. Listing 7-27 demonstrates how to determine if recording is possible on a device.
Test for audio input support after you initialize the audio session—which you typically do upon application launch. You should also implement the inputIsAvailableChanged:
method from the AVAudioSessionDelegate
protocol, described in AVAudioSessionDelegate Protocol Reference, to get messages about changes in input availability.
Listing 7-26 Using the AVAudioSession class to determine if a device supports audio recording
BOOL inputAvailable; |
inputAvailable = [[AVAudioSession sharedInstance] inputIsAvailable]; |
You can accomplish the same thing using the C-based Audio Session Services, as shown in Listing 7-27.
Listing 7-27 Using Audio Session Services to determine if a device supports audio recording
UInt32 audioInputIsAvailable; // 1 |
UInt32 propertySize = sizeof (audioInputIsAvailable); // 2 |
AudioSessionGetProperty ( // 3 |
kAudioSessionProperty_AudioInputAvailable, |
&propertySize, |
&audioInputIsAvailable |
); |
Here’s how this code works:
Declares a variable to hold the value of the kAudioSessionProperty_AudioInputAvailable
property.
Declares a variable and initialize it to the property size.
Gets the value of the kAudioSessionProperty_AudioInputAvailable
property from your initialized audio session. The audioInputIsAvailable
variable is set to a nonzero value if audio input is available.
Because of the characteristics of the Simulator (as described in “Developing with the Audio Session APIs”), you may want to conditionalize your code to allow partial testing in the Simulator.
One approach is to branch based on the return value of an API call. For example, calling the AudioSessionGetProperty
function with the kAudioSessionProperty_AudioRoute
property identifier, to obtain the current audio route, fails in the Simulator. Ensure that you are checking and appropriately responding to the result codes from all of your audio session function calls; the result codes may indicate why your application works correctly on a device but fails in the Simulator.
In addition to correctly using audio session result codes, you can employ preprocessor conditional statements to hide certain code when running in the Simulator. Listing 7-28 shows how to do this.
Listing 7-28 Using preprocessor conditional statements
#if TARGET_IPHONE_SIMULATOR |
#warning *** Simulator mode: audio session code works only on a device |
// Execute subset of code that works in the Simulator |
#else |
// Execute device-only code as well as the other code |
#endif |
A user may not want an application to be interrupted by a competing audio session—for instance, when running an audio recorder to capture a presentation.
There is no programmatic way to ensure that an audio session is never interrupted. The reason is that iOS always gives priority to the phone. iOS also gives high priority to certain alarms and alerts—you wouldn’t want to miss your flight now, would you?
The solution to guaranteeing an uninterrupted recording is for users to deliberately silence their iPhones by taking the following steps:
In the Settings application, ensure that the iPhone has Airplane Mode turned on.
In the Calendar application, ensure that there are no event alarms enabled during the planned recording period.
In the Clock application, ensure that no clock alarms are enabled during the planned recording period.
Do not move the Ring/Silent switch during the recording. When changing Ring/Silent mode, an iPhone may vibrate, depending on user settings.
Do not plug in or unplug a headset during recording. Likewise, do not dock or undock the device during recording.
Do not plug the iPhone into a power source during the recording. When an iPhone gets plugged into power, it beeps or vibrates, according to user settings.
A user guide is a good place to communicate these steps to your users.
Last updated: 2010-07-09