Skip to content

Instantly share code, notes, and snippets.

@mwmwmw
Last active June 3, 2022 18:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mwmwmw/d0942149a5a9d94b43d629c4c92f01b9 to your computer and use it in GitHub Desktop.
Save mwmwmw/d0942149a5a9d94b43d629c4c92f01b9 to your computer and use it in GitHub Desktop.
VU Meter Audio Worklet
export default class VUMeterNode extends AudioWorkletNode {
constructor(context, updateIntervalInMS = 16.67) {
super(context, "vumeter", {
numberOfInputs: 1,
numberOfOutputs: 0,
channelCount: 1,
processorOptions: {
updateIntervalInMS
}
});
this._updateIntervalInMS = updateIntervalInMS;
this._volume = 0;
this._volumeDelta = 0;
this._hit = 0;
this._overage = 0;
this.port.onmessage = (event) => {
const { volume, volumeDelta, hit, overage } = event.data;
this._volume = volume;
this._volumeDelta = volumeDelta;
this._hit = hit;
this._overage = overage;
};
this.port.start();
}
get updateInterval() {
return this._updateIntervalInMS;
}
get volume() {
return this._volume;
}
get volumeDelta() {
return this._volumeDelta;
}
get hit() {
return this._hit;
}
get overage() {
return this._overage;
}
set updateInterval(updateIntervalInMS) {
this._updateIntervalInMS = updateIntervalInMS;
this.port.postMessage({ updateIntervalInMS: updateIntervalInMS });
}
set sensitivity(value) {
const sense = this.parameters.get("sensitivity");
sense.setValueAtTime(value, this.context.currentTime);
}
set smooth(value) {
const sense = this.parameters.get("smooth");
sense.setValueAtTime(value, this.context.currentTime);
}
}
const SMOOTHING_FACTOR = 0.99;
const MINIMUM_VALUE = 0.000001;
registerProcessor('vumeter', class extends AudioWorkletProcessor {
static get parameterDescriptors() {
return [{
name: 'threshold',
defaultValue: 0.2,
minValue: 0,
maxValue: 1,
automationRate: 'k-rate'
}, {
name: 'sensitivity',
defaultValue: 0.1,
minValue: 0,
maxValue: 1,
automationRate: 'k-rate'
}, {
name: 'smooth',
defaultValue: 0.996,
minValue: 0,
maxValue: 1,
automationRate: 'k-rate'
}];
}
constructor(options) {
super();
this._volume = 0;
this._lastVolume = 0;
this.pack = {
volume:0, volumeDelta:0, hit:0, overage:0
}
this._updateIntervalInMS = options.processorOptions.updateIntervalInMS | 16.67;
this._nextUpdateFrame = this._updateIntervalInMS;
this.port.onmessage = event => {
if (event.data.updateIntervalInMS)
this._updateIntervalInMS = event.data.updateIntervalInMS;
}
}
get intervalInFrames() {
return this._updateIntervalInMS / 1000 * sampleRate;
}
process(inputs, outputs, parameters) {
const input = inputs[0];
if (input.length > 0) {
let sum = 0;
// Calculated the squared-sum.
for (let i = 0; i < input[0].length; ++i)
sum += input[0][i] * input[0][i];
// Calculate the RMS level and update the volume.
this._volume = Math.max(Math.sqrt(sum / input[0].length), this._volume * parameters['smooth'][0]);
// Update and sync the volume property with the main thread.
this._nextUpdateFrame -= inputs[0][0].length;
if (this._nextUpdateFrame < 0) {
const delta = this._volume - this._lastVolume;
this._nextUpdateFrame += this.intervalInFrames;
this.pack.volume = this._volume;
this.pack.volumeDelta = delta;
this.pack.hit = delta > parameters['sensitivity'][0];
this.port.postMessage(this.pack);
this._lastVolume = this._volume;
}
}
return true;
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment