26c40ba166
This removes the possibility of feedback loops, which can happen if you run this demo on an Android device. BUG= R=dutton@google.com Review URL: https://webrtc-codereview.appspot.com/5589004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@5258 4adac7df-926f-26a2-2b94-8c16560cd09d
173 lines
5.4 KiB
HTML
173 lines
5.4 KiB
HTML
<!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>
|