虽然说Speex已经被Opus代替了,官方也是想咱们向Opus迁移,可是Speex仍是可使用的。
能够先看Speex的官方教程,仍是挺简单的,可是坑也很多。
要移植到iOS上,首先把Speex的代码打包成.a的静态库,能够google有相关代码,而后也能够直接用编译好的(貌似须要关闭Bitcode),感谢做者。html
在编译好的项目中有:node
还须要在这个工程下的TEST_Speex_001目录中找到SpeexAllHeader.h文件,他帮咱们把须要的Speex的头文件导入好了。git
把以上文件拷贝到你的工程中(最好新建一个Speex的文件夹 方便管理),而后引入,最终结果:github
而后咱们就可使用了。
推荐能够看这两篇文章: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):ui8khz:电话等使用,对于记录人声已经足够使用。
22.05khz:广播使用频率。
44.1kb:音频CD。
48khz:DVD、数字电视中使用。
96khz-192khz:DVD-Audio、蓝光高清等使用。
采样精度经常使用范围为8bit-32bit,而CD中通常都使用16bit。google做者:Ianlie Dark连接:
https://www.zhihu.com/questio...
来源:知乎著做权归做者全部。商业转载请联系做者得到受权,非商业转载请注明出处。编码
一帧:应该是指持续采样时间,这个是很灵活的可使用20ms,也但是200ms,通常来讲时间越短延时就越少。
这样一帧的PCM数据大小就很容易计算出来:PCMBufferSize = 采样率采样时间采样位深/8*通道数 Bytesspa
其实上述的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
有疑问或者纠错,欢迎评论。