-
-
Save hattorix/f95352a780443ff1fc29441d9a490bf8 to your computer and use it in GitHub Desktop.
AudioWorklet で音量取得
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
https://g200kg.github.io/web-audio-api-ja/ のを TypeScript 化と少し機能を整理 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export default class VUMeterNode extends AudioWorkletNode { | |
private _updateIntervalInMS: number | |
private _volume: number | |
constructor(context: BaseAudioContext, updateIntervalInMS: number = null) { | |
super(context, "vumeter", { | |
numberOfInputs: 1, | |
numberOfOutputs: 0, | |
channelCount: 1, | |
processorOptions: { | |
updateIntervalInMS: updateIntervalInMS || 16.67, | |
}, | |
}) | |
// States in AudioWorkletNode | |
this._updateIntervalInMS = updateIntervalInMS | |
this._volume = 0 | |
// Handles updated values from AudioWorkletProcessor | |
this.port.onmessage = (event) => { | |
if (event.data.volume) { | |
this._volume = event.data.volume | |
} | |
} | |
this.port.start() | |
} | |
get updateInterval(): number { | |
return this._updateIntervalInMS | |
} | |
set updateInterval(updateIntervalInMS: number) { | |
this._updateIntervalInMS = updateIntervalInMS | |
this.port.postMessage({ updateIntervalInMS: updateIntervalInMS }) | |
} | |
get volume(): number { | |
// Float の値を dB に変換します | |
return 20 * Math.log10(this._volume) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const SMOOTHING_FACTOR = 0.9 | |
class VUMeterProcessor extends AudioWorkletProcessor { | |
private _volume: number | |
private _updateIntervalInMS: number | |
private _nextUpdateFrame: number | |
constructor(options) { | |
super() | |
this._volume = 0 | |
this._updateIntervalInMS = options.processorOptions.updateIntervalInMS | |
this._nextUpdateFrame = this._updateIntervalInMS | |
this.port.onmessage = (event) => { | |
if (event.data.updateIntervalInMS) { | |
this._updateIntervalInMS = event.data.updateIntervalInMS | |
} | |
} | |
} | |
get intervalInFrames(): number { | |
return (this._updateIntervalInMS / 1000) * sampleRate | |
} | |
process(inputs: Float32Array[][]): boolean { | |
const input = inputs[0] | |
if (input.length > 0) { | |
const samples = input[0] | |
// 入力値の RMS レベルを取得 | |
let rms = rmsLevel(samples) | |
// スムージング | |
this._volume = Math.max(rms, this._volume * SMOOTHING_FACTOR) | |
// 指定された更新間隔で音量をメインスレッドに通知 | |
this._nextUpdateFrame -= samples.length | |
if (this._nextUpdateFrame < 0) { | |
this._nextUpdateFrame += this.intervalInFrames | |
this.port.postMessage({ volume: this._volume }) | |
} | |
} | |
return true | |
} | |
} | |
function peakLevel(input: Float32Array): number { | |
let maxVal = 0.0 | |
for (let i = 0; i < input.length; i++) { | |
const sm = Math.abs(input[i]) | |
if (maxVal < sm) { | |
maxVal = sm | |
} | |
} | |
return maxVal | |
} | |
function rmsLevel(input: Float32Array): number { | |
let sum = 0.0 | |
for (let i = 0; i < input.length; i += 1) { | |
sum += input[i] ** 2 | |
} | |
return Math.sqrt(sum / input.length) | |
} | |
registerProcessor("vumeter", VUMeterProcessor) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment