Fixed import issues and aggregated exports.
This commit is contained in:
110
src/visualization/Visualizer.js
Normal file
110
src/visualization/Visualizer.js
Normal 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;
|
||||
}
|
||||
}
|
77
src/visualization/VisualizerUpdateManager.js
Normal file
77
src/visualization/VisualizerUpdateManager.js
Normal 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;
|
||||
}
|
||||
}
|
2
src/visualization/visualization.js
Normal file
2
src/visualization/visualization.js
Normal file
@@ -0,0 +1,2 @@
|
||||
export { default as Visualizer } from "./Visualizer.js";
|
||||
export { default as VisualizerUpdateManager } from "./VisualizerUpdateManager.js";
|
Reference in New Issue
Block a user