版权声明:本文为原创文章,未经允许不得转载
一、简介
现在有个需求,在局域网内实现实时语音,传输层协议使用UDP协议,如果直接使用AudioRecord进行录制音频流并发送到另一端进行播放,音质会非常差,而且断断续续,原因如下:
采样频率: fm = 44.1KHz
量化位数:16bit
声道配置:2(双声道)
那么,码率 V = 44.1K * 16 *2 = 1411.2 Kbps = 176.4KBps,即每秒传输速率大概176.4KB,
若音频帧时间为20ms,每个音频数据包大小为 size = 176.4KBps * 0.02s = 3.528KB,
一般情况下,我们每次读取一个音频帧的数据,可以取整为3600Byte,
所以 每秒大概发送 176.4/3.6=49 个数据包,每个数据包大小为3.6KB。
如果再考虑到数据报头,实测每秒发送约45个数据包,每秒传输速率大概180KB。
由于一般都是使用手机连接Wifi,这就要求网络质量和硬件设备必须很好,而且信道干扰较弱,并且链接的设备不能过多。只要稍微信号不好,就会导致丢包率特别高,而且延时十分大,根本无法满足通信的需要。在这种情况下,我们就需要进行语音压缩、降噪等处理。
二、局域网语音配置
如果传输的仅仅是语音信息,那么不需要很高的采样频率,可以使用8KHz进行采样,单通道即可。
private int DEFAULT_SAMPLERATEINHZ = 8000; // 采样频率
private int DEFAULT_AUDIOFORMAT = AudioFormat.ENCODING_PCM_16BIT; // 数据格式
private int DEFAULT_STREAMTYPE = AudioManager.STREAM_MUSIC; // 音频类型
private int DEFAULT_CHANNELCONFIG_OUT = AudioFormat.CHANNEL_OUT_MONO; // 声道配置
private int DEFAULT_MODE = AudioTrack.MODE_STREAM; // 输出模式
private int DEFAULT_CHANNELCONFIG_IN = AudioFormat.CHANNEL_IN_MONO; // 声道配置
private int DEFAULT_AUDIOSOURCE = MediaRecorder.AudioSource.MIC; // 音频来源
- 采样频率:8KHz,可以采集比较完整的语音信息。当然,对于高频的信息无能为力;
- 数据格式:16bit,可以较为详细的表示声音的幅度;
- 声道配置:单声道输入和输出,能够适配所有的机型,少部分手机不支持双声道(立体声),如果设置立体声会只出现左耳机有声音的情况;
- 音频类型:音乐流
- 输出模式:音频流
- 音频来源:麦克风
三、Speex
3.1 简介
Speex是一套主要针对语音的开源免费,无专利保护的音频压缩格式。Speex工程着力于通过提供一个可以替代高性能语音编解码来降低语音应用输入门槛 。另外,相对于其它编解码器,Speex也很适合网络应用,在网络应用上有着自己独特的优势。同时,Speex还是GNU工程的一部分,在改版的BSD协议中得到了很好的支持。
3.2 技术特点
Speex是基于CELP并且专门为码率在2-44kbps的语音压缩而设计的。它的特点有:
- 窄带(8kHz),宽带(16kHz)和超宽带(32kHz)压缩于同一位流。
- 强化立体编码
- 数据包丢失隐蔽
- 可变比特率(VBR)
- 语音捕捉(VAD)
- 非连续传输(DTX)
- 定点运算
- 感官回声消除(AEC)
- 噪音屏蔽
3.3 开发-语音压缩
由于语音压缩的底层代码都是用C/C++写的,所以对于我们可怜的Android来说需要使用NDK进行JNI开发了,如果对这方面不了解的