关于APP上语音播报的完整实现(iOS篇)

您所在的位置:网站首页 语音播报怎么写文字 关于APP上语音播报的完整实现(iOS篇)

关于APP上语音播报的完整实现(iOS篇)

2024-07-12 23:35| 来源: 网络整理| 查看: 265

前一段时间,一个“支付宝到账100万”的铃声在网络上火了起来,其实这在APP上,特别支付类的应用里,经常用到,今天我们谈一下其实现方法,给类似这种场景的开发人员一个参考吧。

首先,我们这次是基于推送+语音的方式来实现。

使用sound字段

我们都知道,我们可以在进行推送的时候,指定sound的文件名,来播放指定声音文件。

于是,录好一个声音文件,暂且叫“tts_default.mp3”吧,加入到主工程中。

服务端收到一笔款项的时候,往消息中心发起一个推送,推送的格式和内容如下:

{"aps":{"alert":"XXX到账一笔","badge":1,"sound":"tts_default.mp3"}}

这样,APP在接受到通知的时候,弹出一个通知框,显示“XXX到账一笔”,并伴随一个声音,播放的是语音文件tts_default.mp3。

播报金额

如果收到一笔钱,如果能播放具体金额就更好了,因为金额是变化的,你不可能在工程里添加许多“tts_default.mp3”文件,那我们只有合成金额,在AVFoundation里,有合成声音的API,在第三方,也有如百度、讯飞一样的第三方合成声音的接口,我们测试一下,还是比较生硬。这里我们仿地铁、车站的广播,录了一些基础的声音、和一些数字,我们自己来合成所需的声音。

假如,你要实现的语音格式是这样的:钱到啦到账xx.xx元。

我们录制并预置了一些语音文件打在包里,这些文件包括:

tts_pre.mp3   对应文字为:钱到啦到账

tts.yuan.mp3 对应文字为:元。

另外还有一些表表示数字的,如0、1、2、3、4、5、6、7、8、9、十、百、千、万、点

对应的声音文件为:

tts_0.mp3 ~ tts_9.mp3、tts_ten.mp3、tts_hundred.mp3、tts_thousand.mp3、tts_ten_thousand.mp3、tts_dot.mp3

当我们想播放声音“钱到啦到账0.25元”的时候,我们就可以依次播放声音文件:

tts_pre.mp3、tts_0.mp3、tts_dot.mp3、tts_2.mp3、tts_5.mp3、tts.yuan.mp3

就可以了。

这里牵扯到一个金额转语音文件的算法,后面的Demo有实现,可以参考一下:

-(NSString *)wordsStringFromAmount:(NSString *)numstr;

流程是这样的:

1、后端收到钱,给商家发起一个推送,格式为:

{"aps":{"alert":"钱到啦到账0.25元","badge":1,"amount":0.25, "sound":"tts_default.mp3"}}

2、客户端收到推送,处理金额字段amount,转成对应的播放文件数组。

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{ [[BPAudioManager sharedPlayer] playPushInfo:userInfo completed:nil] ; }

3、开始播放声音文件。

后台播放

当APP在前台的时候,上面那种处理方法是没有问题的,在后台的时候,只会播放一个“tts_default.mp3”这个通用型的语音文件,也没有问题的,但是在后台和APP退出的情况下,playPushInfo这个方法执行一些处理,并播放语音是不可行的,所以还借助其他的方法,好在苹果在iOS10的时候,发布了UNNotificationServiceExtension扩展,关于此扩展,可以网上选择一些资料,主要的核心思想就是,在远程推送到底设备之前,给你一个修改的机会,我们知道,推送体是有限制的,而且推送体大小也会影响推送的效率,借助这个,我们可以修改标题、内容,也可以从网络上请求到内容,再去合成一个新的推送。我们这里不修改内容,主要是用来播放语音。

要使用这个扩展,和其他扩展一样,新建一个target,找到这个模版,然后下一步,就好了。

系统会自动实现两个方法:

- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler ; - (void)serviceExtensionTimeWillExpire;

前者,你需要在这里做一些操作,修改内容,当你完成后,通知系统,这时候,推送才会显示出来。我们这里主要处理推送,并播放声音;后者会在超时的情况下调用。如:

- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler { self.contentHandler = contentHandler; self.bestAttemptContent = [request.content mutableCopy]; //step1: 标记该推送已经在这里处理过了 NSMutableDictionary *dict = [self.bestAttemptContent.userInfo mutableCopy] ; [dict setObject:[NSNumber numberWithBool:YES] forKey:@"hasHandled"] ; self.bestAttemptContent.userInfo = dict ; //step2: 忽略推送中的默认语音文件(有可能是那个recieved.mp3) self.bestAttemptContent.sound = [UNNotificationSound defaultSound] ; //step3: 处理推送信息,播放语音 [[BPAudioManager sharedPlayer] playPushInfo:self.bestAttemptContent.userInfo completed:^{ // 播放完成后,通知系统 self.contentHandler(self.bestAttemptContent); }] ; } - (void)serviceExtensionTimeWillExpire { // Called just before the extension will be terminated by the system. // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used. self.contentHandler(self.bestAttemptContent); }

要激活UNNotificationServiceExtension扩展,需要在字段中添加mutable-content字段,所以新的推送体为:

{"aps":{"alert":"钱到啦到账0.25元","badge":1,"mutable-content":1,"amount":0.25, "sound":"tts_default.mp3"}} mutable-content":1,"amount":0.25, "sound":"tts_default.mp3"}}

BPAudioManager

我们定义了一个声音处理的中间类,因为扩展和APP本身都会使用这个类,所以新建这个文件的时候,注意勾选Targets

- (void) playPushInfo:(NSDictionary *)userInfo completed:(BPAudioPlayCompleted)completed { //获取aps NSDictionary *aps = [userInfo objectForKey:@"aps"] ; //判断是否需要播报语音,因为所有的推送,都会走到这里 BOOL playaudio = [[aps objectForKey:@"playaudio"] boolValue] ; if(!playaudio) { if(completed != nil) { completed() ; } } // 处理 else { self.completed = completed ; NSString *amount = [aps objectForKey:@"amount"] ; NSArray* arrAudioFiles = [self getAudioFilesWithAmount:amount] ; [self playAudioFiles:arrAudioFiles] ; } }

先处理金额,得到语音文件的数组,播放语音这里直接用循环播放的方式了

// 播放声音文件 - (void) playAudioFiles { // 1.获取要播放音频文件的URL NSString *fileName = [audioFiles objectAtIndex:audioIndex] ; NSString *path = [NSString stringWithFormat:@"%@/%@",[[NSBundle mainBundle] resourcePath], fileName] ; NSURL *fileURL = [NSURL fileURLWithPath:path]; // 2.创建 AVAudioPlayer 对象 self.audioPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:fileURL error:nil]; // 4.设置循环播放 self.audioPlayer.numberOfLoops = 0 ; self.audioPlayer.delegate = self; // 5.开始播放 [self.audioPlayer prepareToPlay] ; [self.audioPlayer play]; } // 播放完成回调 - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag { audioIndex += 1 ; if(audioIndex < audioFiles.count) { [self performSelectorOnMainThread:@selector(playAudioFiles) withObject:nil waitUntilDone:NO] ; } else { [self setNormalVolume] ; [self disactivePlayback] ; [self performSelectorOnMainThread:@selector(playCompleted) withObject:nil waitUntilDone:NO] ; } }

到这里,基本就完成了,在后台、退出后台的情况下,可以正常语音播报了。

音量调节

有时候,我们不小心把声音关闭了,或者音量很小,或者静音模式下,那这个时候,播放的声音就可能听不见了,为了防止这个情况发生,我们在播放的时候,适当处理一下,是非要有必要的。

// 设置高音量 - (void) setHighVolume { MPVolumeView*volumeView = [[MPVolumeViewalloc] init]; UISlider*volumeViewSlider = nil; for(UIView*view in[volumeView subviews]){ if([view.class.descriptionisEqualToString:@"MPVolumeSlider"]){ volumeViewSlider = (UISlider*)view; break; } } // 获取系统原来的音量,用于还原 userVolume= volumeViewSlider.value; // 留点余地,设置0.9吧, 值在0.0~1.0之间 if(userVolume< 0.9f) { // 改变系统音量 [volumeViewSlider setValue:0.9fanimated:NO]; // 发一个事件使之生效 [volumeViewSlider sendActionsForControlEvents:UIControlEventTouchUpInside]; } }

然后播放完成的时候,会设置会正常音量

// 设置回正常音量 - (void) setNormalVolume { MPVolumeView*volumeView = [[MPVolumeViewalloc] init]; UISlider* volumeViewSlider = nil; for(UIView*view in[volumeView subviews]){ if([view.class.descriptionisEqualToString:@"MPVolumeSlider"]){ volumeViewSlider = (UISlider*)view; break; } } if(volumeViewSlider.value!=userVolume) { [volumeViewSlider setValue:userVolumeanimated:NO]; [volumeViewSlider sendActionsForControlEvents:UIControlEventTouchUpInside]; } }

然后静音处理:

// 静音模式下,依然可以播放 - (void) activePlayback { [[AVAudioSessionsharedInstance] setCategory:AVAudioSessionCategoryPlaybackerror:NULL]; [[AVAudioSessionsharedInstance] setActive:YESerror:NULL]; } //回归正常 - (void)disactivePlayback { [[AVAudioSessionsharedInstance] setActive:NOerror:NULL]; }

至此,语音播报算是完成了。

1、在iOS10以下,推送利用sound字段,前台可以正常播放,后台、退出的情况下,播放通用声音。

2、iOS以上,推送增加mutable-content字段,可以完美播放。

3、我们增加了一些机制,在低音和静音模式下,也可以正常工作。

附演示Demo

https://github.com/WinterXIE/PushAudio



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3