110 lines
3.3 KiB
JavaScript
110 lines
3.3 KiB
JavaScript
/**
|
|
* Provides a simplified access point to the frequency bins in the form of a visualization update listener.
|
|
*/
|
|
export default class Visualizer {
|
|
|
|
/**
|
|
* @callback visualizerUpdateListener
|
|
* @param {number} delta elapsed time since last update.
|
|
* @param {Uint8Array} bins the bins with varying frequency values.
|
|
*/
|
|
|
|
/**
|
|
*
|
|
* @param {MediaSource|HTMLMediaElement} mediaSource a media source to analyze.
|
|
* @param {number} [fftSize = 1024] the size of the fft window.
|
|
*/
|
|
constructor(mediaSource, fftSize = 1024) {
|
|
this._stream = mediaSource;
|
|
this._analyzing = false;
|
|
this._updateListeners = [];
|
|
this._audioCtx = new window.AudioContext();
|
|
if (mediaSource instanceof HTMLMediaElement) {
|
|
this._source = this._audioCtx.createMediaElementSource(this._stream);
|
|
} else {
|
|
this._source = this._audioCtx.createMediaStreamSource(this._stream);
|
|
}
|
|
this._analyzer = this._audioCtx.createAnalyser();
|
|
this._analyzer.fftSize = fftSize;
|
|
this._buffer = new Uint8Array(this._analyzer.frequencyBinCount);
|
|
this._source.connect(this._analyzer);
|
|
this._analyzer.connect(this._audioCtx.destination);
|
|
|
|
this.lastUpdate = null;
|
|
}
|
|
|
|
|
|
/**
|
|
* Begins analyzing and sending out update pings.
|
|
*/
|
|
analyze() {
|
|
if (this._analyzing) {
|
|
return;
|
|
}
|
|
this._analyzing = true;
|
|
let self = this; // since calling from requestAnimationFrame means "this" is no longer set to produced object.
|
|
const update = (timestamp) => {
|
|
if (!self._analyzing) return;
|
|
|
|
if (!self.lastUpdate) {
|
|
self.lastUpdate = timestamp;
|
|
}
|
|
let delta = timestamp - self.lastUpdate;
|
|
self._analyzer.getByteFrequencyData(self._buffer);
|
|
|
|
self._updateListeners.forEach(listener => {
|
|
listener(delta, self._buffer);
|
|
});
|
|
requestAnimationFrame(update);
|
|
};
|
|
requestAnimationFrame(update);
|
|
}
|
|
|
|
/**
|
|
* Stops the analysis. Listeners will stop receiving bins.
|
|
*/
|
|
stop() {
|
|
this._analyzing = false;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {visualizerUpdateListener} listener the visualizer update listener to be registered.
|
|
* @returns {boolean} true if and only if the listener was successfully added.
|
|
*/
|
|
addUpdateListener(listener) {
|
|
if (this._updateListeners.includes(listener));
|
|
this._updateListeners.push(listener);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {visualizerUpdateListener} listener the visualizer update listener to remove.
|
|
* @returns {boolean} true if and only if the removal of the listener was a success.
|
|
*/
|
|
removeUpdateListener(listener) {
|
|
const removeIndex = this._updateListeners.indexOf(listener);
|
|
if (removeIndex < 0) return false;
|
|
|
|
this._updateListeners.splice(removeIndex, 1);
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* @returns {number} the number of bins based on the size of the FFT window.
|
|
*/
|
|
getNumberOfBins() {
|
|
return this._buffer.length;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @returns {number} the fft window size.
|
|
*/
|
|
getFftSize() {
|
|
return this._analyzer.fftSize;
|
|
}
|
|
} |