UNPKG

recorder-core

Version:

Recorder库: html5 js 录音 mp3 wav ogg webm amr g711a g711u 格式,支持pc和Android、iOS部分浏览器、Hybrid App(提供Android iOS App源码)、微信,提供ASR语音识别转文字 H5版语音通话聊天示例 DTMF编码解码

196 lines (165 loc) 5.99 kB
/* 录音 Recorder扩展,DTMF(电话拨号按键信号)编码生成器,生成按键对应的音频PCM信号 本扩展分两个功能: DTMF_Encode DTMF_EncodeMix 本扩展生成信号代码、原理简单粗暴,纯js实现易于移植,0依赖 使用场景:DTMF按键信号生成,软电话实时发送DTMF按键信号等 https://github.com/xiangyuecn/Recorder */ (function(factory){ var browser=typeof window=="object" && !!window.document; var win=browser?window:Object; //非浏览器环境,Recorder挂载在Object下面 var rec=win.Recorder,ni=rec.i18n; factory(rec,ni,ni.$T,browser); }(function(Recorder,i18n,$T,isBrowser){ "use strict"; /** 本方法用来生成单个按键信号pcm数据,属于底层方法,要混合多个按键信号到别的pcm中请用封装好的DTMF_EncodeMix方法 参数: key: 单个按键0-9#* sampleRate:123 要生成的pcm采样率 duration:100 按键音持续时间 mute:50 按键音前后静音时长 返回: pcm:[Int16,...],生成单个按键信号 **/ Recorder.DTMF_Encode=function(key,sampleRate,duration,mute){ var durSize=Math.floor(sampleRate*(duration||100)/1000); var muteSize=Math.floor(sampleRate*(mute==null?50:mute)/1000); var pcm0=new Int16Array(durSize+muteSize*2); var pcm1=new Int16Array(durSize+muteSize*2); // https://github.com/watilde/node-dtfm/blob/master/encode.js var f0=DTMF_Freqs[key][0]; var f1=DTMF_Freqs[key][1]; var vol=0.3; for(var i=0;i<durSize;i++){ var v0=vol*Math.sin((2 * Math.PI) * f0 * (i / sampleRate)); var v1=vol*Math.sin((2 * Math.PI) * f1 * (i / sampleRate)); pcm0[i+muteSize]=Math.max(-1,Math.min(1,v0))*0x7FFF; pcm1[i+muteSize]=Math.max(-1,Math.min(1,v1))*0x7FFF; }; //简单叠加 低群 和 高群 信号 Mix(pcm0,0,pcm1,0); return pcm0; }; /**返回EncodeMix对象,将输入的按键信号混合到持续输入的pcm流中,当.mix(inputPcms)提供的太短的pcm会无法完整放下一个完整的按键信号,所以需要不停调用.mix(inputPcms)进行混合**/ Recorder.DTMF_EncodeMix=function(set){ return new EncodeMix(set); }; var EncodeMix=function(set){ var This=this; This.set={ duration:100 //按键信号持续时间 ms,最小值为30ms ,mute:25 //按键音前后静音时长 ms,取值为0也是可以的 ,interval:200 //两次按键信号间隔时长 ms,间隔内包含了duration+mute*2,最小值为120ms }; for(var k in set){ This.set[k]=set[k]; }; This.keys=""; This.idx=0; This.state={keyIdx:-1,skip:0}; }; EncodeMix.prototype={ /** 添加一个按键或多个按键 "0" "123#*",后面慢慢通过mix方法混合到pcm中,无返回值 **/ add:function(keys){ this.keys+=keys; } /** 将已添加的按键信号混合到pcm中,pcms:[[Int16,...],...]二维数组,sampleRate:pcm的采样率,index:pcms第一维开始索引,将从这个pcm开始混合。 返回混合状态对象。 注意:调用本方法会修改pcms中的内容,因此混合结果就在pcms内。 **/ ,mix:function(pcms,sampleRate,index){ index||(index=0); var This=this,set=This.set; var newEncodes=[]; var state=This.state; var pcmPos=0; loop: for(var i0=index;i0<pcms.length;i0++){ var pcm=pcms[i0]; var key=This.keys.charAt(This.idx); if(!key){//没有需要处理的按键,把间隔消耗掉 state.skip=Math.max(0, state.skip-pcm.length); } else while(key){ //按键间隔处理 if(state.skip){ var op=pcm.length-pcmPos; if(op<=state.skip){ state.skip-=op; pcmPos=0; continue loop; }; pcmPos+=state.skip; state.skip=0; }; var keyPcm=state.keyPcm; //这个key已经混合过,看看有没有剩余的信号 if(state.keyIdx==This.idx){ if(state.cur>=keyPcm.length){ state.keyIdx=-1; }; }; //新的key,生成信号 if(state.keyIdx!=This.idx){ keyPcm=Recorder.DTMF_Encode(key,sampleRate,set.duration,set.mute); state.keyIdx=This.idx; state.cur=0; state.keyPcm=keyPcm; newEncodes.push({ key:key ,data:keyPcm }); }; //将keyPcm混合到当前pcm中,实际是替换逻辑 var res=Mix(pcm,pcmPos,keyPcm,state.cur,true); state.cur=res.cur; pcmPos=res.last; //下一个按键 if(res.cur>=keyPcm.length){ This.idx++; key=This.keys.charAt(This.idx); state.skip=Math.floor(sampleRate*(set.interval-set.duration-set.mute*2)/1000); }; //当前pcm的位置已消耗完 if(res.last>=pcm.length){ pcmPos=0; continue loop;//下一个pcm }; }; }; return { newEncodes:newEncodes //本次混合新生成的按键信号列表 [{key:"*",data:[Int16,...]},...],如果没有产生新信号将为空数组 ,hasNext:This.idx<This.keys.length //是否还有未混合完的信号 }; } }; //teach.realtime.mix_multiple 抄过来的简单混合算法 var Mix=function(buffer,pos1,add,pos2,mute){ for(var j=pos1,cur=pos2;;j++,cur++){ if(j>=buffer.length || cur>=add.length){ return { last:j ,cur:cur }; }; if(mute){ buffer[j]=0;//置为0即为静音 }; var data_mix,data1=buffer[j],data2=add[cur]; //简单混音算法 https://blog.csdn.net/dancing_night/article/details/53080819 if(data1<0 && data2<0){ data_mix = data1+data2 - (data1 * data2 / -0x7FFF); }else{ data_mix = data1+data2 - (data1 * data2 / 0x7FFF); }; buffer[j]=data_mix; }; }; var DTMF_Freqs={ '1': [697, 1209] ,'2': [697, 1336] ,'3': [697, 1477] ,'A': [697, 1633] ,'4': [770, 1209] ,'5': [770, 1336] ,'6': [770, 1477] ,'B': [770, 1633] ,'7': [852, 1209] ,'8': [852, 1336] ,'9': [852, 1477] ,'C': [852, 1633] ,'*': [941, 1209] ,'0': [941, 1336] ,'#': [941, 1477] ,'D': [941, 1633] }; }));