From c4038d795d8935eab1fda87c03e48c785ed7f157 Mon Sep 17 00:00:00 2001
From: "hta@webrtc.org"
Date: Wed, 11 Dec 2013 08:36:16 +0000
Subject: [PATCH] Rewriting the SoundMeter class to be RMS and be encapsulated
differently
This CL changes the SoundMeter to be root-mean-square.
It also changes the interface between the meter and the display to be based on the display calling down to the meter rather than the meter calling up to the display.
A graphic display of the results is also added.
BUG=
R=cwilso@google.com, dutton@google.com, henrika@webrtc.org, juberti@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/5439004
git-svn-id: http://webrtc.googlecode.com/svn/trunk@5256 4adac7df-926f-26a2-2b94-8c16560cd09d
---
samples/js/base/adapter.js | 16 +-
samples/js/demos/html/local-audio-volume.html | 143 ++++++++++++------
2 files changed, 104 insertions(+), 55 deletions(-)
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.
Press Start, select a microphone, listen to your own voice in loopback,
and see the numbers change as you speak.
-
+ The "instant" volume changes approximately every 50 ms; the "slow"
+ volume approximates the average volume over about a second.
Volume (instant): Not set
- Volume (slow): Not set
+ Volume (slow): Not set
+ Volume
+ Slow
+ Clipping
+