//Chris Xiong 2015,2016,2017,2018 //License: Expat let NSInk=null; let NSAudio=null; let NSVisualization=null; let NSPlayer=null; let NSUI=null; const sh={ elem:function(e) {return document.getElementById(e);}, newelem:function(e) {return document.createElement(e);}, getcookie:function(key) {return document.cookie.replace(new RegExp('(?:(?:^|.*;\\s*)'+key+'\\s*\\=\\s*([^;]*).*$)|^.*$'),'$1');}, useFLAC:function() {return sh.elem('audio').canPlayType('audio/ogg')=='';} }; class Ink { constructor(_vx,_vy,_c) { this.x=NSUI.canvas.width/2;this.y=NSUI.canvas.height/2; this.vx=_vx;this.vy=_vy; this.color=_c>6?6:_c;this.active=true; } update() { const canvas=NSUI.canvas; this.x+=this.vx;this.y+=this.vy; this.vx*=0.995;this.vy*=0.995; if(this.x<-10||this.x>canvas.width+10||this.y<-10||this.y>canvas.height+10) this.active=false; } draw(cctx) { cctx.drawImage(NSInk.inkimg[this.color],this.x+5*window.devicePixelRatio,this.y+5*window.devicePixelRatio); } } NSPlayer={ plistname:null, tracks:null, current:null, shuffle:0, repeat:0, load_playlist:async function(pln,ord) { let r=null; try{ const resp=await fetch(new Request(`/libs/music/player.d/playlists/${pln}.playlist?${new Date().getTime()}`)); r=await resp.text(); }catch(e){return;} let rarr=r.split('\n'); let tarr=[]; const fmt=sh.useFLAC()?'flac':'ogg'; for(let i=1;i{ const ra=shuffle?a.sord:a.ord; const rb=shuffle?b.sord:b.ord; return ra10)NSUI.audio.currentTime=0; else NSPlayer.prev(); }; NSUI.pbnext.onclick=NSPlayer.next.bind(NSPlayer); if(navigator.mediaSession) { navigator.mediaSession.setActionHandler('previoustrack',NSUI.pbprev.onclick); navigator.mediaSession.setActionHandler('nexttrack',NSUI.pbnext.onclick); } }, load_playlists:async function() { const moi=this; let r=null; try{ const resp=await fetch(new Request(`/libs/music/player.d/playlists/playlists?${new Date().getTime()}`)); r=await resp.text(); }catch(e){return;} let rarr=r.split('\n'); let tarr=[]; let cnt=0; for(let i=0;i{ for(let i=0;i{ tarr[rp.plistord]=rp; if(--cnt<=0)a(null); }); } }); await p; for(let i=0;i=this.playlists.length)return; if(setactive) { NSUI.ulplaylists.childNodes[cid].firstChild.classList.add('highlighted'); NSUI.ulplaylists.childNodes[cid].firstChild.classList.add('active'); } //VERY stupid design, but it _should_ work. NSPlayer.switch_playlist(pl,this.playlists[cid].playlist); NSPlayer.sort_playlist(NSPlayer.shuffle); this.playlists[cid].playlist=NSPlayer.tracks; this.present_playlist(cid); this.selectedplist=pl; }, switch_track:function(id) { if(NSPlayer.plistname!=this.selectedplist) this.switch_playlist(this.selectedplist); NSPlayer.play(id); }, set_highlighted:function(pl,t) { if(pl!=this.selectedplist)return; let cid=0; for(;cid0)--this.caps[i]; cctx.fillStyle='hsl('+i*360.0/this.nbins+',100%,25%)'; cctx.fillRect(i*(this.spectrw+this.spectrm),canvas.height-(canvas.height*this.caps[i]/255.)-1,this.spectrw,1); } }catch(e){ for(let i=0;i0)--this.caps[i]; cctx.fillStyle='hsl('+i*360.0/this.nbins+',100%,25%)'; cctx.fillRect(i*(this.spectrw+this.spectrm),canvas.height-this.caps[i]-1,this.spectrw,1); } } }, update_scope:function() { const cctx=this.cctx,canvas=NSUI.canvas; cctx.clearRect(0,0,canvas.width,canvas.height); try{ let timedomv=new Uint8Array(NSAudio.anlznode.frequencyBinCount); NSAudio.anlznode.getByteTimeDomainData(timedomv); cctx.lineWidth=window.devicePixelRatio; cctx.strokeStyle='#000'; if(athm)cctx.strokeStyle='#FFF'; cctx.beginPath(); for(let i=0,x=0;i=this.spectgrmw)this.spectgrmp=0; }catch(e){ this.spectgrmp=0; cctx.clearRect(0,0,canvas.width,canvas.height); } }, updateVisualization:function() { switch(NSUI.vissel.value) { case 'spectrum': NSVisualization.update_spectrum(); break; case 'scope': NSVisualization.update_scope(); break; case 'spectrogram': NSVisualization.update_spectrogram(); break; case 'inkfountain': NSInk.update(); break; } if(NSUI.vissel.value!='none') requestAnimationFrame(NSVisualization.updateVisualization); } }; NSInk={ ic1:['#FF3333','#FF8800','#FFFF00','#CCFF00','#33CCFF','#0000FF','#9966FF'], ic2:['rgba(204,51,51,0.6)','rgba(187,85,0,0.6)','rgba(255,204,0,0.6)','rgba(153,204,0,0.6)','rgba(51,153,255,0.6)','rgba(0,0,102,0.6)','rgba(153,51,204,0.6)'], inks:[], inkimg:[], ifcaps:[], nbinsif:128, inkPrepare:function() { for(let i=0;i<7;++i) { this.inkimg[i]=document.createElement('canvas'); this.inkimg[i].width=this.inkimg[i].height=10*window.devicePixelRatio; const cctx=this.inkimg[i].getContext('2d'); cctx.fillStyle=this.ic1[i]; cctx.beginPath(); cctx.arc(5*window.devicePixelRatio,5*window.devicePixelRatio,3*window.devicePixelRatio,0,2*Math.PI); cctx.fill(); cctx.fillStyle=this.ic2[i]; cctx.beginPath(); cctx.arc(5*window.devicePixelRatio,5*window.devicePixelRatio,5*window.devicePixelRatio,0,2*Math.PI); cctx.fill(); } for(let i=0;i500) { if(this.lastfpsupd) this.fps=1000*this.frms/(Date.now()-this.lastfpsupd),this.frms=0; this.lastfpsupd=Date.now(); } try{ let freqarr=new Uint8Array(NSAudio.anlznode.frequencyBinCount); NSAudio.anlznode.getByteFrequencyData(freqarr); for(let i=0;i7) { let color=Math.floor(i*8.0/this.nbinsif),rad=Math.random()*Math.PI*2; let ndrops=(128-i)/128.*3+1;ndrops*=(r/128.);ndrops=Math.floor(ndrops);if(ndrops<1)ndrops=1; for(let k=0;kthis.ifcaps[i])this.ifcaps[i]=r;else this.ifcaps[i]-=2; } }catch(e){ cctx.clearRect(0,0,canvas.width,canvas.height); } const tu=Date.now(); let aa=0; for(let i=0;i{ sh.elem('overlaytext').innerHTML+="Done!
Click or tap anywhere to start." sh.elem('overlay').onclick=function(){ NSAudio.audioInit();NSAudio.connect(); if(window.location.hash.length) { let p=window.location.hash.substr(1).split('/'); for(let i=0;i