diff --git a/samples/js/base/adapter.js b/samples/js/base/adapter.js index 803ef150cb..e99a16ea6e 100644 --- a/samples/js/base/adapter.js +++ b/samples/js/base/adapter.js @@ -77,13 +77,17 @@ if (navigator.mozGetUserMedia) { }; // Fake get{Video,Audio}Tracks - MediaStream.prototype.getVideoTracks = function() { - return []; - }; + if (!MediaStream.prototype.getVideoTracks) { + MediaStream.prototype.getVideoTracks = function() { + return []; + }; + } - MediaStream.prototype.getAudioTracks = function() { - return []; - }; + if (!MediaStream.prototype.getAudioTracks) { + MediaStream.prototype.getAudioTracks = function() { + return []; + }; + } } else if (navigator.webkitGetUserMedia) { console.log("This appears to be Chrome"); diff --git a/samples/js/demos/html/local-audio-volume.html b/samples/js/demos/html/local-audio-volume.html index 57adf846d0..476825632c 100644 --- a/samples/js/demos/html/local-audio-volume.html +++ b/samples/js/demos/html/local-audio-volume.html @@ -9,7 +9,52 @@ var buttonStart; var buttonStop; var localStream; - var soundMeter; + var reporter; + var audioContext; + + // Meter class that generates a number correlated to audio volume. + // The meter class itself displays nothing, but it makes the + // instantaneous and time-decaying volumes available for inspection. + // It also reports on the fraction of samples that were at or near + // the top of the measurement range. + function SoundMeter(context) { + this.context = context + this.volume = 0.0; + this.slow_volume = 0.0; + this.clip = 0.0; + this.script = context.createScriptProcessor(2048, 1, 1); + that = this; + this.script.onaudioprocess = function(event) { + var input = event.inputBuffer.getChannelData(0); + var i; + var sum = 0.0; + var clipcount = 0; + for (i = 0; i < input.length; ++i) { + sum += input[i] * input[i]; + if (Math.abs(input[i]) > 0.99) { + clipcount += 1 + } + } + that.volume = Math.sqrt(sum / input.length); + that.slow_volume = 0.95 * that.slow_volume + 0.05 * that.volume; + that.clip = clipcount / input.length; + } + } + + SoundMeter.prototype.connectToSource = function(stream) { + console.log('SoundMeter connecting'); + this.mic = this.context.createMediaStreamSource(stream); + this.mic.connect(this.script); + // Necessary to make sample run, but should not be. + this.script.connect(this.context.destination); + } + + SoundMeter.prototype.stop = function() { + this.mic.disconnect(); + this.script.disconnect(); + } + + // End of SoundMeter class. $ = function(id) { return document.getElementById(id); @@ -26,11 +71,13 @@ buttonStart.enabled = true; buttonStop.enabled = false; localStream.stop(); + clearInterval(reporter); + soundMeter.stop(); } function gotStream(stream) { - videoTracks = stream.getVideoTracks(); - audioTracks = stream.getAudioTracks(); + var videoTracks = stream.getVideoTracks(); + var audioTracks = stream.getAudioTracks(); if (audioTracks.length == 1 && videoTracks.length == 0) { console.log('gotStream({audio:true, video:false})'); console.log('Using audio device: ' + audioTracks[0].label); @@ -42,18 +89,25 @@ }; localStream = stream; - soundMeter = new SoundMeter() - meter = $('volume'); - decaying_meter = $('decaying_volume'); - decaying_volume = 0.0; - soundMeter.showVolume = function(volume) { - meter.innerHTML = volume.toFixed(2); - decaying_volume = volume * 0.05 + decaying_volume * 0.95; - decaying_meter.innerHTML = decaying_volume.toFixed(2); - } - soundMeter.connect(stream) + var soundMeter = new SoundMeter(audioContext); + soundMeter.connectToSource(stream); + + // Set up reporting of the volume every 0.2 seconds. + var meter = $('volume'); + var decaying_meter = $('decaying_volume'); + var meter_canvas = $('graphic_volume').getContext('2d'); + var meter_slow = $('graphic_slow').getContext('2d'); + var meter_clip = $('graphic_clip').getContext('2d'); + reporter = setInterval(function() { + meter.textContent = soundMeter.volume.toFixed(2); + decaying_meter.textContent = soundMeter.slow_volume.toFixed(2); + paintMeter(meter_canvas, soundMeter.volume); + paintMeter(meter_slow, soundMeter.slow_volume); + paintMeter(meter_clip, soundMeter.clip); + }, 200); } else { - alert('The media stream contains an invalid amount of audio tracks.'); + alert('The media stream contains an invalid amount of tracks:' + + audioTracks.length + ' audio ' + videoTracks.length + ' video'); stream.stop(); } } @@ -65,6 +119,12 @@ } function onload() { + try { + window.AudioContext = window.AudioContext || window.webkitAudioContext; + audioContext = new AudioContext(); + } catch(e) { + alert('Web Audio API not found'); + } audioElement = $('audio'); buttonStart = $('start'); buttonStop = $('stop'); @@ -72,37 +132,22 @@ buttonStop.disabled = true; } - // Meter class that generates a number. - function SoundMeter() { - this.context = new webkitAudioContext(); - this.volume = 0.0; - } - - SoundMeter.prototype.connect = function(stream) { - console.log('SoundMeter connecting'); - this.mic = this.context.createMediaStreamSource(stream); - this.script = this.context.createScriptProcessor(1024, 1, 1); - that = this; - this.script.onaudioprocess = function(event) { - var input = event.inputBuffer.getChannelData(0); - var i; - var sum = 0.0; - for (i = 0; i < input.length; ++i) { - sum += Math.abs(input[i]); - } - that.showVolume(sum / input.length); - } - console.log('Buffer size ' + this.script.bufferSize); - this.mic.connect(this.script); - // Necessary to make sample run, but should not be. - this.script.connect(this.context.destination) - } - - SoundMeter.prototype.showVolume = function(volume) { - alert('Dummy showVolume called') + function paintMeter(context, number) { + context.clearRect(0, 0, 400, 20); + context.fillStyle = 'red'; + context.fillRect(0, 0, number * 400, 20); } +
@@ -111,16 +156,16 @@ using WebAudio.