This repository has been archived on 2025-05-21. You can view files and clone it, but cannot push or open issues or pull requests.
mp3-to-text/public/extensions/dtmf.encode.js
2025-04-20 18:46:48 +08:00

196 lines
5.9 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
录音 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,...],...]二维数组sampleRatepcm的采样率indexpcms第一维开始索引将从这个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]
};
}));