雖然說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
有疑問或者糾錯,歡迎評論。