iOS-使用Lame轉(zhuǎn)碼:PCM->MP3

本文是iOS 使用AUGraph錄音同時播放(并轉(zhuǎn)碼成MP3)中關(guān)于使用Lame將PCM轉(zhuǎn)碼成MP3的詳細說明。看看時間戳真是觸目驚心,時隔一年我才來填坑,慚愧懺愧。

有錄音需求的朋友請先閱讀上半部分,如果只需要轉(zhuǎn)碼MP3,看本文就夠了。

下載DEMO
DEMO做了兩件事情:

  • 一邊錄音一邊轉(zhuǎn)碼成MP3
  • 將Wav文件轉(zhuǎn)碼成MP3文件

區(qū)別在于前者直接將內(nèi)存中的數(shù)據(jù)轉(zhuǎn)為Mp3,后者要先把文件讀進內(nèi)存再轉(zhuǎn)碼。

編譯Lame庫

1、下載Lame源碼:lame.sourceforge.net
2、下載編譯腳本:lame-ios-build
最近19大,Lame官網(wǎng)我都上不去了,嫌麻煩的可以直接到我的Github上去下載編譯好的Lame庫,兩個文件分別是lame.hlibmp3lame.a,拖進項目即可食用。

準備

轉(zhuǎn)碼文件的話要先搞清楚PCM源的幾個參數(shù):

  • 采樣率
  • 單聲道/雙聲道
  • 每個采樣點的位數(shù)

我DEMO中的測試音頻信息如下,采樣率44.1kHz,雙聲道(Stereo),16bit


wav_info.png

開始使用

代碼節(jié)選自LameConver.m

lame_t lame; //1.聲明一個全局變量
@implementation LameConver
/**
 wav文件轉(zhuǎn)mp3文件

 @param wavPath wav文件路徑(輸入)
 @param mp3Path mp3文件路徑(輸出)
 */
- (void)converWav:(NSString *)wavPath toMp3:(NSString *)mp3Path successBlock:(successBlock)block{
    
    @try {
        FILE *fwav = fopen([wavPath cStringUsingEncoding:NSASCIIStringEncoding], "rb");
        fseek(fwav, 1024*4, SEEK_CUR); //跳過源文件的信息頭,不然在開頭會有爆破音
        FILE *fmp3 = fopen([mp3Path cStringUsingEncoding:NSASCIIStringEncoding], "wb");
        
        lame = lame_init(); //初始化
        lame_set_in_samplerate(lame, 44100.0); //設(shè)置wav的采樣率
        lame_set_num_channels(lame, 2); //聲道,不設(shè)置默認為雙聲道
        lame_init_params(lame);
        
        const int PCM_SIZE = 640 * 2; //雙聲道*2 單聲道640即可
        const int MP3_SIZE = 8800; //計算公式pcm_size * 1.25 + 7200
        short int pcm_buffer[PCM_SIZE];
        unsigned char mp3_buffer[MP3_SIZE];
        
        int read, write;
        
        do {
            //將文件讀進內(nèi)存
            read = fread(pcm_buffer, sizeof(short int), PCM_SIZE, fwav);
            if (read == 0) {
                //當read為0,說明pcm文件已經(jīng)全部讀取完畢,調(diào)用lame_encode_flush即可。
                write = lame_encode_flush(lame, mp3_buffer, MP3_SIZE);
            } else { //當read不為0,調(diào)用lame_encode_buffer_xxx進行轉(zhuǎn)碼
                //雙聲道千萬要使用lame_encode_buffer_interleaved這個函數(shù)
                //32位、單聲道需要調(diào)用其他函數(shù),具體看代碼后面的說明
                write = lame_encode_buffer_interleaved(lame, pcm_buffer, read/2, mp3_buffer, MP3_SIZE);
            }
            //保存mp3文件
            fwrite(mp3_buffer, write, 1, fmp3);
        } while (read != 0);
        //記得各種關(guān)閉
        lame_close(lame);
        fclose(fmp3);
        fclose(fwav);
    } @catch (NSException *exception) {
        NSLog(@"catch exception");
    } @finally {
        block(); //成功轉(zhuǎn)碼后調(diào)用
    }
}

- (void)

整個過程分為三步:

  1. 聲明Lame全局變量
  2. 初始化Lame并設(shè)置各種轉(zhuǎn)碼參數(shù)
  3. 開始轉(zhuǎn)碼

第2步只設(shè)置了采樣率和聲道,實際上還有很多參數(shù)可以設(shè)置:

  • lame_set_quality(lame_global_flags *, int); 設(shè)置壓縮品質(zhì),quality=0..9. 0=best (very slow). 9=worst. 品質(zhì)越好轉(zhuǎn)碼速度越慢
  • lame_set_out_samplerate(lame_global_flags *, int);設(shè)置輸出MP3的采樣率
  • lame_set_VBR(lame_global_flags *, vbr_mode);設(shè)置VBR類型
  • ......

lame.h頭文件中可以看到很多可設(shè)置參數(shù),我沒用到所以沒有仔細看。有興趣的可以去瞧瞧,注釋也很全。

第3步中函數(shù)的調(diào)用,搞清楚輸入源的聲道和每個采樣點的位數(shù),調(diào)用對應(yīng)的函數(shù):

  • lame_encode_buffer單聲道,16位
  • lame_encode_buffer_interleaved雙聲道,16位
  • lame_encode_buffer_float單聲道,32位

Lame就是這么方便簡單,到這里就結(jié)束啦。如果本文對你有幫助,請Star,非常感謝!

fopen

補充一個fopen的知識點
頭文件:#include<stdio.h>
定義函數(shù):FILE * fopen(const char * path,const char * mode);
函數(shù)參數(shù)說明:
path:字符串包含欲打開的文件路徑及文件名。
mode:字符串則代表著流形態(tài),取值如下:

  • r 打開只讀文件,該文件必須存在。
  • r+打開可讀寫的文件,該文件必須存在。
  • w打開只寫文件,若文件存在則文件長度清為0,即該文件內(nèi)容會消失。若文件不存在則建立該文件。
  • w+打開可讀寫文件,若文件存在則文件長度清為零,即該文件內(nèi)容會消失。若文件不存在則建立該文件。
  • a以附加的方式打開只寫文件。若文件不存在,則會建立該文件,如果文件存在,寫入的數(shù)據(jù)會被加到文件尾,即文件原先的內(nèi)容會被保留。
  • a+ 以附加方式打開可讀寫的文件。若文件不存在,則會建立該文件,如果文件存在,寫入的數(shù)據(jù)會被加到文件尾后,即文件原先的內(nèi)容會被保留。

上述的形態(tài)字符串都可以再加一個b字符,如rbw+bab+等組合,加入b字符用來告訴函數(shù)庫打開的文件為二進制文件,而非純文字文件。

返回值:
文件順利打開后,指向該流的文件指針就會被返回,如果文件打開失敗則返回NULL。

參考

stackoverflow
文件操作函數(shù)fread,fwrite,fseek,fopen,fclose解析

關(guān)于我

我是可樂,在職iOS開發(fā),業(yè)余時間獨立開發(fā)App,現(xiàn)有上架作品:Mini記賬
vx公z號:沙拉可樂, 分享獨立開發(fā)的干貨和背后的故事

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,702評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,143評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,553評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,620評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,416評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,940評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,024評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,170評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,709評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,597評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,291評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,029評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,407評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,663評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,403評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,746評論 2 370

推薦閱讀更多精彩內(nèi)容