1.前言

 

 为了解决小企业主在日交易中不方便查看和确认回执的功能痛点,微信建议新版应支持回执的语音提醒功能。本文总结了在iOS平台上开发APP后台唤醒、语音合成和回放等一系列技术时遇到的问题和技巧,希望与大家分享。

 2.技术方案

 

 2.1后台唤醒应用

 

 付款收据的语音提醒要求收款人在收到付款后播放TTS合成语音广播金额。当微信处于前台时,它可以通过模板消息来降低要播放的数量,然后请求TTS数据并播放。然而,应用程序如何请求语音数据并在暂停或终止时播放?

 

 IOS提供了两种方式来唤醒挂起或已被终止的应用程序。它们是无声通知和网络电话推送通知,客户端醒来后将获得30秒的后台运行时间,这足以请求合成语音数据并播放。

 

 具体技术细节如下:

 

 1)无声通知:

 iOS7支持静默通知,但每小时可推送的静默通知数量有限;

 2)网络电话推送通知:

 网络电话推送通知是iOS8支持的一种新的推送类型。与静默通知相比,网络电话推送具有高优先级和低延迟的优点,并且没有次数限制。

 

 相比这两种技术方案,VoIP推送通知显然更适合于语音提示支付和接收的唤醒方案。

 

 2.2TTS合成语音

 

 语音合成方案分为离线合成方案和在线合成方案。离线合成方案节省了网络请求,合成速度更快,节省了网络流量。然而,合成的声音听起来更机械,并且语音速度和停顿的处理更差。如果合成语音的效果不是特别高,我们可以考虑采用iOS附带的高级语音合成框架,以避免语音库的集成,减少安装包的大小。

 

 在线合成方案的效果相对来说更像人声,更富有情感。考虑到产品体验,我们采用了搜索产品部门提供的在线语音合成方案。访问模式可以在本文中找到。合成语音格式支持wav、mp3、silk、amr和speex。经过比较,发现amr在合成相同文本时具有最高的压缩率,但音质明显下降。Silk格式具有第二高的压缩率,并且可以保持相对清晰的声音质量,单个合成语音的大小约为2KB。

 

 2.3醒来后播放音频文件

 

 在请求合成语音后,要在后台或屏幕锁定状态下播放音频文件,应使用音频会话类别回放或音频会话类别回放和录制作为类别值,并根据实际需要选择混合其他或回避其他作为类别选项。

 

 IOS后台唤醒实战:微信收集及到达语音提醒技术总结_1.jpg

 

 应该注意的是,只有iOS10或更高版本支持在应用程序被唤醒后以后台/屏幕锁定状态播放音频。因此,iOS10以下的设备在收到网络电话推送后,只能在本地推送时设置一个固定的铃声,这就是iOS10以下只有“微信收款单”的原因,其背后没有具体的金额值。

 3.静音开关检测

 

 不幸的是,产品发布后不久,它就被一个互联网巨头吐出来了。

 

 IOS后台唤醒战斗:微信收集和到达语音提醒技术总结_2.jpg

 

 从产品体验的角度来看,收到的钱数是随着本地推送的弹出而播出的,这更像是一个特殊的推送环,而苹果对推送环的处理是由一个静音开关控制的,所以有理由进行推理。但是,在上述应用程序被网络电话推送唤醒后,有必要将音频会话类别设置为AVAudioSessionCategoryPlayback或Avaudiosessioncategoryplayandrecord以在后台播放音频文件。这两种模式不受静音开关控制。为了实现这一要求,有必要获得当前静音开关的状态。然而,苹果公司并没有明确提供一个开发途径来获得iOS5之后静音开关的状态,这是一个尴尬的局面。

 

 苹果可以使用以下方法在iOS5之前监控静音键开关:

 0102030405060708091011121314-(BOOL)是已计算的{ CFStringRef路由;uint 32 Routesize = sizeof(CFStringRef);OSStatus状态= AudioSessionGetProperty(kaudiosessionProperty _ AudioRoute,&routeSize,& Route);if(status = = Kaudios essionNoError){ if(route = = NULL | |!CFStringGetLength(路由))返回“是”;}返回否;}

 苹果在iOS5之后禁止以这种方式收听静音按钮。这背后的原因是苹果希望开发人员使用AVAudioSession来提供统一的音频播放效果。

 

 最后,我在Reddit上找到了一个用曲线拯救国家的方法,实现起来并不复杂:使用音频服务播放系统播放0.2s的空白音频,并监控音频播放完成事件。如果从开始播放到回叫完成方法的时间间隔小于0.1秒,这意味着静音开关当前处于打开状态。

 01020304050607080910111213141516171819202122232425262728293031323334353637 void SoundMuteNotificationCompletionProc(SystemSoundId SSId,void * ClientData){ MMsoundSwitchDetector * Detecotr =(_ _ bridge MMsoundSwitchChector *)ClientData;[检测完成];}-(instance type)init { self =[super init];if(self){ NSURL * PathURL =[[NSbundle MainBundle]URlForResource:@ " mute " with extension:@ " caf "];if(AudioServiceCreateSystemSoundId((_ _ bridge CFURlref)PathURl,& _ SoundId)= = KaudioServiceSnoError){ AudioServicesAddsystemSoundCompletion(self . SoundId,CFRunLoopGetMain(),kCFRunLoopDefaultMode,SoundMuteNotificationCompletionProc,(_ _ bridge void *)(self));uint 32 yes = 1;音频服务属性(kAudioServicesPropertyIsUISound,sizeof(_soundId),&_soundId,sizeof(yes),& yes);}否则{ MMErrorWithModule(日志模块,@ "创建声音错误。");_ SoundId = 0;} }返回自我;} -(无效)CheckSoundSwitchStatus:(CheckSwitchStatusCompleteblk)CompleteHandler { if(self . SoundId = = 0){ CompleteHandler(是);返回;} self . CompleteHandler = CompleteHandler;self . BegIntime = CaCurrentMetaDiatime();音频服务显示系统声音(self . SoundId);} -(无效)完成{ CFTim间隔时间=计算当前时间ime()-self . begintime;BOOL isSwitchOn =运行时间> 0.1;if(self . CompleteHandler){ self . CompleteHandler(IsSwitChon);}}

 4.设置声音阈值

 

 来自用户更多反馈的另一个问题是他们听不到广播声音。通过查看日志,发现当语音广播被触发时,用户设置的系统音量太小。首先想到的解决方案是直接设置音频层的音量(或音频队列中的kAudioQueueParam_Volume)。然而,在实验之后,发现这不起作用,并且体积属性受制于系统体积(例如,如果系统体积为0.5,而虚拟层体积为0.6,则最终体积为0.5*0.6 =0.3)。

 

 为了解决音量低的问题,有必要调整系统音量。最后的解决方案借鉴了进入支付显示二维码时自动调整屏幕亮度的方案:如果屏幕亮度没有达到阈值,屏幕亮度将会提高到阈值,当离开页面时,亮度将会设置回原来的亮度。同样,在播放提示音时,如果用户设置的系统音量小于阈值,则将音量调整到阈值。播放提示音后,将提示音恢复到原来的音量。

 

 有两种方法可以控制系统音量。

 

 4.1模式1:通过音乐播放器控制器设置音量

 

 12//此属性已被否决-请改用MPVolumeView进行卷控制。//0.0 ~ 1.0 pmuscplayercontroller * MPC =[MpMusicPlayer controller applicationMusicPlayer];

 第一种方法简单粗暴,设置时会弹出系统音量提示框。如果用户在使用应用程序时突然弹出音量框,会给用户带来麻烦。不建议使用这种方法,苹果公司在iOS7.0之后已经将该属性标记为不推荐使用

 

 4.2模式2:通过MPVolumeView设置音量

 

 第二种方法是在当前视图中添加一个不可见的MPVolumeView,系统音量提示框将不会显示。

 

 需要注意的是,调整系统音量后,需要删除MPVolumeView,否则当用户手动调整音量时,系统音量提示框不会显示。

 

 调整音量的方法是首先在MPVolumeView中获取名为MPVolumeSlider的子视图,并向其发送一个模拟用户操作的事件。

 

 010203040506070809101112-(void)SetSystemVolume:(float)volume { UIslider * VolumeViewSlider = nil;对于[self . m _ PrivateVoulView子视图]中的(UIView *视图){ if([view . class . description IsequiltosString:@ " MpVolumeSlider "]){ VolumeView Slider =(UILider *)视图;休息;} }如果(卷视图滑块!= nil) { [volumeViewSlider设置值:卷anim已设置:否];//发送[volumeview slider sendactionsforcontrol events:uicontroleventtouchupingside];}}



----------------------------------------------------------------------------------

哇谷im_im即时通讯_私有云_公有云-哇谷云科技官网-JM沟通

IM下载体验 - 哇谷IM-企业云办公IM即时聊天社交系统-JM 沟通下载

IM功能与价格 - 哇谷IM-提供即时通讯IM开发-APP搭建私有化-公有云-私有化云-海外云搭建

新闻动态 - 哇谷IM-即时通讯热门动态博客聊天JM沟通APP

哇谷IM-JM沟通热门动态博客短视频娱乐生活

关于哇谷-哇谷IM-提供企业即时通讯IM开发-语音通话-APP搭建私有化-公有云-私有化云-海外云搭建

联系我们 - 哇谷IM-即时通讯IM私有化搭建提供接口与SDK及哇谷云服务

即时通讯IM融云世界

IM即时通讯钉钉技术:企业IM钉钉在后端架构上的优越之处

新的市场叫板环信、融云、腾讯云!开源版IM即使聊天工具

企业IM即时通讯聊天办公APP钉钉技术分析交流

哇谷云-怎么样正确认识海外云服务器

公有云和私有云之间有什么区别?类似融云、环信云、网易云、哇谷云?