Skip to content

Instantly share code, notes, and snippets.

@houkanshan
Last active August 29, 2015 13:56
Show Gist options
  • Save houkanshan/9036745 to your computer and use it in GitHub Desktop.
Save houkanshan/9036745 to your computer and use it in GitHub Desktop.
microphone demo.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<canvas></canvas>
<span class="count"></span>
<script>
var module = { exports: {} }
</script>
<script src="./mainloop.js"></script>
<script src="./virtualizer.js"></script>
<script src="./kick-detector.js"></script>
<script src="./microphone.js"></script>
<script src="./index.js"></script>
</body>
</html>
var microphone = new Microphone({ process: true })
var mainLoop = new MainLoop()
var virtualizer = new Virtualizer()
var kickDetector = new KickDetector()
virtualizer.getArray = function() {
return microphone.getFreqs()
}
var array = []
microphone.defaultProcessor.onaudioprocess = function(e) {
array = e.inputBuffer.getChannelData(0)
}
kickDetector.getArray = function() {
//return microphone.getTimes()
return array
}
//mainLoop.addSpirit(virtualizer)
mainLoop.addSpirit(kickDetector)
mainLoop.start()
microphone.capture()
function addAll(array) {
var total = 0
for(var i = 0, ilen = array.length; i < ilen; i++) {
total = Math.abs(array[i])
}
return total
}
function KickDetector(options) {
options = options || {}
this.timeWindow = options.timeWindow || 100
this.kickCount = 0
this.initLast()
}
var fn = KickDetector.prototype
fn.getArray = function() { return [] }
fn.update = function(dt) {
this.array = this.getArray()
var array = this.array
, total = addAll(array) * 100
if (total < 1) {
this.initLast()
return
}
if (!this.lastTotal) {
this.lastTotal = total
return
}
this.isUp = total > this.lastTotal
if (!this.isUp && this.wasUp) {
this.kicked = true
}
this.wasUp = this.isUp
this.lastTotal = total
}
fn.initLast = function() {
this.wasUp = true // init
this.lastTotal = 1
}
fn.draw = function() {
if (this.kicked && !this.windowOpen) {
this.windowOpen = true
this.kickCount += 1
console.log('kick')
setTimeout(function() {
this.windowOpen = false
this.kicked = false
}.bind(this), this.timeWindow)
}
}
window.requestAnimationFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oCancelAnimationFrame ||
window.msCancelAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
}
}())
window.cancelAnimationFrame = (function(){
return window.cancelAnimationFrame ||
window.webkitCancelAnimationFrame ||
window.mozCancelAnimationFrame ||
window.oCancelAnimationFrame ||
window.msCancelAnimationFrame ||
window.clearTimeout
})
function MainLoop(opts) {
this.spirits = []
}
MainLoop.prototype = {
constructor: MainLoop.prototype.constructor
, start: function() {
this.lasttime = Date.now()
var step = (function () {
this.tick()
this.timer = requestAnimationFrame(step)
}).bind(this)
this.timer = requestAnimationFrame(step)
}
, stop: function() {
cancelAnimationFrame(this.timer)
this.timer = null
}
, isRunning: function() {
return !!this.timer
}
, preUpdate: function() {}
, preDraw: function() {}
, tick: function() {
var now = Date.now()
, dt = (now - this.lasttime)
if (dt < 5) { return }
dt /= 1000
this.lasttime = now
this.preUpdate(dt)
this.preDraw()
this.spirits.forEach(function(spirit, i) {
spirit.update(dt)
spirit.draw()
})
}
, addSpirit: function(spirit) {
var origDestroy = spirit.destroy
, spirits = this.spirits
spirit.destroy = function() {
var index = spirits.indexOf(spirit)
if (index > -1) {
spirits.splice(index, 1);
}
origDestroy.apply(this, arguments)
}
spirits.push(spirit)
}
, resetSpirit: function() {
this.spirits = []
}
}
window.AudioContext = window.AudioContext
|| window.mozAudioContext
|| window.webkitAudioContext
|| window.msAudioContext
|| window.oAudioContext
navigator.getUserMedia = navigator.getUserMedia
|| navigator.mozGetUserMedia
|| navigator.webkitGetUserMedia
|| navigator.msGetUserMedia
|| navigator.oGetUserMedia
function Microphone(options) {
options = options || {}
var audioContext = this.audioContext = new AudioContext()
var bufferSize = this.bufferSize = options.bufferSize || 2048
this.processors = []
if (options.analyse) {
this.defaultAnalyser = this.addAnalyser()
this.freqs = new Uint8Array(this.defaultAnalyser.frequencyBinCount)
this.times = new Uint8Array(this.defaultAnalyser.frequencyBinCount)
}
if (options.process) {
this.defaultProcessor = this.addProcessor()
}
}
var fn = Microphone.prototype
fn.filterNoise = function() {
// Simple noise filter.
var lp = this.lp = this.audioContext.createBiquadFilter()
lp.type = lp.LOWPASS
lp.frequency = 8000
lp.Q = 0.1
var hp = this.hp = this.audioContext.createBiquadFilter()
hp.type = hp.HIGHPASS
hp.frequency = 20
hp.Q = 0.1
}
fn.capture = function() {
this.filterNoise()
navigator.getUserMedia({
audio: true
}, success.bind(this), error.bind(this))
function success(stream) {
this.stream = stream
var src = this.audioContext.createMediaStreamSource(stream)
, node = src
if (this.lp) {
node.connect(this.lp)
node = this.lp
}
if (this.hp) {
node.connect(this.hp)
node = this.hp
}
node.connect(this.audioContext.destination)
this.processors.forEach(function(processor) {
processor(node)
}.bind(this))
}
function error(e) {
throw new Error(e)
}
}
fn.getFreqs = function() {
this.defaultAnalyser.getByteFrequencyData(this.freqs)
return this.freqs
}
fn.getTimes = function() {
this.defaultAnalyser.getByteTimeDomainData(this.times)
return this.times
}
fn.addProcessor = function() {
var processor = this.audioContext.createScriptProcessor(this.bufferSize, 1, 1)
processor.connect(this.audioContext.destination)
this.processors.push(function(source, stream) {
source.connect(processor)
})
return processor
}
fn.addAnalyser = function() {
var analyser = this.audioContext.createAnalyser()
analyser.connect(this.audioContext.destination)
analyser.smoothingTimeConstant = 0.8
analyser.fftSize = this.bufferSize
analyser.minDecibels = -140
analyser.maxDecibels = 0
this.processors.push(function(source, stream) {
source.connect(analyser)
})
return analyser
}
function Virtualizer(options) {
options = options || {}
var height = this.height = options.height || 512
var width = this.width = options.widget || 1024
var canvas = document.querySelector('canvas')
canvas.height = height
canvas.width = width
this.ctx = canvas.getContext('2d')
}
var fn = Virtualizer.prototype
fn.getArray = function() { return [] }
fn.update = function(dt) {
this.array = this.getArray()
}
fn.draw = function() {
this.clearCanvas()
var array = this.array
var length = array.length
var value, percent, offset, barWidth
for(var i = 0, ilen = length; i < length; i++) {
value = array[i]
percent = value / 256
height = this.height * percent
offset = this.height - height - 1
barWidth = this.width / length
this.ctx.fillStyle = '#999'
this.ctx.fillRect(i * barWidth, offset, barWidth, height)
}
}
fn.clearCanvas = function() {
this.ctx.clearRect(0, 0, this.width, this.height)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment