From bf3cef97beb02b2a26e9fd08786086c450bd7408 Mon Sep 17 00:00:00 2001 From: Harrison Deng Date: Sun, 17 Apr 2022 02:29:40 -0500 Subject: [PATCH] Changed how listening to ranges of a visualizer works. --- src/mapping/text.js | 16 +++--- src/visualization/VisualizerUpdateManager.js | 60 ++++++++++++++------ 2 files changed, 51 insertions(+), 25 deletions(-) diff --git a/src/mapping/text.js b/src/mapping/text.js index 54a9059..2c7d275 100644 --- a/src/mapping/text.js +++ b/src/mapping/text.js @@ -14,20 +14,22 @@ import VisualizerUpdateManager from "../visualization/VisualizerUpdateManager.js * Maps the red component of the text color to a certain range of bins. * * @param {HTMLElement} element The element whose text's red value should be mapped. - * @param {number} color Where 0 for red, 1, for blue, 2 for green, and 3 for alpha. - * @param {number} min The lower bound of the bins to be mapped. + * @param {number} color Where r for red, g, for green, b for blue, and a for alpha. + * @param {number} lowerBin The lower bound of the bins to be mapped. * @param {VisualizerUpdateManager} visUpdateManager The visualizer update manager associated with the audio playback you would like the mapping with. * @param {interpolator} interpolator The interpolation function to use. - * @param {number} [max=undefined] The upper bound of the bins to be mapped. If left undefined, then only the bin defined by the min value is used. + * @param {number} [upperBin=undefined] The upper bound of the bins to be mapped. If left undefined, then only the bin defined by the min value is used. * @param {boolean} [reversed=true] If true, then the quieter, the greater the red value. */ -export function mapColor(element, color, min, visUpdateManager, interpolator, max = undefined, reversed = false) { - if (!max) max = min; - visUpdateManager.addVisualizerRangeUpdateListener(min, max, (timeDelta, amp) => { +export function mapColor(element, color, lowerBin, visUpdateManager, interpolator, upperBin = undefined, reversed = false) { + const rgbaStr = "rgba"; + color = rgbaStr.indexOf(color); + if (color < 0) throw new Error("Invalid color parameter provided."); + if (!upperBin) upperBin = lowerBin; + visUpdateManager.addVisualizerRangeUpdateListener(lowerBin, upperBin, (timeDelta, amp) => { const rgba = parseColor(element.style.color); rgba[color] = interpolator(rgba[color], amp, timeDelta); if (reversed) rgba[color] = 255 - rgba[color]; element.style.color = rgbaToHexRgba(rgba); }); } - diff --git a/src/visualization/VisualizerUpdateManager.js b/src/visualization/VisualizerUpdateManager.js index b19b41c..a8d67bf 100644 --- a/src/visualization/VisualizerUpdateManager.js +++ b/src/visualization/VisualizerUpdateManager.js @@ -14,22 +14,34 @@ export default class VisualizerUpdateManager { */ constructor(visualizer) { this._binnedListeners = []; + this._rangedListeners = []; for (let i = 0; i < visualizer.getNumberOfBins(); i++) { this._binnedListeners.push([]); } this._lastBins = new Uint8Array(this._binnedListeners.length); - - visualizer.addUpdateListener((delta, bins) => { + this._visualizer = visualizer; + this._visualizerListener = (delta, bins) => { + const sortedCopyOfRangedListeners = [... this._rangedListeners].sort((a, b) => a[0] - b[0]); // Priority queue could be better. 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], bins[binInd] - lastBin); }); + for (let rangedInd = 0; rangedInd < sortedCopyOfRangedListeners.length; rangedInd++) { // Could switch to a while loop. + const [min, max, listener] = sortedCopyOfRangedListeners[rangedInd]; + if (min > binInd) break; // Don't need to check the rest if the current lowest minimum is greater than the current bin index. + if (binInd <= max) { + listener(delta, bins.slice(min, max)); + sortedCopyOfRangedListeners.shift(); + rangedInd--; + } + } this._lastBins[binInd] = bins[binInd]; } } - }); + }; + visualizer.addUpdateListener(this._visualizerListener); } /** @@ -39,12 +51,18 @@ export default class VisualizerUpdateManager { * @param {number} ampDelta change in amplitude of the frequency bin. */ + /** + * @callback visualizerRangeUpdateListener + * @param {number} timeDelta elapsed time since last update. + * @param {number} bins the bins of the range. + */ + /** * * @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. + * @returns {boolean} true if and only if the updater was added successfully. */ AddVisualizerBinUpdateListener(freqBin, listener) { if (this._binnedListeners[freqBin].includes(listener)) return false; @@ -53,19 +71,18 @@ export default class VisualizerUpdateManager { } /** - * Similar to {@link VisualizerUpdateManager#AddVisualizerBinUpdateListener}, this method adds the same listener to a range of bins. + * Similar to {@link VisualizerUpdateManager#AddVisualizerBinUpdateListener}, this method adds a listener for to a range of bins. * * @param {number} min The lower bound of the bins to listen to (inclusive). * @param {number} max The upper bound of the bins to listen to (inclusive). - * @param {visualizerBinUpdateListener} listener The listener to register to these bins. - * @returns {boolean} True if and only if the listener was added at least once. + * @param {visualizerRangeUpdateListener} listener The listener to register to the range. + * @returns {boolean} True if and only if the ranged listener was added successfully. */ addVisualizerRangeUpdateListener(min, max, listener) { - let added = false; - for (let i = min; i <= max; i++) { - if (this.AddVisualizerBinUpdateListener(i, listener)) added = true; - } - return added; + const rangedListener = [min, max, listener]; + if (this._rangedListeners.includes(rangedListener)) return false; + this._rangedListeners.push(rangedListener); + return true; } /** @@ -87,14 +104,14 @@ export default class VisualizerUpdateManager { * @param {number} min The lower bound of bins to remove the listener from (inclusive). * @param {number} max The upper bound of bin to remove the listener from (inclusive.) * @param {visualizerBinUpdateListener} listener The update listener to remove from the bins. - * @returns {boolean} True if and only if at least one listener was removed. + * @returns {boolean} True if and only if the given listener was removed. */ removeVisualizerRangeUpdateListener(min, max, listener) { - let removed = false; - for (let i = min; i <= max; i++) { - if (this.removeVisualizerBinUpdateListener(i, listener)) removed = true; - } - return removed; + const rangedListener = [min, max, listener]; + const removeIndex = this._rangedListeners.indexOf(rangedListener); + if (removeIndex < 0) return false; + this._binnedListeners.splice(removeIndex, 1); + return true; } /** @@ -124,4 +141,11 @@ export default class VisualizerUpdateManager { bin.length = 0; }); } + + /** + * Unbinds this update manager from the initial visualizer. Effectively meaning this manager will no longer be used. + */ + unbindVisualizer() { + this._visualizer.removeUpdateListener(this._visualizerListener); + } } \ No newline at end of file