This commit is contained in:
VersusTune 2020-04-07 21:44:46 +02:00
commit d1ae2059f7
39 changed files with 1735 additions and 428 deletions

View file

@ -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;
}
}

View file

@ -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();
}
})
});
})();
isInit = true;
}
}
function initGUI() {
generateRotationSliders();
generateColorSliders();
generateWorldSliders();
generateDrawSliders();
generateTranslateSliders();
}
(function () {
if (document.readyState === "complete" || document.readyState === "interactive") {
initGUI()
} else {
document.addEventListener('DOMContentLoaded', initGUI);
}
})()

View file

@ -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]];
}

View file

@ -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};
}

View file

@ -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;
@ -236,3 +236,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]);
}
}

View file

@ -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)
});
}
})