/* * Copyright 2013 Boris Smus. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; function Microphone() { this.is_active = false; this.canvas = null; this.initAudio(); } Microphone.prototype.initAudio = function () { // Start off by initializing a new context. context = new (window.AudioContext || window.webkitAudioContext)(); if (!context.createGain) context.createGain = context.createGainNode; if (!context.createDelay) context.createDelay = context.createDelayNode; if (!context.createScriptProcessor) context.createScriptProcessor = context.createJavaScriptNode; // shim layer with setTimeout fallback window.requestAnimFrame = (function () { return ( window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) { window.setTimeout(callback, 1000 / 60); } ); })(); }; Microphone.prototype.isActive = function () { return this.is_active; }; Microphone.prototype.start = function () { this.is_active = true; this.canvas = document.querySelector("canvas"); // TODO(smus): Remove this ugliness. var isLocalhost = window.location.hostname == "localhost" || window.location.hostname == "127.0.0.1"; if (window.location.protocol != "https:" && !isLocalhost) { alert( "HTTPS is required for microphone access, and this site has no SSL cert yet. Sorry!" ); } navigator.getUserMedia( { audio: true }, this.onStream.bind(this), this.onStreamError.bind(this) ); }; Microphone.prototype.stop = function () { this.is_active = false; this.canvas = null; }; Microphone.prototype.onStream = function (stream) { var input = context.createMediaStreamSource(stream); var filter = context.createBiquadFilter(); filter.frequency.value = 60.0; filter.type = "notch"; filter.Q = 10.0; var analyser = context.createAnalyser(); analyser.fftSize = 2 ** 14; // Connect graph. input.connect(filter); filter.connect(analyser); this.analyser = analyser; // Setup a timer to visualize some stuff. requestAnimFrame(this.visualize.bind(this)); }; Microphone.prototype.onStreamError = function (e) { console.error("Error getting microphone", e); }; Microphone.prototype.visualize = function () { if (this.canvas) { var drawContext = this.canvas.getContext("2d"); drawContext.fillStyle = "white"; drawContext.clearRect(0, 0, this.canvas.width, this.canvas.height); var times = new Float32Array(this.analyser.frequencyBinCount); this.analyser.getFloatTimeDomainData(times); var heightMult = 5; var oldOffset; for (var i = 0; i < times.length; i++) { var value = times[i]; var percent = value * heightMult + 0.5; var height = this.canvas.height * percent; var offset = this.canvas.height - height - 1; var barWidth = this.canvas.width / times.length; //drawContext.fillStyle = 'black'; if (i > 0) { drawContext.beginPath(); drawContext.moveTo((i - 1) * barWidth, oldOffset); drawContext.lineTo(i * barWidth, offset); drawContext.stroke(); drawContext.closePath(); } //drawContext.fillRect(i * barWidth, offset, 1, 1); oldOffset = offset; } } requestAnimationFrame(this.visualize.bind(this)); };