現在很多手機游戲中的聊天系統都加入語音聊天的功能,相比於傳統的文字聊天,語音聊天在MMORPG中顯得尤為重要,畢竟直接口頭交流總比你碼字快得多了,也更直觀些。
實現語音聊天的方法很多,U3D中有不少第三方的插件,提供了很多功能強大的語音功能,具體有哪些我就不一一舉例了(其實我都沒用過- -!),本文想從一個原生開發的角度去實現一個簡單的語音聊天功能。
語音聊天大概流程如圖:
上圖中可以看到,客戶端錄制語音數據,並進行編碼轉換,數據壓縮,然后把語音數據發送到語音服務器,語音服務器進行派發功能(語音服務器也可以對語音進行翻譯)
當客戶端請求或是接收到語音服務器推送過來的語音數據后,對數據進行解壓,轉為可播放的編碼,然后進行播放,流程相當簡單。
但這里我們只探討客戶端這邊的處理,關於怎么搭建語音服務器還有怎么壓縮,並發送語音數據這塊,在這里就不詳細的展開了。
這里可能會遇到的問題有:
1.U3D C# 與 iOS的OC之間是怎么通訊等
2.iOS怎么調用原生的錄音功能和播放功能
3.怎么轉換編碼問題
好吧,針對這三點,我們逐一來愉快地解決:
1.U3D C# 與 iOS的OC之間是怎么通訊
關於這個問題,應該比較簡單,和android的不同,C# 與OC通訊其實有點像把非托管的動態庫倒入C# 中,我們可以在OC中添加一個C++接口
extern "C" void __SendOCMessage(const char* methodName,const char* arg0,const char* arg1);
再在C#中引入接口
private const string IOSSDKDLL = "__Internal";
#if UNITY_IPHONE
[DllImport(IOSSDKDLL, CallingConvention = CallingConvention.Cdecl)]
public static extern void __SendSDKMessage(string methodName,string arg0, string arg1);
#endif
這樣,就可以在C# 中發送消息給OC了,所有消息都可以通過這個接口來發送,只需要判斷參數methodName來執行相應模塊就可以了
反過來,如過OC想發送消息給C# ,我們可以調用U3D提供的OC接口
extern void UnitySendMessage(const char *, const char *, const char *);
第一個參數是場景中的GameObject名字,第二個參數是組件中的方法名字,第三個參數是任意的消息參數。
2.iOS怎么調用原生的錄音功能和播放功能
iOS中的錄音功能,我們可以引入AVFoundation的庫
#import <AVFoundation/AVFoundation.h>
我們會用到AVAudioRecorder和AVAudioPlayer這兩個類,分別是錄音類和播放類
AVAudioRecorder
我們可以創建一個錄音實例進行錄音
//創建錄音文件保存路徑
NSURL *url=[NSURL URLWithString:voiceDataPath];
//創建錄音格式設置
NSDictionary *setting=[NSMutableDictionary dictionary];
//設置錄音格式
[setting setObject:@(kAudioFormatLinearPCM) forKey:AVFormatIDKey];
//設置錄音采樣率,一般采用8000,太低失真比較嚴重
[setting setObject:@(8000) forKey:AVSampleRateKey];
//設置通道,單通道
[setting setObject:@(1) forKey:AVNumberOfChannelsKey];
//每個采樣點位數,分為8、16、24、32,這里采用16位
[setting setObject:@(16) forKey:AVLinearPCMBitDepthKey];
//是否使用浮點數采樣
[setting setObject:@(YES) forKey:AVLinearPCMIsFloatKey];
//創建錄音機
NSError *error=nil;
AVAudioRecorder *audioRecorder = [[AVAudioRecorder alloc]initWithURL:url settings:setting error:&error];
[audioRecorder record];
//停止錄音的時候,調用Stop接口
[audioRecorder stop];
AVAudioPlayer
同樣,我們可以創建一個音頻播放器的實例
NSURL *url=[NSURL URLWithString:voiceDataPath];
NSError *error=nil;
AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:url error:&error];
audioPlayer.numberOfLoops=0;
//設置播放聲音
audioPlayer.volume = 1;
//播放
[audioPlayer prepareToPlay];
//停止播放
[audioPlayer stop];
所以不難想象,結合上面的流程,整個錄音和播放的流程就是
1.錄音的時候,U3D發消息到iOS中創建AVAudioRecorder的實例進行錄音,並附帶參數voiceDataPath為錄音文件的絕對路徑,錄音結束,錄音文件將保存在所傳入的voiceDataPath路徑,並通知回U3D中錄音完成,U3D回調后,將數據發送給語音服務器。
2.播放的時候,U3D中請求語音服務器下載數據,下載完成后把數據儲存在本地,並發消息到iOS中創建AVAudioPlayer的實例進行播放聲音文件,並附帶參數voiceDataPath為聲音文件所在的路徑,然后播放該聲音
大概的流程就是這樣了,應該是流程也比較簡單,只需要封裝下AVAudioPlayer和AVAudioRecorder的接口,就可以實現一個簡單的語音聊天模塊了。
3.怎么轉換編碼問題
我們知道iOS錄制的格式只有wav的格式,這個格式明顯回占用很大的內存空間,不方便發送數據到語音服務器或是下載,所以我們需要准換為壓縮的音頻數據格式易變減少錄音文件的大小,保證語音聊天的流暢體驗。
ARM格式明顯是語音聊天最好的壓縮格式了,在安卓中這個格式可以直接轉換並播放,但在iOS中,並不支持這種格式的播放和轉換,所以需要引入一個轉換的類庫VoiceConverter,這個類庫在gitHub中可以找到,我在隨筆后面會給出,這個類庫簡單直接,提供了兩個借口,可以實現arm和wav的相互轉化
[VoiceConverter wavToAmr:wavPath amrSavePath:amrPath];
[VoiceConverter amrToWav:armPath wavSavePath:wavPath];
所以結合上述錄音和播放的流程
1.我們需要在AVAudioRecorder錄制結束后,把wav格式的voiceData轉化為arm格式然后發送給語音服務器
2.當從語音服務器下載arm的語音文件后,先把語音文件轉為wav格式,再創建AVAudioPlayer對象進行播放
好了關於iOS版本的語音聊天模塊,大概就是這樣了,依靠iOS 原生API的
AVAudioPlayer和AVAudioRecorder就可以實現客戶端的語音錄制和播放功能,再結合語音服務器,這個語音功能就能真正的運行在游戲當中了
最后,關於語音翻譯
這個我倒是沒怎么接觸過,也不知道原生的iOS API有沒提供翻譯功能或有第三方庫可以進語音翻譯,不過聽別的小伙伴說,翻譯都是在語音服務器中完成的,語音服務器調用第三方的接口,可以對語音進行異步翻譯,完成后再推送給客戶端的,有興趣的朋友可以自己再去探索下這個語音翻譯,也可以留言推薦下給我,一起學習下。
VoiceConvert 地址 https://pan.baidu.com/s/1kVDHFMn