帖子

[官方问题解答] 讯飞语音iOS SDK音频问题详解

[复制链接]
  • TA的每日心情
    开心
    2018-9-17 18:48
  • 签到天数: 313 天

    [LV.8]以坛为家I

    2612337  楼主| jmli3 发表于 2017-1-16 16:10:10 1#
    本帖最后由 jmli3 于 2017-3-9 10:17 编辑

    讯飞语音iOS SDK音频问题详解

    本文对开发者在日常使用语音SDK时反馈的问题加以整理,并形成一些使用场景所需要注意的细节。包括播音和录音场景,以及同时使用两种场景的情况。
    本文供大家参考使用,如有错误之处或者有更好的解决方案,欢迎大家及时反馈纠正。

    本文处理方法适合1150及后期版本的讯飞语音SDK

    SDK内部默认的音频服务中断处理
    SDK内部其实做了一些常用的音频服务中断处理,可以基本满足不同应用间的音频中断和恢复场景。
    场景1,在应用A正在播放音乐,此时在应用B中启动语音合成或语音识别,可以使应用A暂停播放,当合成或识别结束后应用A自动恢复播放。
    场景2,在应用B中正在使用语音合成播放音频,此时应用A启动音乐播放,这时应用B中的合成播放将会暂停,待应用A中播放任务结束后自动恢复合成播放。
    场景3,在同一个应用A中先启动音乐播放然后再启动合成或识别时的场景,SDK内部默认的音频服务中断处理可能无法满足大家的需求,此时需要大家先取消SDK的这些默认设置,然后根据实际情况自行设置。
    先介绍如何通过设置参数取消SDK的默认音频中断处理,并说明各个参数的具体功能和设置方法。
    Ø  播放器初始化参数
    功能:在启动播放器前设置AVAudioSession的Category属性;
    设置方法:
    [mw_shl_code=objc,true][_iFlySpeechSynthesizer setParameter:@"1" forKey:[IFlySpeechConstant PLAYER_INIT]];[/mw_shl_code]
    其中参数值:0表示取消默认设置;1表示采用默认设置(默认)
    Ø  播放器DEACTIVE参数
    功能:在播放器关闭后是否调用
    [mw_shl_code=objc,true][[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:NULL];[/mw_shl_code]
    设置方法:
    [mw_shl_code=objc,true][_iFlySpeechSynthesizer setParameter:@"1" forKey:[IFlySpeechConstant PLAYER_DEACTIVE]];[/mw_shl_code]
    其中参数值:0表示不调用;1表示调用(默认)
    Ø  录音器初始化参数
    功能:在启动录音器前设置AVAudioSession的Category属性;
    设置方法:
    [mw_shl_code=objc,true][_iFlySpeechRecognizer setParameter:@"1" forKey:[IFlySpeechConstant RECORDER_INIT]];[/mw_shl_code]
    其中参数值:0表示取消默认设置;1表示采用默认设置(默认)
    Ø  录音器DEACTIVE参数
    功能:在录音器关闭后是否调用
    [mw_shl_code=objc,true][[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation  error:NULL];[/mw_shl_code]
    设置方法:
    [mw_shl_code=objc,true][_iFlySpeechRecognizer setParameter:@"1"forKey:[IFlySpeechConstant RECORDER _DEACTIVE]];[/mw_shl_code]
    其中参数值:0表示不调用;1表示调用(默认)

    注意:以下介绍的不同场景的解决方案都是在取消所有SDK内部默认设置的前提下实施的。即:
    语音合成:
    [mw_shl_code=objc,true][_iFlySpeechSynthesizer setParameter:@"0" forKey:[IFlySpeechConstant PLAYER_INIT]];
    [_iFlySpeechSynthesizer setParameter:@"0" forKey:[IFlySpeechConstant PLAYER_DEACTIVE]];[/mw_shl_code]

    语音识别:
    [mw_shl_code=objc,true][_iFlySpeechRecognizer setParameter:@"0" forKey:[IFlySpeechConstant RECORDER_INIT]];
    [_iFlySpeechRecognizer setParameter:@"0" forKey:[IFlySpeechConstant RECORDER _DEACTIVE]];[/mw_shl_code]


    场景一
    场景说明:
    应用A正在播放音乐,应用B启动语音合成,但不中断应用A播放。

    解决步骤:
    1.    在应用B中设置AVAudioSession的Category属性:
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback  withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];
    2.    启动语音合成:
    [_iFlySpeechSynthesizer startSpeaking:str];


    场景二
    场景说明:
    应用A正在播放音乐,应用B启动语音合成,此时中断应用A播放,当合成结束后自动恢复应用A播放。

    解决步骤:
    1.    在应用B中设置AVAudioSession的Category属性:
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
    2.    启动语音合成:
    [_iFlySpeechSynthesizerstartSpeaking:str];
    3.    合成结束后会调用回调方法:
    -(void)onCompleted:(IFlySpeechError*) error
    在此回调方法中实现如下操作以恢复应用A的音乐播放:
    [[AVAudioSession sharedInstance] setActive:NOwithOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:NULL];
    4.    如果是调用stopSpeaking结束合成,则不会调用步骤3的回调方法。此时需要在调用stopSpeaking之后实现如下操作以恢复应用A的音乐播放:
    [[AVAudioSession sharedInstance] setActive:NOwithOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivationerror:NULL];


    场景三
    场景说明:
           应用A正在播放音乐,应用B启动语音识别,但不中断应用A播放。

    解决步骤:
    1.    在应用B中设置AVAudioSession的Category属性:
    [[AVAudioSession sharedInstance]setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionMixWithOtherserror:nil];
    2.    启动语音识别:
    [_iFlySpeechRecognizerstartListening];


    场景四
    场景说明:
    应用A正在播放音乐,应用B启动语音识别,此时中断应用A播放,当识别结束后自动恢复应用A播放。

    解决步骤:
    1.    在应用B中设置AVAudioSession的Category属性:
    [[AVAudioSession sharedInstance]setCategory:AVAudioSessionCategoryRecord withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:nil];
    2.    启动语音识别:
    [_iFlySpeechRecognizerstartListening];
    3.    识别结束后会调用回调方法:
    - (void)onError:(IFlySpeechError *) error
    在此回调方法中实现如下操作以恢复应用A的音乐播放:
    [[AVAudioSession sharedInstance] setActive:NOwithOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivationerror:NULL];


    场景五
    场景说明:
    应用A正在播放音乐,然后在应用A启动语音识别,但不中断之前的音乐播放。

    解决步骤:
    1.    在应用A启动识别前设置AVAudioSession的Category属性:
    [[AVAudioSession sharedInstance]setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker|AVAudioSessionCategoryOptionAllowBluetooth|AVAudioSessionCategoryOptionMixWithOtherserror:nil];
    2.    启动语音识别:
    [_iFlySpeechRecognizerstartListening];


    场景六
    场景说明:
    应用A正在播放音乐,然后在应用A启动语音识别,此时中断之前的音乐播放。当识别结束后自动恢复音乐播放。

           解决步骤:
    1.    在应用A启动识别前中断正在运行的音乐播放:
    NSDictionary *userInfo = [NSDictionarydictionaryWithObjectsAndKeys:[NSNumbernumberWithInteger:1],AVAudioSessionInterruptionTypeKey, nil];
    [[NSNotificationCenter defaultCenter]postNotificationName:AVAudioSessionInterruptionNotification object:niluserInfo:userInfo];
    2.    设置识别AVAudioSession的Category属性:
    [[AVAudioSession sharedInstance]setCategory:AVAudioSessionCategoryRecord withOptions: AVAudioSessionCategoryOptionAllowBluetootherror:nil];
    3.    启动语音识别:
    [_iFlySpeechRecognizerstartListening];
    4.    识别结束后会调用回调方法:
    - (void)onError:(IFlySpeechError *) error
    在此回调方法中先恢复设置启动识别之前的AVAudioSession的Category属性。然后在此回调方法中恢复应用A之前的音乐播放:
    NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumbernumberWithInteger:0],AVAudioSessionInterruptionTypeKey, nil];
    [[NSNotificationCenter defaultCenter]postNotificationName:AVAudioSessionInterruptionNotification object:niluserInfo:userInfo];

    注意事项:
    1.    切不可在识别结束后直接调用:
    [[AVAudioSession sharedInstance] setActive:NOwithOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivationerror:NULL];
    因为应用中还有音频播放服务未结束,只有在应用中所有音频服务都结束后才可以调用。
    2.    应用A中被中断的音乐播放控件必须有系统中断通知处理。


    场景七
           场景说明:
    应用A正在播音或录音,然后在应用A启动语音合成,但不中断之前的播音或录音。

           解决步骤:
    1.    在应用A启动合成前设置AVAudioSession的Category属性:
    [[AVAudioSession sharedInstance]setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionMixWithOtherserror:nil];
    2.    启动语音合成:
    [_iFlySpeechSynthesizerstartSpeaking:str];


    场景八
           场景说明:
    应用A正在播音或录音,然后在应用A启动语音合成,此时中断之前的播音或录音。当合成结束后自动恢复播音或录音。

           解决步骤:
    1.    在应用A启动合成前中断正在运行的播音或录音:
    NSDictionary *userInfo = [NSDictionarydictionaryWithObjectsAndKeys:[NSNumbernumberWithInteger:1],AVAudioSessionInterruptionTypeKey, nil];
    [[NSNotificationCenter defaultCenter]postNotificationName:AVAudioSessionInterruptionNotification object:niluserInfo:userInfo];
    2.    设置合成AVAudioSession的Category属性:
    [[AVAudioSession sharedInstance]setCategory:AVAudioSessionCategoryPlayback error:nil];
    3.    启动语音合成:
    [_iFlySpeechSynthesizerstartSpeaking:str];
    4.    合成结束后会调用回调方法:
    -(void)onCompleted:(IFlySpeechError*) error
    在此回调方法中先恢复设置启动合成之前的AVAudioSession的Category属性。然后在此回调方法中恢复应用A之前的播音或录音:
    NSDictionary *userInfo = [NSDictionarydictionaryWithObjectsAndKeys:[NSNumber numberWithInteger:0],AVAudioSessionInterruptionTypeKey,nil];
    [[NSNotificationCenter defaultCenter]postNotificationName:AVAudioSessionInterruptionNotification object:niluserInfo:userInfo];

    5.    如果是调用stopSpeaking结束合成,则不会调用步骤4的回调方法。此时需要在调用stopSpeaking之后先恢复设置启动合成之前的AVAudioSession的Category属性。然后在此回调方法中恢复应用A之前的播音或录音:
    NSDictionary *userInfo = [NSDictionarydictionaryWithObjectsAndKeys:[NSNumber numberWithInteger:0],AVAudioSessionInterruptionTypeKey,nil];
    [[NSNotificationCenter defaultCenter]postNotificationName:AVAudioSessionInterruptionNotification object:niluserInfo:userInfo];

           注意事项:
    1.    切不可在合成结束后直接调用:
    [[AVAudioSession sharedInstance] setActive:NOwithOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivationerror:NULL];
    因为应用中还有播放或录音服务未结束,只有在应用中所有音频服务都结束后才可以调用。
    2.    应用A中被中断的播音或录音控件必须有系统中断通知处理。


    特别说明
    1.    上面介绍的只是一些典型场景,还有很多其他复杂场景这里不再一一例举,但基本可以找到类似的解决方法;
    2.    以上各场景中有关AVAudioSession的Category属性设置并不唯一,可根据实际需要作适当更改;
    3.    激活AVAudioSession:
    [[AVAudioSession sharedInstance] setActive:YESerror:NULL];
    一般是系统默认启动的,可以不用调用。


    常见问题
    1.    合成开始播放声音时音量由小变大
    问题说明:
            在启动语音合成播放音频时,播放的音量刚开始由小逐渐变大最后正常。此现象目前只在iPhone 7上出现,其他机型没有出现。

    出现机型:
            iPhone 7 iOS10.1;

    出现场景:
            如果在启动语音合成前如此设置AVAudioSession的Category属性:
    [[AVAudioSession sharedInstance]setCategory:AVAudioSessionCategoryPlayAndRecordwithOptions:AVAudioSessionCategoryOptionDefaultToSpeaker|AVAudioSessionCategoryOptionAllowBluetootherror:nil];

    a.    在合成播放过程中被其他音频服务中断后又恢复会出现此现象;

    b.    在本次合成播放结束后如果没有设置:
    [[AVAudioSession sharedInstance] setActive:NOwithOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivationerror:NULL];
    则当重新启动新的合成服务时会出现此现象。

    解决思路:
    a.    在启动合成前将AVAudioSession的Category属性设置为AVAudioSessionCategoryPlayback,则可以完全避免上述问题。

    b.    若启动合成前AVAudioSession的Category属性仍是AVAudioSessionCategoryPlayAndRecord,则以上场景a问题无法避免。

    c.     若启动合成前AVAudioSession的Category属性仍是AVAudioSessionCategoryPlayAndRecord,则可以通过如下操作避免场景b问题。在每次启动合成服务前设置:
    [[AVAudioSessionsharedInstance] setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivationerror:NULL];
            初步怀疑这可能是iPhone 7 的bug,因为其他机型上并没有出现,如果大家有更好的解决方案,欢迎留言分享。

    2.    AVAudioSession警告
    只有在应用中所有正在运行的音频服务都结束后才可以调用:
    [[AVAudioSession sharedInstance] setActive:NOwithOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivationerror:NULL];
            否则会出现如下警告:
    [avas] AVAudioSession.mm:1074:-[AVAudioSessionsetActive:withOptions:error:]: Deactivating an audio session that has runningI/O. All I/O should be stopped or paused prior to deactivating the audiosession.

    3.    蓝牙耳机无法录音
    所使用的蓝牙耳机必须支持A2DP协议。

    4.     后台无法启动合成或识别
    问题说明:
        当应用A正在处于后台工作时,此时启动应用A音频服务(播放或者录音)或者恢复应用A中被中断的音频服务,音频启动会失败。

    原因分析:
        出现以上现象时系统会报AVAudioSessionErrorCodeCannotInterruptOthers(错误码为560557684)。这是因为当启动后台应用的音频服务时不能影响前台应用的音频服务,因此后台应用的AVAudioSession的Category属性必须含有AVAudioSessionCategoryOptionMixWithOthers 选项。

    解决方法:
        在合成或者识别前如下设置AVAudioSession的Category属性:
    [[AVAudioSessionsharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOtherserror:nil];
    或者
    [[AVAudioSessionsharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionMixWithOtherserror:nil];

    199B2683-DB7B-4D3B-B76E-7DE94EF6E0E0.png
    一头喷火大怪龙 发表于 2017-1-18 11:04:59
    2#
    就不能支持个markdown么,看着贼难受

    楼内回复

    新手发帖,请多多关照  详情 发表于 2017-1-18 14:06
    使用道具 举报 回复
    宏趣网络 发表于 2017-1-18 12:05:47
    3#
    现在语音听写下载的最新的还是1149啊  没有1150

    楼内回复

    1150版本现在已经上线了。  详情 发表于 2017-1-18 16:55
    1150还没上线,最迟明天会上线。  详情 发表于 2017-1-18 14:05
    使用道具 举报 回复
     楼主| jmli3 发表于 2017-1-18 14:05:23
    4#
    宏趣网络 发表于 2017-1-18 12:05
    现在语音听写下载的最新的还是1149啊  没有1150

    1150还没上线,最迟明天会上线。
    使用道具 举报 回复
     楼主| jmli3 发表于 2017-1-18 14:06:30
    5#
    一头喷火大怪龙 发表于 2017-1-18 11:04
    就不能支持个markdown么,看着贼难受

    新手发帖,请多多关照
    使用道具 举报 回复
     楼主| jmli3 发表于 2017-1-18 16:55:21
    6#
    宏趣网络 发表于 2017-1-18 12:05
    现在语音听写下载的最新的还是1149啊  没有1150

    1150版本现在已经上线了。
    使用道具 举报 回复
    宏趣网络 发表于 2017-1-18 19:07:24
    7#
    我的是Unity的游戏应用,应该是“场景六,对着设置还是不行,对IOS开发不熟,不知道什么原因,加了红色部分就好了NSString* cat = [[AVAudioSession sharedInstance] category];
    mCategoryOptions = [[AVAudioSession sharedInstance] categoryOptions];
    if (cat != nil)
    {
            mCategory = [NSString stringWithFormat:@"%@", cat];
            [mCategory retain];
    }
    [mIFlySpeechRecognizer setParameter:@"0" forKey:[IFlySpeechConstant RECORDER_INIT]];[mIFlySpeechRecognizer setParameter:@"0" forKey:[IFlySpeechConstant RECORDER_DEACTIVE]];
    NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInteger:1], AVAudioSessionInterruptionTypeKey, nil];[[NSNotificationCenter defaultCenter] postNotificationName:AVAudioSessionInterruptionNotification object:nil userInfo:userInfo];
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryRecord withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:nil];
    [mIFlySpeechRecognizer startListening]



    然后在
    -(void)onError:(IFlySpeechError *)errorCode
    {
        [[AVAudioSession sharedInstance] setCategory:mCategory withOptions:mCategoryOptions error:nil];
        NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInteger:0], AVAudioSessionInterruptionTypeKey, nil];
        [[NSNotificationCenter defaultCenter] postNotificationName:AVAudioSessionInterruptionNotification object:nil userInfo:userInfo];
    }

    这样设置就好了,希望有用,卡了我们游戏大半个月了,现在终于解决



    楼内回复

    场景六的步骤四有说过要先恢复之前的AVAudioSession 设置,你红色部分添加的就是这个恢复设置。  详情 发表于 2017-1-19 09:03
    使用道具 举报 回复
     楼主| jmli3 发表于 2017-1-19 09:03:00
    8#
    宏趣网络 发表于 2017-1-18 19:07
    我的是Unity的游戏应用,应该是“场景六,对着设置还是不行,对IOS开发不熟,不知道什么原因,加了红色部分 ...

    场景六的步骤四有说过要先恢复之前的AVAudioSession 设置,你红色部分添加的就是这个恢复设置。
    使用道具 举报 回复
    huangzhen826@16 发表于 2017-1-19 12:26:35
    9#
    有结论了share一下啊,谢谢这位大兄弟了,现在很多人都在等这个吧!

    使用道具 举报 回复
    小成不要停 发表于 2017-2-6 11:49:28
    10#
    方案6之前12月份就试过了,不是很合理啊,模拟电话中断会导致界面非常卡顿,至少会卡顿2秒,点击界面后会有明显的延

    楼内回复

    小成不要停这个账号登不上去了,用这个账号回你,下载了最新的sdk还是会出现严重的卡顿现象,点击开按钮后会有2-3秒画面和声音卡顿,因为你这个解决方案是模拟的电话打入,从收到电话到挂断电话之间,应用是不会刷新  详情 发表于 2017-2-7 14:44
    本帖所说的解决方案必须是采用讯飞SDK 1150版本以后才可适用,1150版本是今年一月份才上线的,可以用最新版本SDK再试试  详情 发表于 2017-2-7 09:14

    评分

    参与人数 1语点 +5 收起 理由
    3571123726@qq.c + 5 赞一个!

    查看全部评分

    使用道具 举报 回复