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

Update volume example to use web audio API #1605

Open
wants to merge 1 commit into
base: gh-pages
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/content/getusermedia/volume/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ <h1><a href="//webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC
about a second.</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
<p>The <code>audioContext</code> and <code>stream</code> variables are in global scope, so
you can inspect them from the console.</p>

<a href="https://github.com/webrtc/samples/tree/gh-pages/src/content/getusermedia/volume"
Expand All @@ -75,7 +75,6 @@ <h1><a href="//webrtc.github.io/samples/" title="WebRTC samples homepage">WebRTC


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

<script src="../../../js/lib/ga.js"></script>
Expand Down
31 changes: 14 additions & 17 deletions src/content/getusermedia/volume/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,25 +31,23 @@ const constraints = window.constraints = {

let meterRefresh = null;

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;
}
meterRefresh = setInterval(() => {
instantMeter.value = instantValueDisplay.innerText =
soundMeter.instant.toFixed(2);
slowMeter.value = slowValueDisplay.innerText =
soundMeter.slow.toFixed(2);
clipMeter.value = clipValueDisplay.innerText =
soundMeter.clip;
}, 200);
});
await audioContext.audioWorklet.addModule("js/soundmeter.js");
const soundmeterNode = new AudioWorkletNode(audioContext,"soundmeter");
const source = audioContext.createMediaStreamSource(stream);
source.connect(soundmeterNode).connect(audioContext.destination);

soundmeterNode.port.onmessage = e => {
instantMeter.value = instantValueDisplay.innerText =
e.data.instant.toFixed(2);
slowMeter.value = slowValueDisplay.innerText =
e.data.slow.toFixed(2);
clipMeter.value = clipValueDisplay.innerText =
e.data.clip;
}
}

function handleError(error) {
Expand Down Expand Up @@ -81,7 +79,6 @@ function stop() {
stopButton.disabled = true;

window.stream.getTracks().forEach(track => track.stop());
window.soundMeter.stop();
window.audioContext.close();
clearInterval(meterRefresh);
instantMeter.value = instantValueDisplay.innerText = '';
Expand Down
85 changes: 40 additions & 45 deletions src/content/getusermedia/volume/js/soundmeter.js
Original file line number Diff line number Diff line change
@@ -1,62 +1,57 @@
/*
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
* Copyright (c) 2023 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.
*/

'use strict';

// 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.instant = 0.0;
this.slow = 0.0;
this.clip = 0.0;
this.script = context.createScriptProcessor(2048, 1, 1);
const that = this;
this.script.onaudioprocess = function(event) {
const input = event.inputBuffer.getChannelData(0);
let i;
let sum = 0.0;
let clipcount = 0;
for (i = 0; i < input.length; ++i) {
sum += input[i] * input[i];
if (Math.abs(input[i]) > 0.99) {
clipcount += 1;
class SoundMeter extends AudioWorkletProcessor {
_instant
_slow
_clip
_updateIntervalInMS
_nextUpdateFrame

constructor() {
super();

this._instant=0.0;
this._slow=0.0;
this._clip=0.0;
this._updateIntervalInMS = 50;
this._nextUpdateFrame = this._updateIntervalInMS;
}

process(inputs, outputs, parameters) {
const input = inputs[0];
if (input.length > 0) {
const samples = input[0];
let sum = 0.0;
let clipcount = 0;
for (let i = 0; i < samples.length; ++i) {
sum += samples[i] * samples[i];
if (Math.abs(samples[i]) > 0.99) {
clipcount += 1;
}
}
}
that.instant = Math.sqrt(sum / input.length);
that.slow = 0.95 * that.slow + 0.05 * that.instant;
that.clip = clipcount / input.length;
};
}
this._instant = Math.sqrt(sum / samples.length);
this._slow = 0.95 * this._slow + 0.05 * this._instant;
this._clip = clipcount / samples.length;

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);
this._nextUpdateFrame -= samples.length;
if (this._nextUpdateFrame < 0) {
this._nextUpdateFrame += this._updateIntervalInMS / 1000 * sampleRate;
this.port.postMessage({instant: this._instant, slow: this._slow, clip: this._clip});
}
}
return true;
}
};
}

SoundMeter.prototype.stop = function() {
console.log('SoundMeter stopping');
this.mic.disconnect();
this.script.disconnect();
};
registerProcessor("soundmeter", SoundMeter);