Octave Band
Filter
[
["none","None"],
["octave","Octave"],
["third","Third"],
["sixth","Sixth"]
]
Weighting
[
["none","none"],
["Z","Z"],
["A","A"],
["C","C"]
]
Mark Hovered Freq.
Harmonics
Freq. Scale
\bold{\text{Sample Rate:}} \large{\reg{}{sample-rate}} kHz
\bold{\text{Bin Rate:}} \large{\reg{}{bin-rate-text}} Hz
\bold{\text{Bin Size:}} \large{2\sup{\reg{}{bin-size-exp}}} Samples
\bold{\text{Time Averaging}}
const mic = reg.mic
const fs = mic.audioCtx.sampleRate
const pP = this.newPlot({
position: [this.left + 100 , this.top - this.height/4],
size: [this.width - 130, this.height/4 -20],
rangeX: [0,mic.binSize / fs],
rangeY: [-1,1],
labelX: "Time [s]",
labelGapX: 20,
labelY: "Waveform",
tickStepY: 0.5,
tickArrayX: [0,...sampleLin(5,15,11).map(v=>(2**v) / fs)],
tickEndsX: false,
labelGapX: 30,
})
const pF = this.newPlot({
position: [this.left + 100 , this.bottom + 100],
size: [this.width - 130, this.height/2 + 50],
rangeX: [22,22720],
rangeY: [0,100],
labelX: "Frequency [Hz]",
labelY: "Level [dB]",
labelGapX: 20,
tickArrayX: mic.bands.octaveCenters,
tickStepY: 20,
typeX: "log",
labelGapX: 30,
})
const oscLine = pP.line({
x: ()=>mic.timeArray,
y: ()=>mic.waveForm
})
const specLine = pF.line({
x: ()=>mic.freqArray,
y: ()=>mic.spectrum
})
const hitbox = pF.rect({
position: ()=>pF.position,
size: ()=>pF.size,
anchor: ()=>[0,0],
screen: true,
fill:"transparent"
})
const octBar = pF.bar({
bins:()=>mic.getBandArray(reg.octaveFilter.value),
y: ()=>mic.bandLevels(reg.octaveFilter.value),
opacity: 0.8,
})
const weightLine = pF.plotFn({
y:f=>mic.getWeightFn(reg.weighting.value)(f) + 80,
dash: "20 20",
stroke: color.neutral
})
let mx = 100
const markers = pF.group("markers")
markers.classList.add("hidden")
for(let i = 1; i <= reg.harmonics.max+1; i++){
pF.line({
x:()=>[i*mx,i*mx],
y:()=>[pF.y.range[0],pF.y.range[1]],
stroke:color.highlight,
strokeWidth:1,
group:markers,
}).i = i - 1
pF.text({
position: ()=>[pF.x.scale.toRange(i*mx)+1,pF.y.scale.toRange(pF.y.range[1])],
text: ()=>{
const x = round(i*mx)
return x>=1e3?round(x/1000,i<3?2:1)+" k ":x
},
anchor: ()=>[
i>4?i>5?0.1:0.25:0.5,
i>4?i>5?2:1.75:1.5
],
screen:true,
clip:false,
color:color.highlight,
fontSize:22,
group:markers,
opaque:true,
}).i = i - 1
}
const ttg = pF.group("tooltip")
ttg.classList.add("hidden")
let ttPos = [this.middle,this.center]
let ttCenter, ttMin, ttMax, ttLevel
pF.rect({
position: ()=>ttPos,
size: ()=>[250,140],
corner: 15,
group: ttg,
screen: true,
clip: false,
fill: color.plotBackground,
stroke: color.neutral,
})
pF.text({
position: ()=>[ttPos[0],ttPos[1]+40],
text: ()=>ttLevel,
screen: true,
clip: false,
group: ttg,
})
pF.text({
position: ()=>[ttPos[0],ttPos[1]],
text: ()=>ttCenter,
screen: true,
clip: false,
group: ttg,
})
pF.text({
position: ()=>[ttPos[0],ttPos[1]-40],
text: ()=>ttMax,
screen: true,
clip: false,
group: ttg,
})
mic.onUpdate(m=>{
oscLine.update()
specLine.update()
if (reg.octaveFilter.value != "none") octBar.update()
})
reg.octaveFilter.onUpdate(v=>{
octBar.classList.toggle("hidden",v=="none")
if (v!="none") octBar.updateBins()
octBar.subs.forEach(b=>{
b.addAction(p=>{
ttg.classList.remove("hidden")
ttLevel = "Level: " + round(b.value,1) + " dB"
ttCenter = "Center: " + formatPre(b.center) + "Hz"
ttMax = formatPre(b.min,true)+"- "+formatPre(b.max) + "Hz"
ttPos = [min(pF.toX(b.center),this.width - 125),pF.toY(b.value) + 80]
ttg.update()
b.style.opacity = 0.95
},"hover")
b.addAction(p=>{
ttg.classList.add("hidden")
b.style.opacity = 0.8
},"unhover")
})
})
reg.weighting.onUpdate(v=>{
weightLine.classList.toggle("hidden",v=="none")
mic.weighting = v
weightLine.update()
})
reg.sampleRate.textContent = format(fs/1e3)
reg.binSizeExp.textContent = format(reg.binSize.value)
reg.binSize.onUpdate(v=>{
mic.binSize = 2**v
if(v>7) pP.x.max = 1.001 * mic.binSize / fs
reg.binSizeExp.textContent = format(v)
})
reg.binRateText.textContent = format(reg.binRate.value)
reg.binRate.onUpdate(v=>{
mic.binRate = v
reg.binRateText.textContent = round(v)
})
reg.binSmooth.onUpdate(v=>{
mic.binSmoothing = v
})
reg.freqScale.onUpdate(v=>{
pF.x.type = v?"lin":"log"
weightLine.update()
specLine.update()
if (reg.octaveFilter.value != "none") octBar.update()
})
hitbox.addAction(p=>{
mx = round(p.x)
markers.update()
})
reg.harmonics.onUpdate(v=>{
markers.forEach(t=>t.classList.toggle("hidden",t.i>v))
markers.update()
})
reg.harmonics.value = 2
reg.harmonicMarks.onUpdate((v,V,el)=>{
mx = 100
markers.update()
markers.classList.toggle("hidden",!v)
el.button.style.background = v?color.highlight:""
el.button.style.color = v?color.textInv:""
reg.harmonics.handleText.style.color = v?color.textInv:""
reg.harmonics.handleGroup.firstChild.style.fill = v?color.highlight:""
})
document.addEventListener("keydown",e=>{
e.preventDefault()
if(e.code=="Space") mic.toggle()
})