1. 程式人生 > >ios 訊飛pcm轉mp3處理

ios 訊飛pcm轉mp3處理

最近公司需要做訊飛語音聲音轉文字,並將聲音檔案上傳的功能,遇到三個坑點,記錄下:

1、訊飛iOS版語音聽寫的錄音檔案,如果程式碼不指定存放路徑,是不儲存的,也取不出來的,而且指定別的沙盒路徑無效,只存放在cache目錄下如下:

NSString* path = @"nowSendVoice.pcm";
            [_iFlySpeechRecognizer setParameter:path forKey:[IFlySpeechConstant ASR_AUDIO_PATH]];

這樣獲取到的路徑就是var/.../.../Labrary/cache/nowSendVoice.pcm;這個就是每次語音識別完成(無論是否識別成功,都會在這個路徑生成剛才聽寫的語音檔案)後,而且截止到目前,訊飛只能生成pcm(這種格式音訊檔案比較大)檔案格式,其他的mp3、amr都不支援,所以需要自己轉換成mp3、amr等壓縮過的音訊檔案.

2、拿到pcm檔案後通過lame庫(附贈lame庫下載連結,直接拉到工程裡,匯入.h就可以用了 https://download.csdn.net/download/a787188834/10750140)轉換成mp3檔案時,遇到pcm檔案100多k,而mp3檔案只有10k左右,而且mp3檔案只有2秒,或者1秒,並且全是雜音,這個我這邊是因為設定的訊飛語音聽寫的識別後置端點時間過短,我這邊原先設定的是1s,就會造成,我說了“12345”這段語音,而1秒過後,訊飛識別自動將儲存的“12345”語音刪除,重新開始下一段語音,這樣造成了拿到的語音不是“12345”的語音,而是很短的語音,所以這個坑,只需要改下訊飛的前後置端點就好,我的前端點為1秒,後端點3秒,基本識別的延遲使用者感覺不到,效果也比較好

//設定後端點
            [_iFlySpeechRecognizer setParameter:instance.vadEos forKey:[IFlySpeechConstant VAD_EOS]];
            //設定前端點
            [_iFlySpeechRecognizer setParameter:instance.vadBos forKey:[IFlySpeechConstant VAD_BOS]];

3、就是轉換後的mp3語速變快,並且我的聲音明顯變得尖銳了,這個主要是因為設定的取樣率的問題,我設定訊飛語音取樣率是16k,但是轉換的時候複製別人的程式碼11025.0的取樣率,就造成這個坑了,改下采樣率,將你設定的取樣率跟訊飛的取樣率一至就可以,mp3一般是雙聲道的,所以我這裡訊飛16k取樣率,轉換成mp3的時候是8k取樣率

主要程式碼:使用訊飛語音聽寫功能的例子(訊飛語音轉文字iOS使用連結 https://blog.csdn.net/a787188834/article/details/80319482 ),(訊飛語音同寫完,直播間聲音沒有bug解決:https://blog.csdn.net/a787188834/article/details/78612861)在我的其他博文 有敘述,點連結檢視就好,這裡只貼獲取到訊飛pcm檔案,後轉換mp3的程式碼,而且最好將轉換mp3的程式碼放到非同步執行緒中,不然的話,轉換耗時,可能引起其他程序卡頓,比如直播間播放的時候,發言轉語音,不放在非同步執行緒轉換就可能造成直播間畫面的卡頓.

- (void)convertToMp3WithPcmPathStr:(NSString*)pcmPathStr newMp3PathStr:(NSString*)newMp3PathStr beforeBlock:(void (^)())beforeBlock endBlock:(void(^)())endBlock errorBlock:(void(^)())errorBlock{
    
    NSLog(@"pcm :%@  \n wav:%@",pcmPathStr,newMp3PathStr);
    
    if (beforeBlock) {
        beforeBlock();
    }
    //非同步處理,不然可能引起直播間某些操作卡頓
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        @try {
            int read,write;
            //只讀方式開啟被轉換音訊檔案
            FILE *pcm = fopen([pcmPathStr cStringUsingEncoding:1], "rb");
            fseek(pcm, 4 * 1024, SEEK_CUR);
            //刪除頭,否則在前一秒鐘會有雜音
            //只寫方式開啟生成的MP3檔案
            FILE *mp3 = fopen([newMp3PathStr cStringUsingEncoding:1], "wb");
            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的配置一致,否則會造成轉換不成功
            lame_t lame = lame_init();
            lame_set_in_samplerate(lame, 8000.0);
            //取樣率
            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);
                }
                //二進位制形式寫資料到檔案中 mp3_buffer:資料輸出到檔案的緩衝區首地址 write:一個數據塊的位元組數 1:指定一次輸出資料塊的個數 mp3:檔案指標
                fwrite(mp3_buffer, write, 1, mp3);
                
            } while (read != 0);
            lame_close(lame);
            fclose(mp3);
            fclose(pcm);
            
        } @catch (NSException *exception) {
            NSLog(@"%@",[exception description]);
            if (errorBlock) {
                errorBlock();
            }
        } @finally {
            NSLog(@"MP3生成成功!!!");
            if (endBlock) {
                endBlock();
            }
        }
    });
    
}

更多問題,歡迎加QQ群討論:565191947