diff --git a/backend/demo/index.html b/backend/demo/index.html new file mode 100644 index 0000000..c786ad9 --- /dev/null +++ b/backend/demo/index.html @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/backend/demo/index.js b/backend/demo/index.js new file mode 100644 index 0000000..f0c86db --- /dev/null +++ b/backend/demo/index.js @@ -0,0 +1,393 @@ +/****************** +《【教程】【ASR】实时语音识别、音频文件转文字-阿里云版》 +作者:高坚果 +时间:2020-7-22 22:37:09 + +通过阿里云语音识别(语音转文字)插件 /src/extensions/asr.aliyun.short.js,可实现实时语音识别、单个语音文件转文字。 + +只需要后端提供一个Token生成接口,就能进行语音识别,可直接参考或本地运行此NodeJs后端测试程序:/assets/demo-asr/NodeJsServer_asr.aliyun.short.js,配置好代码里的阿里云账号后,在目录内直接命令行执行`node NodeJsServer_asr.aliyun.short.js`即可运行提供本地测试接口。 + +目前暂未提供其他版本的语音识别插件,比如腾讯云、讯飞等,搭配使用Recorder的onProcess实时处理,可根据自己的业务需求选择对应厂家自行对接即可,如需定制开发请联系作者。 +******************/ + +var asr; + +/*************单个语音文件转文字例子,你也可以录完音后再一次性进行识别***************/ +//将录音文件进行语音识别,支持的录音格式取决于浏览器的支持,兼容性mp3最好,wav次之,其他格式不一定能够解码 +var fileToText=function(audioBlob,fileName){ + if(asr){ + Runtime.Log("上次asr未关闭",1); + return; + }; + Runtime.Log("开始识别文件:《"+fileName+"》,asrProcess中已限制最多识别前60*3-5*(3-1)=170秒 ..."); + $(".recAsrTxt").text(""); + $(".recAsrTime").html(""); + + var url=$(".asrTokenApi").val(); + var urlReq=null; + if(/^\s*\{.*\}\s*$/.test(url)){ + //这里是输入框里面填的json数据,直接success回调即可 + urlReq=function(url,args,success,fail){ + var data; try{ data=JSON.parse(url); }catch(e){}; + if(!data || !data.appkey || !data.token){ + fail("填写的json数据"+(!data?"解析失败":"中缺少appkey或token")); + }else{ + success({ appkey:data.appkey, token:data.token }); + } + } + }; + + var asr2=asr=Recorder.ASR_Aliyun_Short({ + tokenApi:url + ,apiArgs:{ + lang:$("[name=arsLang]:checked").val() + ,xxx:"其他请求参数" + } + ,apiRequest:urlReq //如果提供了token数据,可不发起api请求 + ,asrProcess:function(text,nextDuration,abortMsg){ + /***识别中间结果实回调,必须返回true才能继续识别,否则立即超时停止识别***/ + if(abortMsg){ + //语音识别中途出错,单个文件识别无需任何处理,会自动回调结果 + Runtime.Log("[asrProcess回调]被终止:"+abortMsg,1); + return false; + }; + + $(".recAsrTxt").text(text); + $(".recAsrTime").html("识别时长: "+formatTime(asr2.asrDuration()) + +" 已发送数据时长: "+formatTime(asr2.sendDuration())); + return nextDuration<=3*60*1000;//允许识别3分钟的识别时长(比音频时长小5*2秒) + } + ,log:function(msg,color){ + Runtime.Log(''+msg+'',color); + } + }); + Runtime.Log("语言:"+asr.set.apiArgs.lang); + //语音文件识别只需调用audioToText即可完成识别,简单快速 + asr.audioToText(audioBlob,function(text,abortMsg){ + asr=null; + if(abortMsg){ + Runtime.Log("发现识别中途被终止(一般无需特别处理):"+abortMsg,"#fb8"); + }; + Runtime.Log("文件识别最终结果:"+text, 2); + },function(errMsg){ + asr=null; + Runtime.Log("文件识别结束失败:"+errMsg, 1); + }); +}; +/*************单个语音文件转文字例子 END***************/ + + + +/*************实时语音识别例子*************************/ +/******界面交互处理、打开录音权限******/ +//使用长按录音的方式,可有效控制转换时长,避免不必要的资源浪费 +//长按按钮功能已经封装好了,直接调用 BindTouchButton 即可快速实现长按 +var bindTouchButton=function(){ + DemoFragment.BindTouchButton( + "recTouchBtn" + ,"按住进行录音+识别" + ,"松开结束" + ,{} + ,asrOnTouchStart + ,asrOnTouchEnd + ); +}; + +var rec; +/**打开录音,先得到录音权限**/ +function recOpenClick(){ + $(".recOpenBtn").hide(); + $(".recCloseBtn").show(); + var end=function(isOk){ + if(isOk){ + $(".asrStartBtns").show(); + $(".asrStopBtn").show(); + }; + }; + + rec=Recorder({ + type:"wav" + ,sampleRate:16000 + ,bitRate:16 + ,onProcess:function(buffers,powerLevel,bufferDuration,bufferSampleRate,newBufferIdx,asyncEnd){ + Runtime.Process.apply(null,arguments); + + //实时推入asr处理。asr.input随时都可以调用,就算asr并未start,会缓冲到asr.start完成然后将已input的数据进行识别 + if(asr){ + //buffers是从录音开头到现在的缓冲,因此需要提供 buffersOffset=newBufferIdx + asr.input(buffers, bufferSampleRate, newBufferIdx); + }; + } + }); + var t=setTimeout(function(){ + recAsrStatus("无法录音:权限请求被忽略(超时假装手动点击了确认对话框)",1); + rec=null; + end(false); + },8000); + + recAsrStatus("正在打开录音权限,请稍后..."); + rec.open(function(){//打开麦克风授权获得相关资源 + clearTimeout(t); + recAsrStatus("录音已打开,可以长按录音+识别了",2); + end(true); + },function(msg,isUserNotAllow){//用户拒绝未授权或不支持 + clearTimeout(t); + recAsrStatus((isUserNotAllow?"UserNotAllow,":"")+"无法录音:"+msg, 1); + rec=null; + end(false); + }); +}; +var recCloseClick=function(){ + $(".recOpenBtn").show(); + $(".recCloseBtn").hide(); + $(".asrStartBtns").hide(); + if(rec){ + Runtime.Log("已关闭录音"); + rec.close(); + rec=null; + }else{ + Runtime.Log("未打开录音",1); + } +}; + + +//免长按,这里就是调用的长按时的两个状态方法 功能是一样的 +var asrStartClick_NoTouch=function(){ + asrOnTouchStart(function(){}); +}; +var asrStopClick_NoTouch=function(){ + if(!asr){ + Runtime.Log("未开始识别",1); + return; + }; + asrOnTouchEnd(false,true); +}; + + + + +/******核心的长按录音识别******/ +/**长按开始录音**/ +var asrOnTouchStart=function(cancel){ + if(!rec){ + cancel("未打开录音"); + recAsrStatus("未打开录音",1); + return; + }; + rec.s_isStart=false; + if(asr){ + cancel("上次asr未关闭"); + recAsrStatus("上次asr未关闭",1); + return; + }; + $(".recAsrTxt").text(""); + $(".recAsrTime").html(""); + + var url=$(".asrTokenApi").val(); + var urlReq=null; + if(/^\s*\{.*\}\s*$/.test(url)){ + //这里是输入框里面填的json数据,直接success回调即可 + urlReq=function(url,args,success,fail){ + var data; try{ data=JSON.parse(url); }catch(e){}; + if(!data || !data.appkey || !data.token){ + fail("填写的json数据"+(!data?"解析失败":"中缺少appkey或token")); + }else{ + success({ appkey:data.appkey, token:data.token }); + } + } + }; + + //创建语音识别对象,每次识别都要新建,asr不能共用 + var asr2=asr=Recorder.ASR_Aliyun_Short({ + tokenApi:url + ,apiArgs:{ + lang:$("[name=arsLang]:checked").val() + ,xxx:"其他请求参数" + } + ,apiRequest:urlReq //如果提供了token数据,可不发起api请求 + ,asrProcess:function(text,nextDuration,abortMsg){ + /***实时识别结果,必须返回true才能继续识别,否则立即超时停止识别***/ + if(abortMsg){ + //语音识别中途出错 + recAsrStatus("[asrProcess回调]被终止:"+abortMsg,1); + cancel("语音识别出错");//立即结束录音,就算继续录音也不会识别 + return false; + }; + + $(".recAsrTxt").text(text); + $(".recAsrTime").html("识别时长: "+formatTime(asr2.asrDuration()) + +" 已发送数据时长: "+formatTime(asr2.sendDuration())); + return nextDuration<=2*60*1000;//允许识别2分钟的识别时长(比录音时长小5秒) + } + ,log:function(msg,color){ + Runtime.Log(''+msg+'',color); + } + }); + Runtime.Log("语言:"+asr.set.apiArgs.lang); + recAsrStatus("连接服务器中,请稍后..."); + //打开语音识别,建议先打开asr,成功后再开始录音 + asr.start(function(){//无需特殊处理start和stop的关系,只要调用了stop,会阻止未完成的start,不会执行回调 + //开始录音 + Runtime.Log("开始录音..."); + rec.start(); + rec.s_isStart=true; + + recAsrStatus("滴~~ 已开始语音识别,请讲话(asrProcess中已限制最多识别60*2-5*(2-1)=115秒)...",2); + },function(errMsg){ + recAsrStatus("语音识别开始失败,请重试:"+errMsg,1); + + cancel("语音识别开始失败"); + }); +}; + +/**松开停止录音**/ +var asrOnTouchEnd=function(isCancel,isUser){ + recAsrStatus(isCancel?"已取消":isUser?"已松开":"长按被取消",isUser?0:1); + + var asr2=asr;asr=null;//先干掉asr,防止重复stop + if(!asr2){ + Runtime.Log("未开始识别",1); + }else{ + //asr.stop 和 rec.stop 无需区分先后,同时执行就ok了 + asr2.stop(function(text,abortMsg){ + if(abortMsg){ + abortMsg="发现识别中途被终止(一般无需特别处理):"+abortMsg; + }; + recAsrStatus("语音识别完成"+(abortMsg?","+abortMsg:""),abortMsg?"#f60":2); + Runtime.Log("识别最终结果:"+text, 2); + },function(errMsg){ + recAsrStatus("语音识别"+(!isUser?"被取消":"结束失败")+":"+errMsg, 1); + }); + }; + + var rec2=rec; + if(rec2.s_isStart){ + rec2.s_isStart=false; + rec2.stop(function(blob,duration){ + Runtime.LogAudio(blob,duration,rec2); + },function(errMsg){ + Runtime.Log("录音失败:"+errMsg, 1); + }); + }; +}; + +/**更新状态**/ +var recAsrStatus=function(html,color){ + var elem=document.querySelector(".recAsrStatus"); + elem.style.color=color==1?"red":color==2?"#0b1":(color||null); + elem.innerHTML=html; + + Runtime.Log(html,color); +}; +/*************实时语音识别例子 END***************/ + + + +//破坏环境,测试错误是否被正确处理 +var killToken=function(){ + if(!asr){ + Runtime.Log("未开始语音识别",1); + return; + } + asr.set.apiRequest=function(url,args,success,fail){ + fail("不让获取Token"); + } + Runtime.Log("已设置ASR的apiRequest,下一分钟将无法获得Token"); +}; +var killWs=function(){ + if(!asr || !asr.wsCur){ + Runtime.Log("未开始语音识别",1); + return; + } + asr.wsCur.close(); + Runtime.Log("已强制关闭了ASR的WebSocket连接"); +}; + + + + + +//=====以下代码无关紧要,音频数据源和界面================== +//加载录音框架 +Runtime.Import([ + {url:RootFolder+"/src/recorder-core.js",check:function(){return !window.Recorder}} + ,{url:RootFolder+"/src/engine/wav.js",check:function(){return !Recorder.prototype.wav}} + ,{url:RootFolder+"/src/extensions/asr.aliyun.short.js",check:function(){return !Recorder.ASR_Aliyun_Short}} + + ,{url:RootFolder+"/assets/runtime-codes/fragment.touch_button.js",check:function(){return !window.DemoFragment||!DemoFragment.BindTouchButton}}//引入BindTouchButton +]); + + +//显示控制按钮 +Runtime.Ctrls([ + {html:'\ +