diff --git a/.gitignore b/.gitignore
index 0f2e5aa..27f553a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
/webGLTest.iml
/.idea/
+/build/node_modules/
\ No newline at end of file
diff --git a/build/gulpfile.js b/build/gulpfile.js
new file mode 100644
index 0000000..36d8cf9
--- /dev/null
+++ b/build/gulpfile.js
@@ -0,0 +1,23 @@
+const gulp = require('gulp'),
+ spriteBuild = require('./task/spriteBuilder').buildIconSprites,
+ scss = require('./task/scss').buildCSS,
+ js = require('./task/js').build,
+ gui = require('./task/jsonMinifier').build,
+ worker = require('./task/worker').build;
+
+gulp.task('scss', scss);
+gulp.task('js', js);
+gulp.task('sprite', spriteBuild);
+gulp.task('gui', gui);
+gulp.task('workerJS', worker);
+
+gulp.task('watchMe', () => {
+ gulp.watch('./../raw/javascript/**/*.js', gulp.series('js'));
+ gulp.watch('./../raw/worker/**/*.js', gulp.series('workerJS'));
+ gulp.watch('./../raw/scss/**/*.scss', gulp.series('scss'));
+ gulp.watch('./../raw/gui/**/*.json', gulp.series('gui'));
+});
+
+gulp.task('default', gulp.parallel('js', 'scss', 'sprite', 'gui', 'workerJS'));
+
+gulp.task('watch', gulp.parallel('js', 'scss', 'sprite', 'gui', 'workerJS', 'watchMe'));
diff --git a/build/package.json b/build/package.json
new file mode 100644
index 0000000..b7d731d
--- /dev/null
+++ b/build/package.json
@@ -0,0 +1,36 @@
+{
+ "name": "vtwebbuild",
+ "version": "1.0.0",
+ "description": "Buildprocess for VTWeb like babble, sass and other things",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://gitlab.com/versustune/vtweb.git"
+ },
+ "author": "Maurice Grönwoldt",
+ "license": "ISC",
+ "bugs": {
+ "url": "https://gitlab.com/versustune/vtweb/issues"
+ },
+ "homepage": "https://gitlab.com/versustune/vtweb#readme",
+ "dependencies": {
+ "@fortawesome/fontawesome-pro": "^5.5.0",
+ "@fortawesome/pro-light-svg-icons": "^5.5.0",
+ "@fortawesome/pro-regular-svg-icons": "^5.5.0",
+ "@fortawesome/pro-solid-svg-icons": "^5.5.0",
+ "fontawesome-svg-sprite-generator": "^1.0.0",
+ "gulp": "^4.0.0",
+ "gulp-clean-css": "^4.0.0",
+ "gulp-concat": "^2.6.1",
+ "gulp-rename": "^1.4.0",
+ "gulp-sass": "^4.0.2",
+ "gulp-terser": "^1.1.7",
+ "merge-stream": "^1.0.1",
+ "node-fs-extra": "^0.8.2",
+ "require-dir": "^1.0.0",
+ "svg-sprite": "^1.4.0"
+ }
+}
diff --git a/build/task/js.js b/build/task/js.js
new file mode 100644
index 0000000..58439ba
--- /dev/null
+++ b/build/task/js.js
@@ -0,0 +1,51 @@
+const terser = require('gulp-terser'),
+ concat = require('gulp-concat'),
+ rename = require('gulp-rename'),
+ gulp = require('gulp');
+
+const basePath = __dirname + '/../../raw/javascript/';
+const visualPath = basePath + 'visuals/';
+const visuals = [
+ visualPath + 'sphere.js',
+ visualPath + 'wave.js',
+ visualPath + 'wave2d.js',
+ visualPath + 'water.js',
+ //visualPath + 'experimental.js',
+]
+const config = {
+ src: [
+ basePath + 'utils.js',
+ basePath + 'gl/glUtils.js',
+ basePath + 'gl/Camera.js',
+ basePath + 'template.js',
+ basePath + 'gl/handler.js',
+ basePath + 'audio.js',
+ basePath + 'FileHandler.js',
+ basePath + 'FetchHandler.js',
+ basePath + 'playerConfigHandler.js',
+ basePath + 'player.js',
+ basePath + 'gui.js',
+ basePath + 'visual.js',
+ basePath + 'imageUploader.js',
+ basePath + 'notification.js',
+ basePath + 'config.js',
+ ...visuals,
+ basePath + 'eventHandler.js',
+ basePath + 'select.js',
+ basePath + 'keys.js',
+ basePath + 'startup.js',
+ basePath + 'app.js'
+ ],
+ dest: __dirname + '/../../out/js'
+};
+
+function build() {
+ return gulp.src(config.src)
+ .pipe(concat('scripts.js'))
+ .pipe(gulp.dest(config.dest))
+ .pipe(rename('scripts.min.js'))
+ .pipe(terser().on('error', console.error))
+ .pipe(gulp.dest(config.dest));
+}
+
+module.exports.build = build;
diff --git a/build/task/jsonMinifier.js b/build/task/jsonMinifier.js
new file mode 100644
index 0000000..ef6795c
--- /dev/null
+++ b/build/task/jsonMinifier.js
@@ -0,0 +1,26 @@
+const gulp = require('gulp'),
+ fs = require('fs')
+
+
+const basePath = __dirname + '/../../raw/gui/';
+
+const config = {
+ src: basePath + '*.json',
+ dest: __dirname + '/../../out/gui/'
+}
+
+function build() {
+ fs.readdirSync(basePath).forEach(file => {
+ try {
+ if (fs.lstatSync(basePath + file).isDirectory())
+ return
+ let content = fs.readFileSync(basePath + file).toString()
+ fs.writeFileSync(config.dest + file, JSON.stringify(JSON.parse(content)))
+ } catch (e) {
+ console.error(e)
+ }
+ })
+ return gulp.src(config.src)
+}
+
+module.exports.build = build;
\ No newline at end of file
diff --git a/build/task/scss.js b/build/task/scss.js
new file mode 100644
index 0000000..a52931b
--- /dev/null
+++ b/build/task/scss.js
@@ -0,0 +1,17 @@
+const sass = require('gulp-sass'),
+ clean = require('gulp-clean-css'),
+ gulp = require('gulp');
+
+const config = {
+ src: __dirname + '/../../raw/scss/**/*.scss',
+ dest: __dirname + '/../../out/theme'
+};
+
+function buildCSS() {
+ return gulp.src(config.src)
+ .pipe(sass().on('error', sass.logError))
+ .pipe(clean())
+ .pipe(gulp.dest(config.dest));
+}
+
+module.exports.buildCSS = buildCSS;
diff --git a/build/task/spriteBuilder.js b/build/task/spriteBuilder.js
new file mode 100644
index 0000000..5da45df
--- /dev/null
+++ b/build/task/spriteBuilder.js
@@ -0,0 +1,106 @@
+const faSvgSprite = require('fontawesome-svg-sprite-generator'),
+ SVGSpriter = require('svg-sprite'),
+ path = require('path'),
+ fs = require('fs'),
+ fal = require('@fortawesome/pro-light-svg-icons'),
+ far = require('@fortawesome/pro-regular-svg-icons'),
+ fas = require('@fortawesome/pro-solid-svg-icons'),
+ fsextra = require('node-fs-extra'),
+ gulp = require('gulp');
+
+let toMergeData = [];
+
+function buildIconSprites() {
+ let config = {
+ src: __dirname + '/../../raw/icons',
+ dest: __dirname + '/../../out/icon-sprite.svg',
+ spriter: {
+ shape: {
+ id: {
+ generator: function (name) {
+ return "vt-" + name.replace('.svg', '');
+ }
+ }
+ },
+ "svg": {
+ "xmlDeclaration": false,
+ "doctypeDeclaration": false
+ },
+ "mode": {
+ "symbol": true
+ }
+ }
+ };
+
+ let icons = {
+ fa: [
+ fal.faPlay,
+ fal.faPause,
+ fal.faCaretRight,
+ fal.faCaretLeft,
+ fal.faRandom,
+ fal.faCogs,
+ fal.faFolderUpload,
+ fal.faListMusic,
+ fal.faFileImage,
+ fal.faQuestionCircle,
+ ],
+ vt: []
+ };
+
+ builder.init(config, icons);
+ builder.generateFontAwesome();
+ builder.generateFromIcons();
+ return gulp.src(config.src);
+}
+
+let builder = {
+ init: function (config, icons) {
+ this.config = config;
+ this.icons = icons;
+ },
+
+ generateFontAwesome: function () {
+ let sprite = faSvgSprite.generate(this.icons.fa, {license: '', xmlDeclaration: false});
+ toMergeData.push(sprite.svg);
+ },
+
+ generateFromIcons: function () {
+ builder.merge();
+ },
+
+ merge: function () {
+ if (toMergeData.length === 0) {
+ return;
+ }
+ let data = '';
+ for (let item of toMergeData) {
+ data += item.replace(' ', '').replace(/(]*)/g, '');
+ }
+ data += ' ';
+
+ let dirname = path.dirname(builder.config.dest);
+ if (!fs.existsSync(dirname)) {
+ fsextra.mkdirpSync(dirname);
+ }
+ fs.writeFileSync(builder.config.dest, data);
+ },
+
+ readFiles: function (dir, spriter, icons) {
+ let files = fs.readdirSync(dir);
+ for (let file of files) {
+ let split = file.split('.');
+ let suffix = split[split.length - 1];
+ if (suffix === 'svg' && icons.indexOf('vt-' + split[0]) !== -1) {
+ let filePath = path.resolve(dir, file);
+ spriter.add(
+ filePath,
+ file,
+ fs.readFileSync(filePath, {encoding: 'utf-8'})
+ );
+ }
+ }
+ }
+};
+
+module.exports.buildIconSprites = buildIconSprites;
diff --git a/build/task/worker.js b/build/task/worker.js
new file mode 100644
index 0000000..cbd2ba9
--- /dev/null
+++ b/build/task/worker.js
@@ -0,0 +1,26 @@
+const terser = require('gulp-terser'),
+ concat = require('gulp-concat'),
+ rename = require('gulp-rename'),
+ gulp = require('gulp');
+
+const basePath = __dirname + '/../../raw/worker/';
+const config = {
+ src: [
+ basePath + 'eventHandler.js',
+ basePath + 'database.js',
+ basePath + 'id3.js',
+ basePath + 'app.js',
+ ],
+ dest: __dirname + '/../../out/js'
+};
+
+function build() {
+ return gulp.src(config.src)
+ .pipe(concat('worker.js'))
+ .pipe(gulp.dest(config.dest))
+ .pipe(rename('worker.min.js'))
+ .pipe(terser())
+ .pipe(gulp.dest(config.dest));
+}
+
+module.exports.build = build;
diff --git a/empty.txt b/empty.txt
new file mode 100644
index 0000000..e69de29
diff --git a/favicon.ico b/favicon.ico
new file mode 100755
index 0000000..07f434f
Binary files /dev/null and b/favicon.ico differ
diff --git a/favicon/android-chrome-192x192.png b/favicon/android-chrome-192x192.png
new file mode 100755
index 0000000..e4bdf6a
Binary files /dev/null and b/favicon/android-chrome-192x192.png differ
diff --git a/favicon/android-chrome-512x512.png b/favicon/android-chrome-512x512.png
new file mode 100755
index 0000000..ffd7250
Binary files /dev/null and b/favicon/android-chrome-512x512.png differ
diff --git a/favicon/apple-touch-icon.png b/favicon/apple-touch-icon.png
new file mode 100755
index 0000000..aea74d1
Binary files /dev/null and b/favicon/apple-touch-icon.png differ
diff --git a/favicon/favicon-16x16.png b/favicon/favicon-16x16.png
new file mode 100755
index 0000000..66f2fc6
Binary files /dev/null and b/favicon/favicon-16x16.png differ
diff --git a/favicon/favicon-32x32.png b/favicon/favicon-32x32.png
new file mode 100755
index 0000000..eef4bd3
Binary files /dev/null and b/favicon/favicon-32x32.png differ
diff --git a/favicon/mstile-150x150.png b/favicon/mstile-150x150.png
new file mode 100755
index 0000000..15642f9
Binary files /dev/null and b/favicon/mstile-150x150.png differ
diff --git a/favicon/safari-pinned-tab.svg b/favicon/safari-pinned-tab.svg
new file mode 100755
index 0000000..7ed8f16
--- /dev/null
+++ b/favicon/safari-pinned-tab.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/index.html b/index.html
index 4e1c80a..af4f46a 100644
--- a/index.html
+++ b/index.html
@@ -2,33 +2,109 @@
-
- Rotation
-
-
- Transform
-
-
- Color
-
-
- World
-
-
- Draw
-
+
+
+
+ Loading
-
☰
-
-
-
-
-
+
+
+
+
+
+
+
+ Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et
+ dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
+ clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet,
+ consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,
+ sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no
+ sea takimata sanctus est Lorem ipsum dolor sit amet.
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/js/handler.js b/js/handler.js
index b68f701..884a547 100644
--- a/js/handler.js
+++ b/js/handler.js
@@ -80,4 +80,13 @@ class Shaders {
getProgram(name) {
return this.programs[name];
}
+
+ async loadArray(list, path) {
+ let self = this;
+ for (const e of list) {
+ await self.loadShader(e, path)
+ }
+ await self.createProgramForEach(list)
+ return true;
+ }
}
\ No newline at end of file
diff --git a/js/index.js b/js/index.js
index 2be60e4..95b4392 100644
--- a/js/index.js
+++ b/js/index.js
@@ -1,22 +1,35 @@
-let shaderHandler, gl, c, actx, analyser, peak;
-let positionData = [];
-let positionSize = 8192 * 2 * 2;
+let shaderHandler, gl, c, actx, analyser, peak, isInit = false;
-function createAudioContextStream(stream) {
+function createAudioContextStream(audio) {
let AudioContext = window.AudioContext || window.webkitAudioContext;
if (actx) {
actx.close();
}
actx = new AudioContext();
analyser = actx.createAnalyser();
+ let MEDIA_ELEMENT_NODES = new WeakMap();
let Source;
analyser.fftSize = 4096;
analyser.maxDecibels = 0;
analyser.smoothingTimeConstant = .4;
- Source = actx.createMediaStreamSource(stream);
+ if (audio) {
+ if (MEDIA_ELEMENT_NODES.has(audio)) {
+ Source = MEDIA_ELEMENT_NODES.get(audio);
+ } else {
+ Source = actx.createMediaElementSource(audio);
+ MEDIA_ELEMENT_NODES.set(audio, Source);
+ }
- Source.connect(analyser);
+ Source.connect(analyser);
+ analyser.connect(actx.destination);
+ audio.oncanplay = () => {
+ actx.resume();
+ };
+ audio.onended = function () {
+ actx.pause();
+ };
+ }
return null;
}
@@ -28,26 +41,38 @@ async function init() {
return false;
}
shaderHandler = new Shaders(gl);
- await shaderHandler.loadShader("test", "shaders/");
+ await shaderHandler.loadShader("wave", "shaders/");
//sphere shader
await shaderHandler.loadShader("sphere", "shaders/", gl.VERTEX_SHADER);
- return shaderHandler.createProgramForEach(["test", "sphere"]);
+ return shaderHandler.createProgramForEach(["wave", "sphere"]);
}
-(function () {
- navigator.mediaDevices.getUserMedia({audio: true, video: false})
- .then(createAudioContextStream).then(e => {
- generateRotationSliders();
- generateColorSliders();
- generateWorldSliders();
- generateDrawSliders();
- generateTranslateSliders();
+function createView() {
+ if (!isInit) {
+ createAudioContextStream(audioFile);
init().then(b => {
if (b) {
- sphereObject.drawMode = gl.POINTS;
+ loadConfig();
draw();
}
})
- });
-})();
\ No newline at end of file
+ isInit = true;
+ }
+}
+
+function initGUI() {
+ generateRotationSliders();
+ generateColorSliders();
+ generateWorldSliders();
+ generateDrawSliders();
+ generateTranslateSliders();
+}
+
+(function () {
+ if (document.readyState === "complete" || document.readyState === "interactive") {
+ initGUI()
+ } else {
+ document.addEventListener('DOMContentLoaded', initGUI);
+ }
+})()
\ No newline at end of file
diff --git a/js/old.js b/js/old.js
deleted file mode 100644
index ac3b912..0000000
--- a/js/old.js
+++ /dev/null
@@ -1,88 +0,0 @@
-function readData() {
- let items = analyser.fftSize;
- let dataArray = new Float32Array(items);
- analyser.getFloatTimeDomainData(dataArray);
- let space = 255 / (items + 2);
- let pos = [0, 127.5];
- let x = space;
- peak = 0;
- for (let i = 0; i < items; i++) {
- let data = (dataArray[i] * 125) + 127.5
- if (Math.abs(data) > peak) {
- peak = data;
- }
- pos.push(x);
- pos.push(data);
- x += space;
- }
- pos.push(255, 127.5);
- positions = pos;
-}
-
-let positions = [
- Math.random(), Math.random(),
- Math.random(), Math.random(),
- Math.random(), Math.random(),
-];
-
-/*function draw() {
- readData();
- let program = shaderHandler.getProgram("test");
- let positionAttributeLocation = gl.getAttribLocation(program, "a_position");
- var colorLocation = gl.getUniformLocation(program, "u_color");
- let positionBuffer = gl.createBuffer();
- gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.DYNAMIC_DRAW);
- let vao = gl.createVertexArray();
- gl.bindVertexArray(vao);
- gl.enableVertexAttribArray(positionAttributeLocation);
- gl.vertexAttribPointer(
- positionAttributeLocation, 2, gl.FLOAT, false, 0, 0);
- gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
-
- gl.clearColor(0, 0, 0, 1);
- gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
- gl.useProgram(program);
- gl.uniform4f(colorLocation, peak / 255, (255 - peak) / 255, .2, 1);
- gl.drawArrays(gl.LINE_STRIP, 0, positions.length / 2);
-
- let positionBuffer2 = gl.createBuffer();
- gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer2);
- gl.uniform4f(colorLocation, 0, 0, 1, .3);
- gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.DYNAMIC_DRAW);
- gl.drawArrays(gl.POINTS, 0, positions.length / 2);
-
- requestAnimationFrame(draw);
-}*/
-
-function setupSphere() {
- sphereData = [];
- let data = readData(),
- radData = data[0];
- let sin = Math.sin,
- cos = Math.cos,
- total = sphereObject.total || 50,
- cTotal = (total + 1) * (total + 1),
- r = sphereObject.radius || 1000,
- rx = r / c.width,
- ry = r / c.height,
- counter = 0;
- for (let i = 1; i <= total; i++) {
- let lat = VTUtils.map(i, 0, total, 0, Math.PI, false);
- for (let j = 1; j <= total; j++) {
- let lon = VTUtils.map(j, 0, total, 0, Math.TWO_PI, false);
- let map = VTUtils.map(counter, 0, cTotal, 0, radData.length - 1, false);
- map = Math.floor(map);
- let realRX = rx + radData[map];
- let realRY = ry + radData[map];
- let x = rx * sin(lon) * cos(lat);
- let y = rx * sin(lon) * sin(lat);
- let z = ry * cos(lon);
- sphereData.push(x);
- sphereData.push(y);
- sphereData.push(z);
- counter++;
- }
- }
- return [VTUtils.hsvToRgb(data[1], 1, 1), data[1]];
-}
diff --git a/js/sphere.js b/js/sphere.js
index b83985e..a27d272 100644
--- a/js/sphere.js
+++ b/js/sphere.js
@@ -7,20 +7,21 @@ let sphereObject = {
rotation: [0, 0, 0], //radians
rotateInc: [0.0, 0.0, 0.0], //degreesInc
rotateByBeat: true,
- translate: [1, 1, 1],
+ translate: [0, 0, 0],
total: 50,
radius: 500,
color: {r: 0, g: 0, b: 0},
colorByBeat: true,
- drawMode: 0,
+ drawMode: 5,
sphereMode: 0,
lightPos: [0, 0, 0],
pointSize: 2,
steps: 512,
- dirtyMode: false
+ dirtyMode: false,
+ light: 0.3
}
-function readData() {
+function readDataBar() {
let items = sphereObject.steps;
let dataArray = new Uint8Array(items);
analyser.getByteFrequencyData(dataArray);
@@ -36,6 +37,7 @@ function readData() {
let sphereDataVectors = [], lastTotal = 0;
+// avoid garbage collection each run
function prepareData() {
let total = sphereObject.total;
if (lastTotal !== total) {
@@ -52,7 +54,7 @@ function prepareData() {
function setupSphere() {
sphereData = [];
- let data = readData(),
+ let data = readDataBar(),
radData = data[0],
map = VTUtils.map,
total = sphereObject.total,
@@ -94,14 +96,21 @@ function setupSphere() {
function getAddRad(counter, data, total) {
let mapping, rAdd, map = VTUtils.map;
+ let h = total / 2;
if (sphereObject.sphereMode === 3) {
- let h = total / 2;
if (counter > h) {
mapping = map(counter, h, total, data.length - 1, 0);
} else {
mapping = map(counter, 0, h, 0, data.length - 1);
}
rAdd = data[Math.round(mapping)] || 0;
+ } else if (sphereObject.sphereMode === 4) {
+ if (counter > h) {
+ mapping = map(counter, h, total, 0, data.length - 1);
+ } else {
+ mapping = map(counter, 0, h, data.length - 1, 0);
+ }
+ rAdd = data[Math.round(mapping)] || 0;
} else {
mapping = map(counter, 0, total, 0, data.length - 1);
rAdd = data[Math.round(mapping)] || 0;
@@ -109,18 +118,16 @@ function getAddRad(counter, data, total) {
return rAdd;
}
-let position, world, color, rotate, light, lightPos, lightAngle = 90;
+let position, color, rotate, light, lightPos;
function prepare(program) {
position = gl.getAttribLocation(program, "a_position");
- world = gl.getUniformLocation(program, 'u_world');
color = gl.getUniformLocation(program, "u_color");
light = gl.getUniformLocation(program, "u_light");
rotate = gl.getUniformLocation(program, "u_matrix");
lightPos = gl.getUniformLocation(program, "u_lightPos");
let pointSize = gl.getUniformLocation(program, "u_pointSize");
- gl.uniformMatrix4fv(world, false, TDUtils.yRotation(TDUtils.degToRad(lightAngle)));
- gl.uniform3fv(light, [0.95, 0.62, 0.094]);
+ gl.uniform3fv(light, [sphereObject.light, 0, 0]);
gl.uniform3fv(lightPos, sphereObject.lightPos);
gl.uniform1f(pointSize, sphereObject.pointSize);
}
@@ -135,16 +142,15 @@ function draw() {
gl.useProgram(program);
prepare(program);
let matrix = [
- sphereObject.translate[0] / aspect, 0, 0, 0,
- 0, sphereObject.translate[1], 0, 0,
- 0, 0, sphereObject.translate[2], 0,
- 0, 0, 0, 1
+ 1 / aspect, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ sphereObject.translate[0], sphereObject.translate[1], sphereObject.translate[2], 1
]
matrix = TDUtils.multiply(matrix, TDUtils.xRotation(sphereObject.rotation[0]));
matrix = TDUtils.multiply(matrix, TDUtils.yRotation(sphereObject.rotation[1]));
matrix = TDUtils.multiply(matrix, TDUtils.zRotation(sphereObject.rotation[2]));
gl.uniformMatrix4fv(rotate, false, matrix);
-
let positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(sphereData), gl.DYNAMIC_DRAW);
@@ -152,7 +158,6 @@ function draw() {
gl.bindVertexArray(vao);
gl.enableVertexAttribArray(position);
gl.vertexAttribPointer(position, 3, gl.FLOAT, true, 0, 0);
-
gl.clearColor(0, 0, 0, 1);
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
@@ -186,12 +191,6 @@ function sphereMode(lat, lon, i, counter, rx, ry) {
sin = Math.sin,
cos = Math.cos;
switch (sphereObject.sphereMode) {
- case 0:
- case 3:
- x = rx * sin(lat) * cos(lon);
- y = ry * sin(lat) * sin(lon);
- z = ry * cos(lat);
- break;
case 1:
x = rx * sin(lon) * cos(lat);
y = ry * sin(lon) * sin(lat);
@@ -202,6 +201,11 @@ function sphereMode(lat, lon, i, counter, rx, ry) {
y = ry * sin(lat) * sin(lat);
z = ry * cos(lon);
break;
+ default:
+ x = rx * sin(lat) * cos(lon);
+ y = ry * sin(lat) * sin(lon);
+ z = ry * cos(lat);
+ break;
}
return {x: x, y: y, z: z};
}
\ No newline at end of file
diff --git a/js/utils.js b/js/utils.js
index 68e1886..e5aa86e 100644
--- a/js/utils.js
+++ b/js/utils.js
@@ -131,7 +131,7 @@ class VTVector {
this.z += z;
}
- setXYZ(x,y,z) {
+ setXYZ(x, y, z) {
this.x = x || 0;
this.y = y || 0;
this.z = z || 0;
@@ -196,9 +196,10 @@ class TDUtils {
return dst;
}
- static xRotation(angleInRadians) {
- let c = Math.cos(angleInRadians);
- let s = Math.sin(angleInRadians);
+ static xRotation(angle) {
+ angle = TDUtils.degToRad(angle);
+ let c = Math.cos(angle);
+ let s = Math.sin(angle);
return [
1, 0, 0, 0,
@@ -208,9 +209,10 @@ class TDUtils {
];
}
- static yRotation(angleInRadians) {
- let c = Math.cos(angleInRadians);
- let s = Math.sin(angleInRadians);
+ static yRotation(angle) {
+ angle = TDUtils.degToRad(angle);
+ let c = Math.cos(angle);
+ let s = Math.sin(angle);
return [
c, 0, -s, 0,
@@ -220,9 +222,10 @@ class TDUtils {
];
}
- static zRotation(angleInRadians) {
- let c = Math.cos(angleInRadians);
- let s = Math.sin(angleInRadians);
+ static zRotation(angle) {
+ angle = TDUtils.degToRad(angle);
+ let c = Math.cos(angle);
+ let s = Math.sin(angle);
return [
c, s, 0, 0,
@@ -236,3 +239,40 @@ class TDUtils {
return d * Math.PI / 180;
}
}
+
+function $(sel, s) {
+ return $$(sel, s)[0];
+}
+
+function $$(sel, s) {
+ s = s || document;
+ return s.querySelectorAll(sel);
+}
+
+Node.prototype.addDelegatedEventListener = function (type, aim, cb) {
+ this.addEventListener(type, (event) => {
+ let target = event.target;
+ if (target.matches(aim)) {
+ cb(event, target);
+ } else {
+ let parent = target.closest(aim);
+ if (parent) {
+ cb(event, parent);
+ }
+ }
+ })
+};
+
+function create(name, content) {
+ let d = document.createElement(name);
+ if (content) {
+ d.innerHTML = content;
+ }
+ return d;
+}
+
+function append(to, array) {
+ for (let item in array) {
+ to.appendChild(array[item]);
+ }
+}
\ No newline at end of file
diff --git a/js/gui.js b/js/visualGUI.js
similarity index 72%
rename from js/gui.js
rename to js/visualGUI.js
index a16a30e..a98be8b 100644
--- a/js/gui.js
+++ b/js/visualGUI.js
@@ -1,40 +1,3 @@
-function $(sel, s) {
- return $$(sel, s)[0];
-}
-
-function $$(sel, s) {
- s = s || document;
- return s.querySelectorAll(sel);
-}
-
-Node.prototype.addDelegatedEventListener = function (type, aim, cb) {
- this.addEventListener(type, (event) => {
- let target = event.target;
- if (target.matches(aim)) {
- cb(event, target);
- } else {
- let parent = target.closest(aim);
- if (parent) {
- cb(event, parent);
- }
- }
- })
-};
-
-function create(name, content) {
- let d = document.createElement(name);
- if (content) {
- d.innerHTML = content;
- }
- return d;
-}
-
-function append(to, array) {
- for (let item in array) {
- to.appendChild(array[item]);
- }
-}
-
function generateRotationSliders() {
let group = $('#rotate');
generateSlider(["X", "Y", "Z", "X-Inc", "Y-Inc", "Z-Inc"], 0, 360, 0, group, "rotate");
@@ -43,7 +6,7 @@ function generateRotationSliders() {
function generateTranslateSliders() {
let group = $('#translate');
- generateSlider(["X", "Y", "Z"], -1, 1, 1, group, "translate");
+ generateSlider(["X", "Y", "Z"], -1, 1, 0, group, "translate");
}
function generateColorSliders() {
@@ -54,20 +17,20 @@ function generateColorSliders() {
function generateWorldSliders() {
let group = $('#world');
- generateSlider(["LightPos-X", "LightPos-Y", "LightPos-Z"], -1, 1, 1, group, "light");
- generateSlider(["LightAngle"], 0, 360, 90, group, "light");
+ generateSlider(["LightPos-X", "LightPos-Y", "LightPos-Z"], -100, 100, 0, group, "light");
+ generateSlider(["Light"], 0, 1, 0.3, group, "light");
}
function generateDrawSliders() {
let group = $('#draw'),
g = "drawMode"
generateSlider(["DrawMode"], 0, 6, 0, group, g);
- generateSlider(["Form"], 0, 3, 0, group, g);
+ generateSlider(["Form"], 0, 4, 0, group, g);
generateSlider(["Radius"], 20, 1500, 500, group, g);
- generateSlider(["Total"], 0, 200, 50, group, g);
+ generateSlider(["Total"], 20, 250, 50, group, g);
generateSlider(["PointSize"], 1, 10, 2, group, g, .2);
- generateSlider(["Smoothing"], 0, 1, 0.8, group, g, .05);
- generateSlider(["Steps"], 512, analyser.fftSize / 2, 512, group, g, 256);
+ generateSlider(["Smoothing"], 0, 100, 80, group, g, 1);
+ generateSlider(["Steps"], 256, 2048, 512, group, g, 16);
generateCheckBox(["Dirty"], false, group, g)
}
@@ -102,11 +65,10 @@ function setColors() {
function setWorld() {
let group = $('#world');
- sphereObject.lightPos[0] = getValue($('#LightPos-X', group)) * 0.2;
- sphereObject.lightPos[1] = getValue($('#LightPos-Y', group)) * 0.2;
- sphereObject.lightPos[2] = getValue($('#LightPos-Z', group)) * 0.2;
-
- lightAngle = getValue($('#LightAngle', group))
+ sphereObject.lightPos[0] = getValue($('#LightPos-X', group));
+ sphereObject.lightPos[1] = getValue($('#LightPos-Y', group));
+ sphereObject.lightPos[2] = getValue($('#LightPos-Z', group));
+ sphereObject.light = getValue($('#Light', group));
}
function setDraw() {
@@ -118,7 +80,7 @@ function setDraw() {
sphereObject.pointSize = getValue($('#PointSize', group));
sphereObject.steps = getValue($('#Steps', group));
sphereObject.dirtyMode = $('#drawModeDirty', group).checked;
- analyser.smoothingTimeConstant = getValue($('#Smoothing', group));
+ analyser.smoothingTimeConstant = getValue($('#Smoothing', group)) / 100;
}
function setTranslate() {
@@ -183,6 +145,7 @@ function changeHandler(el) {
} else if (d === "drawMode") {
setDraw();
}
+ saveConfig();
}
document.body.addDelegatedEventListener('input', 'group-input input', (ev, el) => {
@@ -205,4 +168,60 @@ function getValue(slider) {
$('.settings-icon').addEventListener('click', function () {
$('.off-can').classList.toggle("closed");
+ $('.settings-icon').classList.toggle("open");
+})
+
+window.addEventListener('keyup', e => {
+ if (e.key === 'F11') {
+ c.requestFullscreen();
+ return;
+ }
+ if (e.key === 'Escape') {
+ if (document.fullscreenElement) {
+ document.exitFullscreen().then(console.log);
+ }
+ }
+ if (e.key === 'p') {
+ audioFile.play();
+ }
+ if (e.key === 's') {
+ audioFile.pause();
+ }
+});
+
+function saveConfig() {
+ localStorage.setItem('config-sphere', JSON.stringify(sphereObject));
+}
+
+function loadConfig() {
+ let item = localStorage.getItem('config-sphere');
+ if (item && item !== "") {
+ sphereObject = JSON.parse(item);
+ }
+}
+
+let uploadField = $('#upload');
+let audioFile = new Audio();
+let lastSong = null;
+uploadField.addEventListener('change', e => {
+ let file = uploadField.files[0];
+ if (file && file.type.indexOf('audio') !== -1 && file.name.match(".m3u") === null && file.name.match(".wma") === null) {
+ if (lastSong) {
+ URL.revokeObjectURL(lastSong);
+ }
+ audioFile.src = URL.createObjectURL(file);
+ document.title = file.name;
+ lastSong = audioFile.src;
+ createView();
+ }
+})
+
+$('#play').addEventListener('click', e => {
+ if (audioFile.src !== "") {
+ c.requestFullscreen().then(r => {
+ setTimeout(x => {
+ audioFile.play();
+ }, 1000)
+ });
+ }
})
\ No newline at end of file
diff --git a/manifest.json b/manifest.json
new file mode 100755
index 0000000..b66eddf
--- /dev/null
+++ b/manifest.json
@@ -0,0 +1,21 @@
+{
+ "name": "VIS3D by VersusTuneZ",
+ "short_name": "VIS3D",
+ "icons": [
+ {
+ "src": "/favicon/android-chrome-192x192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ },
+ {
+ "src": "/favicon/android-chrome-512x512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ }
+ ],
+ "theme_color": "#3949ab",
+ "background_color": "#212121",
+ "display": "standalone",
+ "start_url": "/index.html",
+ "orientation": "landscape-primary"
+}
diff --git a/out/gui/.gitkeep b/out/gui/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/out/gui/base.json b/out/gui/base.json
new file mode 100644
index 0000000..179f195
--- /dev/null
+++ b/out/gui/base.json
@@ -0,0 +1 @@
+[{"group":"","name":"showSeekbar","showName":"Show Seekbar","type":"checkbox","value":true,"tooltip":"Showing SeekBar at the Bottom of the Screen"},{"group":"","name":"showPlaying","showName":"Show Playing","type":"checkbox","value":true,"tooltip":"Showing \"Now Playing\" Notification"},{"group":"","name":"showPlayingTime","showName":"Now Playing Display Time","type":"slider","value":1000,"max":5000,"min":500,"tooltip":"How long should the Now Playing Notification shown","stepSize":100,"dataType":"int"},{"group":"","name":"seekColor","showName":"SeekBar Color","type":"color","value":"#ffffff","dataType":"rgb","tooltip":"SeekBar Color!"}]
\ No newline at end of file
diff --git a/out/gui/sphere.json b/out/gui/sphere.json
new file mode 100644
index 0000000..3f41f2e
--- /dev/null
+++ b/out/gui/sphere.json
@@ -0,0 +1 @@
+[{"group":"rotation","name":["X","Y","Z"],"props":[0,1,2],"type":"slider","max":360,"min":0},{"group":"rotation","name":["X-Inc","Y-Inc","Z-Inc"],"props":[0,1,2],"type":"slider","max":1,"min":0,"stepSize":0.01},{"group":"rotation","name":"by-Beat","type":"checkbox","value":true,"tooltip":"Rotate the Sphere based by Beat
Low-Freq = Y, Mid-Freq = Z, High-Freq = X"},{"group":"color","name":["R","G","B"],"props":["r","g","b"],"type":"slider","max":255,"min":0,"value":255},{"group":"light","name":["X","Y","Z"],"props":[0,1,2],"type":"slider","max":1,"min":-1,"value":0,"stepSize":0.1},{"group":"light","name":"Light","type":"slider","max":1,"min":0,"stepSize":0.05},{"group":"color","name":"by-Beat","type":"checkbox","value":true,"tooltip":"Draw The Color Strength based on the Low-Freq"},{"group":"draw","name":"DrawMode","type":"slider","max":6,"min":0},{"group":"draw","name":"Form","type":"slider","max":4,"min":0},{"group":"draw","name":"Radius","type":"slider","max":1500,"min":20},{"group":"draw","name":"Total","type":"slider","max":200,"min":20},{"group":"draw","name":"PointSize","type":"slider","max":10,"min":1,"stepSize":0.2},{"group":"draw","name":"Dirty","type":"checkbox","value":false,"tooltip":"Full Draw or with less Points"}]
\ No newline at end of file
diff --git a/out/gui/wave.json b/out/gui/wave.json
new file mode 100644
index 0000000..970e15b
--- /dev/null
+++ b/out/gui/wave.json
@@ -0,0 +1 @@
+[{"group":"","name":"fftSize","showName":"FFT-Size","options":[2048,4096,8192,16384],"value":4096,"type":"select","tooltip":"How Many Items should the FFT Capture and Render","dataType":"int"},{"group":"rotation","name":["X","Y","Z"],"props":["x","y","z"],"type":"slider","max":360,"min":-360,"value":0,"dataType":"int"},{"group":"rotation","name":["X-Inc","Y-Inc","Z-Inc"],"props":["x-inc","y-inc","z-inc"],"type":"slider","max":1,"min":-1,"value":0,"stepSize":0.01,"dataType":"float"},{"group":"light","name":["X","Y","Z"],"props":["x","y","z"],"type":"slider","max":100,"min":-100,"value":0,"dataType":"int"},{"group":"light","name":"light-strength","showName":"Light Brightness","type":"slider","value":0.3,"max":1,"min":0,"stepSize":0.05,"tooltip":"brightness of light-point","dataType":"float"},{"group":"","name":"baseColor","showName":"Base Color","type":"color","value":"#0089ff","dataType":"rgb","tooltip":"Base Color!"},{"group":"","name":"gradientToColor","showName":"Second Color","type":"color","value":"#ff0000","dataType":"rgb","tooltip":"Second Color!"}]
\ No newline at end of file
diff --git a/out/gui/wave2d.json b/out/gui/wave2d.json
new file mode 100644
index 0000000..3727c67
--- /dev/null
+++ b/out/gui/wave2d.json
@@ -0,0 +1 @@
+[{"group":"","name":"fftSize","showName":"FFT-Size","options":[2048,4096,8192,16384,32768],"value":16384,"type":"select","tooltip":"How Many Items should the FFT Capture and Render","dataType":"int"},{"group":"rotation","name":["X","Y","Z"],"props":["x","y","z"],"type":"slider","max":360,"min":-360,"value":0,"dataType":"int"},{"group":"rotation","name":["X-Inc","Y-Inc","Z-Inc"],"props":["x-inc","y-inc","z-inc"],"type":"slider","max":1,"min":-1,"value":0,"stepSize":0.01,"dataType":"float"},{"group":"translate","name":["X","Y","Z"],"props":["x","y","z"],"type":"slider","max":1,"min":-1,"value":0,"stepSize":0.01,"dataType":"float"},{"group":"","name":"fudgeFactor","showName":"Fudge Factor","type":"slider","value":1,"max":2,"min":0,"tooltip":"z to w Fudge","stepSize":0.1,"dataType":"float"},{"group":"","name":"baseColor","showName":"Base Color","type":"color","value":"#0089ff","dataType":"rgb","tooltip":"Base Color!"},{"group":"","name":"gradientToColor","showName":"Second Color","type":"color","value":"#ff0000","dataType":"rgb","tooltip":"Second Color!"}]
\ No newline at end of file
diff --git a/out/icon-sprite.svg b/out/icon-sprite.svg
new file mode 100644
index 0000000..cac5044
--- /dev/null
+++ b/out/icon-sprite.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/out/js/jsmediatags.min.js b/out/js/jsmediatags.min.js
new file mode 100755
index 0000000..03dc4c8
--- /dev/null
+++ b/out/js/jsmediatags.min.js
@@ -0,0 +1,85 @@
+(function(y){"object"===typeof exports&&"undefined"!==typeof module?module.exports=y():"function"===typeof define&&define.amd?define([],y):("undefined"!==typeof window?window:"undefined"!==typeof global?global:"undefined"!==typeof self?self:this).jsmediatags=y()})(function(){return function f(p,q,m){function l(c,a){if(!q[c]){if(!p[c]){var b="function"==typeof require&&require;if(!a&&b)return b(c,!0);if(e)return e(c,!0);a=Error("Cannot find module '"+c+"'");throw a.code="MODULE_NOT_FOUND",a;}a=q[c]=
+{exports:{}};p[c][0].call(a.exports,function(a){var b=p[c][1][a];return l(b?b:a)},a,a.exports,f,p,q,m)}return q[c].exports}for(var e="function"==typeof require&&require,d=0;d
=this._array.length)throw Error("Offset "+c+" hasn't been loaded yet.");return this._array[c]}}],[{key:"canReadFile",value:function(c){return Array.isArray(c)||
+"function"===typeof Buffer&&Buffer.isBuffer(c)}}]);return d}(f("./MediaFileReader"));p.exports=f},{"./MediaFileReader":11}],4:[function(f,p,q){function m(d,c){if("function"!==typeof c&&null!==c)throw new TypeError("Super expression must either be null or a function, not "+typeof c);d.prototype=Object.create(c&&c.prototype,{constructor:{value:d,enumerable:!1,writable:!0,configurable:!0}});c&&(Object.setPrototypeOf?Object.setPrototypeOf(d,c):d.__proto__=c)}var l=function(){function d(c,a){for(var b=
+0;bb.offset&&(e=this._sliceData(b.data,0,e-b.offset),h.data=this._concatData(e,d));c&&(e=this._sliceData(h.data,0,g.offset-h.offset),h.data=this._concatData(e,g.data));this._fileData.splice(a.startIx,a.endIx-a.startIx+1,h)}}},{key:"_concatData",value:function(e,d){if("undefined"!==typeof ArrayBuffer&&ArrayBuffer.isView&&ArrayBuffer.isView(e)){var c=new e.constructor(e.length+d.length);c.set(e,0);c.set(d,e.length);return c}return e.concat(d)}},{key:"_sliceData",
+value:function(e,d,c){return e.slice?e.slice(d,c):e.subarray(d,c)}},{key:"_getChunkRange",value:function(e,d){for(var c,a,b=-1,g=-1,h=0,k=0;k=a-1){b=k;break}}if(-1===b)return{startIx:-1,endIx:-1,insertIx:h};for(k=b;k=a-1&&(g=k),d<=c+1);k++);-1===g&&(g=b);return{startIx:b,endIx:g}}},{key:"hasDataRange",
+value:function(e,d){for(var c=0;c=a.offset&&d=a&&e<=b){d=this._fileData[c];break}}if(d)return d.data[e-d.offset];throw Error("Offset "+e+" hasn't been loaded yet.");}}]);return f}();p.exports=f},{}],6:[function(f,p,q){function m(a,
+b){if("function"!==typeof b&&null!==b)throw new TypeError("Super expression must either be null or a function, not "+typeof b);a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}});b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}var l=function(){function a(a,c){for(var b=0;ba?e[a]:""}};b&&(a.tags.track=b);return a}}],[{key:"getTagIdentifierByteRange",value:function(){return{offset:-128,length:128}}},{key:"canReadTagFormat",value:function(a){return"TAG"===String.fromCharCode.apply(String,a.slice(0,3))}}]);return c}(q);var e="Blues;Classic Rock;Country;Dance;Disco;Funk;Grunge;Hip-Hop;Jazz;Metal;New Age;Oldies;Other;Pop;R&B;Rap;Reggae;Rock;Techno;Industrial;Alternative;Ska;Death Metal;Pranks;Soundtrack;Euro-Techno;Ambient;Trip-Hop;Vocal;Jazz+Funk;Fusion;Trance;Classical;Instrumental;Acid;House;Game;Sound Clip;Gospel;Noise;AlternRock;Bass;Soul;Punk;Space;Meditative;Instrumental Pop;Instrumental Rock;Ethnic;Gothic;Darkwave;Techno-Industrial;Electronic;Pop-Folk;Eurodance;Dream;Southern Rock;Comedy;Cult;Gangsta;Top 40;Christian Rap;Pop/Funk;Jungle;Native American;Cabaret;New Wave;Psychadelic;Rave;Showtunes;Trailer;Lo-Fi;Tribal;Acid Punk;Acid Jazz;Polka;Retro;Musical;Rock & Roll;Hard Rock;Folk;Folk-Rock;National Folk;Swing;Fast Fusion;Bebob;Latin;Revival;Celtic;Bluegrass;Avantgarde;Gothic Rock;Progressive Rock;Psychedelic Rock;Symphonic Rock;Slow Rock;Big Band;Chorus;Easy Listening;Acoustic;Humour;Speech;Chanson;Opera;Chamber Music;Sonata;Symphony;Booty Bass;Primus;Porn Groove;Satire;Slow Jam;Club;Tango;Samba;Folklore;Ballad;Power Ballad;Rhythmic Soul;Freestyle;Duet;Punk Rock;Drum Solo;Acapella;Euro-House;Dance Hall".split(";");
+p.exports=f},{"./MediaFileReader":11,"./MediaTagReader":12}],8:[function(f,p,q){function m(a){switch(a){case 0:a="iso-8859-1";break;case 1:a="utf-16";break;case 2:a="utf-16be";break;case 3:a="utf-8";break;default:a="iso-8859-1"}return a}function l(a,b,d,c){c=d.getStringWithCharsetAt(a+1,b-1,c);a=d.getStringWithCharsetAt(a+1+c.bytesReadCount,b-1-c.bytesReadCount);return{user_description:c.toString(),data:a.toString()}}var e=function(){function a(a,b){for(var d=0;d2.4",tags:{}};var l=a.getByteAt(g+4),m=a.isBitSetAt(g+5,7),n=a.isBitSetAt(g+5,6),x=a.isBitSetAt(g+5,5),p=a.getSynchsafeInteger32At(g+6);g+=10;if(n)if(4===f){var t=a.getSynchsafeInteger32At(g);
+g+=t}else t=a.getLongAt(g,!0),g+=t+4;t={type:"ID3",version:"2."+f+"."+l,major:f,revision:l,flags:{unsynchronisation:m,extended_header:n,experimental_indicator:x,footer_present:!1},size:p,tags:{}};c&&(b=this._expandShortcutTags(c));c=p+10;t.flags.unsynchronisation&&(a=e.getUnsyncFileReader(a,g,p),g=0,c=a.getSize());a=e.readFrames(g,c,a,t,b);for(var q in d)d.hasOwnProperty(q)&&(b=this._getFrameData(a,d[q]))&&(t.tags[q]=b);for(var z in a)a.hasOwnProperty(z)&&(t.tags[z]=a[z]);return t}},{key:"_getFrameData",
+value:function(a,c){for(var b=0,d;d=c[b];b++)if(d in a)return a=a[d]instanceof Array?a[d][0]:a[d],a.data}},{key:"getShortcuts",value:function(){return d}}],[{key:"getTagIdentifierByteRange",value:function(){return{offset:0,length:10}}},{key:"canReadTagFormat",value:function(a){return"ID3"===String.fromCharCode.apply(String,a.slice(0,3))}}]);return a}(q);var d={title:["TIT2","TT2"],artist:["TPE1","TP1"],album:["TALB","TAL"],year:["TYER","TYE"],comment:["COMM","COM"],track:["TRCK","TRK"],genre:["TCON",
+"TCO"],picture:["APIC","PIC"],lyrics:["USLT","ULT"]};p.exports=f},{"./ID3v2FrameReader":8,"./MediaFileReader":11,"./MediaTagReader":12}],10:[function(f,p,q){function m(a,b){if("function"!==typeof b&&null!==b)throw new TypeError("Super expression must either be null or a function, not "+typeof b);a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}});b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}var l=function(){function a(a,
+c){for(var b=0;b=a.getSize())d.onSuccess();else{var g=this,e=a.getLongAt(b,!0);if(0==e||isNaN(e))d.onSuccess();else{var h=a.getStringAt(b+4,4);if(this._isContainerAtom(h)){"meta"==h&&(b+=4);var f=(c?c+".":"")+h;"moov.udta.meta.ilst"===f?a.loadRange([b,b+e],d):a.loadRange([b+
+8,b+8+8],{onSuccess:function(){g._loadAtom(a,b+8,f,d)},onError:d.onError})}else a.loadRange([b+e,b+e+8],{onSuccess:function(){g._loadAtom(a,b+e,c,d)},onError:d.onError})}}}},{key:"_isContainerAtom",value:function(a){return 0<=["moov","udta","meta","ilst"].indexOf(a)}},{key:"_canReadAtom",value:function(a){return"----"!==a}},{key:"_parseData",value:function(a,b){var d={};b=this._expandShortcutTags(b);this._readAtom(d,a,0,a.getSize(),b);for(var e in c)c.hasOwnProperty(e)&&(b=d[c[e]])&&(d[e]="track"===
+e?b.data.track:b.data);return{type:"MP4",ftyp:a.getStringAt(8,4),version:a.getLongAt(12,!0),tags:d}}},{key:"_readAtom",value:function(a,b,d,c,e,f,n){n=void 0===n?"":n+" ";for(var g=d;gd&&(d+=
+65536);return d}},{key:"getSShortAt",value:function(d,c){d=this.getShortAt(d,c);return 32767c&&(c+=4294967296);return c}},{key:"getSLongAt",value:function(d,c){d=this.getLongAt(d,c);return 2147483647c&&(c+=16777216);return c}},{key:"getStringAt",value:function(d,c){for(var a=[],b=d,e=0;bf||224<=f?d[h]=String.fromCharCode(m):(f=(e[a+b]<<8)+e[a+g],a+=2,d[h]=String.fromCharCode(m,f))}return new l(d.join(""),a)},readUTF8String:function(e,d){var c=0;d=Math.min(d||e.length,e.length);239==e[0]&&187==e[1]&&191==e[2]&&(c=3);for(var a=[],b=0;cg)a[b]=String.fromCharCode(g);else if(194<=g&&224>g){var h=e[c++];a[b]=String.fromCharCode(((g&31)<<6)+(h&63))}else if(224<=g&&240>g){h=e[c++];var f=e[c++];a[b]=String.fromCharCode(((g&
+255)<<12)+((h&63)<<6)+(f&63))}else if(240<=g&&245>g){h=e[c++];f=e[c++];var m=e[c++];f=((g&7)<<18)+((h&63)<<12)+((f&63)<<6)+(m&63)-65536;a[b]=String.fromCharCode((f>>10)+55296,(f&1023)+56320)}}return new l(a.join(""),c)},readNullTerminatedString:function(e,d){var c=[];d=d||e.length;for(var a=0;ac._config.disallowedXhrHeaders.indexOf(b.toLowerCase())&&a.setRequestHeader(b,d)}},{key:"_hasResponseHeader",value:function(a,b){a=a.getAllResponseHeaders();if(!a)return!1;a=a.split("\r\n");for(var c=[],d=0;da.offset&&(-a.offset>b||0=b||c)}var e=function(){function a(a,b){for(var c=0;cg.offset&&g.offset<-e/2?c.push(r[f]):d.push(r[f]))}var h=!1;f={onSuccess:function(){if(h){for(var c=0;c max) {
+ let tmp = min;
+ min = max;
+ max = tmp;
+ }
+ return rand * (max - min) + min;
+ }
+ };
+
+ static randomInt(min, max) {
+ return Math.floor(VTUtils.random(min, max));
+ }
+
+ static normalize(val, max, min) {
+ return (val - min) / (max - min);
+ };
+
+ static distance(x, y, x2, y2) {
+ let a = x - x2;
+ let b = y - y2;
+
+ return Math.sqrt(a * a + b * b);
+ }
+
+ static map(n, start1, stop1, start2, stop2, withinBounds) {
+ let newVal = (n - start1) / (stop1 - start1) * (stop2 - start2) + start2;
+ if (!withinBounds) {
+ return newVal;
+ }
+ if (start2 < stop2) {
+ return this.constrain(newVal, start2, stop2);
+ } else {
+ return this.constrain(newVal, stop2, start2);
+ }
+ };
+
+ static constrain(n, low, high) {
+ return Math.max(Math.min(n, high), low);
+ }
+
+ static hsvToRgb(h, s, v) {
+ let r, g, b,
+ i = Math.floor(h * 6),
+ f = h * 6 - i,
+ p = v * (1 - s),
+ q = v * (1 - f * s),
+ t = v * (1 - (1 - f) * s);
+
+ switch (i % 6) {
+ case 0:
+ r = v, g = t, b = p;
+ break;
+ case 1:
+ r = q, g = v, b = p;
+ break;
+ case 2:
+ r = p, g = v, b = t;
+ break;
+ case 3:
+ r = p, g = q, b = v;
+ break;
+ case 4:
+ r = t, g = p, b = v;
+ break;
+ case 5:
+ r = v, g = p, b = q;
+ break;
+ }
+
+ return {r: r, g: g, b: b};
+ }
+
+ static peakRGB(peak) {
+ return {
+ r: peak,
+ g: 1 - peak,
+ b: 0
+ };
+ }
+}
+
+class VTVector {
+ constructor(x, y, z) {
+ this.x = x || 0;
+ this.y = y || 0;
+ this.z = z || 0;
+ }
+
+ //helper
+ static createRandom(x, y, z) {
+ x = x || 1;
+ y = y || 1;
+ z = z || 0;
+ return new VTVector(VTUtils.random(-x, x), VTUtils.random(-y, y), VTUtils.random(-z, z));
+ }
+
+ mult(times) {
+ this.x *= times;
+ this.y *= times;
+ this.z *= times;
+ }
+
+ set(vector) {
+ this.x = vector.x;
+ this.y = vector.y;
+ this.z = vector.z;
+ }
+
+ add(vector) {
+ this.x = this.x + vector.x;
+ this.y = this.y + vector.y;
+ this.z = this.z + vector.z;
+ }
+
+ addXYZ(x, y, z) {
+ this.x += x;
+ this.y += y;
+ this.z += z;
+ }
+
+ setXYZ(x, y, z) {
+ this.x = x || 0;
+ this.y = y || 0;
+ this.z = z || 0;
+ }
+
+ clone() {
+ return new VTVector(this.x, this.y, this.z);
+ }
+}
+
+function $(sel, s) {
+ s = s || document;
+ return s.querySelector(sel);
+}
+
+function $$(sel, s) {
+ s = s || document;
+ return s.querySelectorAll(sel);
+}
+
+Node.prototype.addDelegatedEventListener = function (type, aim, cb) {
+ this.addEventListener(type, (event) => {
+ let target = event.target;
+ if (target.matches(aim)) {
+ cb(event, target);
+ } else {
+ let parent = target.closest(aim);
+ if (parent) {
+ try {
+ cb(event, parent);
+ } catch (e) {
+ NotificationHandler.createNotification("FATAL ERROR WITHIN HANDLER!", "error", 1000);
+ //nothing!
+ }
+ }
+ }
+ })
+};
+
+Node.prototype.hasClass = function (className) {
+ let items = className.split(','),
+ has = null;
+ for (let item of items) {
+ if (has === false) {
+ break;
+ }
+ has = this.classList.contains(item.trim());
+ }
+ return has === true;
+}
+Node.prototype.addClass = function (className) {
+ let items = className.split(',');
+ for (let item of items) {
+ this.classList.add(item.trim());
+ }
+ return this;
+}
+Node.prototype.removeClass = function (className) {
+ let items = className.split(',');
+ for (let item of items) {
+ this.classList.remove(item.trim());
+ }
+ return this;
+}
+Node.prototype.toggleClass = function (className, force) {
+ let items = className.split(',');
+ for (let item of items) {
+ this.classList.toggle(item.trim(), force);
+ }
+}
+Node.prototype.switchClass = function (clOne, clTwo, twoOne) {
+ let cl = this.classList;
+ if (twoOne) {
+ cl.remove(clOne);
+ cl.add(clTwo)
+ } else {
+ cl.remove(clTwo)
+ cl.add(clOne)
+ }
+}
+Node.prototype.toggleCheck = function (className, force) {
+ let cl = this.classList;
+ let items = className.split(',');
+ for (let item of items) {
+ let clOne = item.trim();
+ if (force) {
+ cl.add(clOne);
+ } else {
+ cl.remove(clOne)
+ }
+ }
+}
+
+String.prototype.firstUpper = function () {
+ return this.charAt(0).toUpperCase() + this.slice(1);
+}
+
+File.prototype.toBase64 = function (cb) {
+ const reader = new FileReader();
+ reader.onloadend = cb;
+ reader.readAsDataURL(this);
+}
+
+function b64toBlob(b64Data, type) {
+ const byteCharacters = atob(b64Data);
+ const byteNumbers = new Array(byteCharacters.length);
+ for (let i = 0; i < byteCharacters.length; i++) {
+ byteNumbers[i] = byteCharacters.charCodeAt(i);
+ }
+ const byteArray = new Uint8Array(byteNumbers);
+ return new Blob([byteArray], {type: type});
+}
+
+function create(name, content) {
+ let d = document.createElement(name);
+ if (content) {
+ d.innerHTML = content;
+ }
+ return d;
+}
+
+function append(to, array) {
+ for (let item of array) {
+ to.appendChild(item);
+ }
+}
+
+function hexToRgb(hex) {
+ hex = hex.replace("#", "");
+ let bigint = parseInt(hex, 16),
+ r = (bigint >> 16) & 255,
+ g = (bigint >> 8) & 255,
+ b = bigint & 255;
+
+ return [r / 255, g / 255, b / 255];
+}
+// most of the functions are from https://webglfundamentals.org/webgl/resources/m4.js! but i doesnt want to use them all and make some adjustment to them!
+class TDUtils {
+ static lastMatrix = {m: null};
+
+ static multiply(a, b) {
+ let b00 = b[0];
+ let b01 = b[1];
+ let b02 = b[2];
+ let b03 = b[3];
+ let b10 = b[4];
+ let b11 = b[5];
+ let b12 = b[6];
+ let b13 = b[7];
+ let b20 = b[8];
+ let b21 = b[9];
+ let b22 = b[10];
+ let b23 = b[11];
+ let b30 = b[12];
+ let b31 = b[13];
+ let b32 = b[14];
+ let b33 = b[15];
+ let a00 = a[0];
+ let a01 = a[1];
+ let a02 = a[2];
+ let a03 = a[3];
+ let a10 = a[4];
+ let a11 = a[5];
+ let a12 = a[6];
+ let a13 = a[7];
+ let a20 = a[8];
+ let a21 = a[9];
+ let a22 = a[10];
+ let a23 = a[11];
+ let a30 = a[12];
+ let a31 = a[13];
+ let a32 = a[14];
+ let a33 = a[15];
+ return [
+ b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30,
+ b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31,
+ b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32,
+ b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33,
+ b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30,
+ b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31,
+ b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32,
+ b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33,
+ b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30,
+ b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31,
+ b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32,
+ b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33,
+ b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30,
+ b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31,
+ b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32,
+ b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33
+ ];
+ }
+
+ static translate(m, tx, ty, tz, dst) {
+ dst = dst || new Float32Array(16);
+
+ let m00 = m[0],
+ m01 = m[1],
+ m02 = m[2],
+ m03 = m[3],
+ m10 = m[4],
+ m11 = m[5],
+ m12 = m[6],
+ m13 = m[7],
+ m20 = m[8],
+ m21 = m[9],
+ m22 = m[10],
+ m23 = m[11],
+ m30 = m[12],
+ m31 = m[13],
+ m32 = m[14],
+ m33 = m[15];
+ dst[0] = m00;
+ dst[1] = m01;
+ dst[2] = m02;
+ dst[3] = m03;
+ dst[4] = m10;
+ dst[5] = m11;
+ dst[6] = m12;
+ dst[7] = m13;
+ dst[8] = m20;
+ dst[9] = m21;
+ dst[10] = m22;
+ dst[11] = m23;
+
+ dst[12] = m00 * tx + m10 * ty + m20 * tz + m30;
+ dst[13] = m01 * tx + m11 * ty + m21 * tz + m31;
+ dst[14] = m02 * tx + m12 * ty + m22 * tz + m32;
+ dst[15] = m03 * tx + m13 * ty + m23 * tz + m33;
+
+ return dst;
+ }
+
+ static xRotation(angle) {
+ angle = TDUtils.degToRad(angle);
+ let c = Math.cos(angle);
+ let s = Math.sin(angle);
+
+ return [
+ 1, 0, 0, 0,
+ 0, c, s, 0,
+ 0, -s, c, 0,
+ 0, 0, 0, 1,
+ ];
+ }
+
+ static yRotation(angle) {
+ angle = TDUtils.degToRad(angle);
+ let c = Math.cos(angle);
+ let s = Math.sin(angle);
+
+ return [
+ c, 0, -s, 0,
+ 0, 1, 0, 0,
+ s, 0, c, 0,
+ 0, 0, 0, 1,
+ ];
+ }
+
+ static zRotation(angle) {
+ angle = TDUtils.degToRad(angle);
+ let c = Math.cos(angle);
+ let s = Math.sin(angle);
+
+ return [
+ c, s, 0, 0,
+ -s, c, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1,
+ ];
+ }
+
+ static degToRad(d) {
+ return d * Math.PI / 180;
+ }
+
+ static scale(sx, sy, sz, dst) {
+ dst = dst || new Float32Array(16);
+ dst[0] = sx;
+ dst[5] = sy;
+ dst[10] = sz;
+ return dst;
+ }
+
+ static lookAt(cameraPosition, target, up, dst) {
+ dst = dst || new Float32Array(16);
+ let zAxis = TDUtils.normalize(
+ TDUtils.subtractVectors(cameraPosition, target));
+ let xAxis = TDUtils.normalize(TDUtils.cross(up, zAxis));
+ let yAxis = TDUtils.normalize(TDUtils.cross(zAxis, xAxis));
+
+ dst[0] = xAxis[0];
+ dst[1] = xAxis[1];
+ dst[2] = xAxis[2];
+ dst[4] = yAxis[0];
+ dst[5] = yAxis[1];
+ dst[6] = yAxis[2];
+ dst[8] = zAxis[0];
+ dst[9] = zAxis[1];
+ dst[10] = zAxis[2];
+ dst[12] = cameraPosition[0];
+ dst[13] = cameraPosition[1];
+ dst[14] = cameraPosition[2];
+ dst[15] = 1;
+
+ return dst;
+ }
+
+ static cross(a, b, dst) {
+ dst = dst || new Float32Array(3);
+ dst[0] = a[1] * b[2] - a[2] * b[1];
+ dst[1] = a[2] * b[0] - a[0] * b[2];
+ dst[2] = a[0] * b[1] - a[1] * b[0];
+ return dst;
+ }
+
+ static normalize(v, dst) {
+ dst = dst || new Float32Array(3);
+ let length = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
+ if (length > 0.00001) {
+ dst[0] = v[0] / length;
+ dst[1] = v[1] / length;
+ dst[2] = v[2] / length;
+ }
+ return dst;
+ }
+
+ static subtractVectors(a, b, dst) {
+ dst = dst || new Float32Array(3);
+ dst[0] = a[0] - b[0];
+ dst[1] = a[1] - b[1];
+ dst[2] = a[2] - b[2];
+ return dst;
+ }
+
+ static perspective(fieldOfViewInRadians, aspect, near, far, dst) {
+ dst = dst || new Float32Array(16);
+ let f = Math.tan(Math.PI * 0.5 - 0.5 * fieldOfViewInRadians),
+ rangeInv = 1.0 / (near - far);
+
+ dst[0] = f / aspect;
+ dst[5] = f;
+ dst[10] = (near + far) * rangeInv;
+ dst[11] = -1;
+ dst[14] = near * far * rangeInv * 2;
+ return dst;
+ }
+
+ static projection(width, height, depth) {
+ return [
+ 2 / width, 0, 0, 0,
+ 0, -2 / height, 0, 0,
+ 0, 0, 2 / depth, 0,
+ -1, 1, 0, 1,
+ ];
+ }
+
+ static inverse(m, dst) {
+ dst = dst || new Float32Array(16);
+ let m00 = m[0],
+ m01 = m[1],
+ m02 = m[2],
+ m03 = m[3],
+ m10 = m[4],
+ m11 = m[5],
+ m12 = m[6],
+ m13 = m[7],
+ m20 = m[8],
+ m21 = m[9],
+ m22 = m[10],
+ m23 = m[11],
+ m30 = m[12],
+ m31 = m[13],
+ m32 = m[14],
+ m33 = m[15],
+ tmp_0 = m22 * m33,
+ tmp_1 = m32 * m23,
+ tmp_2 = m12 * m33,
+ tmp_3 = m32 * m13,
+ tmp_4 = m12 * m23,
+ tmp_5 = m22 * m13,
+ tmp_6 = m02 * m33,
+ tmp_7 = m32 * m03,
+ tmp_8 = m02 * m23,
+ tmp_9 = m22 * m03,
+ tmp_10 = m02 * m13,
+ tmp_11 = m12 * m03,
+ tmp_12 = m20 * m31,
+ tmp_13 = m30 * m21,
+ tmp_14 = m10 * m31,
+ tmp_15 = m30 * m11,
+ tmp_16 = m10 * m21,
+ tmp_17 = m20 * m11,
+ tmp_18 = m00 * m31,
+ tmp_19 = m30 * m01,
+ tmp_20 = m00 * m21,
+ tmp_21 = m20 * m01,
+ tmp_22 = m00 * m11,
+ tmp_23 = m10 * m01,
+
+ t0 = (tmp_0 * m11 + tmp_3 * m21 + tmp_4 * m31) -
+ (tmp_1 * m11 + tmp_2 * m21 + tmp_5 * m31),
+ t1 = (tmp_1 * m01 + tmp_6 * m21 + tmp_9 * m31) -
+ (tmp_0 * m01 + tmp_7 * m21 + tmp_8 * m31),
+ t2 = (tmp_2 * m01 + tmp_7 * m11 + tmp_10 * m31) -
+ (tmp_3 * m01 + tmp_6 * m11 + tmp_11 * m31),
+ t3 = (tmp_5 * m01 + tmp_8 * m11 + tmp_11 * m21) -
+ (tmp_4 * m01 + tmp_9 * m11 + tmp_10 * m21),
+
+ d = 1.0 / (m00 * t0 + m10 * t1 + m20 * t2 + m30 * t3);
+
+ dst[0] = d * t0;
+ dst[1] = d * t1;
+ dst[2] = d * t2;
+ dst[3] = d * t3;
+ dst[4] = d * ((tmp_1 * m10 + tmp_2 * m20 + tmp_5 * m30) -
+ (tmp_0 * m10 + tmp_3 * m20 + tmp_4 * m30));
+ dst[5] = d * ((tmp_0 * m00 + tmp_7 * m20 + tmp_8 * m30) -
+ (tmp_1 * m00 + tmp_6 * m20 + tmp_9 * m30));
+ dst[6] = d * ((tmp_3 * m00 + tmp_6 * m10 + tmp_11 * m30) -
+ (tmp_2 * m00 + tmp_7 * m10 + tmp_10 * m30));
+ dst[7] = d * ((tmp_4 * m00 + tmp_9 * m10 + tmp_10 * m20) -
+ (tmp_5 * m00 + tmp_8 * m10 + tmp_11 * m20));
+ dst[8] = d * ((tmp_12 * m13 + tmp_15 * m23 + tmp_16 * m33) -
+ (tmp_13 * m13 + tmp_14 * m23 + tmp_17 * m33));
+ dst[9] = d * ((tmp_13 * m03 + tmp_18 * m23 + tmp_21 * m33) -
+ (tmp_12 * m03 + tmp_19 * m23 + tmp_20 * m33));
+ dst[10] = d * ((tmp_14 * m03 + tmp_19 * m13 + tmp_22 * m33) -
+ (tmp_15 * m03 + tmp_18 * m13 + tmp_23 * m33));
+ dst[11] = d * ((tmp_17 * m03 + tmp_20 * m13 + tmp_23 * m23) -
+ (tmp_16 * m03 + tmp_21 * m13 + tmp_22 * m23));
+ dst[12] = d * ((tmp_14 * m22 + tmp_17 * m32 + tmp_13 * m12) -
+ (tmp_16 * m32 + tmp_12 * m12 + tmp_15 * m22));
+ dst[13] = d * ((tmp_20 * m32 + tmp_12 * m02 + tmp_19 * m22) -
+ (tmp_18 * m22 + tmp_21 * m32 + tmp_13 * m02));
+ dst[14] = d * ((tmp_18 * m12 + tmp_23 * m32 + tmp_15 * m02) -
+ (tmp_22 * m32 + tmp_14 * m02 + tmp_19 * m12));
+ dst[15] = d * ((tmp_22 * m22 + tmp_16 * m02 + tmp_21 * m12) -
+ (tmp_20 * m12 + tmp_23 * m22 + tmp_17 * m02));
+
+ return dst;
+ }
+
+ static aspectView(aspect) {
+ return [
+ 1 / aspect, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1
+ ]
+ }
+
+ static updateRotate(rotation, def) {
+ let value = vConf.get(rotation, def) + vConf.get(rotation + '-inc', 0)
+ if (value > 360) {
+ value -= 360;
+ } else if (value < -360) {
+ value += 360;
+ }
+ vConf.set(rotation, value);
+ }
+
+ static makeZToWMatrix(fudgeFactor) {
+ return [
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, fudgeFactor,
+ 0, 0, 0, 1,
+ ];
+ }
+}
+
+class GLHelper {
+ constructor(program) {
+ this.matrix = new Float32Array(16);
+ this.program = program;
+ }
+
+ static uniform4fv(program, name, data) {
+ let uniform = gl.getUniformLocation(program, name);
+ gl.uniform4fv(uniform, data);
+ }
+
+ static uniform3fv(program, name, data) {
+ let uniform = gl.getUniformLocation(program, name);
+ gl.uniform3fv(uniform, data);
+ }
+
+ static uniform1f(program, name, data) {
+ let uniform = gl.getUniformLocation(program, name);
+ gl.uniform1f(uniform, data);
+ }
+
+ rotateX(deg) {
+ this.matrix = TDUtils.multiply(this.matrix, TDUtils.xRotation(deg));
+ }
+
+ rotateY(deg) {
+ this.matrix = TDUtils.multiply(this.matrix, TDUtils.yRotation(deg));
+ }
+
+ rotateZ(deg) {
+ this.matrix = TDUtils.multiply(this.matrix, TDUtils.zRotation(deg));
+ }
+
+ scale(scaling) {
+ this.matrix = TDUtils.multiply(this.matrix, TDUtils.scale(scaling[0], scaling[1], scaling[2]))
+ }
+
+ project(depth) {
+ depth = depth || (c.width > c.height) ? c.width : c.height;
+ this.matrix = TDUtils.projection(c.width, c.height, depth)
+ }
+
+ translate(t) {
+ this.matrix = TDUtils.translate(this.matrix, t[0] || 0, t[1] || 0, t[2] || 0);
+ }
+
+ addFudgeFactor(fudgeFactor) {
+ this.matrix = TDUtils.multiply(TDUtils.makeZToWMatrix(fudgeFactor), this.matrix);
+ }
+
+ applyMatrix() {
+ let matrix = gl.getUniformLocation(this.program, "u_matrix");
+ gl.uniformMatrix4fv(matrix, false, this.matrix);
+ }
+}
+class Camera {
+ constructor() {
+ this.mouse;
+ this.rotation = {
+ x: 0,
+ y: 0
+ }
+ this.lastMouse;
+ this.mousePressed = false;
+ this.translate = {
+ x: 0,
+ y: 0,
+ z: 0
+ }
+ }
+
+ async init() {
+ this.mouse = {
+ x: 0,
+ y: 0
+ }
+ window.addEventListener('mousedown', this.mouseDown.bind(this));
+ window.addEventListener('mouseup', this.mouseUp.bind(this));
+ window.addEventListener('mousemove', this.mouseMove.bind(this), {passive: true});
+ eventHandler.addEvent('keys-ArrowUp, keys-ArrowDown, keys-ArrowLeft, keys-ArrowRight, keys-KeyQ, keys-KeyE', this.keyPressed.bind(this));
+ }
+
+ mouseDown() {
+ this.mousePressed = true;
+ this.lastMouse = null;
+ }
+
+ mouseUp() {
+ this.mousePressed = false;
+ this.lastMouse = null;
+ }
+
+ mouseMove(event) {
+ if (!this.mousePressed || gui.modal.open) {
+ return;
+ }
+ if (this.lastMouse) {
+ let mouse = this.mouse,
+ rotate = this.rotation;
+ mouse.x += (this.lastMouse.x - event.clientX) * 0.2;
+ mouse.y += (this.lastMouse.y - event.clientY) * 0.2;
+ rotate.x = VTUtils.map(mouse.x, -c.width, c.width, 180, -180, false);
+ rotate.y = VTUtils.map(mouse.y, -c.height, c.height, 180, -180, false);
+ }
+ this.lastMouse = {
+ x: event.clientX,
+ y: event.clientY
+ }
+ }
+
+ keyPressed(data) {
+ switch (data) {
+ case 'keys-ArrowUp':
+ this.translate.z += 10;
+ break;
+ case 'keys-ArrowDown':
+ this.translate.z -= 10;
+ break;
+ case 'keys-ArrowLeft':
+ this.translate.x -= 10;
+ break;
+ case 'keys-ArrowRight':
+ this.translate.x += 10;
+ break;
+ case 'keys-KeyQ':
+ this.translate.y += 10;
+ break;
+ case 'keys-KeyE':
+ this.translate.y -= 10;
+ break;
+ }
+ }
+}
+class Template {
+ constructor() {
+ this.tpl = {};
+ }
+
+ async loadTemplate(name) {
+ let self = this;
+ if (!this.tpl[name]) {
+ self.tpl[name] = await FetchHandler.loadFile(templateDir + name + '.tpl', false)
+ }
+ }
+
+ async loadArray(names) {
+ for (let name of names) {
+ await this.loadTemplate(name);
+ }
+ }
+
+ parseTemplate(name, data) {
+ if (!this.tpl[name]) {
+ return ""
+ }
+ let m, d = this.tpl[name];
+ while ((m = templateEx.exec(d)) !== null) {
+ if (m.index === templateEx.lastIndex) {
+ templateEx.lastIndex++;
+ }
+ let key = m[0];
+ let value = data[m[1]];
+ if (value === undefined || value === null) {
+ value = "";
+ }
+ d = d.replace(key, value)
+ }
+ return d;
+ }
+}
+
+const templateEx = /\$(.*?)\$/gm;
+const templateDir = "/out/tpl/"
+class ShaderHandler {
+ constructor(gl) {
+ this.gl = gl;
+ this.shaderNames = [];
+ this.shaders = {};
+ this.programs = {};
+ }
+
+ setGL(gl) {
+ this.gl = gl;
+ }
+
+ async loadShader(name, path) {
+ this.shaderNames.push(name);
+ await this.load(name, path + name + ".vert", this.gl.VERTEX_SHADER);
+ await this.load(name, path + name + ".frag", this.gl.FRAGMENT_SHADER);
+ }
+
+ async load(name, url, type) {
+ let realName = name + "_" + type;
+ if (!this.shaders[realName]) {
+ let data = await FetchHandler.loadFile(url, false);
+ let shader = this.createShader(data, type);
+ if (shader) {
+ this.shaders[realName] = shader;
+ }
+ }
+ return !!this.shaders[realName];
+ }
+
+ getShader(name, type) {
+ let realName = name + "_" + type;
+ return this.shaders[realName];
+ }
+
+ getAllShaders() {
+ return this.shaderNames;
+ }
+
+ async createProgramForEach(arr) {
+ arr = arr || this.shaderNames;
+ for (let i = 0; i < arr.length; i++) {
+ let shader = arr[i];
+ let v = await shaderHandler.createProgram(shader, [shader])
+ if (!v) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ createShader(source, type) {
+ let gl = this.gl;
+ let shader = gl.createShader(type);
+ gl.shaderSource(shader, source);
+ gl.compileShader(shader);
+ if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
+ return shader;
+ }
+ console.error(gl.getShaderInfoLog(shader));
+ gl.deleteShader(shader);
+ return null;
+ }
+
+ createProgram(name, shaders) {
+ let gl = this.gl;
+ let pro = gl.createProgram();
+ for (let i = 0; i < shaders.length; i++) {
+ gl.attachShader(pro, this.getShader(shaders[i], gl.VERTEX_SHADER));
+ gl.attachShader(pro, this.getShader(shaders[i], gl.FRAGMENT_SHADER));
+ }
+ gl.linkProgram(pro);
+ if (gl.getProgramParameter(pro, gl.LINK_STATUS)) {
+ this.programs[name] = pro;
+ return pro;
+ }
+
+ console.log(gl.getProgramInfoLog(pro));
+ gl.deleteProgram(pro);
+ return null;
+ }
+
+ getProgram(name) {
+ return this.programs[name];
+ }
+
+ use(name) {
+ let pro = this.programs[name];
+ this.gl.useProgram(pro);
+ return pro;
+ }
+
+ async loadArray(list, path) {
+ let self = this;
+ for (const e of list) {
+ await self.loadShader(e, path)
+ }
+ await self.createProgramForEach(list)
+ }
+}
+const AudioContext = window.AudioContext || window.webkitAudioContext;
+
+class AudioHandler {
+ async init() {
+ let self = this;
+ self.isStarted = false;
+ self.audioFile = new Audio();
+ self.actx = new AudioContext();
+ self.analyser = self.actx.createAnalyser();
+ self.analyser.fftSize = 4096;
+ self.lastSong = null;
+ await self.connectAll();
+ }
+
+ async connectAll() {
+ let self = this;
+ self.source = self.actx.createMediaElementSource(self.audioFile);
+ self.source.connect(self.analyser);
+ self.analyser.connect(self.actx.destination);
+ self.audioFile.addEventListener('ended', player.nextSong.bind(player));
+ }
+
+ async start() {
+ if (this.audioFile.src === '') {
+ return;
+ }
+ if (!this.isStarted) {
+ this.isStarted = true;
+ await this.actx.resume();
+ }
+ }
+
+ async stop() {
+ if (this.isStarted) {
+ this.isStarted = false;
+ await this.actx.suspend();
+ }
+ }
+
+ fftSize(size) {
+ this.analyser.fftSize = size;
+ }
+
+ smoothing(float) {
+ this.analyser.smoothingTimeConstant = float;
+ }
+
+ loadSong(file) {
+ if (!file) {
+ NotificationHandler.createNotification("Sorry! Currently no Song is uploaded!", "error", 2000);
+ return false;
+ }
+ let self = this,
+ src = file.file;
+ if (self.lastSong) {
+ URL.revokeObjectURL(self.lastSong);
+ }
+ self.lastSong = this.audioFile.src = URL.createObjectURL(src);
+ if (!this.isStarted) {
+ this.start().catch(alert);
+ }
+ this.audioFile.play().then(e => {
+ if (pConf.get("showPlaying", "true")) {
+ NotificationHandler.createNotification("Now Playing: " + file.getAudioName(), "info", pConf.get("showPlayingTime", 1000));
+ }
+ window.dispatchEvent(new CustomEvent('playSong'));
+ }).catch(e => {
+ NotificationHandler.createNotification(e.message, "error", 1000);
+ player.nextSong();
+ });
+ }
+
+ getIntArray(steps) {
+ let dataArray = new Uint8Array(steps);
+ this.analyser.getByteFrequencyData(dataArray);
+ return dataArray;
+ }
+
+ getFloatArray() {
+ let dataArray = new Float32Array(this.analyser.fftSize);
+ this.analyser.getFloatTimeDomainData(dataArray);
+ return dataArray;
+ }
+}
+// Handler around the Playlist file to keep track on the ID3 and more!
+class AudioPlayerFile {
+ constructor(file, index) {
+ this.file = file;
+ this.name = this.getName();
+ this.id3 = null;
+ this.index = index;
+ }
+
+ getName() {
+ let name = this.file.name.split(".");
+ name.pop();
+ name = name.join(".");
+ return name;
+ }
+
+ getID3Tag(force) {
+ if (!force && this.id3 !== null) {
+ return this.id3;
+ }
+ eventHandler.sendData('getData', {
+ file: this.file,
+ name: this.name,
+ index: this.index,
+ force: force === true
+ });
+ return {
+ title: this.name,
+ artist: 'VA',
+ }
+ }
+
+ getAudioName() {
+ let tag = this.getID3Tag();
+ return template.parseTemplate('audio-information', tag);
+ }
+}
+class FetchHandler {
+ static files = {};
+
+ static async loadFiles(array, isJSON) {
+ let content = [];
+ for (let i = 0; i < array; i++) {
+ content.push(await FetchHandler.loadFile(array[i], isJSON));
+ }
+ return content;
+ }
+
+ static async loadFile(filename, isJSON) {
+ filename += '?v=' + version;
+ let files = FetchHandler.files;
+ if (files[filename]) {
+ return files[filename];
+ }
+ let data = await FetchHandler.tryFromCache(filename);
+ if (isJSON) {
+ data = JSON.parse(data);
+ }
+ files[filename] = data;
+ return data;
+ }
+
+ static async tryFromCache(filename) {
+ if (caches) {
+ let cache = await caches.open('vis3d-pwa-1');
+ let data = await cache.match(filename);
+ if (!data) {
+ data = await fetch(filename);
+ }
+ return await data.text();
+ }
+ }
+}
+class PlayerConfigHandler {
+ async init() {
+ await template.loadArray([
+ 'config/nav',
+ 'config/content',
+ 'config/visualitem'
+ ]);
+ this.last = 'base';
+ $('.settings-icon').addEventListener('click', this.open.bind(this));
+ $('modal-content').addDelegatedEventListener('click', '.config-nav .item', this.navHandler.bind(this));
+ }
+
+ open() {
+ if (this.content === undefined) {
+ let content = template.parseTemplate('config/nav', {});
+ content += template.parseTemplate('config/content', {content: ""});
+ this.content = content;
+ }
+ gui.modal.renderModal('Settings', this.content, "by VersusTuneZ");
+ this.handleById();
+ gui.modal.showModal();
+ }
+
+ navHandler(e, el) {
+ this.last = el.dataset.id;
+ this.handleById();
+ }
+
+ handleById() {
+ let id = this.last;
+ new VisualConfig(id === 'visual', id === 'base');
+ let active = $('.config-nav .item.active'),
+ current = $('.config-nav .item[data-id="' + id + '"]');
+ if (active) {
+ active.removeClass('active');
+ }
+ if (current) {
+ current.addClass('active');
+ }
+ }
+}
+
+class VisualConfig {
+ static visualTemplates = {};
+
+ constructor(showVisual, renderBase) {
+ this.content = $('modal-content .config-content');
+ if (showVisual) {
+ this.renderVisualConfig(visual.c);
+ } else {
+ if (renderBase) {
+ this.renderBase();
+ } else {
+ this.renderVisuals();
+ }
+ }
+ }
+
+ renderVisuals() {
+ let keys = Object.keys(visual.visuals),
+ content = '';
+ for (let i = 0; i < keys.length; i++) {
+ content += template.parseTemplate('config/visualitem', {
+ title: visual.visuals[keys[i]].name,
+ id: keys[i],
+ active: keys[i] === visual.c ? 'active' : ''
+ })
+ }
+ content += ' ';
+ this.content.innerHTML = content;
+ }
+
+ async renderBase() {
+ let data = await this.loadVisualConfig('base'),
+ div = create('section');
+ div.addClass('base');
+ div.innerHTML = GUIHelper.fromJSON(data, pConf);
+ this.content.innerHTML = div.outerHTML;
+ }
+
+ // the name loads the json and handle it!
+ async renderVisualConfig(name) {
+ let data = await this.loadVisualConfig(name, vConf),
+ div = create('section');
+ div.addClass('visual');
+ div.innerHTML = GUIHelper.fromJSON(data, vConf);
+ div.innerHTML += GUIHelper.createButton({
+ action: "resetVConf",
+ name: "Reset Visual Config"
+ });
+ div.innerHTML += GUIHelper.createButton({
+ action: "makeModalTransparent",
+ name: "toggle Modal Opacity"
+ })
+ this.content.innerHTML = div.outerHTML;
+ }
+
+ async loadVisualConfig(name) {
+ let tem = VisualConfig.visualTemplates;
+ if (!tem[name]) {
+ //load config and save it
+ tem[name] = await FetchHandler.loadFile('/out/gui/' + name + ".json", true);
+ }
+ return tem[name];
+ }
+}
+
+class Player {
+ async init() {
+ this.playlist = new Playlist();
+ }
+
+ nextSong() {
+ let next = this.playlist.getNext();
+ audioHandler.loadSong(next);
+ }
+
+ prevSong() {
+ let next = this.playlist.getPrevious();
+ audioHandler.loadSong(next);
+ }
+
+ playStop() {
+ if (!audioHandler.lastSong) {
+ let next = this.playlist.getCurrent();
+ audioHandler.loadSong(next);
+ return;
+ }
+ let audioFile = audioHandler.audioFile;
+ if (audioFile.paused) {
+ audioFile.play();
+ } else {
+ audioFile.pause();
+ }
+ window.dispatchEvent(new CustomEvent('playSong'));
+ }
+
+ stop() {
+ if (!audioHandler.lastSong) {
+ return;
+ }
+ let audioFile = audioHandler.audioFile;
+ audioFile.pause();
+ audioFile.currentTime = 0;
+ window.dispatchEvent(new CustomEvent('playSong'));
+ }
+
+ playByID(number) {
+ this.playlist.index = number;
+ let next = this.playlist.getCurrent();
+ audioHandler.loadSong(next);
+ }
+}
+
+const PAGINATIONLIMIT = 50;
+
+class Playlist {
+ constructor() {
+ this.list = [];
+ this.shuffled = [];
+ this.index = 0;
+ this.page = 0;
+ this.isShuffle = pConf.get("shuffle", false);
+ $('body').addDelegatedEventListener('change', 'input[type="file"]', this.changeFiles.bind(this));
+ $('body').addDelegatedEventListener('click', '.pagination .item', this.handlePagination.bind(this));
+ eventHandler.addEvent('id3-request', this.handle.bind(this));
+ eventHandler.addEvent('id3-request-force', this.forceID3.bind(this));
+ }
+
+ shuffle() {
+ // only shuffle if more then 2 elements are in
+ let len = this.list.length;
+ if (len < 3) {
+ this.shuffled = [0, 1, 2];
+ return;
+ }
+ // the current-list need to be shuffled...
+ for (let i = 0; i < len; i++) {
+ let random = VTUtils.randomInt(0, len - 1);
+ this.swap(i, random);
+ }
+ }
+
+ swap(a, b) {
+ this.shuffled[a] = b;
+ this.shuffled[b] = a;
+ }
+
+ getNext() {
+ let items = this.list,
+ len = items.length - 1,
+ next = this.index + 1;
+ if (next > len) {
+ next = 0;
+ }
+ this.index = next;
+ return items[this.getRealIndex()];
+ }
+
+ getPrevious() {
+ let items = this.list,
+ len = items.length - 1,
+ next = this.index - 1;
+ if (next < 0) {
+ next = len;
+ }
+ this.index = next;
+ return items[this.getRealIndex()];
+ }
+
+ getCurrent() {
+ return this.list[this.getRealIndex()];
+ }
+
+ // on new upload... this has to be an array!
+ setPlaylist(files) {
+ this.index = 0;
+ this.forceData = undefined;
+ this.list = files;
+ this.shuffle();
+ }
+
+ handlePagination(event, el) {
+ if (el.hasClass('inactive')) {
+ return;
+ }
+ if (el.hasClass('next-site')) {
+ this.renderPagination(this.page + 1);
+ } else {
+ this.renderPagination(this.page - 1);
+ }
+ }
+
+ renderPagination(page) {
+ if (page === undefined) {
+ page = this.page;
+ }
+ let length = this.list.length,
+ maxSite = Math.ceil(length / PAGINATIONLIMIT) - 1;
+ if (page < 0) {
+ page = 0;
+ }
+ if (page > maxSite) {
+ page = maxSite;
+ }
+ let s = page * PAGINATIONLIMIT,
+ e = s + PAGINATIONLIMIT,
+ data = "";
+ this.page = page;
+ if (e >= length) {
+ e = length;
+ }
+ if (length > 0) {
+ let items = this.list;
+ for (let i = s; i < e; i++) {
+ let obj = {
+ index: i.toString(),
+ nr: i + 1,
+ title: items[this.getRealIndex(i)].getAudioName(),
+ active: !audioHandler.audioFile.paused && i === this.index ? 'active' : ''
+ }
+ data += template.parseTemplate("playlist-item", obj);
+ }
+ } else {
+ data = "