前言
最近在开发过程中,有语音播放的需求,其实之前项目集成了科大讯飞的语音,但在开发过程中遇到了一问题,特此总结一下
需求
APP 收到推送消息,然后语音播报一下内容,类似支付宝和微信的收付款语音提醒,没说后台必须也可以播放,但有一点是:收到多少推送消息,就播多少条!!!也没见过支付宝、微信的一次多人同时支付是怎么播报的,反正最终是体验了我们自己的APP的这个功能。
先说一下科大讯飞的简单使用
科大讯飞集成播放识别等,其实并没有什么难点,集成完 SDK,在需要的地方实例化一下,播放对象是一个单例对象,所以只需要实例化一次即可:
/**
* 初始化讯飞语音合成对象
*/
-(void)creatVoice
{
// 合成对象的单例
_iFlySpeechSynthesizer = [IFlySpeechSynthesizer sharedInstance];
//设置协议委托对象
_iFlySpeechSynthesizer.delegate = self;
//设置合成参数
//设置在线工作方式
[_iFlySpeechSynthesizer setParameter:[IFlySpeechConstant TYPE_CLOUD]
forKey:[IFlySpeechConstant ENGINE_TYPE]];
//设置音量,取值范围 0~100
[_iFlySpeechSynthesizer setParameter:@"100"
forKey: [IFlySpeechConstant VOLUME]];
//发音人,默认为”xiaoyan”,可以设置的参数列表可参考“合成发音人列表”
[_iFlySpeechSynthesizer setParameter:@"xiaoyan"
forKey: [IFlySpeechConstant VOICE_NAME]];
//保存合成文件名,如不再需要,设置为nil或者为空表示取消,默认目录位于library/cache下
// [_iFlySpeechSynthesizer setParameter:@"tts.pcm"
// forKey: [IFlySpeechConstant TTS_AUDIO_PATH]];
}
还有一些其他的设置参数,根据需要设置,也可以动态来设置:
// 科大讯飞
//发音人,默认为”xiaoyan”,可以设置的参数列表可参考“合成发音人列表”
// https://doc.xfyun.cn/msc_ios/语音合成.html
// @"xiaofeng"---@"xiaomei"---@"xiaoyan"---@"kaiselin"
[_iFlySpeechSynthesizer setParameter:voiceName?voiceName:@"xiaoyan"
forKey: [IFlySpeechConstant VOICE_NAME]];
//设置音量,取值范围 0~100
[_iFlySpeechSynthesizer setParameter:volume?volume:@"100"
forKey: [IFlySpeechConstant VOLUME]];
//设置语速,取值范围 0~100
[_iFlySpeechSynthesizer setParameter:speed?speed:@"100"
forKey: [IFlySpeechConstant SPEED]];
// 调用此函数进行合成,如果发生错误会回调错误`onCompleted`
[_iFlySpeechSynthesizer startSpeaking:voiceContent];
so easy ! 就连科大讯飞的文档也极其简单,可以看一下:
到这里其实已经 OK 了,基本的语音播放功能已经实现了,单条消息可以无障碍播放了,但是距离需求还有一点距离。
同时收到多条消息播放的问题
遇见这个需求,本来想简便一点,省一点事,看看讯飞有没有类似这样的处理,同时给多条内容,调用播放,然并卵,每次调用都会从头开始重新播放新的一条消息,并没有什么处理,没办法啊,只能自己想办法了😌
解决办法,思路:
每次收到推送消息,都存到数组中,监听这个数组元素的变化,在监听到变化后,就调用 讯飞的播放语音播放。
这时就涉及到了一个数组监听的问题,可以移步刚写的另一篇文章
具体代码如下:
- 在收到推送消息的时候:
#pragma mark -- 处理推送消息
- (void)handleUserInfo:(NSDictionary *)dic
{
NSString *voiceContent = [NSString stringWithFormat:@"%@",[dic objectForKey:@"voice"]];
// 注意这里不要用[array addObject:]这种方法要用下面的方法
[[obserVer mutableArrayValueForKeyPath:@"dataArray"] addObject:voiceContent];
}
- 监听消息处理:
#pragma mark -- 监听调用方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:@"dataArray"]) {
//
if (obserVer.dataArray.count > 0) {
// if (!_iFlySpeechSynthesizer.isSpeaking) {
curVoice = obserVer.dataArray.firstObject;
[_iFlySpeechSynthesizer startSpeaking:curVoice];
// }
}
}
}
- 科大讯飞语音播放结束回调方法监听处理:
#pragma mark -- IFlySpeechSynthesizerDelegate
// 结束回调
- (void)onCompleted:(IFlySpeechError*) error
{
// [[obserVer mutableArrayValueForKeyPath:@"dataArray"] removeObject:curVoice];
// 删除,添加都会出发监听方法
[[obserVer mutableArrayValueForKeyPath:@"dataArray"] removeObjectAtIndex:0];
[_iFlySpeechSynthesizer stopSpeaking];
// 删除,添加都会出发监听方法,所以不能在这里播放
// if (obserVer.dataArray.count > 0) {
// NSString *voiceContent = [obserVer.dataArray objectAtIndex:0];
// [_iFlySpeechSynthesizer startSpeaking:voiceContent];
// }
// [[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:NULL];
}
//合成开始
- (void) onSpeakBegin
{
}
//合成缓冲进度
- (void) onBufferProgress:(int) progress message:(NSString *)msg {
}
//合成播放进度
- (void) onSpeakProgress:(int) progress beginPos:(int)beginPos endPos:(int)endPos
{
}
小结
这里多条消息播放的思路大概是:每次收到推送消息,都存到数组中,监听这个数组元素的变化,在监听到变化后,就调用 讯飞的播放语音播放,同时监听讯飞播放的回调,播放完成以后,删除数组中的第一个元素,这时又会触发数组的监听方法,接着在获取到监听的方法中判断一下数组的数量,继续播放第一条,删一个播一个。
网友评论