微信語音播報(轉)

微信iOS收款到賬語音提醒開發(fā)總結

一、背景

為了解決小商戶老板們在頻繁交易中不方便核對、確認到賬的痛點谋作,產(chǎn)品MM提出了新版本需要支持收款到賬語音提醒功能。這篇文章總結了開發(fā)過程中遇到的坑和一些小技巧乎芳。

二遵蚜、技術方案

后臺喚醒App

收款到賬語音提醒需要收款方在收到款后,播放一段TTS合成語音播報金額奈惑,微信在前臺時可以通過模板消息將需要播報的金額帶下來吭净,再請求TTS數(shù)據(jù)并播放,但是app在掛起或者被kill掉的情況下要如何請求語音數(shù)據(jù)并播放呢肴甸?

iOS提供了兩種方式喚醒處于掛起或已經(jīng)被kill掉的app寂殉。分別是Silent Notification和VoIP Push Notification,客戶端在被喚醒之后將獲得30s的后臺運行時間原在,這段運行時間足以請求合成語音數(shù)據(jù)并播放友扰。

1.Silent Notification:

Silent Notification在iOS7以上便可以支持,但是每小時能推送的Silent Notification次數(shù)有限制庶柿。

2.VoIP Push Notification

VoIP Push Notification則是在iOS8以上才支持的新Push類型村怪,相比于Silent Notification,VoIP Push具有高優(yōu)先級浮庐、低延遲的優(yōu)勢甚负,并且沒有次數(shù)限制。

對比這兩種技術方案,VoIP Push Notification明顯更適合用于收款到賬語音提醒的喚醒方案梭域。

TTS合成語音

TTS語音合成方案分為離線合成方案和在線合成方案斑举,離線合成方案省去網(wǎng)絡請求,合成速度更快病涨,節(jié)省網(wǎng)絡流量懂昂,但是合成音的聽起來比較機械,語速和停頓的處理較差一些没宾。如果對合成音的效果要求不是特別高,可以考慮采用iOS自帶的AVSpeechSynthesis框架沸柔,免去語音庫的合入循衰,減少安裝包大小。

在線合成方案的效果則相對更像人聲褐澎,富有感情会钝。考慮到產(chǎn)品體驗工三,我們采用了搜索產(chǎn)品部提供的在線語音合成方案迁酸,接入方式可以看這篇文章。合成音格式支持wav俭正,mp3奸鬓,silk,amr掸读,speex串远,對比后發(fā)現(xiàn),在合成相同文本的情況下儿惫,amr的壓縮率最高澡罚,但是能聽到音質下降明顯。silk格式壓縮率次高肾请,且能保持相對清晰的音質留搔,單條合成語音大小在2KB左右。

喚醒后播放音頻文件

在請求到合成語音后铛铁,要在后臺或者鎖屏狀態(tài)下播放音頻文件隔显,AVAudio Session的Category值需要使用AVAudioSessionCategoryPlayback或是AVAudioSessionCategoryPlayAndRecord,CategoryOptions根據(jù)實際需要可選擇MixWithOthers(與其他聲音混音)或是DuckOthers(調低其他聲音的音量)避归。

需要注意的是荣月,只有iOS10以上才支持app被喚醒后在后臺/鎖屏狀態(tài)下播放音頻。所以iOS10以下的設備梳毙,在收到VoIP Push后只能在local push上設定一段固定鈴聲哺窄,這也是為什么iOS10以下只有“微信支付收款到賬”,而沒有后面具體的金額數(shù)值。

三萌业、靜音開關檢測

不幸的是坷襟,在產(chǎn)品發(fā)布后沒多久就受到了某互聯(lián)網(wǎng)大佬的吐槽。

從產(chǎn)品體驗上來說生年,收款到賬的金額播報是隨著local push的彈出一起播放的婴程,更像是一種特殊的push鈴聲,而蘋果對push鈴聲的處理是受到靜音開關控制的抱婉,所以講道理档叔,這個吐槽是合理的。然而前面提到App在被VoIP Push喚醒之后蒸绩,需要將AudioSessionCategory設置為AVAudioSessionCategoryPlayback或AVAudioSessionCategoryPlayAndRecord才可以在后臺播放音頻文件衙四,這兩種模式是不受靜音開關控制的。要實現(xiàn)這個需求患亿,就必須獲取當前靜音開關的狀態(tài)传蹈。而蘋果在iOS5之后并沒有明確地提供一種方式讓開發(fā)獲取靜音開關的狀態(tài),這就陷入了一個尷尬的局面步藕。

蘋果在iOS5之前可以使用以下方式監(jiān)聽靜音鍵開關

- (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之后便禁止了使用這種方式監(jiān)聽靜音按鍵惦界,背后的原因應該是蘋果希望開發(fā)者使用AVAudioSession來提供統(tǒng)一的音頻播放效果。

最后我在Reddit上找到了一種曲線救國的方式咙冗,實現(xiàn)起來也不復雜:使用AudioServicesPlaySystemSound播放一段0.2s的空白音頻沾歪,并監(jiān)聽音頻播放完成事件,如果從開始播放到回調完成方法的間隔時間小于0.1s乞娄,則意味當前靜音開關為開啟狀態(tài)瞬逊。

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);? }}

四、設置聲音閾值

另外一個用戶反饋較多的問題是聽不到播報聲音仪或,通過查看日志發(fā)現(xiàn)是觸發(fā)語音播報時确镊,用戶設置的系統(tǒng)音量過小所導致。首先想到的解決方案是直接設置AVAudioPlayer的volume(或者是AudioQueue中的kAudioQueueParam_Volume)范删,然而實驗過后發(fā)現(xiàn)這樣行不通蕾域,volume屬性受制于系統(tǒng)音量(比如系統(tǒng)volume是0.5,AVAudioPlayer的音量是0.6到旦,則最終的音量為0.5*0.6 =0.3)旨巷。要解決音量過小的問題,還是需要通過調節(jié)系統(tǒng)音量添忘。最終的解決方案借鑒了進入收付款展示二維碼時自動調節(jié)屏幕亮度的方案:如果屏幕亮度未達到閾值采呐,則調高屏幕亮度到閾值,離開頁面時搁骑,將亮度設回原亮度斧吐。同理又固,播放提示音時,若用戶設置的系統(tǒng)音量小于閾值煤率,則調節(jié)到閾值仰冠。提示音播放完畢后,將提示音調回原音量蝶糯。

控制系統(tǒng)音量有兩種方式:

方式一:通過MPMusicPlayerController設置音量

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

第一種方式簡單粗暴洋只,在設置的時候會彈出系統(tǒng)音量提示框,如果用戶在使用app的過程突然彈出音量框昼捍,會對用戶造成困擾识虚,不建議使用這種方式,并且蘋果在iOS7.0以后已將該屬性標為deprecated妒茬。

方式二:通過MPVolumeView設置音量

第二種方式則是將一個看不見的MPVolumeView添加到當前視圖上舷礼,系統(tǒng)音量提示框就不會顯示了

需要注意的是,在調節(jié)完系統(tǒng)音量需要將MPVolumeView移除郊闯,否則后續(xù)用戶手動調節(jié)音量會出現(xiàn)系統(tǒng)音量提示框不顯示的情況。

調節(jié)音量的方式蛛株,則是先取到MPVolumeView中名為MPVolumeSlider的子View团赁,并對其發(fā)送模擬用戶操作的事件。

- (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];? }}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末谨履,一起剝皮案震驚了整個濱河市欢摄,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌笋粟,老刑警劉巖怀挠,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異害捕,居然都是意外死亡绿淋,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門尝盼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來吞滞,“玉大人,你說我怎么就攤上這事盾沫〔迷” “怎么了?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵赴精,是天一觀的道長佩捞。 經(jīng)常有香客問我,道長蕾哟,這世上最難降的妖魔是什么一忱? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任莲蜘,我火速辦了婚禮,結果婚禮上掀潮,老公的妹妹穿的比我還像新娘菇夸。我一直安慰自己,他們只是感情好仪吧,可當我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布庄新。 她就那樣靜靜地躺著,像睡著了一般薯鼠。 火紅的嫁衣襯著肌膚如雪择诈。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天出皇,我揣著相機與錄音羞芍,去河邊找鬼。 笑死郊艘,一個胖子當著我的面吹牛荷科,可吹牛的內容都是我干的。 我是一名探鬼主播纱注,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼畏浆,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了狞贱?” 一聲冷哼從身側響起刻获,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎瞎嬉,沒想到半個月后蝎毡,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡氧枣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年沐兵,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片便监。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡痒筒,死狀恐怖,靈堂內的尸體忽然破棺而出茬贵,到底是詐尸還是另有隱情簿透,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布解藻,位于F島的核電站老充,受9級特大地震影響,放射性物質發(fā)生泄漏螟左。R本人自食惡果不足惜啡浊,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一觅够、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧巷嚣,春花似錦喘先、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至坝茎,卻和暖如春涤姊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背嗤放。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工思喊, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人次酌。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓恨课,卻偏偏與公主長得像,于是被迫代替她去往敵國和親岳服。 傳聞我的和親對象是個殘疾皇子庄呈,可洞房花燭夜當晚...
    茶點故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內容