How to let my app audio to nicely interrupt iPhone

2020-07-06 08:59发布

My iOS 7 app vocalizes texts when necessary.

What I'd like to do is enable the user to listen to his music or podcasts (or any other app using audio) while mine is running.

The expected behavior is that others audio either mixe or duck when my app speaks, then the others audio resume his volume at the initial level right after.

I have tried many ways to achieve this goal, but nothing is good enough, as I list the issues I face, after the code.

My current implementation is based on creating a session prior to play or text-to-speech as follows:

+ (void)setAudioActive {

    [[self class] setSessionActiveWithMixing:YES];
}

After the play/speech, I set i to idled as follows:

+ (void)setAudioIdle {
    [[self class] setSessionActiveWithMixing:NO];
}

The core function which handle the session setup accordingly to the active parameter, as follows:

+ (void)setSessionActiveWithMixing:(BOOL)active
{
    NSError *error = NULL;
    BOOL     success;

    AVAudioSession *session = [AVAudioSession sharedInstance];

    static NSInteger counter = 0;

    success = [session setActive:NO error:&error];
    if (error) {
        DDLogError(@"startAudioMixAndBackground: session setActive:NO, %@", error.description);
    }
    else {
        counter--; if (counter<0) counter = 0;
    }

    if (active) {
        AVAudioSessionCategoryOptions options = AVAudioSessionCategoryOptionAllowBluetooth
            //|AVAudioSessionCategoryOptionDefaultToSpeaker
            |AVAudioSessionCategoryOptionDuckOthers
        ;


        success = [session setCategory://AVAudioSessionCategoryPlayback
                   AVAudioSessionCategoryPlayAndRecord
                           withOptions: options
                                 error: &error];
        if (error) {
            // Do some error handling
            DDLogError(@"startAudioMixAndBackground: setCategory:AVAudioSessionCategoryPlayback, %@", error.description);
        }
        else {
            //activate the audio session
            success = [session setActive:YES error:&error];
            if (error) {
                DDLogError(@"startAudioMixAndBackground: session setActive:YES, %@", error.description);
            }
            else {
                counter++;
            }
        }
    }

    DDLogInfo(@"Audio session counter is: %ld",counter);
}

My current issues are:

1) When my app start to speak, I hear some king of glitch in the sound, which makes it not nice;

2) When I connect the route to bluetooth, the underlying audio (say the Podcast or ipod music) goes very low and sounds noisy, which make my solution merely unusable, my users will reject this level au poor quality.

3) When others bluetooth connected devices tried to emit sounds (say a GPS in a car or instance), my App does not receive any interrupts (or I handle it wrongly), see my code as follows:

- (void)startAudioMixAndBackground {

    // initialize our AudioSession -
    // this function has to be called once before calling any other AudioSession functions
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioSessionDidChangeInterruptionType:)
                                                 name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];

    // set our default audio session state
    [[self class] setAudioIdle];

    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];

    if ([self canBecomeFirstResponder]) {
        [self becomeFirstResponder];
    }

    @synchronized(self) {
        self.okToPlaySound = YES;
    }

    //MPVolumeSettingsAlertShow();
}
//  want remote control events (via Control Center, headphones, bluetooth, AirPlay, etc.)
- (void)remoteControlReceivedWithEvent:(UIEvent *)event
{
    if (event.type == UIEventTypeRemoteControl)
    {
        switch(event.subtype)
        {
            case UIEventSubtypeRemoteControlPause:
            case UIEventSubtypeRemoteControlStop:
                [[self class] setAudioIdle];
                break;
            case UIEventSubtypeRemoteControlPlay:
                [[self class] setAudioActive];
                break;
            default:
                break;
        }
    }
}

#pragma mark - Audio Support

- (void)audioSessionDidChangeInterruptionType:(NSNotification *)notification
{
    AVAudioSessionInterruptionType interruptionType = [[[notification userInfo]
                                                        objectForKey:AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];

    if (AVAudioSessionInterruptionTypeBegan == interruptionType)
    {
       DDLogVerbose(@"Session interrupted: --- Begin Interruption ---");
    }
    else if (AVAudioSessionInterruptionTypeEnded == interruptionType)
    {
        DDLogVerbose(@"Session interrupted: --- End Interruption ---");
    }
}

1条回答
ゆ 、 Hurt°
2楼-- · 2020-07-06 09:12

Your issue is most likely due to the category you are setting: AVAudioSessionCategoryPlayAndRecord. The PlayAndRecord category does not allow your app to mix/duck audio with other apps. You should reference the docs on Audio Session Categories again here: https://developer.apple.com/library/ios/documentation/avfoundation/reference/AVAudioSession_ClassReference/Reference/Reference.html. It seems like AVAudioSessionCategoryAmbient is probably more what your looking for.

查看更多
登录 后发表回答