一、背景

为了解决小商户老板们在频繁交易中不方便核对、确认到账的痛点,产品MM提出了新版本需要支持收款到账语音提醒功能。这篇文章总结了开发过程中遇到的坑和一些小技巧。

二、技术方案

后台唤醒App

收款到账语音提醒需要收款方在收到款后,播放一段TTS合成语音播报金额,微信在前台时可以通过模板消息将需要播报的金额带下来,再请求TTS数据并播放,但是app在挂起或者被kill掉的情况下要如何请求语音数据并播放呢?
iOS提供了两种方式唤醒处于挂起或已经被kill掉的app。分别是Silent Notification和VoIP Push Notification,客户端在被唤醒之后将获得30s的后台运行时间,这段运行时间足以请求合成语音数据并播放。

1.Silent Notification:
Silent Notification在iOS7以上便可以支持,但是每小时能推送的Silent Notification次数有限制。

2.VoIP Push Notification
VoIP Push Notification则是在iOS8以上才支持的新Push类型,相比于Silent Notification,VoIP Push具有高优先级、低延迟的优势,并且没有次数限制。
对比这两种技术方案,VoIP Push Notification明显更适合用于收款到账语音提醒的唤醒方案。

TTS合成语音

TTS语音合成方案分为离线合成方案和在线合成方案,离线合成方案省去网络请求,合成速度更快,节省网络流量,但是合成音的听起来比较机械,语速和停顿的处理较差一些。如果对合成音的效果要求不是特别高,可以考虑采用iOS自带的AVSpeechSynthesis框架,免去语音库的合入,减少安装包大小。

在线合成方案的效果则相对更像人声,富有感情。考虑到产品体验,我们采用了搜索产品部提供的在线语音合成方案,接入方式可以看这篇文章。合成音格式支持wav,mp3,silk,amr,speex,对比后发现,在合成相同文本的情况下,amr的压缩率最高,但是能听到音质下降明显。silk格式压缩率次高,且能保持相对清晰的音质,单条合成语音大小在2KB左右。

唤醒后播放音频文件

在请求到合成语音后,要在后台或者锁屏状态下播放音频文件,AVAudio Session的Category值需要使用AVAudioSessionCategoryPlayback或是AVAudioSessionCategoryPlayAndRecord,CategoryOptions根据实际需要可选择MixWithOthers(与其他声音混音)或是DuckOthers(调低其他声音的音量)。

需要注意的是,只有iOS10以上才支持app被唤醒后在后台/锁屏状态下播放音频。所以iOS10以下的设备,在收到VoIP Push后只能在local push上设定一段固定铃声,这也是为什么iOS10以下只有“微信支付收款到账”,而没有后面具体的金额数值。

三、静音开关检测

不幸的是,在产品发布后没多久就受到了某互联网大佬的吐槽。

从产品体验上来说,收款到账的金额播报是随着local push的弹出一起播放的,更像是一种特殊的push铃声,而苹果对push铃声的处理是受到静音开关控制的,所以讲道理,这个吐槽是合理的。然而前面提到App在被VoIP Push唤醒之后,需要将AudioSessionCategory设置为AVAudioSessionCategoryPlayback或AVAudioSessionCategoryPlayAndRecord才可以在后台播放音频文件,这两种模式是不受静音开关控制的。要实现这个需求,就必须获取当前静音开关的状态。而苹果在iOS5之后并没有明确地提供一种方式让开发获取静音开关的状态,这就陷入了一个尴尬的局面。

苹果在iOS5之前可以使用以下方式监听静音键开关


- (BOOL)isMuted
{  CFStringRef route;  UInt32 routeSize = sizeof(CFStringRef);  OSStatus status = AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &routeSize, &route);  if (status == kAudioSessionNoError)  {  if (route == NULL || !CFStringGetLength(route))  return YES;  }  return NO;
}

苹果在iOS5之后便禁止了使用这种方式监听静音按键,背后的原因应该是苹果希望开发者使用AVAudioSession来提供统一的音频播放效果。

最后我在Reddit上找到了一种曲线救国的方式,实现起来也不复杂:使用AudioServicesPlaySystemSound播放一段0.2s的空白音频,并监听音频播放完成事件,如果从开始播放到回调完成方法的间隔时间小于0.1s,则意味当前静音开关为开启状态。


void SoundMuteNotificationCompletionProc(SystemSoundID  ssID,void* clientData){MMSoundSwitchDetector* detecotr = (__bridge MMSoundSwitchDetector*)clientData;[detecotr complete];
}- (instancetype)init {self = [super init];if (self) {NSURL *pathURL = [[NSBundle mainBundle] URLForResource:@"mute" withExtension:@"caf"];if (AudioServicesCreateSystemSoundID((__bridge CFURLRef)pathURL, &_soundId) == kAudioServicesNoError){AudioServicesAddSystemSoundCompletion(self.soundId, CFRunLoopGetMain(), kCFRunLoopDefaultMode, SoundMuteNotificationCompletionProc,(__bridge void *)(self));UInt32 yes = 1;AudioServicesSetProperty(kAudioServicesPropertyIsUISound, sizeof(_soundId),&_soundId,sizeof(yes), &yes);} else {MMErrorWithModule(LOGMODULE, @"Create Sound Error.");_soundId = 0;}}return self;
}- (void)checkSoundSwitchStatus:(CheckSwitchStatusCompleteBlk)completHandler {if (self.soundId == 0) {completHandler(YES);return;}self.completeHandler = completHandler;self.beginTime = CACurrentMediaTime();AudioServicesPlaySystemSound(self.soundId);
}- (void)complete {CFTimeInterval elapsed = CACurrentMediaTime() - self.beginTime;BOOL isSwitchOn = elapsed > 0.1;if (self.completeHandler) {self.completeHandler(isSwitchOn);}
}

四、设置声音阈值

另外一个用户反馈较多的问题是听不到播报声音,通过查看日志发现是触发语音播报时,用户设置的系统音量过小所导致。首先想到的解决方案是直接设置AVAudioPlayer的volume(或者是AudioQueue中的kAudioQueueParam_Volume),然而实验过后发现这样行不通,volume属性受制于系统音量(比如系统volume是0.5,AVAudioPlayer的音量是0.6,则最终的音量为0.5*0.6 =0.3)。要解决音量过小的问题,还是需要通过调节系统音量。最终的解决方案借鉴了进入收付款展示二维码时自动调节屏幕亮度的方案:如果屏幕亮度未达到阈值,则调高屏幕亮度到阈值,离开页面时,将亮度设回原亮度。同理,播放提示音时,若用户设置的系统音量小于阈值,则调节到阈值。提示音播放完毕后,将提示音调回原音量。

控制系统音量有两种方式:

方式一:通过MPMusicPlayerController设置音量

MPMusicPlayerController *mpc = [MPMusicPlayerController applicationMusicPlayer];
//This property is deprecated -- use MPVolumeView for volume control instead.
mpc.volume = 0;  //0.0~1.0

第一种方式简单粗暴,在设置的时候会弹出系统音量提示框,如果用户在使用app的过程突然弹出音量框,会对用户造成困扰,不建议使用这种方式,并且苹果在iOS7.0以后已将该属性标为deprecated。

方式二:通过MPVolumeView设置音量

第二种方式则是将一个看不见的MPVolumeView添加到当前视图上,系统音量提示框就不会显示了
需要注意的是,在调节完系统音量需要将MPVolumeView移除,否则后续用户手动调节音量会出现系统音量提示框不显示的情况。

调节音量的方式,则是先取到MPVolumeView中名为MPVolumeSlider的子View,并对其发送模拟用户操作的事件。

- (void)setSystemVolume:(float)volume {UISlider* volumeViewSlider = nil;for (UIView *view in [self.m_privateVoulmeView subviews]){if ([view.class.description isEqualToString:@"MPVolumeSlider"]){volumeViewSlider = (UISlider*)view;break;}}if (volumeViewSlider != nil) {[volumeViewSlider setValue:volume animated:NO];//通过send[volumeViewSlider sendActionsForControlEvents:UIControlEventTouchUpInside];}
}

本文为腾讯Bugly公众号投稿,作者:littleliang,未经作者同意,请勿转载。

微信 iOS 收款到帐语音提醒开发总结相关推荐

  1. IOS、安卓IM语音聊天开发初探部分心得——网络基础篇

    今儿是个还债的日子,没错,承诺了许久的网络传输篇虽然没有千呼万唤,不过我还是厚着脸皮始出来,继上两篇文章总结了一下开发IM语音功能时所遇到的音频问题,而今天就让我们来看看这个让我们看似近在眼前,确始终 ...

  2. iOS 推送语音播报(类似支付宝微信的收款提醒)

    项目需求: 近期项目有个需求,实现类似支付宝微信收款后的语音播报如:支付宝到账xx元.要求是APP在前台运行.锁屏.杀死进程后都会有语音播报. 预想方案: 1.通过UIBackgroundTaskId ...

  3. 分享一款市面比较好用的收款工具, SHOUK8 个人微信支付宝收款工具 微信支付宝财付通免签约收款即时到帐辅助

    分享一款市面比较好用的收款工具 SHOUK8 个人微信支付宝收款工具  微信支付宝财付通免签约收款即时到帐辅助 支持个人支付宝微信 个人微信支付宝免签约,不经过第三方中转,自己应用支持对接任何语言开发 ...

  4. 微信开发常用技巧(3)-微信ios返回为静态返回,防止微信做缓存

    ###两个问题:微信ios返回为静态返回,防止微信做缓存 这几天在做微信开发遇到一些问题,后续应该还会有博客发出,希望可以帮助大家 ####1.微信ios返回为静态返回的问题 微信ios的静态返回操作 ...

  5. 微信iOS WKWebview 网页开发适配指南

    微信2017.1.6日推送一条消息: 微信iOS客户端 将于2017年3月1日前逐步升级为WKWebview内核,需要网页开发者提前做好网站的兼容检查和适配. 背景 WKWebView 是苹果在iOS ...

  6. jquery实现app开发闹钟功能_jquery+html5时钟特效代码共享(可设置闹钟并且语音提醒)...

    本文实例讲述了Jquery+html5可设置闹钟并且会语音提醒的时钟特效.共享给大家供大家参考.详细如下: 这是一个使用Jquery+html5实现可设置闹钟并且会语音提醒的时钟特效代码,超逼真的模拟 ...

  7. Go 1.15 正式发布;华为确认成立屏幕驱动芯片部门;微信 iOS 版 7.0.15 正式版发布| 极客头条

    程序员专区 Go 1.15 正式发布 Go 1.15 新版发布了,主要更新: 新的链接器 编译器改进,包括略微小了些的二进制文件 内嵌 tzdata(时区数据) 增加 testing.TB.TempD ...

  8. 【文末有惊喜!】iOS日历攻略:提醒调休并过滤法定节假日

    本文字数:4281字 预计阅读时间:29分钟 背景 笔者五一之前补班的时候,闹钟没响,早上差点迟到了.笔者闹钟设置的是周一到周五,因为iPhone没有法定节假日和补休的设置....笔者就想要解决这个痛 ...

  9. 微信个人收款码于订单应用之解决方案

    本文将通过以下几个方面,来详细阐述微信个人收款是如何应用于小型订单系统的: 微信个人收款应用背景 目前有哪些实现方式 微信个人收款与订单匹配的基本思路 实现微信个人收款应用需要注意哪些问题 总结 1. ...

  10. 知识就是力量:博客中实现个人微信免签收款,让知识产生价值

    源起: 写文章/博客除了记录一些容易忘记的知识以外,也会有一些创造.创意,一些个人的经验积累和心血.希望获得读者的认同和赞赏,或者是付费阅读. 前段时间就自己建立了一个静态博客,用的Hugo(我翻译叫 ...

最新文章

  1. 青少年编程竞赛交流群周报(第041周)
  2. 第一次当领导,一定要知道的5个工具
  3. 陶哲轩实分析引理 11.1.4
  4. 我国博士生招生规模变化、争论与进路
  5. center.php,mycenter.php
  6. SpokenEnglish01_ When's it due?
  7. (转)Android text文字阴影设置
  8. 安恒赛php_安恒四月赛部分Writeup
  9. 【转】Android自动化测试(UiAutomator)简要介绍
  10. 达梦数据库授权到期替换key文件
  11. printf 和 puts
  12. 熊绎:我看软件工程师的职业规划(转载)
  13. Motion planning for self-driving cars课程笔记1:应用雷达数据生成占用栅格地图(Occupancy Grid Map)
  14. java连连看项目汇报_java连连看小项目 - osc_x4wlt5vu的个人空间 - OSCHINA - 中文开源技术交流社区...
  15. 量子计算机技术难,量子计算机是什么工作原理运行的?现在制造还存在什么技术上的难...
  16. 科技交流英语(2022秋)Unit1-Unit8题干速查
  17. pandas技巧:用一列的非空值填充另一列对应行的空值
  18. 最近想给自己的Unity游戏接入广告
  19. vr技术在计算机教学中的应用,VR/AR技术在中小学基础教学中的应用分析
  20. C语言初学知识准备(Linux方面)

热门文章

  1. 五大主流浏览器内核的源起以及国内各大浏览器内核总结
  2. 用vuejs如何实现ajax,vuejs使用FormData实现ajax上传图片文件
  3. 大牛解密阿里云直播技术平台
  4. 命令行启动MySQL数据库
  5. 用户登录功能设计思路
  6. python分秒换算_如何将度分秒转换为度分
  7. 怎么用imp命令把dmp文件从本地导入到远处的数据库服务器,用imp命令导入dmp文件后,数据库什么也没有...
  8. 安装Windows刷机adb环境手册
  9. YUV420->RGB888格式转换的CPP实现
  10. 条码仓库管理系统在食品行业中的应用