iOS Reference Library Apple Developer
Search

Audio Session Cookbook

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.

Initializing an Audio Session

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:

  1. Use NULL to use the default (main) run loop.

  2. Use NULL to use the default run loop mode.

  3. 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.

  4. Data you intend to be passed to your interruption listener callback function when the audio session object invokes it.

Activating and Deactivating the Audio Session

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.

Setting the Category

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:

  1. Defines a new variable of type UInt32 and initializes it with the identifier for the category you want to apply to the audio session.

  2. 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.

  3. The size, in bytes, of the property value that you are applying.

  4. The category you want to apply to the audio session.

Checking if Other Audio is Playing During App Launch

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:

  1. Defines a variable to hold the value of the audio session’s kAudioSessionProperty_OtherAudioIsPlaying property.

  2. Obtains the value of the kAudioSessionProperty_OtherAudioIsPlaying property, assigning it to the otherAudioIsPlaying variable.

  3. If other audio is playing when your application launches, assigns the AVAudioSessionCategoryAmbient category to your audio session; otherwise, assigns the AVAudioSessionCategorySoloAmbient category.

Modifying Playback Mixing Behavior

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:

  1. 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.

  2. The size, in bytes, of the property value that you are applying.

  3. 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.

Ensuring That Audio Continues When the Screen Locks

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.”

Configuring Audio Units to Continue Playing When the Screen Locks

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.

Apple recommends that you optimize for minimal power consumption, unless your application has special needs that demand lower latency, such as VOIP.

Optimizing for Minimal Power Consumption

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.

Optimizing for Low Latency

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.

Redirecting Output Audio

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:

  1. 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.

  2. 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:

  1. The identifiers you can use for redirecting output audio are described in Audio Session Category Route Overrides.

  2. The identifier, or key, for the audio session property you want to set.

  3. The size, in bytes, of the property value that you are applying.

  4. 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
);

Supporting Bluetooth Audio Input

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.

Responding to Audio Session Interruptions

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:

Using the AVAudioSessionDelegate Protocol to Handle Audio Interruptions

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.

Handling Interruptions with the AVAudioPlayer Class

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;
    }
}

Defining Interruption Methods

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:

  1. 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.

  2. The AudioSessionSetProperty function assigns a value to a designated property in your audio session object.

  3. The audio session property you want to set a value for—here, the “audio category” property.

  4. The size of the value you are assigning to the property.

  5. The address of the variable containing the category identifier.

  6. Activates the audio session, which you do just before resuming playback.

    Note: Activation is required here, because your audio session was deactivated when the interruption started. If you do not activate your audio session after an interruption ends, audio features, including playback and recording, do not work in your application.

  7. Invokes the resume method of the audioPlayer object.

Defining an Interruption Listener Callback Function

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:

  1. 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.

  2. The interruption state, which is a constant from the Audio Session Interruption States enumeration.

  3. Initializes an AudioViewController object instance to the reference passed in by the inUserData parameter.

  4. If true, an audio session interruption has just begun. Your recording or playback has been stopped.

  5. If currently recording, flushes recording buffers and updates the user interface to show the stopped condition.

  6. If currently playing, saves playback state and updates the user interface to show the stopped (or, effectively, paused) condition.

  7. Sets a flag to indicate that playback was interrupted. This flag gets used if the callback is invoked again with an “end interruption” state.

  8. If the interruption was removed, and the app had been playing, resumes playback.

Registering Your Interruption Callback with the Audio Session

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.”

Responding to Interruptions When Using OpenAL

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
}

Responding to Audio Hardware Route Changes

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.

Defining Route Change Methods

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:

  1. 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.

  2. Invokes the resumePlayback method in the current object when the user taps the rightmost button.

  3. Responds to the user tapping the leftmost of the two buttons in the alert. Invokes the playOrStop: method in the current object.

  4. Releases the UIAlertView object, which was allocated earlier in the property listener callback function (see “Defining a Property Listener Callback Function”).

Defining a Property Listener Callback Function

To respond to a route change, a property listener callback function must:

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:

  1. 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.

  2. The identifier for the property that this callback function gets notified about.

  3. The size, in bytes, of the data in the inPropertyValue parameter.

  4. 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.

  5. Ensures that the callback was invoked for the correct audio session property change.

  6. 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.

  7. If application sound is not playing, there's nothing to do, so return.

  8. 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.

  9. 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.

Registering Your Property Listener Callback with the Audio Session

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:

  1. Declares and initializes a variable to the identifier for the property you want to monitor.

  2. Registers your property listener callback function with your initialized audio session.

  3. The identifier for the property you want to monitor.

  4. A reference to your hardware listener callback function.

  5. Data you want the audio session to pass back to your callback function.

Querying and Using Audio Hardware Characteristics

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.

Specifying Preferred Hardware I/O Buffer Duration

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:

  1. Declares and initializes a value for I/O buffer duration, in seconds.

  2. 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.

Obtaining and Using the Hardware Sample Rate

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:

  1. 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.

  2. 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.

Determining Whether a Device Supports Recording

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:

  1. Declares a variable to hold the value of the kAudioSessionProperty_AudioInputAvailable property.

  2. Declares a variable and initialize it to the property size.

  3. 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.

Running Your App in the Simulator

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

Providing Usage Guidelines

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:

  1. In the Settings application, ensure that the iPhone has Airplane Mode turned on.

  2. In the Calendar application, ensure that there are no event alarms enabled during the planned recording period.

  3. In the Clock application, ensure that no clock alarms are enabled during the planned recording period.

  4. Do not move the Ring/Silent switch during the recording. When changing Ring/Silent mode, an iPhone may vibrate, depending on user settings.

  5. Do not plug in or unplug a headset during recording. Likewise, do not dock or undock the device during recording.

  6. 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

Did this document help you? Yes It's good, but... Not helpful...