Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a demo for "Unprocessed volume" #993

Closed
wants to merge 10 commits into from
78 changes: 78 additions & 0 deletions src/content/getusermedia/unprocessed-volume/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<!DOCTYPE html>
<!--
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
-->
<html>
<head>

<meta charset="utf-8">
<meta name="description" content="WebRTC code samples">
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1">
<meta itemprop="description" content="Client-side WebRTC code samples">
<meta itemprop="image" content="../../../images/webrtc-icon-192x192.png">
<meta itemprop="name" content="WebRTC code samples">
<meta name="mobile-web-app-capable" content="yes">
<meta id="theme-color" name="theme-color" content="#ffffff">

<base target="_blank">

<title>Audio stream volume</title>

<link rel="icon" sizes="192x192" href="../../../images/webrtc-icon-192x192.png">
<link href="//fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="../../../css/main.css" />

</head>

<body>

<div id="container">

<h1><a href="//webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC samples</a> <span>Audio stream volume</span></h1>

<p>Measure the volume of a local media stream using WebAudio.</p>

<div id="meters">
<div id="instant">
<div class="label">Instant: </div>
<meter high="0.25" max="1" value="0"></meter>
<div class="value"></div>
</div>
<div id="slow">
<div class="label">Slow: </div>
<meter high="0.25" max="1" value="0"></meter>
<div class="value"></div>
</div>
<div id="unprocessed">
<div class="label">Unprocessed: </div>
<meter high="0.25" max="1" value="0"></meter>
<div class="value"></div>
</div>
</div>

<p>The 'instant' volume changes approximately every 50ms; the 'slow' volume approximates the average volume over about a second.</p>
<p>The third meter shows the volume of a track without audio processing applied; this should be the "input volume".</p>
<p>Note that you will not hear your own voice; use the <a href="../audio">local audio rendering demo</a> for that.</p>
<p>The <code>audioContext</code>, <code>stream</code> and <code>soundMeter</code> variables are in global scope, so you can inspect them from the console.</p>

<p id="errorMsg" style="color: red;"></p>
<p>
<a href="https://github.com/webrtc/samples/tree/gh-pages/src/content/getusermedia/volume" title="View source for this page on GitHub" id="viewSource">View source on GitHub</a>
</p>

</div>


<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="../../../js/common.js"></script>
<script src="../volume/js/soundmeter.js"></script>
<script src="js/main.js"></script>

<script src="../../../js/lib/ga.js"></script>

</body>
</html>
105 changes: 105 additions & 0 deletions src/content/getusermedia/unprocessed-volume/js/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/

/* global AudioContext, SoundMeter */

'use strict';

const instantMeter = document.querySelector('#instant meter');
const slowMeter = document.querySelector('#slow meter');
const unprocessedMeter = document.querySelector('#unprocessed meter');

const instantValueDisplay = document.querySelector('#instant .value');
const slowValueDisplay = document.querySelector('#slow .value');
const unprocessedValueDisplay = document.querySelector('#unprocessed .value');
const errorMsg = document.querySelector('#errorMsg');

try {
window.AudioContext = window.AudioContext || window.webkitAudioContext;
window.audioContext = new AudioContext();
} catch (e) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are not printing the actual error, e is unused.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe you can do try { } catch { }.

alert('Web Audio API not supported.');
}

// Put variables in global scope to make them available to the browser console.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what what this means.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sam wrote this... but var at top scope have always been available in the console or window[]. No longer true for es6 const/let notably.

let constraints = window.constraints = {
audio: {echoCancellation: true},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be echoCancellation: {exact:true} so that we fail if it cannot be satisfied?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find it confusing and error prone that the same constraints are setup up as a global variable, then used, then modified. If the demo were to have a reset button it would stop working unless this is explicitly reset. I've also not seen "let constraints = window.constraints = ...". Wouldn't "let constraints" work? Or if that didn't work, why not "var constraints"? I don't see this variable adding anything.

Can we just pass the desired constraints when we call getUserMedia? Not have a variable for it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ping @alvestrand. I didn't update this part. :)

video: false
};

async function handleSuccess(stream) {
// Put variables in global scope to make them available to the
// browser console.
window.stream = stream;
const soundMeter = window.soundMeter = new SoundMeter(window.audioContext);
errorMsg.innerText = '';

try {
// eslint-disable-next-line no-unused-vars
const ignored = await soundMeter.connectToSource(stream);
setInterval(function() {
instantMeter.value = instantValueDisplay.innerText =
soundMeter.instant.toFixed(2);
slowMeter.value = slowValueDisplay.innerText =
soundMeter.slow.toFixed(2);
}, 200);
} catch (e) {
handleError(e);
}

const audioSettings = JSON.stringify(stream.getAudioTracks()[0].getSettings());
console.log('First track settings:', audioSettings);

// Set up second track with audio processing disabled
constraints.audio = {echoCancellation: {exact: false}};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any reason why this is {exact: false} and not false?

trace('Getting second audio stream');
try {
const secondStream = await navigator.mediaDevices.getUserMedia(constraints);
// eslint-disable-next-line no-unused-vars
const ignore = await handleUnprocessedStream(secondStream);
} catch (e) {
handleError(e);
}
}

async function handleUnprocessedStream(stream) {
trace('Got second audio stream');
trace('Second track settings: ',
JSON.stringify(stream.getAudioTracks()[0].getSettings()));
trace('Second track constraints: ',
JSON.stringify(stream.getAudioTracks()[0].getConstraints()));
const unprocMeter = window.unprocMeter = new SoundMeter(window.audioContext);
try {
// eslint-disable-next-line no-unused-vars
const ignored = await unprocMeter.connectToSource(stream);
setInterval(function() {
unprocessedMeter.value = unprocessedValueDisplay.innerText =
unprocMeter.slow.toFixed(2);
}, 200);
} catch (e) {
handleError(e);
}
}

function handleError(error) {
trace('navigator.getUserMedia error: ', error);
errorMsg.innerText = `navigator.getUserMedia error: ${error}`;
}

async function init() {
try {
const stream = await navigator.mediaDevices.getUserMedia(constraints);
// eslint-disable-next-line no-unused-vars
const ignored = await handleSuccess(stream);
} catch (e) {
handleError(e);
}
}

// noinspection JSIgnoredPromiseFromCall
init();
27 changes: 19 additions & 8 deletions src/content/getusermedia/volume/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,14 @@ const constraints = window.constraints = {
video: false
};

function handleSuccess(stream) {
async function handleSuccess(stream) {
// Put variables in global scope to make them available to the
// browser console.
window.stream = stream;
const soundMeter = window.soundMeter = new SoundMeter(window.audioContext);
soundMeter.connectToSource(stream, function(e) {
if (e) {
alert(e);
return;
}
try {
// eslint-disable-next-line no-unused-vars
const ignored = await soundMeter.connectToSource(stream);
setInterval(() => {
instantMeter.value = instantValueDisplay.innerText =
soundMeter.instant.toFixed(2);
Expand All @@ -49,11 +47,24 @@ function handleSuccess(stream) {
clipMeter.value = clipValueDisplay.innerText =
soundMeter.clip;
}, 200);
});
} catch (e) {
handleError(e);
}
}

function handleError(error) {
console.log('navigator.getUserMedia error: ', error);
}

navigator.mediaDevices.getUserMedia(constraints).then(handleSuccess).catch(handleError);
async function init() {
try {
const stream = await navigator.mediaDevices.getUserMedia(constraints);
// eslint-disable-next-line no-unused-vars
const ignored = await handleSuccess(stream);
} catch (e) {
handleError(e);
}
}

// noinspection JSIgnoredPromiseFromCall
init();
30 changes: 15 additions & 15 deletions src/content/getusermedia/volume/js/soundmeter.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,22 +37,22 @@ function SoundMeter(context) {
};
}

SoundMeter.prototype.connectToSource = function(stream, callback) {
console.log('SoundMeter connecting');
try {
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);
if (typeof callback !== 'undefined') {
callback(null);
}
} catch (e) {
console.error(e);
if (typeof callback !== 'undefined') {
callback(e);
SoundMeter.prototype.connectToSource = function(stream) {
return new Promise((resolve, reject) => {
console.log('SoundMeter connecting');
try {
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);
resolve();
} catch (e) {
console.error(e);
if (typeof callback !== 'undefined') {
reject(e);
}
}
}
});
};

SoundMeter.prototype.stop = function() {
Expand Down