iOS 通過lame將錄制音頻轉(zhuǎn)換成Mp3

下載

1、下載編譯腳本:https://github.com/kewlbear/lame-ios-build
2、下載lame:http://lame.sourceforge.net
3、新建文件夾用來存放下載下來的lame和腳本文件
4、在新建的文件夾下運行:./build-lame.sh,運行結(jié)果如下圖

編譯完成后生成fat-lame文件夾,將fat-lame文件夾中的lame.hlibmp3lame.a導入工程即可

使用

將編譯完成后的lame庫加入到工程中

注意:
1、初始化lame的時候,要設(shè)置1為單通道。設(shè)置單聲道會更大程度減少壓縮后文件的體積。
lame_set_num_channels(lame,1); //設(shè)置1為單通道,默認為2雙通道
2、lame_close(lame); 之前需要添加:
lame_mp3_tags_fid(lame, mp3);// 可解決獲取時長不準的問題

引入庫頭文件

//ConvertAudioFile.m
#import "lame.h"

錄制完成后轉(zhuǎn)碼

//這是錄完再轉(zhuǎn)碼的方法, 如果錄音時間比較長的話,會要等待幾秒...
+ (void)conventToMp3WithCafFilePath:(NSString *)cafFilePath
                        mp3FilePath:(NSString *)mp3FilePath
                         sampleRate:(int)sampleRate
                           callback:(void(^)(BOOL result))callback
{

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        @try {
            int read, write;

            FILE *pcm = fopen([cafFilePath cStringUsingEncoding:1], "rb");  //source 被轉(zhuǎn)換的音頻文件位置
            fseek(pcm, 4*1024, SEEK_CUR);                                   //skip file header
            FILE *mp3 = fopen([mp3FilePath cStringUsingEncoding:1], "wb+");  //output 輸出生成的Mp3文件位置

            const int PCM_SIZE = 8192;
            const int MP3_SIZE = 8192;
            short int pcm_buffer[PCM_SIZE*2];
            unsigned char mp3_buffer[MP3_SIZE];

            lame_t lame = lame_init();
            lame_set_num_channels(lame,1);//設(shè)置1為單通道,默認為2雙通道
            lame_set_in_samplerate(lame, sampleRate);
            lame_set_VBR(lame, vbr_default);
            lame_init_params(lame);

            do {

                read = (int)fread(pcm_buffer, 2*sizeof(short int), PCM_SIZE, pcm);
                if (read == 0) {
                write = lame_encode_flush(lame, mp3_buffer, MP3_SIZE);

                } else {
                    write = lame_encode_buffer_interleaved(lame, pcm_buffer, read, mp3_buffer, MP3_SIZE);
                }

                fwrite(mp3_buffer, write, 1, mp3);

            } while (read != 0);

            lame_mp3_tags_fid(lame, mp3);

            lame_close(lame);
            fclose(mp3);
            fclose(pcm);
        }
        @catch (NSException *exception) {
            NSLog(@"%@",[exception description]);
            if (callback) {
                callback(NO);
            }
        }
        @finally {
            NSLog(@"-----\n  MP3生成成功: %@   -----  \n", mp3FilePath);
            if (callback) {
                callback(YES);
            }
        }
    });

調(diào)用

[ConvertAudioFile conventToMp3WithCafFilePath:self.cafPathmp3FilePath:self.mp3Path

{

sampleRate:ETRECORD_RATE callback:^(BOOL result)

NSLog(@"---- 轉(zhuǎn)碼完成 --- result %d ---- ", result);}];

邊錄制邊轉(zhuǎn)碼

通常我們是在錄制結(jié)束之后, 再進行轉(zhuǎn)碼; 當錄制的時間較長 , 會消耗的時間比較長,用戶需要等待轉(zhuǎn)碼結(jié)束后,才能操作; 但是如果我們使用邊錄制,邊轉(zhuǎn)碼的方式, 開另外開個線程同時進行轉(zhuǎn)碼,則幾乎沒有等待的時間。

具體實現(xiàn)方法:當錄音進行中時, 會持續(xù)讀取到指定大小文件來進行編碼,讀取不到,則線程休眠在while的條件中, 我們收到錄音結(jié)束的條件,則會結(jié)束do while的循環(huán)。我們需要在錄制結(jié)束后發(fā)送個信號, 讓 do while跳出循環(huán)。

- (void)conventToMp3WithCafFilePath:(NSString *)cafFilePath
                        mp3FilePath:(NSString *)mp3FilePath
                         sampleRate:(int)sampleRate
                           callback:(void(^)(BOOL result))callback
{
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        
        @try {
            int read, write;
            
            FILE *pcm = fopen([cafFilePath cStringUsingEncoding:1], "rb");  //source 被轉(zhuǎn)換的音頻文件位置
            FILE *mp3 = fopen([mp3FilePath cStringUsingEncoding:1], "wb+");  //output 輸出生成的Mp3文件位置
            
            const int PCM_SIZE = 8192;
            const int MP3_SIZE = 8192;
            short int pcm_buffer[PCM_SIZE*2];
            unsigned char mp3_buffer[MP3_SIZE];
            
            //這里要注意,lame的配置要跟AVAudioRecorder的配置一致,否則會造成轉(zhuǎn)換不成功
            lame_t lame = lame_init();
            lame_set_num_channels(lame,1);//設(shè)置1為單通道,默認為2雙通道 設(shè)置單聲道會更大程度減少壓縮后文件的體積
            lame_set_in_samplerate(lame, sampleRate);//采樣率
            lame_set_VBR(lame, vbr_default);
            lame_init_params(lame);
            
            BOOL isSkipPCMHeader = NO;
            
            __weak typeof(self) weakSelf = self;
            
            do {
                long curpos = ftell(pcm);
                long startPos = ftell(pcm);
                fseek(pcm, 0, SEEK_END);
                long endPos = ftell(pcm);
                long length = endPos - startPos;
                fseek(pcm, curpos, SEEK_SET);
                
                if (length > PCM_SIZE * 2 * sizeof(short int)) {
                    
                    if (!isSkipPCMHeader) {
                        //Uump audio file header, If you do not skip file header
                        //you will heard some noise at the beginning!!!
                        fseek(pcm, 4 * 1024, SEEK_CUR);
                        isSkipPCMHeader = YES;
                        NSLog(@"skip pcm file header !!!!!!!!!!");
                    }
                    
                    read = (int)fread(pcm_buffer, 2 * sizeof(short int), PCM_SIZE, pcm);
                    write = lame_encode_buffer_interleaved(lame, pcm_buffer, read, mp3_buffer,
                                                           MP3_SIZE);
                    fwrite(mp3_buffer, write, 1, mp3);
                    
                    NSLog(@"read %d bytes", write);}
                
                else {
                        
                        [NSThread sleepForTimeInterval:0.05];
                        
//                        MyLog(@"sleep");
                    
                }
                
            } while (! weakSelf.stopRecord);
            
            lame_mp3_tags_fid(lame, mp3);// 解決獲取時長不準的問題
            
            lame_close(lame);
            fclose(mp3);
            fclose(pcm);
        }
        @catch (NSException *exception) {
            MyLog(@"%@",[exception description]);
            if (callback) {
                callback(NO);
            }
        }
        @finally {
            MyLog(@"-----\n  MP3生成成功: %@   -----  \n", mp3FilePath);
            if (callback) {
                callback(YES);
            }
        }
    });
}

調(diào)用

[[ConvertAudioFile sharedInstance] conventToMp3WithCafFilePath:self.cafPathmp3FilePath:self.mp3Path

sampleRate:ETRECORD_RATE callback:^(BOOL result)

{
NSLog(@"---- 轉(zhuǎn)碼完成 --- result %d ---- ", result);

}];

參考:
https://blog.csdn.net/u011270282/article/details/77483359
https://blog.csdn.net/lovechris00/article/details/79034036
https://blog.csdn.net/ysy441088327/article/details/7392842
https://blog.csdn.net/lovechris00/article/details/52033555

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容