Skip to content

Instantly share code, notes, and snippets.

@matsub
Created November 16, 2018 06:15
Show Gist options
  • Save matsub/d05e2aad43b0f49afd64e480f8c5630f to your computer and use it in GitHub Desktop.
Save matsub/d05e2aad43b0f49afd64e480f8c5630f to your computer and use it in GitHub Desktop.
WebRTC+WebAudio
<!doctype html>
<input type="range" value="1" min="0" max="1" step="0.05">
<script src="VolumeController.js"></script>
<script>
async function main() {
const input = document.querySelector("input")
const stream = await navigator.mediaDevices.getUserMedia({
video: false,
audio: true
})
attachVolumeController(canvas, stream)
}
main()
</script>
class VolumeController {
constructor(audioCtx, inputElement) {
inputElement.addEventListener('input', e => {
this.gainNode.gain.value = inputElement.value
})
const gainNode = audioCtx.createGain()
gainNode.gain.value = inputElement.value
gainNode.connect(audioCtx.destination)
this.gainNode = gainNode
}
get node() {
return this.gainNode
}
}
function attachVolumeController (inputElement, stream) {
const audioCtx = new AudioContext()
// create controller
const controller = new VolumeController(audioCtx, inputElement)
// connect audio context
const source = audioCtx.createMediaStreamSource(stream)
source.connect(controller.node)
}
<!doctype html>
<canvas id="canvas" width="320" height="20"></canvas>
<script src="VolumeIndicator.js"></script>
<script>
async function main() {
const canvas = document.querySelector("canvas")
const stream = await navigator.mediaDevices.getUserMedia({
video: false,
audio: true
})
attachVolumeIndicator(canvas, stream)
}
main()
</script>
function getAverageVolume(array) {
const sum = array.reduce((a,b)=>a+b, 0)
return sum / array.length
}
class VolumeIndicator {
constructor(audioCtx, canvas) {
const indicator = new Indicator(canvas)
// setup a analyser
const analyser = audioCtx.createAnalyser()
analyser.smoothingTimeConstant = 0.3
analyser.fftSize = 1024
// setup a script node
const processor = audioCtx.createScriptProcessor(2048, 1, 1)
processor.onaudioprocess = this.setup(analyser, indicator)
analyser.connect(processor)
processor.connect(audioCtx.destination)
this.analyser = analyser
}
setup(analyser, indicator) {
const array = new Uint8Array(analyser.frequencyBinCount)
return () => {
// get the average for the first channel
analyser.getByteFrequencyData(array)
const average = getAverageVolume(array)
indicator.draw(average*2)
}
}
get node() {
return this.analyser
}
}
class Indicator {
constructor(canvas) {
this.width = canvas.width
this.height = canvas.height
this.scale = this.width/120
const ctx = canvas.getContext("2d")
const gradient = ctx.createLinearGradient(0, 0, this.width, 0)
gradient.addColorStop(0,'#004400')
gradient.addColorStop(0.5,'#00ff44')
gradient.addColorStop(1,'#ff4400')
ctx.fillStyle = gradient
ctx.lineWidth = 4
ctx.strokeStyle = "#ffffff"
this.ctx = ctx
}
draw(strength) {
const ctx = this.ctx
ctx.clearRect(0, 0, this.width, this.height)
ctx.fillRect(0, 0, strength*this.scale, this.height)
for (let x=0; x < this.width; x+=8) {
ctx.beginPath()
ctx.moveTo(x, 0)
ctx.lineTo(x, this.height)
ctx.stroke()
}
}
}
function attachVolumeIndicator (canvas, stream) {
const audioCtx = new AudioContext()
// create indicator
const indicator = new VolumeIndicator(audioCtx, canvas)
// connect audio context
const source = audioCtx.createMediaStreamSource(stream)
source.connect(indicator.node)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment