| <!DOCTYPE html> |
| <html> |
| <head> |
| <meta charset="utf-8"> |
| <title>Local Audio Rendering Demo</title> |
| <script type="text/javascript" src="../../base/adapter.js"></script> |
| <script> |
| var buttonStart; |
| var buttonStop; |
| var localStream; |
| 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); |
| }; |
| |
| function start() { |
| var constraints = {audio:true, video:false}; |
| getUserMedia(constraints, gotStream, gotStreamFailed); |
| buttonStart.disabled = true; |
| buttonStop.disabled = false; |
| } |
| |
| function stop() { |
| buttonStart.enabled = true; |
| buttonStop.enabled = false; |
| localStream.stop(); |
| clearInterval(reporter); |
| soundMeter.stop(); |
| } |
| |
| function gotStream(stream) { |
| 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); |
| stream.onended = function() { |
| console.log('stream.onended'); |
| buttonStart.disabled = false; |
| buttonStop.disabled = true; |
| }; |
| |
| localStream = 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 number of tracks:' |
| + audioTracks.length + ' audio ' + videoTracks.length + ' video'); |
| stream.stop(); |
| } |
| } |
| |
| function gotStreamFailed(error) { |
| buttonStart.disabled = false; |
| buttonStop.disabled = true; |
| alert('Failed to get access to local media. Error code: ' + error.code); |
| } |
| |
| function onload() { |
| try { |
| window.AudioContext = window.AudioContext || window.webkitAudioContext; |
| audioContext = new AudioContext(); |
| } catch(e) { |
| alert('Web Audio API not found'); |
| } |
| buttonStart = $('start'); |
| buttonStop = $('stop'); |
| buttonStart.enabled = true; |
| buttonStop.disabled = true; |
| } |
| |
| function paintMeter(context, number) { |
| context.clearRect(0, 0, 400, 20); |
| context.fillStyle = 'red'; |
| context.fillRect(0, 0, number * 400, 20); |
| } |
| |
| </script> |
| <style> |
| button { |
| font: 14px sans-serif; |
| padding: 8px; |
| } |
| canvas { |
| border:1px solid #000000; |
| } |
| </style> |
| </head> |
| |
| <body onload="onload()"> |
| <h2>Measuring the volume of an audio stream using WebAudio</h2> |
| <p>Demonstrates measuring the volume of a local media stream |
| using WebAudio.<br> |
| Press Start, select a microphone, listen to your own voice in loopback, |
| and see the numbers change as you speak.</p> |
| The "instant" volume changes approximately every 50 ms; the "slow" |
| volume approximates the average volume over about a second. |
| <br> |
| Note that you will NOT hear your own voice; use the |
| <a href="local-audio-rendering.html">local audio rendering demo</a> |
| for that. |
| <p> |
| <button id="start" onclick="start()">Start</button> |
| <button id="stop" onclick="stop()">Stop</button><br><br> |
| Volume (instant): <span id="volume">Not set</span><br> |
| Volume (slow): <span id="decaying_volume">Not set</span><br> |
| <canvas id="graphic_volume" width="400" height="20"></canvas> Volume<br> |
| <canvas id="graphic_slow" width="400" height="20"></canvas> Slow<br> |
| <canvas id="graphic_clip" width="400" height="20"></canvas> Clipping |
| |
| </body> |
| </html> |