Created
February 3, 2021 21:33
-
-
Save battlesnake/7719c7f3a18e936515f8c1087334a673 to your computer and use it in GitHub Desktop.
Dirty and simple web audio demo, plotting oscilloscope and spectrum
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>Audio stuff</title> | |
<style> | |
html, | |
body { | |
height: 100%; | |
padding: 0; | |
margin: 0; | |
} | |
body { | |
background: black; | |
overflow: hidden; | |
} | |
.view, | |
.bars { | |
position: absolute; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
} | |
.bars { | |
display: flex; | |
} | |
.bar { | |
flex-grow: 1; | |
} | |
.freq .bars { | |
align-items: flex-end; | |
} | |
.time .bars { | |
align-items: center; | |
} | |
.freq .bar { | |
background: white; | |
} | |
.time .bar { | |
background: green; | |
} | |
.freq .labels { | |
position: absolute; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
display: flex; | |
align-items: flex-end; | |
justify-content: space-between; | |
} | |
.freq .label { | |
font-family: monospace; | |
font-size: 14px; | |
color: yellow; | |
font-weight: bold; | |
box-shadow: 0px 0px 0px 2px rgba(0,0,0,1); | |
} | |
</style> | |
</head> | |
<body> | |
<div class="freq view"> | |
<div class="bars"> | |
</div> | |
<div class="labels"> | |
<div class="label fmin"> | |
</div> | |
<div class="label fmid"> | |
</div> | |
<div class="label fmax"> | |
</div> | |
</div> | |
</div> | |
<div class="time view"> | |
<div class="bars"> | |
</div> | |
</div> | |
<script> | |
(() => { | |
const um = navigator.getUserMedia({ | |
video: false, | |
audio: true | |
}, callback, console.log) | |
let ctx | |
let anal | |
let buffer | |
function callback(stream) { | |
ctx = new AudioContext({ | |
sampleRate: 48000, | |
latencyHint: 'interactive' | |
}) | |
mic = ctx.createMediaStreamSource(stream) | |
anal = ctx.createAnalyser() | |
anal.fftSize = 512 | |
anal.smoothingTimeConstant = 0.8 | |
bufferLength = anal.frequencyBinCount | |
buffer = new Uint8Array(bufferLength) | |
mic.connect(anal) | |
// anal.connect(ctx.destination) | |
analyse() | |
} | |
function plotFreq() { | |
anal.getByteFrequencyData(buffer) | |
const elements = [] | |
const range = 60 | |
for (let i = 0; i < anal.frequencyBinCount; ++i) { | |
const db = 20 * Math.log10(buffer[i] / 255) | |
const height = Math.max((db + range) / range, 0) * 100 | |
elements.push(`<div class="bar" style="height: ${height}%;"></div>`) | |
} | |
document.querySelector('.freq .bars').innerHTML = elements.join('') | |
const max_freq = anal.frequencyBinCount * ctx.sampleRate / anal.fftSize / 1000 | |
document.querySelector('.freq .label.fmid').innerHTML = `${(max_freq / 2).toFixed(1)}kHz` | |
document.querySelector('.freq .label.fmax').innerHTML = `${max_freq.toFixed(1)}kHz` | |
} | |
function plotTime() { | |
anal.getByteTimeDomainData(buffer) | |
const elements = [] | |
for (let i = 0; i < anal.frequencyBinCount; ++i) { | |
const height = Math.abs(buffer[i] - 128) * 100 / 128 | |
elements.push(`<div class="bar" style="height: ${height}%;"></div>`) | |
} | |
document.querySelector('.time .bars').innerHTML = elements.join('') | |
} | |
function analyse() { | |
setTimeout(analyse, 100) | |
plotFreq() | |
plotTime() | |
} | |
})() | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment