PageSourceSearch

https://asenno.com/_app/immutable/chunks/CMswT0Nq.js

js asenno.com collected 2026-10-08 13:54:08 UTC 5,478 bytes, 45 lines download raw bytes

1const e={id:"thd-analyzer",title:"THD Analyzer",description:"Measure total harmonic distortion with FFT analysis and windowing options.",category:"Distortion Analysis",defaultSettings:{frequency:1e3,level:-20,windowFunction:"hann",fftSize:8192,sweepMode:!1,sweepStartLevel:-60,sweepEndLevel:0,sweepDuration:10,harmonicsToMeasure:10,playing:!1,showSpectrum:!0,showHarmonics:!0},windowFunctions:{none:"None (Rectangular)",hann:"Hann",hamming:"Hamming",blackman:"Blackman",blackmanHarris:"Blackman-Harris",flattop:"Flat Top"},fftSizes:{2048:"2048",4096:"4096",8192:"8192",16384:"16384",32768:"32768"},applyWindow(data,e){const t=data.length,a=Math.PI
2switch(e){case"hann":for(let e=0;e<t;e++)data[e]*=.5*(1-Math.cos(2*a*e/(t-1)))
3break
4case"hamming":for(let e=0;e<t;e++)data[e]*=.54-.46*Math.cos(2*a*e/(t-1))
5break
6case"blackman":for(let e=0;e<t;e++)data[e]*=.42-.5*Math.cos(2*a*e/(t-1))+.08*Math.cos(4*a*e/(t-1))
7break
8case"blackmanHarris":for(let e=0;e<t;e++){const n=.35875,r=.48829,o=.14128,s=.01168
9data[e]*=n-r*Math.cos(2*a*e/(t-1))+o*Math.cos(4*a*e/(t-1))-s*Math.cos(6*a*e/(t-1))}break
10case"flattop":for(let e=0;e<t;e++)data[e]*=.21557895-.41663158*Math.cos(2*a*e/(t-1))+.277263158*Math.cos(4*a*e/(t-1))-.083578947*Math.cos(6*a*e/(t-1))+.006947368*Math.cos(8*a*e/(t-1))}return data},calculateTHD(e,t,a,n,r){const o=n/r
11let s=0,i=t
12for(let a=Math.max(0,t-2);a<=Math.min(e.length-1,t+2);a++)e[a]>s&&(s=e[a],i=a)
13const l=s*s
14let c=0
15const w=[]
16for(let t=2;t<=a+1;t++){const a=Math.round(i*t)
17if(a>=e.length)break
18let n=0,r=a
19for(let t=Math.max(0,a-2);t<=Math.min(e.length-1,a+2);t++)e[t]>n&&(n=e[t],r=t)
20const l=n*n
21c+=l,w.push({number:t,frequency:r*o,level:20*Math.log10(n/s),power:l})}const u=100*Math.sqrt(c/l)
22return{thd:u,thdDb:20*Math.log10(u/100),fundamental:{frequency:i*o,level:20*Math.log10(s),power:l},harmonics:w}},createNodes(e,t){const a=e.createOscillator()
23a.type="sine"
24const n=e.createGain(),r=e.createAnalyser()
25return r.fftSize=8192,r.smoothingTimeConstant=0,a.connect(n),n.connect(r),r.connect(t),{oscillator:a,levelGain:n,analyser:r,dataArrayTime:new Float32Array(r.fftSize),dataArrayFreq:new Float32Array(r.frequencyBinCount),windowedData:new Float32Array(r.fftSize),measurementData:{thd:0,thdDb:-100,fundamental:null,harmonics:[],sweepData:[]},isStarted:!1}},start(e,t,a){const{oscillator:n,levelGain:r,analyser:o}=e
26n.frequency.setValueAtTime(t.frequency,a.currentTime)
27const s=Math.pow(10,t.level/20)
28r.gain.setValueAtTime(s,a.currentTime),o.fftSize!==t.fftSize&&(o.fftSize=t.fftSize,e.dataArrayTime=new Float32Array(o.fftSize),e.dataArrayFreq=new Float32Array(o.frequencyBinCount),e.windowedData=new Float32Array(o.fftSize)),e.isStarted||(n.start(),e.isStarted=!0),e.measurementInterval=setInterval(()=>{this.measureTHD(e,t,a)},100),t.sweepMode&&this.startSweep(e,t,a)},measureTHD(e,t,a){const{analyser:n,dataArrayTime:r,dataArrayFreq:o,windowedData:s,measurementData:i}=e
29n.getFloatTimeDomainData(r)
30for(let e=0;e<r.length;e++)s[e]=r[e]
31this.applyWindow(s,t.windowFunction),n.getFloatFrequencyData(o)
32const l=new Float32Array(o.length)
33for(let e=0;e<o.length;e++)l[e]=Math.pow(10,o[e]/20)
34const c=a.sampleRate,w=Math.round(t.frequency*n.fftSize/c),u=this.calculateTHD(l,w,t.harmonicsToMeasure,c,n.fftSize)
35Object.assign(i,u),t.sweepMode&&e.sweepActive&&i.sweepData.push({level:t.level,thd:u.thd,thdDb:u.thdDb,time:a.currentTime-e.sweepStartTime})},startSweep(e,t,a){const{levelGain:n}=e,{measurementData:r}=e
36r.sweepData=[],t.sweepEndLevel,t.sweepStartLevel,t.sweepDuration,e.sweepStartTime=a.currentTime,e.sweepActive=!0
37const o=Math.pow(10,t.sweepStartLevel/20)
38n.gain.setValueAtTime(o,a.currentTime)
39const s=Math.pow(10,t.sweepEndLevel/20)
40n.gain.exponentialRampToValueAtTime(s,a.currentTime+t.sweepDuration),e.sweepTimeout=setTimeout(()=>{e.sweepActive=!1,t.sweepMode=!1},1e3*t.sweepDuration)},stop(e){e.oscillator&&e.isStarted&&(e.oscillator.stop(),e.isStarted=!1),e.measurementInterval&&(clearInterval(e.measurementInterval),e.measurementInterval=null),e.sweepTimeout&&(clearTimeout(e.sweepTimeout),e.sweepTimeout=null),e.sweepActive=!1},updateSettings(e,t,a){const{oscillator:n,levelGain:r,analyser:o}=e
41if(e.isStarted){if(n.frequency.exponentialRampToValueAtTime(t.frequency,a.currentTime+.01),!e.sweepActive){const e=Math.pow(10,t.level/20)
42r.gain.exponentialRampToValueAtTime(Math.max(1e-5,e),a.currentTime+.01)}o.fftSize!==t.fftSize&&(o.fftSize=t.fftSize,e.dataArrayTime=new Float32Array(o.fftSize),e.dataArrayFreq=new Float32Array(o.frequencyBinCount),e.windowedData=new Float32Array(o.fftSize)),t.sweepMode&&!e.sweepActive&&this.startSweep(e,t,a)}},getMeasurementData:e=>e.measurementData,getSpectrumData(e){const{analyser:t,dataArrayFreq:a}=e
43return t.getFloatFrequencyData(a),a},keyboard:{handlers:{" ":e=>({playing:!e.playing}),s:e=>({sweepMode:!e.sweepMode}),w:e=>{const t=Object.keys((void 0).windowFunctions),a=t.indexOf(e.windowFunction)
44return{windowFunction:t[(a+1)%t.length]}},ArrowUp:e=>({level:Math.min(0,e.level+1)}),ArrowDown:e=>({level:Math.max(-100,e.level-1)})}},visualization:{type:"thd-spectrum",fftSize:8192,showSpectrum:!0,showHarmonics:!0,showTHDMeter:!0,showSweepGraph:!0},controls:{showFrequencySlider:!0,showLevelSlider:!0,showWindowSelector:!0,showFFTSizeSelector:!0,showSweepControls:!0,showHarmonicsSlider:!0,showSpectrumDisplay:!0,showTHDReadout:!0,showPlayButton:!0,infoText:"Measure total harmonic distortion with FFT analysis. Press S for sweep mode, W to cycle window functions."}}
45export{e as default}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.