Fixed linear easing function.
This commit is contained in:
parent
ad6c4b15e4
commit
5313d20fff
@ -32,6 +32,9 @@
|
||||
"import/extensions": [
|
||||
"error",
|
||||
"ignorePackages"
|
||||
],
|
||||
"no-unused-vars": [
|
||||
"warn"
|
||||
]
|
||||
},
|
||||
"plugins": [
|
||||
|
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -3,6 +3,7 @@
|
||||
"audioshowkit",
|
||||
"audioshowkitlib",
|
||||
"ckeditor",
|
||||
"Ctrls",
|
||||
"easings",
|
||||
"linebreak",
|
||||
"musicplayer",
|
||||
|
4218
package-lock.json
generated
4218
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -12,10 +12,12 @@
|
||||
"test:console": "mocha tests/**",
|
||||
"watch:docs": "nodemon --watch dist/ --watch tutorials/ --watch src/ --exec 'npm run docs' -e js,mjs,json,htm,html,xml,markdown,md,xhtml --ignore ./tutorials/assets/js/audioshowkit.js",
|
||||
"watch:lib": "webpack --watch --config webpack.dev.cjs",
|
||||
"serve:docs": "cd ./docs && live-server",
|
||||
"build": "npm run build:dev",
|
||||
"test": "npm run test:console",
|
||||
"watch": "npm run watch:lib",
|
||||
"docs": "jsdoc -c jsdoc.config.json && cp ./dist/audioshowkit.js ./tutorials/assets/js/audioshowkit.js && syncdir ./tutorials/assets/ ./docs/assets/"
|
||||
"docs": "jsdoc -c jsdoc.config.json && cp ./dist/audioshowkit.js ./tutorials/assets/js/audioshowkit.js && syncdir ./tutorials/assets/ ./docs/assets/",
|
||||
"serve": "npm run serve:docs"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
@ -32,6 +34,7 @@
|
||||
"eslint-plugin-jsdoc": "^39.2.2",
|
||||
"eslint-webpack-plugin": "^3.1.1",
|
||||
"jsdoc": "^3.6.10",
|
||||
"live-server": "^1.2.1",
|
||||
"mocha": "^9.2.2",
|
||||
"mocha-junit-reporter": "^2.0.2",
|
||||
"nodemon": "^2.0.15",
|
||||
|
@ -11,14 +11,14 @@ import { mapBinNumerical, mapRangedAvgNumerical } from "./numeric.js";
|
||||
* @param {number} conf.growUpper The upper limit of the width.
|
||||
* @param {string} conf.unit The unit the upper and lower bounds are measured in.
|
||||
* @param {number} conf.lowerBin The lower boundary of bins to be mapped (or the single bin to be mapped if the upper bin is undefined).
|
||||
* @param {VisUpdateRouter} conf.visUpdateManager The visualizer update manager to be mapped to.
|
||||
* @param {VisUpdateRouter} conf.visUpdateRouter The visualizer update manager to be mapped to.
|
||||
* @param {interpolator} conf.interpolator The interpolation function to be used to transition from one value to the next.
|
||||
* @param {number} [conf.upperBin] The upper bin or undefined, which results in mapping to a single bin.
|
||||
* @param {boolean} [conf.reversed=false] If true, then high amplitudes are mapped to lower values and vice versa.
|
||||
* @returns {{bin: number, listener: VisUpdateRouter.visualizerBinUpdateListener}|{lower: number, upper: number, listener: VisUpdateRouter.visualizerRangedUpdateListener}} The listener that was added (ranged if an upper bound was provided, binned otherwise).
|
||||
*/
|
||||
function mapWidth({ element, growLower, growUpper, unit, lowerBin, visUpdateManager, interpolator, upperBin = undefined, reversed = false }) {
|
||||
const getter = () => element.style.width;
|
||||
function mapWidth({ element, growLower, growUpper, unit, lowerBin, visUpdateRouter, interpolator, upperBin = undefined, reversed = false }) {
|
||||
const getter = () => parseInt(element.style.width) || 0;
|
||||
const setter = (value) => element.style.width = value + unit;
|
||||
const conf = {
|
||||
minVal: growLower,
|
||||
@ -27,7 +27,7 @@ function mapWidth({ element, growLower, growUpper, unit, lowerBin, visUpdateMana
|
||||
getter: getter,
|
||||
setter: setter,
|
||||
interpolator: interpolator,
|
||||
visUpdateManager: visUpdateManager,
|
||||
visUpdateRouter: visUpdateRouter,
|
||||
reversed: reversed
|
||||
};
|
||||
|
||||
@ -49,13 +49,13 @@ function mapWidth({ element, growLower, growUpper, unit, lowerBin, visUpdateMana
|
||||
* @param {number} conf.growUpper The upper limit of the height.
|
||||
* @param {string} conf.unit The unit the upper and lower bounds are measured in.
|
||||
* @param {number} conf.lowerBin The lower boundary of bins to be mapped (or the single bin to be mapped if the upper bin is undefined).
|
||||
* @param {VisUpdateRouter} conf.visUpdateManager The visualizer update manager to be mapped to.
|
||||
* @param {VisUpdateRouter} conf.visUpdateRouter The visualizer update manager to be mapped to.
|
||||
* @param {interpolator} conf.interpolator The interpolation function to be used to transition from one value to the next.
|
||||
* @param {number} [conf.upperBin] The upper bin or undefined, which results in mapping to a single bin.
|
||||
* @param {boolean} [conf.reversed=false] If true, then high amplitudes are mapped to lower values and vice versa.
|
||||
* @returns {{bin: number, listener: VisUpdateRouter.visualizerBinUpdateListener}|{lower: number, upper: number, listener: VisUpdateRouter.visualizerRangedUpdateListener}} The listener that was added (ranged if an upper bound was provided, binned otherwise).
|
||||
*/
|
||||
function mapHeight({ element, growLower, growUpper, unit, lowerBin, visUpdateManager, interpolator, upperBin = undefined, reversed = false }) {
|
||||
function mapHeight({ element, growLower, growUpper, unit, lowerBin, visUpdateRouter, interpolator, upperBin = undefined, reversed = false }) {
|
||||
const getter = () => element.style.height;
|
||||
const setter = (value) => element.style.height = value + unit;
|
||||
const conf = {
|
||||
@ -65,7 +65,7 @@ function mapHeight({ element, growLower, growUpper, unit, lowerBin, visUpdateMan
|
||||
getter: getter,
|
||||
setter: setter,
|
||||
interpolator: interpolator,
|
||||
visUpdateManager: visUpdateManager,
|
||||
visUpdateRouter: visUpdateRouter,
|
||||
reversed: reversed
|
||||
};
|
||||
|
||||
|
@ -24,30 +24,32 @@ import VisUpdateRouter from "../visualization/VisUpdateRouter.js";
|
||||
* @param {numericalGetter} conf.getter The getter callback to be used to get the current number.
|
||||
* @param {numericalSetter} conf.setter The setter callback to be used to set the new number.
|
||||
* @param {interpolator} conf.interpolator The interpolation function to use.
|
||||
* @param {VisUpdateRouter} conf.visUpdateManager the visualizer update manager this mapping corresponds with.
|
||||
* @param {VisUpdateRouter} conf.visUpdateRouter the visualizer update manager this mapping corresponds with.
|
||||
* @param {boolean} [conf.reversed = false] If true, then high amplitudes will be mapped to low values and vice versa.
|
||||
* @returns {{lower: number, upper: number, listener: VisUpdateRouter.visualizerRangedUpdateListener}} An object containing the lower and upper bounds for the range of a listener, which is also in the object.
|
||||
*/
|
||||
function mapRangedAvgNumerical({ minVal, maxVal, lowerBin = undefined, upperBin, getter, setter, interpolator, visUpdateManager, reversed = false }) {
|
||||
function mapRangedAvgNumerical({ minVal, maxVal, lowerBin = undefined, upperBin, getter, setter, interpolator, visUpdateRouter, reversed = false }) {
|
||||
console.log("mapping average numerical.");
|
||||
const rangedListener = {
|
||||
lower: lowerBin,
|
||||
upper: upperBin,
|
||||
listener: (timeDelta, bins) => {
|
||||
const normalBins = [...bins];
|
||||
// TODO: Future: add weighting / distribution system?
|
||||
let average = 0;
|
||||
let normAvg = 0;
|
||||
for (let i = 0; i < normalBins.length; i++) {
|
||||
normalBins[i] = normalBins[i] / 255.0;
|
||||
average += normalBins[i];
|
||||
normAvg += normalBins[i];
|
||||
}
|
||||
average /= normalBins.length;
|
||||
normAvg /= normalBins.length;
|
||||
|
||||
const range = maxVal - minVal;
|
||||
let interpolated = interpolator(getter(), average, timeDelta);
|
||||
let interpolated = interpolator((getter() - minVal) / range, normAvg, Math.min(timeDelta, 1));
|
||||
if (reversed) interpolated = 1 - interpolated;
|
||||
setter(minVal + range * interpolated);
|
||||
}
|
||||
};
|
||||
if (visUpdateManager.addVisualizerRangedUpdateListener(rangedListener)) {
|
||||
if (visUpdateRouter.addVisualizerRangedUpdateListener(rangedListener)) {
|
||||
return rangedListener;
|
||||
}
|
||||
return null; // Technically doesn't occur since the functions are anonymous?
|
||||
@ -63,21 +65,22 @@ function mapRangedAvgNumerical({ minVal, maxVal, lowerBin = undefined, upperBin,
|
||||
* @param {numericalGetter} conf.getter The callback to be used to get the current number.
|
||||
* @param {numericalSetter} conf.setter The callback to be used to set the new number.
|
||||
* @param {interpolator} conf.interpolator The interpolation function to use.
|
||||
* @param {VisUpdateRouter} conf.visUpdateManager The update manager to map to.
|
||||
* @param {VisUpdateRouter} conf.visUpdateRouter The update manager to map to.
|
||||
* @param {boolean} [conf.reversed] If true, then high amplitudes will be mapped to lower values and vice versa.
|
||||
* @returns {{bin: number, listener: VisUpdateRouter.visualizerBinUpdateListener}} The bin listener that was added.
|
||||
*/
|
||||
function mapBinNumerical({ minVal, maxVal, bin, getter, setter, interpolator, visUpdateManager, reversed = false }) {
|
||||
function mapBinNumerical({ minVal, maxVal, bin, getter, setter, interpolator, visUpdateRouter, reversed = false }) {
|
||||
console.log("mapping numerical...");
|
||||
const listener = {
|
||||
bin: bin,
|
||||
listener: (timeDelta, amp) => {
|
||||
const range = maxVal - minVal;
|
||||
let interpolated = interpolator(getter(), amp, timeDelta);
|
||||
let interpolated = interpolator(getter(), amp, Math.min(timeDelta, 1));
|
||||
if (reversed) interpolated = 1 - interpolated;
|
||||
setter(minVal + range * interpolated);
|
||||
}
|
||||
};
|
||||
if (visUpdateManager.AddVisualizerBinUpdateListener(listener)) {
|
||||
if (visUpdateRouter.AddVisualizerBinUpdateListener(listener)) {
|
||||
return listener;
|
||||
}
|
||||
return null; // Technically doesn't occur since the functions are anonymous?
|
||||
|
@ -9,13 +9,13 @@ import { mapBinNumerical, mapRangedAvgNumerical } from "./numeric.js";
|
||||
* @param {HTMLElement} rgbaMapConfiguration.element The element whose text's red value should be mapped.
|
||||
* @param {number} rgbaMapConfiguration.color Where r for red, g, for green, b for blue, and a for alpha.
|
||||
* @param {number} rgbaMapConfiguration.lowerBin The lower bound of the bins to be mapped.
|
||||
* @param {VisualizerUpdateManager} rgbaMapConfiguration.visUpdateManager The visualizer update manager associated with the audio playback you would like the mapping with.
|
||||
* @param {VisualizerUpdateManager} rgbaMapConfiguration.visUpdateRouter The visualizer update manager associated with the audio playback you would like the mapping with.
|
||||
* @param {interpolator} rgbaMapConfiguration.interpolator The interpolation function to use.
|
||||
* @param {number} [rgbaMapConfiguration.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} [rgbaMapConfiguration.reversed=true] If true, then the quieter, the greater the red value.
|
||||
* @returns {{bin: number, listener: VisualizerUpdateManager.visualizerBinUpdateListener}|{lower: number, upper: number, listener: visualizerRangedUpdateListener}} The ranged listener that was added.
|
||||
*/
|
||||
function mapRgba({ element, color, lowerBin, visUpdateManager, interpolator, upperBin = undefined, reversed = false }) {
|
||||
function mapRgba({ element, color, lowerBin, visUpdateRouter, interpolator, upperBin = undefined, reversed = false }) {
|
||||
const rgbaStr = "rgba";
|
||||
color = rgbaStr.indexOf(color);
|
||||
if (color < 0) throw new Error("Invalid color parameter provided.");
|
||||
@ -32,7 +32,7 @@ function mapRgba({ element, color, lowerBin, visUpdateManager, interpolator, upp
|
||||
getter: getter,
|
||||
setter: setter,
|
||||
interpolator: interpolator,
|
||||
visUpdateManager: visUpdateManager,
|
||||
visUpdateRouter: visUpdateRouter,
|
||||
reversed: reversed
|
||||
};
|
||||
if (upperBin) {
|
||||
@ -52,13 +52,13 @@ function mapRgba({ element, color, lowerBin, visUpdateManager, interpolator, upp
|
||||
* @param {number} fontSizeMapConfiguration.growUpper The upper limit of the font size.
|
||||
* @param {string} fontSizeMapConfiguration.unit The unit the upper and lower bounds are measured in.
|
||||
* @param {number} fontSizeMapConfiguration.lowerBin The lower boundary of bins to be mapped (or the single bin to be mapped if the upper bin is undefined).
|
||||
* @param {VisualizerUpdateManager} fontSizeMapConfiguration.visUpdateManager the visualizer update manager to be mapped to.
|
||||
* @param {VisualizerUpdateManager} fontSizeMapConfiguration.visUpdateRouter the visualizer update manager to be mapped to.
|
||||
* @param {interpolator} fontSizeMapConfiguration.interpolator The interpolation function to be used to transition from one value to the next.
|
||||
* @param {number} [fontSizeMapConfiguration.upperBin=undefined] The upper bin, or undefined, which results in mapping to a single bin.
|
||||
* @param {boolean} [fontSizeMapConfiguration.reversed=false] If true, then high amplitudes are mapped to lower values and vice versa.
|
||||
* @returns {{bin: number, listener: VisualizerUpdateManager.visualizerBinUpdateListener}|{lower: number, upper: number, listener: VisualizerUpdateManager.visualizerRangedUpdateListener}} The listener that was added (ranged if an upper bound was provided, binned otherwise).
|
||||
*/
|
||||
function mapFontSize({ element, growLower, growUpper, unit, lowerBin, visUpdateManager, interpolator, upperBin = undefined, reversed = false }) {
|
||||
function mapFontSize({ element, growLower, growUpper, unit, lowerBin, visUpdateRouter, interpolator, upperBin = undefined, reversed = false }) {
|
||||
const getter = () => parseInt(element.style.fontSize);
|
||||
const setter = (value) => element.style.fontSize = value + unit;
|
||||
const conf = {
|
||||
@ -68,7 +68,7 @@ function mapFontSize({ element, growLower, growUpper, unit, lowerBin, visUpdateM
|
||||
getter: getter,
|
||||
setter: setter,
|
||||
interpolator: interpolator,
|
||||
visUpdateManager: visUpdateManager,
|
||||
visUpdateRouter: visUpdateRouter,
|
||||
reversed: reversed
|
||||
};
|
||||
if (upperBin) {
|
||||
|
@ -162,12 +162,15 @@ export default class Music {
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a visualizer for this music. If there did not exist one, one is instantiated.
|
||||
*
|
||||
* If the provided fft window size is different from the current, a new one will also be instantiated.
|
||||
*
|
||||
* @param {number} [fftSize=1024] the size of the FFT window.
|
||||
* @returns {Visualizer} returns the visualizer.
|
||||
*/
|
||||
getVisualizer(fftSize = 1024) {
|
||||
if (this.#visualizer && this.#visualizer.getFftSize() === fftSize) return this.#visualizer;
|
||||
fetchVisualizer(fftSize = 1024) {
|
||||
if (this.#visualizer && this.#visualizer.fftSize === fftSize) return this.#visualizer;
|
||||
this.#visualizer = new Visualizer(this.fetchAudio(), fftSize);
|
||||
return this.#visualizer;
|
||||
}
|
||||
|
@ -13,7 +13,8 @@ import MusicPlaylist from "./MusicPlaylist.js";
|
||||
*/
|
||||
export default class VisMusicPlayer extends MusicPlayer {
|
||||
#fftSize = 1024;
|
||||
#visualizerUpdateManager;
|
||||
#visUpdateRouter;
|
||||
#visUpdateRouterListeners = null;
|
||||
|
||||
/**
|
||||
* Instantiates a music player with visualization features.
|
||||
@ -23,75 +24,54 @@ export default class VisMusicPlayer extends MusicPlayer {
|
||||
*/
|
||||
constructor(playlist, fftSize = 1024) {
|
||||
super(playlist);
|
||||
playlist.addPositionChangeListener(this.#onMusicChange); // First time only: after setting the playlist, add the listener.
|
||||
this.#fftSize = fftSize;
|
||||
this.#updateVisUpdateManager();
|
||||
this.#visUpdateRouter = new VisUpdateRouter(fftSize);
|
||||
}
|
||||
|
||||
#updateVisUpdateManager() {
|
||||
this.#visualizerUpdateManager = new VisUpdateRouter(this.getCurrentMusicVisualizer());
|
||||
}
|
||||
#onMusicChange = () => {
|
||||
this.#visUpdateRouterListeners = this.#visUpdateRouter.retrieveListeners();
|
||||
this.#visUpdateRouter?.unbindVisualizer();
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the the playlist.
|
||||
*/
|
||||
set playlist(playlist) {
|
||||
if (!this.playlist) {
|
||||
super.playlist = playlist;
|
||||
return; // HACK: If it's the first time, then wait until super() is done.
|
||||
}
|
||||
this.playlist?.removePositionChangeListener(this.#onMusicChange);
|
||||
this.playlist.addPositionChangeListener(this.#onMusicChange);
|
||||
super.playlist = playlist;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {boolean} true if and only if successful in changing to the next music.
|
||||
*/
|
||||
next() {
|
||||
const updateListeners = this.#visualizerUpdateManager.getBinnedListeners();
|
||||
if (!super.next()) return false;
|
||||
this.#updateVisUpdateManager();
|
||||
|
||||
this.#visualizerUpdateManager.setBinnedListeners(updateListeners);
|
||||
return true;
|
||||
get playlist() {
|
||||
return super.playlist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Jumps to the previous music if possible.
|
||||
*
|
||||
* @returns {boolean} true if and only if successful in switching to the previous music in the playlist.
|
||||
*/
|
||||
previous() {
|
||||
const updateListeners = this.#visualizerUpdateManager.getBinnedListeners();
|
||||
if (!super.previous()) return false;
|
||||
|
||||
this.#updateVisUpdateManager();
|
||||
|
||||
this.#visualizerUpdateManager.setBinnedListeners(updateListeners);
|
||||
return true;
|
||||
playCurrent() {
|
||||
console.log("attempted play from vis");
|
||||
super.playCurrent();
|
||||
const visualizer = this.playlist.currentMusic.fetchVisualizer();
|
||||
this.#visUpdateRouter.visualizer = visualizer;
|
||||
visualizer.analyze();
|
||||
if (this.#visUpdateRouterListeners) this.#visUpdateRouter.loadListeners(this.#visUpdateRouterListeners);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} index the index of the music to change to.
|
||||
* @returns {boolean} true if and only if successful in jumping to the given index.
|
||||
*/
|
||||
changeCurrent(index) {
|
||||
const updateListeners = this.VisualizerUpdateManager.getBinnedListeners();
|
||||
if (!super.changeCurrent(index)) return false;
|
||||
this.#updateVisUpdateManager();
|
||||
|
||||
this.#visualizerUpdateManager.setBinnedListeners = updateListeners();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {VisUpdateRouter} The current visualizer update manager.
|
||||
*/
|
||||
get currentVisualizerUpdateManager() {
|
||||
return this.#visualizerUpdateManager();
|
||||
get visUpdateRouter() {
|
||||
return this.#visUpdateRouter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Visualizer} The current music's visualizer.
|
||||
*/
|
||||
get currentMusicVisualizer() {
|
||||
return this.getCurrentMusic().getVisualizer();
|
||||
return this.playlist.currentMusic.fetchVisualizer(this.#fftSize);
|
||||
}
|
||||
}
|
@ -16,11 +16,13 @@
|
||||
*/
|
||||
function createEaseLinear(rate) {
|
||||
return (current, dest, frameDelta) => {
|
||||
let direction = 1;
|
||||
if (dest < current) {
|
||||
direction = -1;
|
||||
const res = current - (rate * frameDelta);
|
||||
return res < dest ? dest : res;
|
||||
} else {
|
||||
const res = current + (rate * frameDelta);
|
||||
return res > dest ? dest : res;
|
||||
}
|
||||
return current + (Math.min(rate * frameDelta, dest) * direction);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
import Visualizer from "./Visualizer.js";
|
||||
|
||||
/**@module */
|
||||
|
||||
/**
|
||||
@ -27,42 +25,52 @@ export default class VisUpdateRouter {
|
||||
#rangedListeners = [];
|
||||
#lastBins;
|
||||
#visualizer;
|
||||
#visualizerListener;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Visualizer} visualizer the visualizer this manager obtains data from.
|
||||
* @param {number} fftSize the fft window size of the visualizer this manager obtains data from.
|
||||
*/
|
||||
constructor(visualizer) {
|
||||
constructor(fftSize) {
|
||||
this.#binnedListeners = [];
|
||||
this.#rangedListeners = [];
|
||||
for (let i = 0; i < visualizer.getNumberOfBins(); i++) {
|
||||
const bins = fftSize / 2; // TODO: Clean this up.
|
||||
for (let i = 0; i < bins; i++) {
|
||||
this.#binnedListeners.push([]);
|
||||
}
|
||||
this.#lastBins = new Uint8Array(this.#binnedListeners.length);
|
||||
this.#visualizer = visualizer;
|
||||
this.#visualizerListener = (delta, bins) => {
|
||||
const sortedCopyOfRangedListeners = [... this.#rangedListeners]; // We assume this is sorted properly. A 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 { lower, upper, listener } = sortedCopyOfRangedListeners[rangedInd];
|
||||
if (lower > binInd) break; // Don't need to check the rest if the current lowest minimum is greater than the current bin index.
|
||||
if (binInd <= upper) {
|
||||
listener(delta, bins.slice(lower, upper));
|
||||
sortedCopyOfRangedListeners.shift();
|
||||
rangedInd--;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#visualizerListener = (delta, bins) => {
|
||||
const sortedCopyOfRangedListeners = [... this.#rangedListeners]; // We assume this is sorted properly. A 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 { lower, upper, listener } = sortedCopyOfRangedListeners[rangedInd];
|
||||
if (lower > binInd) break; // Don't need to check the rest if the current lowest minimum is greater than the current bin index.
|
||||
if (binInd <= upper) {
|
||||
listener(delta, bins.slice(lower, upper));
|
||||
sortedCopyOfRangedListeners.shift();
|
||||
rangedInd--;
|
||||
}
|
||||
this.#lastBins[binInd] = bins[binInd];
|
||||
}
|
||||
this.#lastBins[binInd] = bins[binInd];
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the current visualizer.
|
||||
*/
|
||||
set visualizer(visualizer) {
|
||||
console.log("vis update router visualizer set.");
|
||||
if (this.#visualizer) this.unbindVisualizer();
|
||||
this.#visualizer = visualizer;
|
||||
visualizer.addUpdateListener(this.#visualizerListener);
|
||||
}
|
||||
|
||||
@ -91,6 +99,7 @@ export default class VisUpdateRouter {
|
||||
* @returns {boolean} True if and only if the ranged listener was added successfully.
|
||||
*/
|
||||
addVisualizerRangedUpdateListener({ lower, upper, listener }) {
|
||||
console.log("range update listener added.");
|
||||
const rangedListener = {
|
||||
lower: lower,
|
||||
upper: upper,
|
||||
@ -174,9 +183,10 @@ export default class VisUpdateRouter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Unbinds this update manager from the initial visualizer. Effectively meaning this manager will no longer be used.
|
||||
* Unbinds this update manager from the initial visualizer.
|
||||
*/
|
||||
unbindVisualizer() {
|
||||
this.#visualizer.removeUpdateListener(this.#visualizerListener);
|
||||
this.#visualizer = null;
|
||||
}
|
||||
}
|
@ -83,6 +83,7 @@ export default class Visualizer {
|
||||
* @returns {boolean} true if and only if the listener was successfully added.
|
||||
*/
|
||||
addUpdateListener(listener) {
|
||||
console.log("visualizer received listener");
|
||||
if (this.#updateListeners.includes(listener));
|
||||
this.#updateListeners.push(listener);
|
||||
return true;
|
||||
|
@ -63,8 +63,8 @@
|
||||
const ask = window.audioshowkit;
|
||||
const playlist = new ask.player.MusicPlaylist("Awesome Music");
|
||||
|
||||
playlist.add("https://res.sys.reslate.xyz/dl/audio/moments.mp3", "Moments", "Lost Identities x Robbie Rosen");
|
||||
playlist.add("https://res.sys.reslate.xyz/dl/audio/XXI.mp3", "XXI", "QR");
|
||||
playlist.add("/assets/audio/moments.mp3", "Moments", "Lost Identities x Robbie Rosen");
|
||||
playlist.add("assets/audio/XXI.mp3", "XXI", "QR");
|
||||
|
||||
const player = new ask.player.MusicPlayer(playlist);
|
||||
const playElem = player.generatePlayElement();
|
||||
|
75
tutorials/VisMusicPlayer.html
Normal file
75
tutorials/VisMusicPlayer.html
Normal file
@ -0,0 +1,75 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<link rel="stylesheet" href="./assets/css/prism.css" />
|
||||
<script src="./assets/js/prism.js" defer></script>
|
||||
<script src="./assets/js/audioshowkit.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div>
|
||||
<h1>Visualizing Music</h1>
|
||||
<p>This is the fun part. We can use a {@link module:player/VisualizedMusicPlayer} and a {@link module:player/MusicPlaylist} to create a music player that is like {@link module:player/MusicPlayer} but with the ability to automatically fetch the current {@link module:visualization/Visualizer}. On top of that, it then routes that visualizer data to {@link module:visualization/VisualizerUpdateManager} which can be to make much more refined mappings.</p>
|
||||
This library comes with a variety of mapping tools:
|
||||
<ul>
|
||||
<li>Want to map ranges of frequency bins to a plethora of element style properties? Take a look at {@link module:mapping/mappings}!</li>
|
||||
<li>Check out {@link module:patterns/canvas} for built in canvas patterns.</li>
|
||||
<li>We even do font size and color with the {@link module:mapping/text} module!</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Instantiation</h2>
|
||||
<p>Exactly like when instantiating a normal music player, you will need a playlist. Other than that, it's simple.</p>
|
||||
<pre><code class="language-js">
|
||||
const ask = window.audioshowkit; // Get a reference to the audioshowkit stuff.
|
||||
const playlist = previousPlaylist; // We are assuming you have a playlist ready.
|
||||
const player = new ask.player.VisualizedMusicPlayer(playlist) // Creates a new music player with the playlist.
|
||||
</code></pre>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Playback</h2>
|
||||
<p>Since the usage of playback is the same as a normal {@link module:player/MusicPlayer}, see [the MusicPlayer tutorial]{@tutorial MusicPlayer} for more information.</p>
|
||||
<div id="playback-ctrls">
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Visualization</h2>
|
||||
<p>The actual visualization can be performed in a variety of ways. We can use canvases, or even better, actual HTML elements!</p>
|
||||
<div class="result">
|
||||
<div id="width-map-demo" style="height: 2rem; background-color: black;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const ask = window.audioshowkit;
|
||||
const playlist = new ask.player.MusicPlaylist("Awesome Music");
|
||||
|
||||
playlist.add("/assets/audio/XXI.mp3", "XXI", "QR");
|
||||
playlist.add("/assets/audio/moments.mp3", "Moments", "Lost Identities x Robbie Rosen");
|
||||
|
||||
const player = new ask.player.VisMusicPlayer(playlist);
|
||||
const playbackCtrlsElem = document.getElementById("playback-ctrls");
|
||||
const prevElem = player.generatePreviousElement();
|
||||
const playElem = player.generatePlayElement();
|
||||
const nextElem = player.generateNextElement();
|
||||
playbackCtrlsElem.appendChild(prevElem);
|
||||
playbackCtrlsElem.appendChild(playElem);
|
||||
playbackCtrlsElem.appendChild(nextElem);
|
||||
|
||||
const widthElem = document.getElementById("width-map-demo");
|
||||
console.log("attempting to map");
|
||||
ask.mappings.dimensions.mapWidth({
|
||||
element: widthElem,
|
||||
growLower: 2,
|
||||
growUpper: 5,
|
||||
unit: "rem",
|
||||
lowerBin: 0,
|
||||
upperBin: 128,
|
||||
visUpdateRouter: player.visUpdateRouter,
|
||||
interpolator: ask.support.easings.createEaseLinear(1)
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
Loading…
x
Reference in New Issue
Block a user