现在很多手机游戏中的聊天系统都加入语音聊天的功能,相比于传统的文字聊天,语音聊天在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