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();
}
}
});
}