如何在AVSpeechUtterance后保持一致地停止AVAudioSession

7
我想要做的是,在后台音频应用正在播放音频时,允许我的应用程序使用AVSpeechSynthesizer发出话语。 当我的应用正在说话时,我希望背景应用程序的音频会“减弱”,然后在我的应用结束说话后返回其原始音量。
在我的AudioFeedback类中,我这样初始化设置AVAudioSessions:
    self.session = [AVAudioSession sharedInstance];
    NSError *error;
    [self.session setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionDuckOthers error:&error];

每当我想要说出新的话语时,我会执行以下操作。我遵循了《AVSpeechSynthesizer存在的问题,有什么解决方法?》中所提到的建议,每次创建一个新的AVSpeechSynthesizer来“确保”始终接收到取消操作(它似乎有效,但我不确定原因)。
- (AVSpeechUtterance *) utteranceWithString: (NSString *) string
{
    AVSpeechUtterance *utterance = [AVSpeechUtterance  speechUtteranceWithString:string];
    utterance.voice = [AVSpeechSynthesisVoice voiceWithLanguage:@"en-ES"];
    [utterance setRate:(AVSpeechUtteranceDefaultSpeechRate+AVSpeechUtteranceMinimumSpeechRate)/2.0];
    return utterance;
}

- (void) sayString: (NSString *) string cancelPrevious: (BOOL) cancelPrevious
{
    [self.session setActive:enabled error:nil];
    if (cancelPrevious) {
        AVSpeechSynthesizer *oldSynthesizer = self.voice;
        self.voice = nil;
        [oldSynthesizer stopSpeakingAtBoundary:AVSpeechBoundaryImmediate];
        self.voice = [[AVSpeechSynthesizer alloc] init];
        self.voice.delegate = self;
    }

    // Keep track of the final utterance, we'll use this to determine whether or not we should stop the audio session
    self.finalUtterance = [self utteranceWithString:string];
    [self.voice speakUtterance:self.finalUtterance];
}

在我的AVSpeechSynthesizer代理方法中,我会检查是否应该停止音频会话以将背景音量恢复到正常水平,如果当前的AVSpeechSynthesizer和当前的AVSpeechUtterance与最后已知的合成器和话语相匹配。
-(void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didFinishSpeechUtterance:(AVSpeechUtterance *)utterance
{
    NSError *error;
    // Only stop the audio session if this is the last created synthesizer
    if (synthesizer == self.voice  && self.finalUtterance == utterance) {
        if ([self.session setActive:enabled error:&error]]) {
            NSLog(@"Stopped the audio session: Speech synthesizer still speaking %d", synthesizer.speaking);
        } else {
            NSLog(@"ERROR failed to stop the audio session: %@. Speech synthesizer still speaking %d", error, synthesizer.speaking);
        }
    }
}

我遇到的问题是,有时音频会话可以正常停止,但有时却无法停止,并出现以下错误:

Error Domain=NSOSStatusErrorDomain Code=2003329396 "The operation couldn’t be completed. (OSStatus error 2003329396.)"

我不确定如何保证能够成功停止 AVAudioSession。我尝试在无法停止音频会话时不断调用[[AVAudioSession sharedInstance] setActive:NO error:&error],但这似乎没有奏效。希望能得到帮助,非常感谢!

1
对于那些一直在关注的人,我已经联系了苹果技术支持,并要求他们就这个问题提交一个错误报告。至于解决方法,我已经改用“AVAudioSessionCategoryOptionMixWithOthers”而不是“AVAudioSessionCategoryOptionDuckOthers”。ShadowDES似乎有一个有限的解决方法,但对我来说不可行,但可能适用于其他人。我自己还没有尝试过。 - kross
2个回答

1

我发现如果等几秒钟再尝试,它就可以工作了。我正在使用这段代码。

-(void)configureAVAudioSession:(bool)active {

    AVAudioSession *audioSession = [AVAudioSession sharedInstance];

    if (deactivationAttempt < 3) {  // This doesn't always work first time, but don't want to keep trying forever if it decides not to work ever.

        NSError *activationError = nil;
        success = [audioSession setActive:active error:&activationError];

        if (!success) {
            NSLog(@"(de)activation ERROR");
            ++deactivationAttempt;
            [self performSelector:@selector(configureAVAudioSession:) withObject:false afterDelay:2.0];
        }
        else {  // Success!
            deactivationAttempt = 0;
        }
    }
    else {  // Failed, but reset the counter in case we have more luck next time
        deactivationAttempt = 0;
    }
}

来自文档:

如果有任何相关的音频对象(例如队列、转换器、播放器或录音机)正在运行,停用会话将失败。

我猜需要一段时间才能从队列中清除话语。


当您尝试停用音频会话时,可能合成器尚未完成发声。您可以使用<AVSpeechSynthesizerDelegate>协议。对我来说,它始终运行良好,没有错误。 - roberto.buratti

0
我曾经遇到过同样的问题,我相信我已经找到了它的原因。对我来说,如果要朗读的短语太短,就会出现错误。如果它足够长,就不会出现错误。
我进行了测试。
NSString *phraseToSay = [NSString stringWithFormat:@"One Two Three Four Five"]; // Gives the error
NSString *phraseToSay = [NSString stringWithFormat:@"One Two Three Four Five Six"]; // No error

我找到的唯一解决方案是让phraseToSay变得更长。

哦,有趣。不幸的是,这对我来说不是可用的解决方法,因为我的短语长度各不相同,而且通常都很长。谢谢分享!希望这能帮助其他人。 - kross

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接