微信iOS收款到賬語音提醒開發總結

来源:http://www.cnblogs.com/bugly/archive/2017/09/11/7504984.html
-Advertisement-
Play Games

TTS語音合成方案分為離線合成方案和線上合成方案,離線合成方案省去網路請求,合成速度更快,節省網路流量,但是合成音的聽起來比較機械,語速和停頓的處理較差一些。如果對合成音的效果要求不是特別高,可以考慮採用iOS自帶的AVSpeechSynthesis框架,免去語音庫的合入,減少安裝包大小。 ...


本文來自於騰訊Bugly公眾號(weixinBugly),未經作者同意,請勿轉載,原文地址:https://mp.weixin.qq.com/s/yYCaPMxHGT9LyRyAPewVWQ

作者:littleliang

一、背景

為瞭解決小商戶老闆們在頻繁交易中不方便核對、確認到賬的痛點,產品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];
    }
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 效果 源碼 https://github.com/YouXianMing/Animations 特點 1. 每一個row都可以像使用UITableView的cell一樣可以定製,繼承CustomPickerView即可 2. 數據源通過PickerViewDataAdapter來指定,每一個Pick ...
  • iOS精選源碼 iOS優質博客 首先你要花點時間針對objective-c語言的學習;畢竟這個是iOS開發的基礎(你也可以嘗試用Swift,但此項目只是針對OC),編程套路其實都是差不多,多寫多想多實踐;關於環境的搭建就不在本文進行介紹,這部分內容可以自行百度或谷歌,都有相應的說明;對於一個剛入門總 ...
  • 一、編譯生成FFmpeg的頭文件和so庫 如果不知道怎麼做可以看這裡:http://blog.csdn.net/qsw15923/article/details/77943515 二、新建項目 在新建時勾選下方的include C++support,然後一路下一步即可 創建完項目後如果之前沒有配置N ...
  • 【新版】Android技術博客精華彙總(原文鏈接內持續更新) http://www.apkbus.com/thread-313856-1-1.html Kotlin Kotlin學習資料彙總 http://www.apkbus.com/blog-261991-68033.html 使用Kotlin來 ...
  • Android精選源碼 Android優質博客 從零開始搭建一個項目(rxJava+Retrofit+Dagger2) 下http://www.apkbus.com/blog-873057-72599.htmltip:本文所使用到的技術有 RxJava,Retrofit,Glide,Dagger2, ...
  • FFmpeg是很好用的一個音視頻庫,功能強大,但是用起來並不是很方便。之前一直不想用FFmpeg,因為感覺編譯太麻煩,但是到了不得不用的時候了,沒辦法,參考了網上大神的方法,在這裡自己也記錄一下方便以後再次查看。 一、環境 Ubuntu14.04 二、NDK環境配置 NDK下載鏈接:https:// ...
  • 當我們使用Xcode進行開發的時候,並不是所有的時候都需要將代碼運行在iPhone,有時候模擬器就可以解決這些問題, 但是當你使用模擬器的時候會發現,在TextFiled中輸入信息時,如果你是用模擬器上的鍵盤進行操作,OK沒問題, 但是當你是用鍵盤輸入信息的話,那麼你會發現模擬器上的鍵盤就不會再顯示 ...
  • 那麼 Appium 到底是怎麼工作的呢? Appium 官方教程好難啊, 我自己總結了一份超簡單 Appium 教程。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...