虽说Speex已经被Opus代替了,官方也是想我们向Opus迁移,但是Speex还是可以使用的。
可以先看Speex的官方教程,还是挺简单的,但是坑也不少。
要移植到iOS上,首先把Speex的代码打包成.a的静态库,可以google有相关代码,然后也可以直接用编译好的(貌似需要关闭Bitcode),感谢作者。
在编译好的项目中有:
还需要在这个工程下的TEST_Speex_001目录中找到SpeexAllHeader.h文件,他帮我们把需要的Speex的头文件导入好了。
把以上文件拷贝到你的工程中(最好新建一个Speex的文件夹 方便管理),然后引入,最终结果:
然后我们就可以使用了。
推荐可以看这两篇文章:Speex编解码器,(原创)speex与wav格式音频文件的互相转换,后一篇虽然是安卓的,但给了我很大的启发。让我知道了Speex的每次读取20字节,然后解析成320字节,编码则是相反的。
【音频的组成】
当前,我们所说的音频,都是数字音频。数字音频由采样频率、采样精度、声音通道数三个部分组成。
- 采样频率:既采样率,指记录声音时每秒的采样个数,它用赫兹(Hz)来表示。一般用44.1kHz,也可以是>8kHz,11.025kHz,48kHz,96kHz等。
- 采样精度:指记录声音的动态范围,它以位(Bit)为单位,单个音频采样用得较多的是16位,当然也可以使用8位,24位,甚至32位
- 声音通道:既声道数(1-8个),用的较多的是2声道,也有单声道,5.1声道,7.1声道。
通俗点说,我们可以把声波看成是一条曲线,我们知道,曲线是由点组成的,采样率就是每秒长度(上图横轴)中点的个数。而采样精度就是动态范围(上图竖轴)中点的个数。这两个维度的定位越细,声音的真实还原度就越高,音质也就会更好,当然,音频文件也就会越大。上面那个同事遇到的客户所说的,就是SONY公司最新发布的音频格式Hi-Res Audio,是192kHz / 24bit,6通道录制的音频文件,无损格式的大小当然就会在200多兆了。
采样率根据使用类型不同大概有以下几种(k既千位符号,1khz=1000hz):8khz:电话等使用,对于记录人声已经足够使用。
22.05khz:广播使用频率。
44.1kb:音频CD。
48khz:DVD、数字电视中使用。
96khz-192khz:DVD-Audio、蓝光高清等使用。
采样精度常用范围为8bit-32bit,而CD中一般都使用16bit。作者:Ianlie Dark链接:
https://www.zhihu.com/questio...
来源:知乎著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
一帧:应该是指持续采样时间,这个是很灵活的可以使用20ms,也可是200ms,一般来说时间越短延时就越少。
这样一帧的PCM数据大小就很容易计算出来:PCMBufferSize = 采样率采样时间采样位深/8*通道数 Bytes
其实上述的20 -> 320字节是有和音频有关,和Speex没啥关系。
就目前来看的确和音频的位数有关(之前不确定),PCM音频大小有这个计算公式:
数据量(字节/秒)= (采样频率(Hz)× 采样位数(bit)× 声道数)/ 8
我的是 8000Hz采样率 16位音频 单声道(1),所以320字节:
(8000 * 16 * 1) / 8 = 16000(字节/秒) / 1000 = 16(字节/毫秒)
然后转换成毫秒: 16(字节/毫秒) * 20毫秒 = 320(字节/毫秒)
20ms是一帧音频的长度
所以意味着8k采样率 16位音频 单声道的一帧长度(20ms)的音频大小是320字节
然后如果用的是short的数组,那么这个数组的长度应该是160,因为一个short等于两个字节。
接下来贴出代码(待完善内存释放和噪音抑制):
SpeexToPcm.h
#import <Foundation/Foundation.h>
@interface SpeexToPcm : NSObject
/**
把20个char的speexData转换成160个shot的pcmData 320字节
@param speexData speex的数据
@param pcmData 转换出来的数据
@return 返回转换结果 0表示ok
*/
- (int) converWith: (char*)speexData toPcm:(short*)pcmData;
@end
SpeexToPcm.m
#import "SpeexToPcm.h"
#import "SpeexAllHeader.h"
#define SPEEX_SIZE 20
#define PCM_SIZE 160
@interface SpeexToPcm() {
/* 保存编码的状态 */
void *decoder;
/* 保存字节(也就是sppex数据 但是要经过speex的转换) 他们可以被speex常规读写 */
SpeexBits bits;
}
@end
@implementation SpeexToPcm
- (instancetype)init
{
self = [super init];
if (self) {
/**
返回一个新创建的解码器状态结构的句柄。
Parameters:
mode Speex mode (one of speex_nb_mode or speex_wb_mode)
*/
// 其中speex_nb_mode是SpeexMode类型的变量,表示的是窄带模式。还有speex_wb_mode表示宽带模 式、speex_uwb_mode表示超宽带模式。
decoder = speex_decoder_init(&speex_nb_mode);
// 是否启用增强器
int tmp = 1;
/**
像ioctl函数一样用来控制编码器参数
Parameters:
state Decoder state
request ioctl-type request (one of the SPEEX_* macros)SPEEX_GET_ENH | SPEEX_SET_ENH
ptr Data exchanged to-from function
*/
speex_decoder_ctl(decoder, SPEEX_SET_ENH, &tmp);
// 为SpeexBits结构初始化和分配资源
speex_bits_init(&bits);
}
return self;
}
- (int) converWith: (char*)speexData toPcm:(short*)pcmData{
// 初始化来自存储器区域中的数据的比特流
// void speex_bits_read_from(SpeexBits *bits, const char *bytes, int len);
speex_bits_read_from(&bits, speexData, SPEEX_SIZE);
memset(pcmData, 0, PCM_SIZE);
/**
Parameters:
state Decoder state
bits Bit-stream from which to decode the frame (NULL if the packet was lost)
out Where to write the decoded frame
*/
return speex_decode_int(decoder, &bits, pcmData);
}
@end
有疑问或者纠错,欢迎评论。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。