scratch-audio/src/Loudness.js
Christopher Willis-Ford 068aca6136 chore: lint fixes
2023-12-15 14:44:01 -08:00

82 lines
2.8 KiB
JavaScript

const log = require('./log');
class Loudness {
/**
* Instrument and detect a loudness value from a local microphone.
* @param {AudioContext} audioContext - context to create nodes from for
* detecting loudness
* @constructor
*/
constructor (audioContext) {
/**
* AudioContext the mic will connect to and provide analysis of
* @type {AudioContext}
*/
this.audioContext = audioContext;
/**
* Are we connecting to the mic yet?
* @type {Boolean}
*/
this.connectingToMic = false;
/**
* microphone, for measuring loudness, with a level meter analyzer
* @type {MediaStreamSourceNode}
*/
this.mic = null;
}
/**
* Get the current loudness of sound received by the microphone.
* Sound is measured in RMS and smoothed.
* Some code adapted from Tone.js: https://github.com/Tonejs/Tone.js
* @return {number} loudness scaled 0 to 100
*/
getLoudness () {
// The microphone has not been set up, so try to connect to it
if (!this.mic && !this.connectingToMic) {
this.connectingToMic = true; // prevent multiple connection attempts
navigator.mediaDevices.getUserMedia({audio: true}).then(stream => {
this.audioStream = stream;
this.mic = this.audioContext.createMediaStreamSource(stream);
this.analyser = this.audioContext.createAnalyser();
this.mic.connect(this.analyser);
this.micDataArray = new Float32Array(this.analyser.fftSize);
})
.catch(err => {
log.warn(err);
});
}
// If the microphone is set up and active, measure the loudness
if (this.mic && this.audioStream.active) {
this.analyser.getFloatTimeDomainData(this.micDataArray);
let sum = 0;
// compute the RMS of the sound
for (let i = 0; i < this.micDataArray.length; i++){
sum += Math.pow(this.micDataArray[i], 2);
}
let rms = Math.sqrt(sum / this.micDataArray.length);
// smooth the value, if it is descending
if (this._lastValue) {
rms = Math.max(rms, this._lastValue * 0.6);
}
this._lastValue = rms;
// Scale the measurement so it's more sensitive to quieter sounds
rms *= 1.63;
rms = Math.sqrt(rms);
// Scale it up to 0-100 and round
rms = Math.round(rms * 100);
// Prevent it from going above 100
rms = Math.min(rms, 100);
return rms;
}
// if there is no microphone input, return -1
return -1;
}
}
module.exports = Loudness;