1 line
13 KiB
JavaScript
1 line
13 KiB
JavaScript
class VTUtils{static random(t,e){let a=Math.random();if(void 0===t)return a;if(void 0===e)return t instanceof Array?t[Math.floor(a*t.length)]:a*t;if(t>e){let a=t;t=e,e=a}return a*(e-t)+t}static randomInt(t,e){return Math.floor(VTUtils.random(t,e))}static normalize(t,e,a){return(t-a)/(e-a)}static distance(t,e,a,i){let s=t-a,r=e-i;return Math.sqrt(s*s+r*r)}static map(t,e,a,i,s,r){let l=(t-e)/(a-e)*(s-i)+i;return r?i<s?this.constrain(l,i,s):this.constrain(l,s,i):l}static constrain(t,e,a){return Math.max(Math.min(t,a),e)}static hsvToRgb(t,e,a){var i,s,r,l=Math.floor(6*t),n=6*t-l,o=a*(1-e),h=a*(1-n*e),d=a*(1-(1-n)*e);switch(l%6){case 0:i=a,s=d,r=o;break;case 1:i=h,s=a,r=o;break;case 2:i=o,s=a,r=d;break;case 3:i=o,s=h,r=a;break;case 4:i=d,s=o,r=a;break;case 5:i=a,s=o,r=h}return{r:i,g:s,b:r}}static peakRGB(t){return{r:t,g:1-t,b:0}}}class VTVector{constructor(t,e,a){this.x=t||0,this.y=e||0,this.z=a||0}static createRandom(t,e,a){return t=t||1,e=e||1,a=a||0,new VTVector(VTUtils.random(-t,t),VTUtils.random(-e,e),VTUtils.random(-a,a))}mult(t){this.x*=t,this.y*=t,this.z*=t}set(t){this.x=t.x,this.y=t.y,this.z=t.z}add(t){this.x=this.x+t.x,this.y=this.y+t.y,this.z=this.z+t.z}addXYZ(t,e,a){this.x+=t,this.y+=e,this.z+=a}setXYZ(t,e,a){this.x=t||0,this.y=e||0,this.z=a||0}clone(){return new VTVector(this.x,this.y,this.z)}}class TDUtils{static multiply(t,e){let a=e[0],i=e[1],s=e[2],r=e[3],l=e[4],n=e[5],o=e[6],h=e[7],d=e[8],c=e[9],u=e[10],g=e[11],p=e[12],f=e[13],y=e[14],m=e[15],S=t[0],w=t[1],x=t[2],T=t[3],v=t[4],A=t[5],P=t[6],E=t[7],L=t[8],M=t[9],R=t[10],F=t[11],D=t[12],H=t[13],I=t[14],b=t[15],U=[];return U[0]=a*S+i*v+s*L+r*D,U[1]=a*w+i*A+s*M+r*H,U[2]=a*x+i*P+s*R+r*I,U[3]=a*T+i*E+s*F+r*b,U[4]=l*S+n*v+o*L+h*D,U[5]=l*w+n*A+o*M+h*H,U[6]=l*x+n*P+o*R+h*I,U[7]=l*T+n*E+o*F+h*b,U[8]=d*S+c*v+u*L+g*D,U[9]=d*w+c*A+u*M+g*H,U[10]=d*x+c*P+u*R+g*I,U[11]=d*T+c*E+u*F+g*b,U[12]=p*S+f*v+y*L+m*D,U[13]=p*w+f*A+y*M+m*H,U[14]=p*x+f*P+y*R+m*I,U[15]=p*T+f*E+y*F+m*b,U}static xRotation(t){let e=Math.cos(t),a=Math.sin(t);return[1,0,0,0,0,e,a,0,0,-a,e,0,0,0,0,1]}static yRotation(t){let e=Math.cos(t),a=Math.sin(t);return[e,0,-a,0,0,1,0,0,a,0,e,0,0,0,0,1]}static zRotation(t){let e=Math.cos(t),a=Math.sin(t);return[e,a,0,0,-a,e,0,0,0,0,1,0,0,0,0,1]}static degToRad(t){return t*Math.PI/180}}function $(t,e){return $$(t,e)[0]}function $$(t,e){return(e=e||document).querySelectorAll(t)}function create(t,e){let a=document.createElement(t);return e&&(a.innerHTML=e),a}function append(t,e){for(let a in e)t.appendChild(e[a])}Node.prototype.addDelegatedEventListener=function(t,e,a){this.addEventListener(t,t=>{let i=t.target;if(i.matches(e))a(t,i);else{let s=i.closest(e);s&&a(t,s)}})},Node.prototype.hasClass=function(t){return this.classList.contains(t)},Node.prototype.addClass=function(t){return this.classList.add(t)},Node.prototype.removeClass=function(t){return this.classList.remove(t)};class Template{constructor(){this.tpl={}}async loadTemplate(t){let e=this;this.tpl[t]||await fetch(templateDir+t+".tpl").then(t=>t.text()).then(a=>{e.tpl[t]=a})}async loadArray(t){for(let e of t)await this.loadTemplate(e)}parseTemplate(t,e){if(!this.tpl[t])return"";let a,i=this.tpl[t];for(;null!==(a=templateEx.exec(i));){a.index===templateEx.lastIndex&&templateEx.lastIndex++;let t=a[0];i=i.replace(t,e[a[1]]||"")}return i}parseFromAPI(t,e,a){fetch(t).then(t=>t.json()).then(t=>{a(this.parseTemplate(e,t))}).catch(console.error)}}const templateEx=/\$(.*?)\$/gm,templateDir="out/tpl/";class ShaderHandler{constructor(t){this.gl=t,this.shaderNames=[],this.shaders={},this.programs={}}setGL(t){this.gl=t}async loadShader(t,e){this.shaderNames.push(t),await this.load(t,e+t+".vert",this.gl.VERTEX_SHADER),await this.load(t,e+t+".frag",this.gl.FRAGMENT_SHADER)}async load(t,e,a){let i=t+"_"+a;if(!this.shaders[i]){let t=await fetch(e),s=this.createShader(await t.text(),a);s&&(this.shaders[i]=s)}return!!this.shaders[i]}getShader(t,e){let a=t+"_"+e;return this.shaders[a]}getAllShaders(){return this.shaderNames}async createProgramForEach(t){t=t||this.shaderNames;for(let e=0;e<t.length;e++){let a=t[e];if(!await shaderHandler.createProgram(a,[a]))return!1}return!0}createShader(t,e){let a=this.gl,i=a.createShader(e);return a.shaderSource(i,t),a.compileShader(i),a.getShaderParameter(i,a.COMPILE_STATUS)?i:(console.error(a.getShaderInfoLog(i)),a.deleteShader(i),null)}createProgram(t,e){let a=this.gl,i=a.createProgram();for(let t=0;t<e.length;t++)a.attachShader(i,this.getShader(e[t],a.VERTEX_SHADER)),a.attachShader(i,this.getShader(e[t],a.FRAGMENT_SHADER));return a.linkProgram(i),a.getProgramParameter(i,a.LINK_STATUS)?(this.programs[t]=i,i):(console.log(a.getProgramInfoLog(i)),a.deleteProgram(i),null)}getProgram(t){return this.programs[t]}use(t){let e=this.programs[t];return this.gl.useProgram(e),e}async loadArray(t,e){let a=this;for(const i of t)await a.loadShader(i,e);await a.createProgramForEach(t)}}const AudioContext=window.AudioContext||window.webkitAudioContext;class AudioHandler{async init(){this.isStarted=!1,this.audioFile=new Audio,this.actx=new AudioContext,this.analyser=this.actx.createAnalyser(),this.analyser.fftSize=4096,this.lastSong=null,await this.connectAll()}async connectAll(){this.source=this.actx.createMediaElementSource(this.audioFile),this.source.connect(this.analyser),this.analyser.connect(this.actx.destination),this.audioFile.addEventListener("ended",player.nextSong.bind(player))}async start(){""!==this.audioFile.src&&(this.isStarted||(this.isStarted=!0,await this.actx.resume()))}async stop(){this.isStarted&&(this.isStarted=!1,await this.actx.suspend())}fftSize(t){this.analyser.fftSize=t}smoothing(t){this.analyser.smoothingTimeConstant=t}loadSong(t){let e=this;e.lastSong&&URL.revokeObjectURL(e.lastSong),e.lastSong=this.audioFile.src=URL.createObjectURL(t),this.isStarted||this.start(),this.audioFile.play()}getIntArray(t){let e=new Uint8Array(t);return this.analyser.getByteFrequencyData(e),e}getFloatArray(){let t=new Float32Array(this.analyser.frequencyBinCount);return this.analyser.getFloatTimeDomainData(t),t}}class Player{async init(){this.playlist=new Playlist}nextSong(){let t=this.playlist.getNext();audioHandler.loadSong(t.file)}prevSong(){let t=this.playlist.getPrevious();audioHandler.loadSong(t.file)}playStop(){if(!audioHandler.lastSong){let t=this.playlist.getCurrent();audioHandler.loadSong(t.file)}let t=audioHandler.audioFile;t.paused?t.play():t.pause()}playByID(t){let e=this.playlist.getFile(t);audioHandler.loadSong(e.file)}}const PAGINATIONLIMIT=50;class Playlist{constructor(){this.list=[],this.shuffled=[],this.index=0,this.page=0,this.isShuffle=!1,$("body").addDelegatedEventListener("change",'input[type="file"]',this.changeFiles.bind(this)),$("body").addDelegatedEventListener("click",".pagination .item",this.handlePagination.bind(this))}shuffle(){let t=this.list.length;t<3&&(this.shuffled=this.list);for(let e=0;e<t;e++){let a=VTUtils.randomInt(0,t-1);this.swap(e,a)}}swap(t,e){this.shuffled[t]=this.list[e],this.shuffled[e]=this.list[t]}getNext(){let t=this.isShuffle?this.shuffled:this.list,e=t.length-1,a=this.index+1;return a>e&&(a=0),this.index=a,t[a]}getPrevious(){let t=this.isShuffle?this.shuffled:this.list,e=t.length-1,a=this.index-1;return a<0&&(a=e),this.index=a,t[a]}getCurrent(){return(this.isShuffle?this.shuffled:this.list)[this.index]}getFile(t){return(this.isShuffle?this.shuffled:this.list)[t]}setPlaylist(t){this.index=0,this.list=t,this.shuffle()}handlePagination(t,e){e.hasClass("inactive")||(e.hasClass("next-site")?this.renderPagination(this.page+1):this.renderPagination(this.page-1))}renderPagination(t){let e=this.list.length,a=Math.ceil(e/50)-1;t<0&&(t=0),t>a&&(t=a);let i=50*t,s=i+50,r="";if(this.page=t,s>=e&&(s=e),e>0){let t=this.isShuffle?this.shuffled:this.list;for(let e=i;e<s;e++){let a={index:e,nr:e+1,title:t[e].name};r+=template.parseTemplate("playlist-item",a)}}else r="<h1>No Songs uploaded!</h1>";let l=a>1&&t<a;gui.modal.renderModal("Playlist",template.parseTemplate("playlist",{content:r}),template.parseTemplate("playlist-footer",{prevActive:t>0?"active":"inactive",nextActive:l?"active":"inactive",page:t+1+" / "+parseInt(a+1)}))}changeFiles(t,e){let a=[],i=0;for(let t of e.files)if(t&&-1!==t.type.indexOf("audio")&&null===t.name.match(".m3u")){let e=t.name.split(".");e.pop(),e=e.join("."),a.push({file:t,name:e,index:i++})}this.setPlaylist(a),a.length>0?this.renderPagination(0):alert("No Valid AudioFiles found!")}}class GUI{async init(){this.data={},this.modal=new Modal,await this.loadForVis(),await template.loadArray(["playlist-item","playlist","playlist-footer"]),this.initDropZone()}async loadForVis(){let t=visual.c;null==this.data[t]&&(this.data[t]=await fetch("out/gui/"+t+".json").then(t=>t.json()))}renderModal(t,e){let a=$("#modal"),i=a.parentNode,s=$("header .headline",a),r=$("modal-content",a);s.innerHTML=e,r.innerHTML=t,i.classList.remove("hide")}initDropZone(){"drag dragstart dragend dragover dragenter dragleave drop".split(" ").forEach(t=>{window.addEventListener(t,async t=>{t.preventDefault(),t.stopPropagation(),"drop"===t.type&&(t.dataTransfer.files.length>0?player.playlist.changeFiles(t,t.dataTransfer):alert("Sorry you need to upload files!"))})})}}class Modal{constructor(){this.currentModal="",this.modal=$("#modal"),this.parent=this.modal.parentNode,this.modal.addDelegatedEventListener("click","header .close",this.closeModal.bind(this))}resetModal(){this.renderModal("","","")}renderModal(t,e,a){this.currentModal=t,this.renderHeader(t),this.renderContent(e),this.renderFooter(a),this.showModal()}renderHeader(t){$("header .headline",this.modal).innerHTML=t}renderContent(t){$("modal-content",this.modal).innerHTML=t}renderFooter(t){$("modal-footer",this.modal).innerHTML=t}closeModal(){this.parent.addClass("hide")}isCurrent(t){return t===this.currentModal}showModal(){this.parent.removeClass("hide")}}class Visual{constructor(){this.data=[],this.dataArray=[]}updateData(){}draw(){}setup(){}}class VisualDrawer{constructor(){this.visuals={sphere:new Sphere,wave:new Wave,water:new Water},this.c="wave"}init(){this.visuals[this.c].setup(),this.updateLoop()}switch(t){null!=this.visuals[t]&&(this.c=t,this.visuals[this.c].setup())}updateLoop(){let t=shaderHandler.use(this.c),e=this.visuals[this.c];e.updateData(),e.draw(t),requestAnimationFrame(this.updateLoop.bind(this))}}class Config{constructor(){this.config={},this.name=""}loadConfigByName(t){this.saveConfig(),this.name="config-"+t,this.config=JSON.parse(this.name)}saveConfig(){""!==this.name&&localStorage.setItem(this.name,JSON.stringify(this.config))}addItem(t,e){this.config[t]=e}removeItem(t){delete this.config[t]}getItem(t,e){let a=this.config[t];return null==a&&(this.config[t]=e,a=e),a}}class Sphere extends Visual{draw(){}setup(){}}class Wave extends Visual{updateData(){this.data=[];let t=audioHandler.getFloatArray(),e=2/t.length,a=-1;for(let i=0;i<t.length;i++)this.data.push(a,t[i],t[i]),a+=e}draw(t){c.width=window.innerWidth,c.height=window.innerHeight,this.prepare(t);let e=this.position,a=(this.color,gl.createBuffer());this.rotate(t),gl.bindBuffer(gl.ARRAY_BUFFER,a),gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(this.data),gl.DYNAMIC_DRAW);let i=gl.createVertexArray();gl.bindVertexArray(i),gl.enableVertexAttribArray(e),gl.vertexAttribPointer(e,3,gl.FLOAT,!0,0,0),gl.clearColor(0,0,0,1),gl.enable(gl.DEPTH_TEST),gl.depthFunc(gl.LEQUAL),gl.clearDepth(2),gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT),gl.viewport(0,0,gl.canvas.width,gl.canvas.height),gl.drawArrays(gl.LINE_STRIP||gl.POINTS,0,this.data.length/3)}rotate(t){let e=[1/(c.height/c.width),0,0,0,0,1,0,0,0,0,1,0,0,0,0,1];e=TDUtils.multiply(e,TDUtils.xRotation(config.getItem("xRotate",0))),e=TDUtils.multiply(e,TDUtils.yRotation(config.getItem("yRotate",0))),e=TDUtils.multiply(e,TDUtils.zRotation(config.getItem("zRotate",0)));let a=gl.getUniformLocation(t,"u_matrix");gl.uniformMatrix4fv(a,!1,e)}setup(){audioHandler.fftSize(16384)}prepare(t){this.position=gl.getAttribLocation(t,"a_position"),this.color=gl.getUniformLocation(t,"u_color")}}class Water extends Visual{draw(){}setup(){audioHandler.fftSize(256)}}async function initHandler(){let t=$("body");$(".playlist.menu-icon").addEventListener("click",t=>{player.playlist.renderPagination(player.playlist.page)}),t.addDelegatedEventListener("click",".playlist-item",(t,e)=>{let a=e.dataset.index;player.playByID(parseInt(a)),togglePlayButton("pause")}),t.addDelegatedEventListener("click",".controls button",(t,e)=>{switch(e.id){case"previous":player.prevSong();break;case"next":player.nextSong();break;case"play":player.playStop()}togglePlayButton(audioHandler.audioFile.paused?"play":"pause")})}function togglePlayButton(t){$$("#play .icon").forEach(e=>{e.dataset.name===t?e.removeClass("hide"):e.addClass("hide")})}const shaderHandler=new ShaderHandler(null),audioHandler=new AudioHandler,gui=new GUI,visual=new VisualDrawer,template=new Template,player=new Player,config=new Config;let c=null,gl=null;async function startUP(){if(c=document.body.querySelector("#c"),gl=c.getContext("webgl2"),!gl)return alert("SORRY THE BROWSER DOESN'T SUPPORT WEBGL2"),!1;shaderHandler.setGL(gl),await shaderHandler.loadArray(["wave","sphere","water"],"shaders/"),await audioHandler.init(),await player.init(),await visual.init(),await gui.init(),await initHandler()}startUP().then(t=>{setTimeout(t=>{$(".loading-screen").remove()},100)}); |