5

In iOS 7, after my audio interruption listener gets called, any attempt to restore the audio session seems to fail silently.

My interruption listener calls

NSError *activationError = nil;
[[AVAudioSession sharedInstance] setActive:YES error:&activationError];

But the app's audio session is dead as soon as the alarm clock rings. The listener gets called with appropriate begin and end states.

It worked just fine on iOS 6.

I have heard that this is a bug in iOS 7 and that there is a workaround, but can't find it.

Does anyone know a link to a workaround or Technical Note from Apple?

EDIT: I found that I HAVE to use AVAudioSessionCategoryPlayback instead of kAudioSessionCategory_AmbientSound. Now it works. But it is not the category I wanted.

openfrog
  • 40,201
  • 65
  • 225
  • 373

3 Answers3

1

Based on Apple's Audio session programming guide, you should listen and react to changes in your interruption handler. This means that your code can/should handle the end of the interruptions too, based on the received parameter interruptionState.

Check out "Audio Interruption Handling Techniques" on this link, I think it will help you a lot: https://developer.apple.com/library/ios/documentation/Audio/Conceptual/AudioSessionProgrammingGuide/HandlingAudioInterruptions/HandlingAudioInterruptions.html

Good luck, Z.

Zoltán
  • 1,422
  • 17
  • 22
  • 2
    The programming guide does not provide much help: Zero sample code. In the case where you are using audio units, it only says to "reactivate the audio session", and I am doing just that (without any errors) but the sound stays muted. – Nicolas Miari Apr 21 '14 at 04:18
1

You can see my code below. But First,

You should select this bit target | Background Modes | Audio, Airplay and Picture in Picture from Capabilities.

//  ViewController.m
//  AVAudioPlayer

#import "ViewController.h"

@import AVFoundation;
@import MediaPlayer;

@interface ViewController ()

@property (strong, nonatomic) AVAudioPlayer *audioPlayer;
@property (weak, nonatomic) IBOutlet UISlider *volumeSlider;
@property (weak, nonatomic) IBOutlet UISlider *rateSlider;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [self setupAudio];
}

- (void) setupAudio {
    NSError *error;
    [[AVAudioSession sharedInstance] setActive:YES error:&error];
    if (error != nil) {
        NSAssert(error == nil, @"");
    }

    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];
    if (error != nil) {
        NSAssert(error == nil, @"");
    }

    NSURL *soundURL = [[NSBundle mainBundle] URLForResource:@"SoManyTimes" withExtension:@"mp3"];
    self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:soundURL error:&error];
    if (error != nil) {
        NSAssert(error == nil, @"");
    }

    //[self.audioPlayer setVolume:0.8];
    self.audioPlayer.enableRate = YES;
    [self.audioPlayer prepareToPlay];
    [self.audioPlayer setVolume:self.volumeSlider.value];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioInterrupt:) name:AVAudioSessionInterruptionNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioRouteChanged:) name:AVAudioSessionRouteChangeNotification object:nil];

    MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
    [commandCenter.playCommand addTarget:self action:@selector(playButtonPressed:)];
    [commandCenter.stopCommand addTarget:self action:@selector(stopButtonPressed:)];
    [commandCenter.pauseCommand addTarget:self action:@selector(stopButtonPressed:)];

}

- (void) audioRouteChanged:(NSNotification*)notification {
    NSNumber *reason = (NSNumber*)[notification.userInfo valueForKey:AVAudioSessionRouteChangeReasonKey];
    switch ([reason integerValue]) {
        case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
        case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:
            [self stopButtonPressed:nil];
            break;

        default:
            break;
    }
}

- (void) audioInterrupt:(NSNotification*)notification {
    NSNumber *interruptionType = (NSNumber*)[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey];
    switch ([interruptionType integerValue]) {
        case AVAudioSessionInterruptionTypeBegan:
            [self stopButtonPressed:nil];
            break;
        case AVAudioSessionInterruptionTypeEnded:
        {
            if ([(NSNumber*)[notification.userInfo valueForKey:AVAudioSessionInterruptionOptionKey] intValue] == AVAudioSessionInterruptionOptionShouldResume) {
                [self playButtonPressed:nil];
            }
            break;
        }
        default:
            break;
    }
}

- (IBAction)playButtonPressed:(id)sender {
    BOOL played = [self.audioPlayer play];
    if (!played) {
        NSLog(@"Error");
    }

    MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageNamed:@"CoverArt"]];

    NSDictionary *songInfo = @{

                               MPMediaItemPropertyTitle:@"So Many Times",
                               MPMediaItemPropertyArtist:@"The Jellybricks",
                               MPMediaItemPropertyAlbumTitle:@"Soap Opera",
                               MPMediaItemPropertyArtwork:albumArt,
                               MPMediaItemPropertyPlaybackDuration:@(self.audioPlayer.duration),
                               MPNowPlayingInfoPropertyElapsedPlaybackTime:@(self.audioPlayer.currentTime),

                               };

    [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:songInfo];

}

- (IBAction)stopButtonPressed:(id)sender {
    [self.audioPlayer stop];
}

- (IBAction)volumeSliderChanged:(UISlider*)sender {
    [self.audioPlayer setVolume:sender.value];
}

- (IBAction)rateSliderChanged:(UISlider*)sender {
    [self.audioPlayer setRate:sender.value];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void) dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

@end
Durul Dalkanat
  • 7,266
  • 4
  • 35
  • 36
0

I'm also using the AVAudioSessionCategoryPlayback category but interruptions do not automatically resume playback. Check out Detecting active AVAudioSessions on iOS device. The chosen answer has detailed instructions on how to handle session interrupts.

Community
  • 1
  • 1
vhs
  • 9,316
  • 3
  • 66
  • 70