Fixed import issues and aggregated exports.

This commit is contained in:
2022-04-16 04:05:51 -05:00
parent 868c0f36dc
commit bf35220703
13 changed files with 1074 additions and 17 deletions

View File

@@ -0,0 +1,110 @@
/**
* A visualizer for an audio stream.
*/
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;
}
}

View File

@@ -0,0 +1,77 @@
import Visualizer from "./Visualizer.js";
export default class VisualizerUpdateManager {
/**
*
* @param {Visualizer} visualizer the visualizer this manager obtains data from.
*/
constructor(visualizer) {
this._binnedListeners = [];
for (let i = 0; i < visualizer.getNumberOfBins(); i++) {
this._binnedListeners.push([]);
}
this._lastBins = new Uint8Array(this._binnedListeners.length);
visualizer.addUpdateListener((delta, bins) => {
for (let binInd = 0; binInd < this._lastBins.length; binInd++) {
const lastBin = this._lastBins[binInd];
if (lastBin !== bins[binInd]) {
this._binnedListeners[binInd].forEach(listener => {
listener(delta, bins[binInd] - lastBin);
});
this._lastBins[binInd] = bins[binInd];
}
}
});
}
/**
* @callback visualizerBinUpdateListener
* @param {number} timeDelta elapsed time since last update.
* @param {number} ampDelta change in amplitude of the frequency bin.
*/
/**
*
* @param {number} freqBin the frequency bin this update listener should listen to.
* @param {visualizerBinUpdateListener} listener the listener itself that will be called upon the bin updating.
* @returns {boolean} true if and only if the updater was added successfully, otherwise, false.
*/
AddVisualizerBinUpdateListener(freqBin, listener) {
if (this._binnedListeners[freqBin].includes(listener)) return false;
this._binnedListeners[freqBin].push(listener);
return true;
}
/**
*
* @param {number} freqBin the frequency bin the update listener to be removed from is in.
* @param {visualizerBinUpdateListener} listener the listener that is to be removed.
* @returns {boolean} true if and only if the listener was successfully removed.
*/
removeVisualizerBinUpdateListener(freqBin, listener) {
const removeIndex = this._binnedListeners[freqBin].indexOf(listener);
if (removeIndex < 0) return false;
this._binnedListeners[freqBin].splice(removeIndex, 1);
return true;
}
/**
*
* @param {visualizerBinUpdateListener[][]} binnedListeners an array of the same length as the number of bins where each element is another array containing the listeners for that bin.
* @returns {boolean} true if and only if successfully loaded the new listeners.
*/
setBinnedListeners(binnedListeners) {
if (binnedListeners.length !== this._binnedListeners.length) return false;
this._binnedListeners = binnedListeners;
return true;
}
/**
*
* @returns {visualizerBinUpdateListener[][]} an array of the same length as the number of bins where each element is another array containing the listeners for that bin.
*/
getBinnedListeners() {
return this._binnedListeners;
}
}

View File

@@ -0,0 +1,2 @@
export { default as Visualizer } from "./Visualizer.js";
export { default as VisualizerUpdateManager } from "./VisualizerUpdateManager.js";