iOS解碼Speex數據(包含編譯好的文件)

雖然說Speex已經被Opus代替了,官方也是想咱們向Opus遷移,可是Speex仍是可使用的。
能夠先看Speex的官方教程,仍是挺簡單的,可是坑也很多。
要移植到iOS上,首先把Speex的代碼打包成.a的靜態庫,能夠google有相關代碼,而後也能夠直接用編譯好的(貌似須要關閉Bitcode),感謝做者。html

在編譯好的項目中有:
**須要的文件**node

還須要在這個工程下的TEST_Speex_001目錄中找到SpeexAllHeader.h文件,他幫咱們把須要的Speex的頭文件導入好了。git

把以上文件拷貝到你的工程中(最好新建一個Speex的文件夾 方便管理),而後引入,最終結果:github

***項目文件,其中SpeexToPcm文件是我本身的類,下面會貼出代碼***

導入.a庫

而後咱們就可使用了。
推薦能夠看這兩篇文章: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):ui

8khz:電話等使用,對於記錄人聲已經足夠使用。
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

有疑問或者糾錯,歡迎評論。

相關文章
相關標籤/搜索