AudioSessionInitialize Workarounds

Core Audio

Yesterday I decided to go after one of my audio bugs by take a look how AVAudioPlayer does it right, and I discovered a little thing about Audio Session.

Here are two workarounds for AudioSessionInitialize.

I never liked AudioSessionInitialize because you can set the InterruptionListener and it’s ClientData just once:

/*
 * Discussion
 * Your application must call this function before making any other Audio
 * Session Services calls. You may activate and deactivate your audio session
 * as needed (see AudioSessionSetActive), but should initialize it only once.
 */

OSStatus AudioSessionInitialize (
   CFRunLoopRef                        inRunLoop,
   CFStringRef                         inRunLoopMode,
   AudioSessionInterruptionListener    inInterruptionListener,
   void                                *inClientData
);

So you need a helper class like this where you have to set the active AudioPlayer:

#import "AudioInterruptionListener.h"

void interruptionListenerCallback (
                                   void *inUserData,
                                   UInt32   interruptionState
) {
    AudioInterruptionListener *listener = (AudioInterruptionListener *) inUserData;
    DoItYourselfPlayer *player = listener.doItYourselfPlayer;
    
    if (!player)
        return;
    
    if (interruptionState == kAudioSessionBeginInterruption) {
        [player performSelectorOnMainThread:@selector(audioStreamPlayerBeginInterruption)
                                 withObject:nil
                              waitUntilDone:YES];
    } else if ((interruptionState == kAudioSessionEndInterruption)) {
        [player performSelectorOnMainThread:@selector(audioStreamPlayerEndInterruption)
                                 withObject:nil
                              waitUntilDone:YES];
    }
}

@implementation AudioInterruptionListener

static AudioInterruptionListener *sharedAudioInterruptionListener;

@synthesize doItYourselfPlayer;

+ (void)initialize {
    if (!sharedAudioInterruptionListener) {
        sharedAudioInterruptionListener = [[AudioInterruptionListener alloc] init];
        AudioSessionInitialize (
                                NULL,
                                NULL,
                                interruptionListenerCallback,
                                sharedAudioInterruptionListener
                                );
    }
}

+ (AudioInterruptionListener *)sharedAudioInterruptionListener
{
    return sharedAudioInterruptionListener;
}

@end

This is fine, but I was no big fan of this helper class when I wrote it.

AVAudioPlayer works without such helper class, but how? Well don’t do this at home, because ‘intr’ is not documented in “Audio Session Services Property Identifiers.”, but you can also add an InterruptionListener with AudioSessionAddPropertyListener, the code should look something along this lines:

– (BOOL)prepareToPlay {
    pthread_once(&interruptionListenerOneTimeInit, interruptionListenerInit);
    …
    AudioSessionAddPropertyListener(‘intr’, interruptionListenerCallback, self);
}

- (void)stop {
    AudioSessionRemovePropertyListenerWithUserData(‘intr’, interruptionListenerCallback, self);
    …
}

#pragma mark -
#pragma mark Audio Session Implementations

pthread_once_t          interruptionListenerOneTimeInit = PTHREAD_ONCE_INIT;

void interruptionListenerInit(void)
{
    AudioSessionInitialize(NULL, NULL, NULL, NULL);
}

void interruptionListenerCallback (
                                    void                      *inClientData,
                                    AudioSessionPropertyID    inID,
                                    UInt32                    inDataSize,
                                    const void                *inData
) {
    DoItYourselfPlayer *player = inClientData;
    if (inID == ‘intr’) {
        UInt32 *interruptionState = (UInt32 *)inData;
        
        if (*interruptionState == kAudioSessionBeginInterruption) {
              [player performSelectorOnMainThread:@selector(audioStreamPlayerBeginInterruption)
                                 withObject:nil
                              waitUntilDone:YES];
        } else if (*interruptionState == kAudioSessionEndInterruption) {
              [player performSelectorOnMainThread:@selector(audioStreamPlayerEndInterruption)
                                 withObject:nil
                              waitUntilDone:YES];
            }
        }
    }
}

If you agree that Apple should expose this functionality to third parties, please submit a duplicate for Radar ID# 6467253.

Happy Holidays hacking!

About these ads
This entry was posted in Development. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s