UNPKG

doppler

Version:

Motion sensing using the doppler effect

131 lines (107 loc) 4.12 kB
window.doppler = (function() { var AuContext = (window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.oAudioContext || window.msAudioContext); var ctx = new AuContext(); var osc = ctx.createOscillator(); // This is just preliminary, we'll actually do a quick scan // (as suggested in the paper) to optimize this. var freq = 20000; // See paper for this particular choice of frequencies var relevantFreqWindow = 33; var getBandwidth = function(analyser, freqs) { var primaryTone = freqToIndex(analyser, freq); var primaryVolume = freqs[primaryTone]; // This ratio is totally empirical (aka trial-and-error). var maxVolumeRatio = 0.001; var leftBandwidth = 0; do { leftBandwidth++; var volume = freqs[primaryTone-leftBandwidth]; var normalizedVolume = volume / primaryVolume; } while (normalizedVolume > maxVolumeRatio && leftBandwidth < relevantFreqWindow); var rightBandwidth = 0; do { rightBandwidth++; var volume = freqs[primaryTone+rightBandwidth]; var normalizedVolume = volume / primaryVolume; } while (normalizedVolume > maxVolumeRatio && rightBandwidth < relevantFreqWindow); return { left: leftBandwidth, right: rightBandwidth }; }; var freqToIndex = function(analyser, freq) { var nyquist = ctx.sampleRate / 2; return Math.round( freq/nyquist * analyser.fftSize/2 ); }; var indexToFreq = function(analyser, index) { var nyquist = ctx.sampleRate / 2; return nyquist/(analyser.fftSize/2) * index; }; var optimizeFrequency = function(osc, analyser, freqSweepStart, freqSweepEnd) { var oldFreq = osc.frequency.value; var audioData = new Uint8Array(analyser.frequencyBinCount); var maxAmp = 0; var maxAmpIndex = 0; var from = freqToIndex(analyser, freqSweepStart); var to = freqToIndex(analyser, freqSweepEnd); for (var i = from; i < to; i++) { osc.frequency.value = indexToFreq(analyser, i); analyser.getByteFrequencyData(audioData); if (audioData[i] > maxAmp) { maxAmp = audioData[i]; maxAmpIndex = i; } } // Sometimes the above procedure seems to fail, not sure why. // If that happends, just use the old value. if (maxAmpIndex == 0) { return oldFreq; } else { return indexToFreq(analyser, maxAmpIndex); } }; var readMicInterval = 0; var readMic = function(analyser, userCallback) { var audioData = new Uint8Array(analyser.frequencyBinCount); analyser.getByteFrequencyData(audioData); var band = getBandwidth(analyser, audioData); userCallback(band); readMicInterval = setTimeout(readMic, 1, analyser, userCallback); }; var handleMic = function(stream, callback, userCallback) { // Mic var mic = ctx.createMediaStreamSource(stream); var analyser = ctx.createAnalyser(); analyser.smoothingTimeConstant = 0.5; analyser.fftSize = 2048; mic.connect(analyser); // Doppler tone osc.frequency.value = freq; osc.type = osc.SINE; osc.start(0); osc.connect(ctx.destination); // There seems to be some initial "warm-up" period // where all frequencies are significantly louder. // A quick timeout will hopefully decrease that bias effect. setTimeout(function() { // Optimize doppler tone freq = optimizeFrequency(osc, analyser, 19000, 22000); osc.frequency.value = freq; clearInterval(readMicInterval); callback(analyser, userCallback); }); }; return { init: function(callback) { navigator.getUserMedia_ = (navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia); navigator.getUserMedia_({ audio: { optional: [{ echoCancellation: false }] } }, function(stream) { handleMic(stream, readMic, callback); }, function() { console.log('Error!') }); }, stop: function () { clearInterval(readMicInterval); } } })(window, document);