akko
Version:
Music visualising framework based on WebGL and Web Audio API.
156 lines (134 loc) • 4.56 kB
JavaScript
/**
* @author Timur Kuzhagaliyev <tim.kuzh@gmail.com>
* @copyright 2017
* @license GPL-3.0
*/
const THREE = require('three');
const elementResizeEvent = require('element-resize-event');
const Visualisers = require('./visualisers');
class VisualisationCore {
/**
* @param {object} data
* @param {Element} data.parentElement
* @param {boolean} data.useDefaultVisualisers
* @param {object} data.analyser
*/
constructor(data) {
this.parentElement = data.parentElement;
this.frequencyDataArray = null;
this.analyser = data.analyser;
/**
* @callback visualiserListener
* @param {Track[]} visualisers
* @param {int} currentVisualiserIndex
*/
/** @type {visualiserListener[]} */
this.listeners = [];
this.visualisers = data.useDefaultVisualisers ? this.prepareDefaultVisualisers() : [];
this.currentVisualiserIndex = -1;
}
prepareDefaultVisualisers() {
let visualisers = [];
for (let key in Visualisers) {
if (!Visualisers.hasOwnProperty(key)) continue;
let visualiserClass = Visualisers[key];
visualisers.push(new visualiserClass());
}
return visualisers;
}
notifyListeners() {
for(let i = 0; i < this.listeners.length; i++) {
let listener = this.listeners[i];
listener(this.visualisers, this.currentVisualiserIndex);
}
}
/**
* @param {visualiserListener} listener
*/
addListener(listener) {
this.listeners.push(listener);
this.notifyListeners();
}
/**
* @param {Visualiser} visualiser
*/
addVisualiser(visualiser) {
this.visualisers.push(visualiser);
this.notifyListeners();
}
prepare() {
let width = this.parentElement.offsetWidth;
let height = this.parentElement.offsetHeight;
this.renderer = new THREE.WebGLRenderer();
this.renderer.setSize(width, height);
this.canvas = this.renderer.domElement;
this.parentElement.appendChild(this.canvas);
}
start() {
this.setupListeners();
this.renderLoop();
this.notifyListeners();
}
/**
* @param {int} index
*/
useVisualiser(index) {
let visualiser = this.visualisers[index];
if (visualiser) this.prepareVisualiser(visualiser);
if (this.visualiser) this.visualiser.pause();
this.currentVisualiserIndex = index;
this.visualiser = visualiser;
this.notifyListeners();
}
/**
* @param {Visualiser} visualiser
*/
prepareVisualiser(visualiser) {
this.analyser.fftSize = visualiser.fftSize;
this.analyser.smoothingTimeConstant = visualiser.smoothingTimeConstant;
this.frequencyDataArray = new Float32Array(this.analyser.frequencyBinCount);
this.timeDomainDataArray = new Float32Array(this.analyser.frequencyBinCount);
let data = {
renderer: this.renderer,
analyser: this.analyser,
width: this.canvas.clientWidth,
height: this.canvas.clientHeight,
};
if (!visualiser.isInitialised()) visualiser.init(data);
else if (visualiser.isPaused()) visualiser.revive(data);
visualiser.resize(data);
}
setupListeners() {
elementResizeEvent(this.parentElement, this.onParentResize.bind(this));
}
renderLoop() {
if (this.visualiser) {
if (this.analyser) {
this.analyser.getFloatFrequencyData(this.frequencyDataArray);
this.analyser.getFloatTimeDomainData(this.timeDomainDataArray);
}
this.visualiser.update({
renderer: this.renderer,
analyser: this.analyser,
frequencyData: this.frequencyDataArray,
timeDomainData: this.timeDomainDataArray,
});
} else {
// TODO: Display warning about no visualiser
}
setTimeout(() => {
requestAnimationFrame(this.renderLoop.bind(this));
}, 1000 / 30 );
}
onParentResize() {
let width = this.parentElement.offsetWidth;
let height = this.parentElement.offsetHeight;
this.renderer.setSize(width, height);
if (this.visualiser) this.visualiser.resize({
renderer: this.renderer,
width,
height,
});
}
}
module.exports = VisualisationCore;