背景:語音識別需求,理所當然需要錄音,而且是PC端錄音,直接上Recorder,H5錄音的一個組件。
git官方鏈接
https://github.com/xiangyuecn/Recorder
官方使用示例
https://xiangyuecn.github.io/Recorder/
官方使用示例源碼
view-source:https://xiangyuecn.github.io/Recorder/QuickStart.html
1、依賴引入
// 錄音
import '../../lib/recorder/recorder.wav.min';
// 波形圖(可以忽略這個波形圖,只是爲了示例,波形圖用Recorder中推薦的就挺好的)
import WaveView from '../../lib/recorder/wavview';
我們這個老項目中以前有用過這個組件,是直接將人家的打包js放在了一個js文件中直接引入的,emm....我是直接複用的,哈哈。
推薦用import引入,按照官方文檔指引引入使用即可;
2、開始使用:聲明recorder對象
聲明一個全局的recorder對象,設置Recorder對象的一些屬性;
-
type: 輸出類型:mp3,wav
-
sampleRate: 採樣率;
-
bitRate: 比特率 wav:16或8位,MP3:8kbps 1k/s,8kbps 2k/s 錄音文件很小;
-
bufferSize: H5錄音時的AudioContext緩衝大小;
-
onProcess: 錄音實時回調,可在裏面設置波形圖,發送實時數據,獲取錄音時長,限制最長錄音等功能;
因爲錄音類型是wav,後端wav要求(16k,16bit,單通道)
recorder = useRef(window.Recorder({
type: 'wav',
sampleRate: 16000,
bitRate: 16,
bufferSize: 4096,
onProcess: (
buffers, // 緩衝的PCM數據,爲從開始錄音到現在的所有pcm片段
powerLevel, // 當前緩衝的音量級別0-100
bufferDuration, // 已緩衝時長
bufferSampleRate, // 緩衝使用的採樣率
) => {
// 波形圖
waveView.input(buffers[buffers.length - 1], powerLevel, bufferSampleRate);
// 音頻轉換數據,即發送到後端的數據
recordTransformData = window.Recorder.SampleData(buffers, bufferSampleRate, 16000,
{
index: recordTransformData ? recordTransformData.index : 0,
offset: recordTransformData ? recordTransformData.offset : 0.0,
});
// socket發送數據(需要全局聲明,這裏僅作爲示例)
socket.send(recordTransformData.data);
// 獲取錄音時長
setAudioDuration(bufferDuration);
// 限制錄音1分鐘,若超出給出友好提示
if (bufferDuration / 1000 >= 60) {
message.error('您已錄製超過最大時長: 1分鐘。馬上爲您分析數據~');
// 接近限制時間,停止錄音
stopRecord(bufferDuration); // 傳參是因爲setAudioDuration在自動停止的時候沒有賦上值
}
},
}), []);
3、點擊按鈕開始錄音
有了recoder錄音對象之後,我們就可以點擊按鈕開始錄音;
官方說明:open()和close()最好分開按鈕操作,但是需求不允許我們這樣;
open(): 打開錄音資源, 兩個參數。
- True()
- False(msg,isUserNotAllow)
特別說明下這裏遇到的問題,我起初用的時候沒有仔細看源碼,自己再點擊開始錄音之前多此一舉去判斷麥克風是否被禁用:
導致了一個bug:當停止錄音關閉錄音的時候瀏覽器錄音的小紅點還在,給人錯覺還在錄音,資源沒有釋放;
其實open方法的第二個回調就是這個作用,打開錄音失敗,可以進行友好提示;
start(): 開啓錄音
const startRecord = useCallback(() => {
// Recorder.IsOpen函數表示,是否已經打開了錄音,所有工作都已經準備好了,就等接收音頻數據了;如果沒有打開,直接返回
if (window.Recorder.IsOpen()) {
return;
}
recorder.current.open(() => {
// 打開錄音成功的回調:
// 開始錄音
recorder.current.start();
// 波形圖
waveView = WaveView({ el: canvasRef.current, linear1: [0, '#356CBC', 1, '#356CBC'], linear2: [0, '#356CBC', 1, '#356CBC'] });
}, (msg, isUserNotAllow) => {
// 打開錄音失敗的回調
if (isUserNotAllow) {
message.warning('無法錄音:' + msg + ',請允許瀏覽器使用麥克風並重新評測', 5);
} else {
message.warning('無法錄音:' + msg, 5);
}
});
}, [type]);
4、點擊按鈕關閉錄音
錄音完成的時候,需要點擊按鈕關閉錄音。
stop(): 停止錄音,結束錄音並返回錄音數據blob對象,有3個參數
-
True(blob,duration) blob:錄音數據audio/mp3|wav格式;duration:錄音時長,單位毫秒;
-
False(msg);
-
autoClose:false 可選,是否自動調用close,默認爲false
close():關閉錄音,釋放資源
const stopRecord = useCallback(() => {
// 判斷錄音是否打開,如果沒有打開直接返回
if (!window.Recorder.IsOpen()) {
return;
}
socket.send('end');
socket.close();
recorder.current.stop((blob) => {
// 獲取錄音的url用來播放音頻
const wavUrlStr = window.URL.createObjectURL(blob);
setAudioUrl(wavUrlStr);
// 關閉釋放錄音資源
recorder.current.close(() => {
recordTransformData = null;
});
}, () => {
recorder.current.stop(() => {}, () => {}, false);
}, false);
}, [setIsRecording, setAudioUrl]);
5、特別說明
最後,這裏有一個需要注意的點,服務器渲染的話,頁面引入Recorder錄音組件的時候需要將ssr設置爲false,因爲服務器渲染的時候頁面在服務器上面是找不到Window對象的。
import dynamic from 'next/dynamic';
const Recorder = dynamic(() => import('./Recorder'), { ssr: false });
綜上所述,這就是我用五步法實現了錄音的功能,這個五步法適用於大多數簡單的錄音功能,並且錄音組件的源碼中有很多詳細的註釋,如果要添加額外的功能,可以詳細看下源碼再進行設置。
波形圖可以忽略哈;emm.....直接用Recoder文檔中推薦的就挺好。