在uniapp中使用Recorder-UniCore插件可以實(shí)現(xiàn)跨平臺(tái)錄音功能髓抑,uniapp自帶的recorderManager接口不支持H5、錄音格式和實(shí)時(shí)回調(diào)onFrameRecorded兼容性不好优幸,用Recorder插件可避免這些問題吨拍。
DCloud插件市場(chǎng)下載插件(有demo項(xiàng)目源碼):https://ext.dcloud.net.cn/plugin?name=Recorder-UniCore
Recorder-UniCore插件特性
- 支持vue2、vue3网杆、nvue
- 支持編譯成:H5羹饰、Android App、iOS App碳却、微信小程序
- 支持已有的大部分錄音格式:mp3队秩、wav、pcm昼浦、amr刹碾、ogg、g711a座柱、g711u等
- 支持實(shí)時(shí)處理,包括變速變調(diào)物舒、實(shí)時(shí)上傳色洞、ASR語音轉(zhuǎn)文字
- 支持可視化波形顯示
[圖片上傳失敗 真6]
集成到項(xiàng)目中
1、通過npm安裝recorder-core
//在uniapp項(xiàng)目跟目錄進(jìn)行npm安裝
npm install recorder-core
2冠胯、下載導(dǎo)入Recorder-UniCore
插件
// 到插件市場(chǎng) https://ext.dcloud.net.cn/plugin?name=Recorder-UniCore 下載插件
然后添加到你的項(xiàng)目中 /uni_modules/Recorder-UniCore
3火诸、在vue頁面文件內(nèi)引入js
<script> /**這里是邏輯層**/
//必須引入的Recorder核心(文件路徑是 /src/recorder-core.js 下同)
import Recorder from 'recorder-core' //使用import、require都行
//必須引入的RecordApp核心文件(文件路徑是 /src/app-support/app.js)
import RecordApp from 'recorder-core/src/app-support/app'
//所有平臺(tái)必須引入的uni-app支持文件(如果編譯出現(xiàn)路徑錯(cuò)誤荠察,請(qǐng)把@換成 ../../ 這種)
import '@/uni_modules/Recorder-UniCore/app-uni-support.js'
/** 需要編譯成微信小程序時(shí)置蜀,引入微信小程序支持文件 **/
// #ifdef MP-WEIXIN
import 'recorder-core/src/app-support/app-miniProgram-wx-support.js'
// #endif
/** H5、小程序環(huán)境中:引入需要的格式編碼器悉盆、可視化插件盯荤,App環(huán)境中在renderjs中引入 **/
// #ifdef H5 || MP-WEIXIN
//按需引入你需要的錄音格式支持文件,如果需要多個(gè)格式支持焕盟,把這些格式的編碼引擎js文件統(tǒng)統(tǒng)引入進(jìn)來即可
import 'recorder-core/src/engine/mp3'
import 'recorder-core/src/engine/mp3-engine' //如果此格式有額外的編碼引擎(*-engine.js)的話秋秤,必須要加上
//可選的插件支持項(xiàng)
import 'recorder-core/src/extensions/waveview'
// #endif
</script>
<!-- #ifdef APP -->
<script module="yourModuleName" lang="renderjs">
/**需要編譯成App時(shí),你需要添加一個(gè)renderjs模塊脚翘,然后一模一樣的import上面那些js(微信的js除外)
灼卢,因?yàn)锳pp中默認(rèn)是在renderjs(WebView)中進(jìn)行錄音和音頻編碼**/
import 'recorder-core'
import RecordApp from 'recorder-core/src/app-support/app'
import '../../uni_modules/Recorder-UniCore/app-uni-support.js' //renderjs中似乎不支持"@/"打頭的路徑,如果編譯路徑錯(cuò)誤請(qǐng)改正路徑即可
//按需引入你需要的錄音格式支持文件来农,和插件
import 'recorder-core/src/engine/mp3'
import 'recorder-core/src/engine/mp3-engine'
import 'recorder-core/src/extensions/waveview'
export default {
mounted(){
//App的renderjs必須調(diào)用的函數(shù)鞋真,傳入當(dāng)前模塊this
RecordApp.UniRenderjsRegister(this);
},
methods: {
//這里定義的方法,在邏輯層中可通過 RecordApp.UniWebViewVueCall(this,'this.xxxFunc()') 直接調(diào)用
//調(diào)用邏輯層的方法沃于,請(qǐng)直接用 this.$ownerInstance.callMethod("xxxFunc",{args}) 調(diào)用涩咖,二進(jìn)制數(shù)據(jù)需轉(zhuǎn)成base64來傳遞
}
}
</script>
<!-- #endif -->
調(diào)用錄音
/**在邏輯層中編寫**/
//import ... 上面那些import代碼
export default {
data() { return {} }
,mounted() {
this.isMounted=true;
//頁面onShow時(shí)【必須調(diào)用】的函數(shù)海诲,傳入當(dāng)前組件this
RecordApp.UniPageOnShow(this);
}
,onShow(){ //onShow可能比mounted先執(zhí)行,頁面可能還未準(zhǔn)備好
if(this.isMounted) RecordApp.UniPageOnShow(this);
}
,methods:{
//請(qǐng)求錄音權(quán)限
recReq(){
//編譯成App時(shí)提供的授權(quán)許可(編譯成H5抠藕、小程序?yàn)槊赓M(fèi)授權(quán)可不填寫)饿肺;如果未填寫授權(quán)許可,將會(huì)在App打開后第一次調(diào)用請(qǐng)求錄音權(quán)限時(shí)盾似,彈出“未獲得商用授權(quán)時(shí)敬辣,App上僅供測(cè)試”提示框
//RecordApp.UniAppUseLicense='我已獲得UniAppID=*****的商用授權(quán)';
RecordApp.UniWebViewActivate(this); //App環(huán)境下必須先切換成當(dāng)前頁面WebView
RecordApp.RequestPermission(()=>{
console.log("已獲得錄音權(quán)限,可以開始錄音了");
},(msg,isUserNotAllow)=>{
if(isUserNotAllow){//用戶拒絕了錄音權(quán)限
//這里你應(yīng)當(dāng)編寫代碼進(jìn)行引導(dǎo)用戶給錄音權(quán)限零院,不同平臺(tái)分別進(jìn)行編寫
}
console.error("請(qǐng)求錄音權(quán)限失敻仍尽:"+msg);
});
}
//開始錄音
,recStart(){
//錄音配置信息
var set={
type:"mp3",sampleRate:16000,bitRate:16 //mp3格式,指定采樣率hz告抄、比特率kbps撰茎,其他參數(shù)使用默認(rèn)配置;注意:是數(shù)字的參數(shù)必須提供數(shù)字打洼,不要用字符串龄糊;需要使用的type類型,需提前把格式支持文件加載進(jìn)來募疮,比如使用wav格式需要提前加載wav.js編碼引擎
,onProcess:(buffers,powerLevel,duration,sampleRate,newBufferIdx,asyncEnd)=>{
//全平臺(tái)通用:可實(shí)時(shí)上傳(發(fā)送)數(shù)據(jù)炫惩,配合Recorder.SampleData方法,將buffers中的新數(shù)據(jù)連續(xù)的轉(zhuǎn)換成pcm上傳阿浓,或使用mock方法將新數(shù)據(jù)連續(xù)的轉(zhuǎn)碼成其他格式上傳他嚷,可以參考Recorder文檔里面的:Demo片段列表 -> 實(shí)時(shí)轉(zhuǎn)碼并上傳-通用版;基于本功能可以做到:實(shí)時(shí)轉(zhuǎn)發(fā)數(shù)據(jù)芭毙、實(shí)時(shí)保存數(shù)據(jù)筋蓖、實(shí)時(shí)語音識(shí)別(ASR)等
//注意:App里面是在renderjs中進(jìn)行實(shí)際的音頻格式編碼操作,此處的buffers數(shù)據(jù)是renderjs實(shí)時(shí)轉(zhuǎn)發(fā)過來的退敦,修改此處的buffers數(shù)據(jù)不會(huì)改變r(jià)enderjs中buffers粘咖,所以不會(huì)改變生成的音頻文件,可在onProcess_renderjs中進(jìn)行修改操作就沒有此問題了苛聘;如需清理buffers內(nèi)存涂炎,此處和onProcess_renderjs中均需要進(jìn)行清理,H5设哗、小程序中無此限制
//注意:如果你要用只支持在瀏覽器中使用的Recorder擴(kuò)展插件唱捣,App里面請(qǐng)?jiān)趓enderjs中引入此擴(kuò)展插件,然后在onProcess_renderjs中調(diào)用這個(gè)插件网梢;H5可直接在這里進(jìn)行調(diào)用震缭,小程序不支持這類插件;如果調(diào)用插件的邏輯比較復(fù)雜战虏,建議封裝成js文件拣宰,這樣邏輯層党涕、renderjs中直接import,不需要重復(fù)編寫
//H5巡社、小程序等可視化圖形繪制膛堤,直接運(yùn)行在邏輯層;App里面需要在onProcess_renderjs中進(jìn)行這些操作
// #ifdef H5 || MP-WEIXIN
if(this.waveView) this.waveView.input(buffers[buffers.length-1],powerLevel,sampleRate);
// #endif
}
,onProcess_renderjs:`function(buffers,powerLevel,duration,sampleRate,newBufferIdx,asyncEnd){
//App中在這里修改buffers才會(huì)改變生成的音頻文件
//App中是在renderjs中進(jìn)行的可視化圖形繪制晌该,因此需要寫在這里肥荔,this是renderjs模塊的this(也可以用This變量);如果代碼比較復(fù)雜朝群,請(qǐng)直接在renderjs的methods里面放個(gè)方法xxxFunc燕耿,這里直接使用this.xxxFunc(args)進(jìn)行調(diào)用
if(this.waveView) this.waveView.input(buffers[buffers.length-1],powerLevel,sampleRate);
}`
,takeoffEncodeChunk:true?null:(chunkBytes)=>{
//全平臺(tái)通用:實(shí)時(shí)接收到編碼器編碼出來的音頻片段數(shù)據(jù),chunkBytes是Uint8Array二進(jìn)制數(shù)據(jù)姜胖,可以實(shí)時(shí)上傳(發(fā)送)出去
//App中如果未配置RecordApp.UniWithoutAppRenderjs時(shí)誉帅,建議提供此回調(diào),因?yàn)殇浺艚Y(jié)束后會(huì)將整個(gè)錄音文件從renderjs傳回邏輯層右莱,由于uni-app的邏輯層和renderjs層數(shù)據(jù)交互性能實(shí)在太拉跨了蚜锨,大點(diǎn)的文件傳輸會(huì)比較慢,提供此回調(diào)后可避免Stop時(shí)產(chǎn)生超大數(shù)據(jù)回傳
}
,takeoffEncodeChunk_renderjs:true?null:`function(chunkBytes){
//App中這里可以做一些僅在renderjs中才生效的事情慢蜓,不提供也行踏志,this是renderjs模塊的this(也可以用This變量)
}`
,start_renderjs:`function(){
//App中可以放一個(gè)函數(shù),在Start成功時(shí)renderjs中會(huì)先調(diào)用這里的代碼胀瞪,this是renderjs模塊的this(也可以用This變量)
//放一些僅在renderjs中才生效的事情,比如初始化饲鄙,不提供也行
}`
,stop_renderjs:`function(arrayBuffer,duration,mime){
//App中可以放一個(gè)函數(shù)凄诞,在Stop成功時(shí)renderjs中會(huì)先調(diào)用這里的代碼,this是renderjs模塊的this(也可以用This變量)
//放一些僅在renderjs中才生效的事情忍级,不提供也行
}`
};
RecordApp.UniWebViewActivate(this); //App環(huán)境下必須先切換成當(dāng)前頁面WebView
RecordApp.Start(set,()=>{
console.log("已開始錄音");
//創(chuàng)建音頻可視化圖形繪制帆谍,App環(huán)境下是在renderjs中繪制,H5轴咱、小程序等是在邏輯層中繪制汛蝙,因此需要提供兩段相同的代碼
//view里面放一個(gè)canvas,canvas需要指定寬高(下面style里指定了300*100)
//<canvas type="2d" class="recwave-WaveView" style="width:300px;height:100px"></canvas>
RecordApp.UniFindCanvas(this,[".recwave-WaveView"],`
this.waveView=Recorder.WaveView({compatibleCanvas:canvas1, width:300, height:100});
`,(canvas1)=>{
this.waveView=Recorder.WaveView({compatibleCanvas:canvas1, width:300, height:100});
});
},(msg)=>{
console.error("開始錄音失斊臃巍:"+msg);
});
}
//暫停錄音
,recPause(){
if(RecordApp.GetCurrentRecOrNull()){
RecordApp.Pause();
console.log("已暫停");
}
}
//繼續(xù)錄音
,recResume(){
if(RecordApp.GetCurrentRecOrNull()){
RecordApp.Resume();
console.log("繼續(xù)錄音中...");
}
}
//停止錄音
,recStop(){
RecordApp.Stop((arrayBuffer,duration,mime)=>{
//全平臺(tái)通用:arrayBuffer是音頻文件二進(jìn)制數(shù)據(jù)窖剑,可以保存成文件或者發(fā)送給服務(wù)器
//App中如果在Start參數(shù)中提供了stop_renderjs,renderjs中的函數(shù)會(huì)比這個(gè)函數(shù)先執(zhí)行
//注意:當(dāng)Start時(shí)提供了takeoffEncodeChunk后戈稿,你需要自行實(shí)時(shí)保存錄音文件數(shù)據(jù)西土,因此Stop時(shí)返回的arrayBuffer的長度將為0字節(jié)
//如果當(dāng)前環(huán)境支持Blob,也可以直接構(gòu)造成Blob文件對(duì)象鞍盗,和Recorder使用一致
if(typeof(Blob)!="undefined" && typeof(window)=="object"){
var blob=new Blob([arrayBuffer],{type:mime});
console.log(blob, (window.URL||webkitURL).createObjectURL(blob));
}
},(msg)=>{
console.error("結(jié)束錄音失斝枇恕:"+msg);
});
}
}
}
上面代碼中包含了開始錄音跳昼、結(jié)束錄音、暫停肋乍、繼續(xù)的功能方法代碼鹅颊,在view中放幾個(gè)按鈕進(jìn)行點(diǎn)擊調(diào)用即可;在onProcess
回調(diào)中可以做到錄音數(shù)據(jù)實(shí)時(shí)處理墓造,可視化圖形的繪制操作也是在onProcess
中進(jìn)行的(Recorder提供了多中可視化波形顯示)堪伍,H5、App滔岳、小程序均可使用杠娱。
要編譯成App時(shí),記得先在 manifest.json
中配置好Android和iOS的錄音權(quán)限聲明:
//Android需要勾選的權(quán)限谱煤,第二個(gè)必須勾選摊求,不然使用H5錄音時(shí)將沒法打開麥克風(fēng)
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
//iOS需要聲明的權(quán)限
NSMicrophoneUsageDescription
上傳錄音
在上面錄音recStop代碼中,結(jié)束錄音后會(huì)得到ArrayBuffer二進(jìn)制數(shù)據(jù)刘离,將ArrayBuffer上傳到服務(wù)器即可室叉;實(shí)時(shí)處理中也支持上傳,實(shí)時(shí)得到音頻數(shù)據(jù)的ArrayBuffer后按下面的上傳方法上傳即可硫惕。
上傳方式一(簡單):轉(zhuǎn)成Base64文本上傳
//由于是base64文本茧痕,因此直接使用普通的接口請(qǐng)求就可以了,代碼簡單,H5恼除、App踪旷、小程序通用
uni.request({
url: "上傳接口地址"
,method: "POST"
,header: { "content-type":"application/x-www-form-urlencoded" }
,data: {
audio: uni.arrayBufferToBase64(arrayBuffer)
,... 其他表單參數(shù) ...
}
,success: (res) => { }
,fail: (err)=>{ }
});
上傳方式二(復(fù)雜):使用上傳表單上傳 multipart/form-data
//使用multipart/form-data表單上傳文件,在uniapp中支持不是很好豁辉,每個(gè)平臺(tái)單獨(dú)處理
// #ifdef H5
//H5中直接使用瀏覽器提供的File接口構(gòu)造一個(gè)文件
uni.uploadFile({
url: "上傳接口地址"
,file: new File([arrayBuffer], "recorder.mp3")
,name: "audio"
,formData: {
... 其他表單參數(shù) ...
}
,success: (res) => { }
,fail: (err)=>{ }
});
// #endif
// #ifdef APP
//App中直接將二進(jìn)制數(shù)據(jù)保存到本地文件令野,然后再上傳
RecordApp.UniSaveLocalFile("recorder.mp3",arrayBuffer,(savePath)=>{
uni.uploadFile({
url: "上傳接口地址"
,filePath: savePath
,name: "audio"
,formData: {
... 其他表單參數(shù) ...
}
,success: (res) => { }
,fail: (err)=>{ }
});
},(err)=>{});
// #endif
// #ifdef MP-WEIXIN
//小程序中需要將二進(jìn)制數(shù)據(jù)保存到本地文件,然后再上傳
var savePath=wx.env.USER_DATA_PATH+"/recorder.mp3";
wx.getFileSystemManager().writeFile({
filePath:savePath
,data:arrayBuffer
,encoding:"binary"
,success:()=>{
wx.uploadFile({
url: "上傳接口地址"
,filePath: savePath
,name: "audio"
,formData: {
... 其他表單參數(shù) ...
}
,success: (res) => { }
,fail: (err)=>{ }
});
}
,fail:(e)=>{ }
});
// #endif
ASR語音識(shí)別
假如你的服務(wù)器提供了識(shí)別接口徽级,可以參考上面的文件上傳气破,將文件上傳給你的服務(wù)器后,服務(wù)器將識(shí)別結(jié)果返回給前端餐抢,此方式可以適配:騰訊云现使、阿里云、訊飛等的一句話語音識(shí)別旷痕,或自己搭建的語音識(shí)別碳锈,比較簡單。
實(shí)時(shí)的語音識(shí)別可以參考Recorder-UniCore插件的demo項(xiàng)目欺抗,demo源碼里面有個(gè)page_asr.vue
示例頁面殴胧,可以做到邊錄音邊返回識(shí)別結(jié)果;此demo使用的是阿里云接口,其他語音識(shí)別接口同樣的可以在onProcess
中進(jìn)行實(shí)時(shí)處理即可完成對(duì)接团滥,可以參考Recorder H5錄音開源庫 https://github.com/xiangyuecn/Recorder 中的實(shí)時(shí)上傳處理demo代碼竿屹,不難做到邊錄音邊上傳到語音識(shí)別,H5灸姊、App拱燃、小程序中也是通用的。
【完】