perf: 更新录音插件

This commit is contained in:
Pang 2023-12-16 21:11:20 +08:00
parent 84d3e4f617
commit dbb9162267
4 changed files with 481 additions and 7 deletions

View File

@ -827,6 +827,8 @@ export default {
type: "mp3",
bitRate: 64,
sampleRate: 32000,
audioTrackSet: null,
disableEnvInFix: false,
onProcess: (buffers, powerLevel, duration, sampleRate, newBufferIdx, asyncEnd) => {
this.recordWave?.input(buffers[buffers.length - 1], powerLevel, sampleRate);
this.recordDuration = duration;

View File

@ -1,6 +1,366 @@
/*
录音
录音 Recorder扩展频率直方图显示
使用本扩展需要引入src/extensions/lib.fft.js支持直方图特意优化主要显示0-5khz语音部分线性其他高频显示区域较小不适合用来展示音乐频谱可自行修改源码恢复成完整的线性频谱或修改成倍频程频谱伯德图对数频谱本可视化插件可以移植到其他语言环境如需定制可联系作者
https://github.com/xiangyuecn/Recorder
src: extensions/frequency.histogram.view.js
本扩展核心算法主要参考了Java开源库jmp123 版本0.3 的代码
https://www.iteye.com/topic/851459
https://sourceforge.net/projects/jmp123/files/
*/
!function(){"use strict";var t=function(t){return new e(t)},d="FrequencyHistogramView",e=function(t){var e=this,r={scale:2,fps:20,lineCount:30,widthRatio:.6,spaceWidth:0,minHeight:0,position:-1,mirrorEnable:!1,stripeEnable:!0,stripeHeight:3,stripeMargin:6,fallDuration:1e3,stripeFallDuration:3500,linear:[0,"rgba(0,187,17,1)",.5,"rgba(255,215,0,1)",1,"rgba(255,102,0,1)"],stripeLinear:null,shadowBlur:0,shadowColor:"#bbb",stripeShadowBlur:-1,stripeShadowColor:"",onDraw:function(t,e){}};for(var a in t)r[a]=t[a];e.set=t=r;var i=t.elem;i&&("string"==typeof i?i=document.querySelector(i):i.length&&(i=i[0])),i&&(t.width=i.offsetWidth,t.height=i.offsetHeight);var o=t.scale,n=t.width*o,l=t.height*o;if(!n||!l)throw new Error(d+"无宽高");var h=e.elem=document.createElement("div"),s=["","transform-origin:0 0;","transform:scale("+1/o+");"];h.innerHTML='<div style="width:'+t.width+"px;height:"+t.height+'px;overflow:hidden"><div style="width:'+n+"px;height:"+l+"px;"+s.join("-webkit-")+s.join("-ms-")+s.join("-moz-")+s.join("")+'"><canvas/></div></div>';var f=e.canvas=h.querySelector("canvas");e.ctx=f.getContext("2d");if(f.width=n,f.height=l,i&&(i.innerHTML="",i.appendChild(h)),!Recorder.LibFFT)throw new Error("需要lib.fft.js支持");e.fft=Recorder.LibFFT(1024),e.lastH=[],e.stripesH=[]};e.prototype=t.prototype={genLinear:function(t,e,r,a){for(var i=t.createLinearGradient(0,r,0,a),o=0;o<e.length;)i.addColorStop(e[o++],e[o++]);return i},input:function(t,e,r){var a=this;a.sampleRate=r,a.pcmData=t,a.pcmPos=0,a.inputTime=Date.now(),a.schedule()},schedule:function(){var t=this,e=t.set,r=Math.floor(1e3/e.fps);t.timer||(t.timer=setInterval(function(){t.schedule()},r));var a=Date.now(),i=t.drawTime||0;if(a-t.inputTime>1.3*e.stripeFallDuration)return clearInterval(t.timer),void(t.timer=0);if(!(a-i<r)){t.drawTime=a;for(var o=t.fft.bufferSize,n=t.pcmData,l=t.pcmPos,h=new Int16Array(o),s=0;s<o&&l<n.length;s++,l++)h[s]=n[l];t.pcmPos=l;var f=t.fft.transform(h);t.draw(f,t.sampleRate)}},draw:function(t,e){var r=this,a=r.set,i=r.ctx,o=a.scale,n=a.width*o,l=a.height*o,h=a.lineCount,s=r.fft.bufferSize,f=a.position,d=Math.abs(a.position),c=1==f?0:l,p=l;d<1&&(c=p/=2,p=Math.floor(p*(1+d)),c=Math.floor(0<f?c*(1-d):c*(1+d)));for(var u=r.lastH,w=r.stripesH,v=Math.ceil(p/(a.fallDuration/(1e3/a.fps))),g=Math.ceil(p/(a.stripeFallDuration/(1e3/a.fps))),m=a.stripeMargin*o,M=1<<(Math.round(Math.log(s)/Math.log(2)+3)<<1),b=Math.log(M)/Math.log(10),L=20*Math.log(32767)/Math.log(10),y=s/2,S=Math.min(y,Math.floor(5e3*y/(e/2))),C=S==y,H=C?h:Math.round(.8*h),R=S/H,D=C?0:(y-S)/(h-H),x=0,F=0;F<h;F++){var T=Math.ceil(x);x+=F<H?R:D;for(var B=Math.min(Math.ceil(x),y),E=0,j=T;j<B;j++)E=Math.max(E,Math.abs(t[j]));var I=M<E?Math.floor(17*(Math.log(E)/Math.log(10)-b)):0,q=p*Math.min(I/L,1);u[F]=(u[F]||0)-v,q<u[F]&&(q=u[F]),q<0&&(q=0),u[F]=q;var z=w[F]||0;if(q&&z<q+m)w[F]=q+m;else{var P=z-g;P<0&&(P=0),w[F]=P}}i.clearRect(0,0,n,l);var W=r.genLinear(i,a.linear,c,c-p),k=a.stripeLinear&&r.genLinear(i,a.stripeLinear,c,c-p)||W,A=r.genLinear(i,a.linear,c,c+p),G=a.stripeLinear&&r.genLinear(i,a.stripeLinear,c,c+p)||A;i.shadowBlur=a.shadowBlur*o,i.shadowColor=a.shadowColor;var V=a.mirrorEnable,J=V?2*h-1:h,K=a.widthRatio,N=a.spaceWidth*o;0!=N&&(K=(n-N*(J+1))/n);for(var O=Math.max(1*o,Math.floor(n*K/J)),Q=(n-J*O)/(J+1),U=a.minHeight*o,X=V?n/2-(Q+O/2):0,Y=(F=0,X);F<h;F++)Y+=Q,$=Math.floor(Y),q=Math.max(u[F],U),0!=c&&(_=c-q,i.fillStyle=W,i.fillRect($,_,O,q)),c!=l&&(i.fillStyle=A,i.fillRect($,c,O,q)),Y+=O;if(a.stripeEnable){var Z=a.stripeShadowBlur;i.shadowBlur=(-1==Z?a.shadowBlur:Z)*o,i.shadowColor=a.stripeShadowColor||a.shadowColor;var $,_,tt=a.stripeHeight*o;for(F=0,Y=X;F<h;F++)Y+=Q,$=Math.floor(Y),q=w[F],0!=c&&((_=c-q-tt)<0&&(_=0),i.fillStyle=k,i.fillRect($,_,O,tt)),c!=l&&(l<(_=c+q)+tt&&(_=l-tt),i.fillStyle=G,i.fillRect($,_,O,tt)),Y+=O}if(V){var et=Math.floor(n/2);i.save(),i.scale(-1,1),i.drawImage(r.canvas,Math.ceil(n/2),0,et,l,-et,0,et,l),i.restore()}a.onDraw(t,e)}},Recorder[d]=t}();
(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";
var FrequencyHistogramView=function(set){
return new fn(set);
};
var ViewTxt="FrequencyHistogramView";
var fn=function(set){
var This=this;
var o={
/*
elem:"css selector" //自动显示到dom并以此dom大小为显示大小
//或者配置显示大小手动把frequencyObj.elem显示到别的地方
,width:0 //显示宽度
,height:0 //显示高度
H5环境以上配置二选一
compatibleCanvas: CanvasObject //提供一个兼容H5的canvas对象需支持getContext("2d")支持设置width、height支持drawImage(canvas,...)
,width:0 //canvas显示宽度
,height:0 //canvas显示高度
非H5环境使用以上配置
*/
scale:2 //缩放系数应为正整数使用2(3? no!)倍宽高进行绘制,避免移动端绘制模糊
,fps:20 //绘制帧率,不可过高
,lineCount:30 //直方图柱子数量数量的多少对性能影响不大密集运算集中在FFT算法中
,widthRatio:0.6 //柱子线条宽度占比为所有柱子占用整个视图宽度的比例剩下的空白区域均匀插入柱子中间默认值也基本相当于一根柱子占0.6一根空白占0.4设为1不留空白当视图不足容下所有柱子时也不留空白
,spaceWidth:0 //柱子间空白固定基础宽度柱子宽度自适应当不为0时widthRatio无效当视图不足容下所有柱子时将不会留空白允许为负数让柱子发生重叠
,minHeight:0 //柱子保留基础高度position不为±1时应该保留点高度
,position:-1 //绘制位置,取值-1到1-1为最底下0为中间1为最顶上小数为百分比
,mirrorEnable:false //是否启用镜像,如果启用,视图宽度会分成左右两块,右边这块进行绘制,左边这块进行镜像(以中间这根柱子的中心进行镜像)
,stripeEnable:true //是否启用柱子顶上的峰值小横条position不是-1时应当关闭否则会很丑
,stripeHeight:3 //峰值小横条基础高度
,stripeMargin:6 //峰值小横条和柱子保持的基础距离
,fallDuration:1000 //柱子从最顶上下降到最底部最长时间ms
,stripeFallDuration:3500 //峰值小横条从最顶上下降到底部最长时间ms
//柱子颜色配置:[位置css颜色...] 位置: 取值0.0-1.0之间
,linear:[0,"rgba(0,187,17,1)",0.5,"rgba(255,215,0,1)",1,"rgba(255,102,0,1)"]
//峰值小横条渐变颜色配置取值格式和linear一致留空为柱子的渐变颜色
,stripeLinear:null
,shadowBlur:0 //柱子阴影基础大小设为0不显示阴影如果柱子数量太多时请勿开启非常影响性能
,shadowColor:"#bbb" //柱子阴影颜色
,stripeShadowBlur:-1 //峰值小横条阴影基础大小设为0不显示阴影-1为柱子的大小如果柱子数量太多时请勿开启非常影响性能
,stripeShadowColor:"" //峰值小横条阴影颜色,留空为柱子的阴影颜色
//当发生绘制时会回调此方法参数为当前绘制的频率数据和采样率可实现多个直方图同时绘制只消耗一个input输入和计算时间
,onDraw:function(frequencyData,sampleRate){}
};
for(var k in set){
o[k]=set[k];
};
This.set=set=o;
var cCanvas="compatibleCanvas";
if(set[cCanvas]){
var canvas=This.canvas=set[cCanvas];
}else{
if(!isBrowser)throw new Error($T.G("NonBrowser-1",[ViewTxt]));
var elem=set.elem;
if(elem){
if(typeof(elem)=="string"){
elem=document.querySelector(elem);
}else if(elem.length){
elem=elem[0];
};
};
if(elem){
set.width=elem.offsetWidth;
set.height=elem.offsetHeight;
};
var thisElem=This.elem=document.createElement("div");
thisElem.style.fontSize=0;
thisElem.innerHTML='<canvas style="width:100%;height:100%;"/>';
var canvas=This.canvas=thisElem.querySelector("canvas");
if(elem){
elem.innerHTML="";
elem.appendChild(thisElem);
};
};
var scale=set.scale;
var width=set.width*scale;
var height=set.height*scale;
if(!width || !height){
throw new Error($T.G("IllegalArgs-1",[ViewTxt+" width=0 height=0"]));
};
canvas.width=width;
canvas.height=height;
var ctx=This.ctx=canvas.getContext("2d");
if(!Recorder.LibFFT){
throw new Error($T.G("NeedImport-2",[ViewTxt,"src/extensions/lib.fft.js"]));
};
This.fft=Recorder.LibFFT(1024);
//柱子所在高度
This.lastH=[];
//峰值小横条所在高度
This.stripesH=[];
};
fn.prototype=FrequencyHistogramView.prototype={
genLinear:function(ctx,colors,from,to){
var rtv=ctx.createLinearGradient(0,from,0,to);
for(var i=0;i<colors.length;){
rtv.addColorStop(colors[i++],colors[i++]);
};
return rtv;
}
,input:function(pcmData,powerLevel,sampleRate){
var This=this;
This.sampleRate=sampleRate;
This.pcmData=pcmData;
This.pcmPos=0;
This.inputTime=Date.now();
This.schedule();
}
,schedule:function(){
var This=this,set=This.set;
var interval=Math.floor(1000/set.fps);
if(!This.timer){
This.timer=setInterval(function(){
This.schedule();
},interval);
};
var now=Date.now();
var drawTime=This.drawTime||0;
if(now-This.inputTime>set.stripeFallDuration*1.3){
//超时没有输入,顶部横条已全部落下,干掉定时器
clearInterval(This.timer);
This.timer=0;
This.lastH=[];//重置高度再绘制一次,避免定时不准没到底就停了
This.stripesH=[];
This.draw(null,This.sampleRate);
return;
};
if(now-drawTime<interval){
//没到间隔时间,不绘制
return;
};
This.drawTime=now;
//调用FFT计算频率数据
var bufferSize=This.fft.bufferSize;
var pcm=This.pcmData;
var pos=This.pcmPos;
var arr=new Int16Array(bufferSize);
for(var i=0;i<bufferSize&&pos<pcm.length;i++,pos++){
arr[i]=pcm[pos];
};
This.pcmPos=pos;
var frequencyData=This.fft.transform(arr);
//推入绘制
This.draw(frequencyData,This.sampleRate);
}
,draw:function(frequencyData,sampleRate){
var This=this,set=This.set;
var ctx=This.ctx;
var scale=set.scale;
var width=set.width*scale;
var height=set.height*scale;
var lineCount=set.lineCount;
var bufferSize=This.fft.bufferSize;
//计算高度位置
var position=set.position;
var posAbs=Math.abs(set.position);
var originY=position==1?0:height;//y轴原点
var heightY=height;//最高的一边高度
if(posAbs<1){
heightY=heightY/2;
originY=heightY;
heightY=Math.floor(heightY*(1+posAbs));
originY=Math.floor(position>0?originY*(1-posAbs):originY*(1+posAbs));
};
var lastH=This.lastH;
var stripesH=This.stripesH;
var speed=Math.ceil(heightY/(set.fallDuration/(1000/set.fps)));
var stripeSpeed=Math.ceil(heightY/(set.stripeFallDuration/(1000/set.fps)));
var stripeMargin=set.stripeMargin*scale;
var Y0=1 << (Math.round(Math.log(bufferSize)/Math.log(2) + 3) << 1);
var logY0 = Math.log(Y0)/Math.log(10);
var dBmax=20*Math.log(0x7fff)/Math.log(10);
var fftSize=bufferSize/2;
var fftSize5k=Math.min(fftSize,Math.floor(fftSize*5000/(sampleRate/2)));//5khz所在位置8000采样率及以下最高只有4khz
var fftSize5kIsAll=fftSize5k==fftSize;
var line80=fftSize5kIsAll?lineCount:Math.round(lineCount*0.8);//80%的柱子位置
var fftSizeStep1=fftSize5k/line80;
var fftSizeStep2=fftSize5kIsAll?0:(fftSize-fftSize5k)/(lineCount-line80);
var fftIdx=0;
for(var i=0;i<lineCount;i++){
//不采用jmp123的非线性划分频段录音语音并不适用于音乐的频率应当弱化高频部分
//80%关注0-5khz主要人声部分 20%关注剩下的高频,这样不管什么采样率都能做到大部分频率显示一致。
var start=Math.ceil(fftIdx);
if(i<line80){
//5khz以下
fftIdx+=fftSizeStep1;
}else{
//5khz以上
fftIdx+=fftSizeStep2;
};
var end=Math.min(Math.ceil(fftIdx),fftSize);
//参考AudioGUI.java .drawHistogram方法
//查找当前频段的最大"幅值"
var maxAmp=0;
if(frequencyData){
for (var j=start; j<end; j++) {
maxAmp=Math.max(maxAmp,Math.abs(frequencyData[j]));
};
};
//计算音量
var dB= (maxAmp > Y0) ? Math.floor((Math.log(maxAmp)/Math.log(10) - logY0) * 17) : 0;
var h=heightY*Math.min(dB/dBmax,1);
//使柱子匀速下降
lastH[i]=(lastH[i]||0)-speed;
if(h<lastH[i]){h=lastH[i];};
if(h<0){h=0;};
lastH[i]=h;
var shi=stripesH[i]||0;
if(h&&h+stripeMargin>shi) {
stripesH[i]=h+stripeMargin;
}else{
//使峰值小横条匀速度下落
var sh =shi-stripeSpeed;
if(sh < 0){sh = 0;};
stripesH[i] = sh;
};
};
//开始绘制图形
ctx.clearRect(0,0,width,height);
var linear1=This.genLinear(ctx,set.linear,originY,originY-heightY);//上半部分的填充
var stripeLinear1=set.stripeLinear&&This.genLinear(ctx,set.stripeLinear,originY,originY-heightY)||linear1;//上半部分的峰值小横条填充
var linear2=This.genLinear(ctx,set.linear,originY,originY+heightY);//下半部分的填充
var stripeLinear2=set.stripeLinear&&This.genLinear(ctx,set.stripeLinear,originY,originY+heightY)||linear2;//上半部分的峰值小横条填充
//计算柱子间距
ctx.shadowBlur=set.shadowBlur*scale;
ctx.shadowColor=set.shadowColor;
var mirrorEnable=set.mirrorEnable;
var mirrorCount=mirrorEnable?lineCount*2-1:lineCount;//镜像柱子数量翻一倍-1根
var widthRatio=set.widthRatio;
var spaceWidth=set.spaceWidth*scale;
if(spaceWidth!=0){
widthRatio=(width-spaceWidth*(mirrorCount+1))/width;
};
var lineWidth=Math.max(1*scale,Math.floor((width*widthRatio)/mirrorCount));//柱子宽度至少1个单位
var spaceFloat=(width-mirrorCount*lineWidth)/(mirrorCount+1);//均匀间隔,首尾都留空,可能为负数,柱子将发生重叠
//绘制柱子
var minHeight=set.minHeight*scale;
var mirrorSubX=spaceFloat+lineWidth/2;
var XFloat=mirrorEnable?width/2-mirrorSubX:0;//镜像时,中间柱子位于正中心
for(var i=0,xFloat=XFloat,x,y,h;i<lineCount;i++){
xFloat+=spaceFloat;
x=Math.floor(xFloat);
h=Math.max(lastH[i],minHeight);
//绘制上半部分
if(originY!=0){
y=originY-h;
ctx.fillStyle=linear1;
ctx.fillRect(x, y, lineWidth, h);
};
//绘制下半部分
if(originY!=height){
ctx.fillStyle=linear2;
ctx.fillRect(x, originY, lineWidth, h);
};
xFloat+=lineWidth;
};
//绘制柱子顶上峰值小横条
if(set.stripeEnable){
var stripeShadowBlur=set.stripeShadowBlur;
ctx.shadowBlur=(stripeShadowBlur==-1?set.shadowBlur:stripeShadowBlur)*scale;
ctx.shadowColor=set.stripeShadowColor||set.shadowColor;
var stripeHeight=set.stripeHeight*scale;
for(var i=0,xFloat=XFloat,x,y,h;i<lineCount;i++){
xFloat+=spaceFloat;
x=Math.floor(xFloat);
h=stripesH[i];
//绘制上半部分
if(originY!=0){
y=originY-h-stripeHeight;
if(y<0){y=0;};
ctx.fillStyle=stripeLinear1;
ctx.fillRect(x, y, lineWidth, stripeHeight);
};
//绘制下半部分
if(originY!=height){
y=originY+h;
if(y+stripeHeight>height){
y=height-stripeHeight;
};
ctx.fillStyle=stripeLinear2;
ctx.fillRect(x, y, lineWidth, stripeHeight);
};
xFloat+=lineWidth;
};
};
//镜像,从中间直接镜像即可
if(mirrorEnable){
var srcW=Math.floor(width/2);
ctx.save();
ctx.scale(-1,1);
ctx.drawImage(This.canvas,Math.ceil(width/2),0,srcW,height,-srcW,0,srcW,height);
ctx.restore();
};
if(frequencyData){
set.onDraw(frequencyData,sampleRate);
};
}
};
Recorder[ViewTxt]=FrequencyHistogramView;
}));

View File

@ -1,6 +1,118 @@
/*
录音
时域转频域快速傅里叶变换(FFT)
https://github.com/xiangyuecn/Recorder
src: extensions/lib.fft.js
var fft=Recorder.LibFFT(bufferSize)
bufferSize取值2的n次方
fft.bufferSize 实际采用的bufferSize
fft.transform(inBuffer)
inBuffer:[Int16,...] 数组长度必须是bufferSize
返回[Float64(Long),...]长度为bufferSize/2
*/
Recorder.LibFFT=function(r){"use strict";var s,v,d,l,F,b,g,m;return function(r){var o,t,a,f;for(s=Math.round(Math.log(r)/Math.log(2)),d=((v=1<<s)<<2)*Math.sqrt(2),l=[],F=[],b=[0],g=[0],m=[],o=0;o<v;o++){for(a=o,f=t=0;t!=s;t++)f<<=1,f|=1&a,a>>>=1;m[o]=f}var n,u=2*Math.PI/v;for(o=(v>>1)-1;0<o;o--)n=o*u,g[o]=Math.cos(n),b[o]=Math.sin(n)}(r),{transform:function(r){var o,t,a,f,n,u,e,h,M=1,i=s-1;for(o=0;o!=v;o++)l[o]=r[m[o]],F[o]=0;for(o=s;0!=o;o--){for(t=0;t!=M;t++)for(n=g[t<<i],u=b[t<<i],a=t;a<v;a+=M<<1)e=n*l[f=a+M]-u*F[f],h=n*F[f]+u*l[f],l[f]=l[a]-e,F[f]=F[a]-h,l[a]+=e,F[a]+=h;M<<=1,i--}t=v>>1;var c=new Float64Array(t);for(n=-(u=d),o=t;0!=o;o--)e=l[o],h=F[o],c[o-1]=n<e&&e<u&&n<h&&h<u?0:Math.round(e*e+h*h);return c},bufferSize:v}};
(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";
/*
从FFT.java 移植Java开源库jmp123 版本0.3
https://www.iteye.com/topic/851459
https://sourceforge.net/projects/jmp123/files/
*/
Recorder.LibFFT=function(bufferSize){
var FFT_N_LOG,FFT_N,MINY;
var real, imag, sintable, costable;
var bitReverse;
var FFT_Fn=function(bufferSize) {//bufferSize只能取值2的n次方
FFT_N_LOG=Math.round(Math.log(bufferSize)/Math.log(2));
FFT_N = 1 << FFT_N_LOG;
MINY = ((FFT_N << 2) * Math.sqrt(2));
real = [];
imag = [];
sintable = [0];
costable = [0];
bitReverse = [];
var i, j, k, reve;
for (i = 0; i < FFT_N; i++) {
k = i;
for (j = 0, reve = 0; j != FFT_N_LOG; j++) {
reve <<= 1;
reve |= (k & 1);
k >>>= 1;
}
bitReverse[i] = reve;
}
var theta, dt = 2 * Math.PI / FFT_N;
for (i = (FFT_N >> 1) - 1; i > 0; i--) {
theta = i * dt;
costable[i] = Math.cos(theta);
sintable[i] = Math.sin(theta);
}
}
/*
用于频谱显示的快速傅里叶变换
inBuffer 输入FFT_N个实数返回 FFT_N/2个输出值(复数模的平方)
*/
var getModulus=function(inBuffer) {
var i, j, k, ir, j0 = 1, idx = FFT_N_LOG - 1;
var cosv, sinv, tmpr, tmpi;
for (i = 0; i != FFT_N; i++) {
real[i] = inBuffer[bitReverse[i]];
imag[i] = 0;
}
for (i = FFT_N_LOG; i != 0; i--) {
for (j = 0; j != j0; j++) {
cosv = costable[j << idx];
sinv = sintable[j << idx];
for (k = j; k < FFT_N; k += j0 << 1) {
ir = k + j0;
tmpr = cosv * real[ir] - sinv * imag[ir];
tmpi = cosv * imag[ir] + sinv * real[ir];
real[ir] = real[k] - tmpr;
imag[ir] = imag[k] - tmpi;
real[k] += tmpr;
imag[k] += tmpi;
}
}
j0 <<= 1;
idx--;
}
j = FFT_N >> 1;
var outBuffer=new Float64Array(j);
/*
* 输出模的平方:
* for(i = 1; i <= j; i++)
* inBuffer[i-1] = real[i] * real[i] + imag[i] * imag[i];
*
* 如果FFT只用于频谱显示,可以"淘汰"幅值较小的而减少浮点乘法运算. MINY的值
* 和Spectrum.Y0,Spectrum.logY0对应.
*/
sinv = MINY;
cosv = -MINY;
for (i = j; i != 0; i--) {
tmpr = real[i];
tmpi = imag[i];
if (tmpr > cosv && tmpr < sinv && tmpi > cosv && tmpi < sinv)
outBuffer[i - 1] = 0;
else
outBuffer[i - 1] = Math.round(tmpr * tmpr + tmpi * tmpi);
}
return outBuffer;
}
FFT_Fn(bufferSize);
return {transform:getModulus,bufferSize:FFT_N};
};
}));

File diff suppressed because one or more lines are too long