看到這裏的時候一定要理解並運行上文的《前端recorder-core實時錄音並繪製波形,blob常規數據格式》,這樣對Recorder有個概念。
在創建錄音對象的時候有一個takeoffEncodeChunk回調,該回調為實時編碼環境,會接管編碼器輸出。
當編碼器實時編碼出一塊有效的二進制音頻數據時實時回調此方法,也就是説每次獲取到有效的二進制音頻數據takeoffEncodeChunk都會回調一次,返回二進制的Uint8Array,就是編碼出來的音頻數據片段(chunkBytes),所有的chunkBytes拼接在一起即為完整音頻。
// 創建錄音對象
rec = Recorder({
type: "mp3", // 格式
sampleRate: 16000, // 錄音的採樣率
bitRate: 16, // 比特率
onProcess: (buffers: any, powerLevel: any, _: any, bufferSampleRate: any) => {
if (wave) {
wave.input(buffers[buffers.length - 1], powerLevel, bufferSampleRate);
}
},
takeoffEncodeChunk: (chunkBytes: any) => {
//【關鍵代碼】接管實時轉碼,推入實時處理
console.log("接管實時轉碼,推入實時處理", chunkBytes);
}
});
使用takeoffEncodeChunk後需要注意,這個模式是流式輸出的,Recorder不會自動保存每一次的片段數據,需要我們自行實時保存錄音文件數據,因此Recorder.stop停止時返回的blob的長度將為0字節。
完整代碼,vue3+ts+arco.design,複製到項目中可這直接運行
<template>
<div class="snow-page">
<div class="snow-inner">
<a-space direction="vertical" fill>
<!-- 波形繪製區域 -->
<div style="display: inline-block; vertical-align: bottom; border: 1px solid #cccccc">
<div style="width: 300px; height: 100px" ref="recwave"></div>
</div>
<a-space>
<a-button type="primary" @click="recOpen">打開錄音權限</a-button>
<a-button type="primary" @click="recStart">開始錄音</a-button>
<a-button @click="recStop">結束錄音</a-button>
</a-space>
</a-space>
</div>
</div>
</template>
<script setup lang="ts">
//必須引入的核心
import Recorder from "recorder-core";
//引入mp3格式支持文件;如果需要多個格式支持,把這些格式的編碼引擎js文件放到後面統統引入進來即可
import "recorder-core/src/engine/mp3";
import "recorder-core/src/engine/mp3-engine";
//錄製wav格式的用這一句就行
import "recorder-core/src/engine/wav";
//可選的插件支持項,這個是波形可視化插件
import "recorder-core/src/extensions/waveview";
//ts import 提示:npm包內已自帶了.d.ts聲明文件(不過是any類型)
let rec: any;
let recBlob: any;
let wave: any;
const recwave = ref(null);
// 打開錄音
const recOpen = () => {
// 創建錄音對象
rec = Recorder({
type: "mp3", // 格式
sampleRate: 16000, // 錄音的採樣率
bitRate: 16, // 比特率
onProcess: (buffers: any, powerLevel: any, _: any, bufferSampleRate: any) => {
if (wave) {
wave.input(buffers[buffers.length - 1], powerLevel, bufferSampleRate);
}
},
takeoffEncodeChunk: (chunkBytes: any) => {
RealTimeSendTry(chunkBytes, false);
}
});
if (!rec) {
alert("當前瀏覽器不支持錄音功能!");
return;
}
// 打開錄音,獲得權限
rec.open(
() => {
console.log("錄音已打開");
if (recwave.value) {
// 創建音頻可視化圖形繪製對象
wave = Recorder.WaveView({ elem: recwave.value });
}
},
(msg: any, isUserNotAllow: any) => {
// 用户拒絕了錄音權限,或者瀏覽器不支持錄音
console.log((isUserNotAllow ? "UserNotAllow," : "") + "無法錄音:" + msg);
}
);
};
const RealTimeSendTry = (chunkBytes: any, isClose: any) => {
//沒有指定固定的幀大小,直接把chunkBytes發送出去即可
if (chunkBytes.length > 0) {
//*********發送方式二:直接ArrayBuffer二進制發送***************
let arrayBuffer = chunkBytes.buffer;
console.log("二進制發送", chunkBytes, arrayBuffer);
//可以實現
//WebSocket send(arrayBuffer) ...
//WebRTC send(arrayBuffer) ...
//XMLHttpRequest send(arrayBuffer) ...
}
//最後一次調用發送,注意此時的frameBytes不一定有數據,可能長度為0
//請勿假設調用了rec.stop之後的takeoffEncodeChunk一定有回調並且是最後一幀,因為可能會回調 0-2 幀
//請以isClose為準,isClose的當前幀或者前一幀,只要是有數據的就是最後一幀;因此如果需要獲得最後一幀數據,可延遲一幀的發送,isClose時取當前幀或延遲的這幀作為最後一幀
if (isClose) {
// 可做關閉邏輯處理
console.log("最後一幀", chunkBytes, chunkBytes.length);
}
};
// 開始錄音
const recStart = () => {
if (!rec) return console.error("未打開錄音");
rec.start();
console.log("已開始錄音");
};
// 結束錄音
const recStop = () => {
if (!rec) return console.error("未打開錄音");
rec.stop(
(blob: any, duration: any) => {
recBlob = blob;
const localUrl = (window.URL || webkitURL).createObjectURL(blob);
console.log("結束錄音", blob, localUrl, "時長:" + duration + "ms");
rec.close(); // 關閉錄音,釋放錄音資源
rec = null;
RealTimeSendTry(new Uint8Array(0), true); //最後一次發送
},
(err: any) => {
console.error("結束錄音出錯:" + err);
rec.close(); //關閉錄音,釋放錄音資源
rec = null;
}
);
};
</script>