mirror of
https://github.com/scratchfoundation/scratch-audio.git
synced 2025-01-20 14:30:13 -05:00
82 lines
2.8 KiB
JavaScript
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;
|