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
This commit is contained in:
parent
77507eff4f
commit
c4038d795d
@ -77,13 +77,17 @@ if (navigator.mozGetUserMedia) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Fake get{Video,Audio}Tracks
|
// Fake get{Video,Audio}Tracks
|
||||||
MediaStream.prototype.getVideoTracks = function() {
|
if (!MediaStream.prototype.getVideoTracks) {
|
||||||
return [];
|
MediaStream.prototype.getVideoTracks = function() {
|
||||||
};
|
return [];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
MediaStream.prototype.getAudioTracks = function() {
|
if (!MediaStream.prototype.getAudioTracks) {
|
||||||
return [];
|
MediaStream.prototype.getAudioTracks = function() {
|
||||||
};
|
return [];
|
||||||
|
};
|
||||||
|
}
|
||||||
} else if (navigator.webkitGetUserMedia) {
|
} else if (navigator.webkitGetUserMedia) {
|
||||||
console.log("This appears to be Chrome");
|
console.log("This appears to be Chrome");
|
||||||
|
|
||||||
|
@ -9,7 +9,52 @@
|
|||||||
var buttonStart;
|
var buttonStart;
|
||||||
var buttonStop;
|
var buttonStop;
|
||||||
var localStream;
|
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) {
|
$ = function(id) {
|
||||||
return document.getElementById(id);
|
return document.getElementById(id);
|
||||||
@ -26,11 +71,13 @@
|
|||||||
buttonStart.enabled = true;
|
buttonStart.enabled = true;
|
||||||
buttonStop.enabled = false;
|
buttonStop.enabled = false;
|
||||||
localStream.stop();
|
localStream.stop();
|
||||||
|
clearInterval(reporter);
|
||||||
|
soundMeter.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
function gotStream(stream) {
|
function gotStream(stream) {
|
||||||
videoTracks = stream.getVideoTracks();
|
var videoTracks = stream.getVideoTracks();
|
||||||
audioTracks = stream.getAudioTracks();
|
var audioTracks = stream.getAudioTracks();
|
||||||
if (audioTracks.length == 1 && videoTracks.length == 0) {
|
if (audioTracks.length == 1 && videoTracks.length == 0) {
|
||||||
console.log('gotStream({audio:true, video:false})');
|
console.log('gotStream({audio:true, video:false})');
|
||||||
console.log('Using audio device: ' + audioTracks[0].label);
|
console.log('Using audio device: ' + audioTracks[0].label);
|
||||||
@ -42,18 +89,25 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
localStream = stream;
|
localStream = stream;
|
||||||
soundMeter = new SoundMeter()
|
var soundMeter = new SoundMeter(audioContext);
|
||||||
meter = $('volume');
|
soundMeter.connectToSource(stream);
|
||||||
decaying_meter = $('decaying_volume');
|
|
||||||
decaying_volume = 0.0;
|
// Set up reporting of the volume every 0.2 seconds.
|
||||||
soundMeter.showVolume = function(volume) {
|
var meter = $('volume');
|
||||||
meter.innerHTML = volume.toFixed(2);
|
var decaying_meter = $('decaying_volume');
|
||||||
decaying_volume = volume * 0.05 + decaying_volume * 0.95;
|
var meter_canvas = $('graphic_volume').getContext('2d');
|
||||||
decaying_meter.innerHTML = decaying_volume.toFixed(2);
|
var meter_slow = $('graphic_slow').getContext('2d');
|
||||||
}
|
var meter_clip = $('graphic_clip').getContext('2d');
|
||||||
soundMeter.connect(stream)
|
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 {
|
} 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();
|
stream.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -65,6 +119,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onload() {
|
function onload() {
|
||||||
|
try {
|
||||||
|
window.AudioContext = window.AudioContext || window.webkitAudioContext;
|
||||||
|
audioContext = new AudioContext();
|
||||||
|
} catch(e) {
|
||||||
|
alert('Web Audio API not found');
|
||||||
|
}
|
||||||
audioElement = $('audio');
|
audioElement = $('audio');
|
||||||
buttonStart = $('start');
|
buttonStart = $('start');
|
||||||
buttonStop = $('stop');
|
buttonStop = $('stop');
|
||||||
@ -72,37 +132,22 @@
|
|||||||
buttonStop.disabled = true;
|
buttonStop.disabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Meter class that generates a number.
|
function paintMeter(context, number) {
|
||||||
function SoundMeter() {
|
context.clearRect(0, 0, 400, 20);
|
||||||
this.context = new webkitAudioContext();
|
context.fillStyle = 'red';
|
||||||
this.volume = 0.0;
|
context.fillRect(0, 0, number * 400, 20);
|
||||||
}
|
|
||||||
|
|
||||||
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')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
<style>
|
||||||
|
button {
|
||||||
|
font: 14px sans-serif;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
canvas {
|
||||||
|
border:1px solid #000000;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body onload="onload()">
|
<body onload="onload()">
|
||||||
@ -111,16 +156,16 @@
|
|||||||
using WebAudio.<br>
|
using WebAudio.<br>
|
||||||
Press Start, select a microphone, listen to your own voice in loopback,
|
Press Start, select a microphone, listen to your own voice in loopback,
|
||||||
and see the numbers change as you speak.</p>
|
and see the numbers change as you speak.</p>
|
||||||
<style>
|
The "instant" volume changes approximately every 50 ms; the "slow"
|
||||||
button {
|
volume approximates the average volume over about a second.
|
||||||
font: 14px sans-serif;
|
|
||||||
padding: 8px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<audio id="audio" autoplay="autoplay" controls="controls"></audio><br><br>
|
<audio id="audio" autoplay="autoplay" controls="controls"></audio><br><br>
|
||||||
<button id="start" onclick="start()">Start</button>
|
<button id="start" onclick="start()">Start</button>
|
||||||
<button id="stop" onclick="stop()">Stop</button><br><br>
|
<button id="stop" onclick="stop()">Stop</button><br><br>
|
||||||
Volume (instant): <span id="volume">Not set</span><br>
|
Volume (instant): <span id="volume">Not set</span><br>
|
||||||
Volume (slow): <span id="decaying_volume">Not set</span>
|
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>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user