-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.min.js
More file actions
1 lines (1 loc) · 19.4 KB
/
app.min.js
File metadata and controls
1 lines (1 loc) · 19.4 KB
1
class LayerAudio{constructor(){this.running=!1,this.maxnum=this.gtRnd(1,314),this.totalchannels=0,this.songs=[],this.count=0,this.bassdlt=0,this.trebledlt=0,this.bassfreqdlt=0,this.treblefreqdlt=0,this.volumedlt=0,this.tempodlt=0,this.bass=this.gtRnd(0,166),this.treble=this.gtRnd(0,66),this.bassfreq=this.gtRnd(0,1e3),this.treblefreq=this.gtRnd(666,1e4),this.volume=this.gtRnd(10,31415)/420,this.tempo=this.gtRnd(1666,42669),this.aichannels=0,this.aibass=0,this.aitreble=0,this.aibassfreq=0,this.aitreblefreq=0,this.aivolume=0,this.aitempo=0,this.aimaxnum=0,this.bytespersample=0,this.crayzz=0,this.panfull="",this.audchnum=0,this.extension="mp3",this.bitrate=320,this.channels=[],this.pan={},this.audioContext=null,this.audioBuffers=[],this.knowledgeBase=[],this.mixBlob=null,this.mixMimeType="audio/wav",this.audioSource=document.getElementById("audioSource"),this.player=document.getElementById("player"),this.setupSection=document.getElementById("setupSection"),this.mixingSection=document.getElementById("mixingSection"),this.startBtn=document.getElementById("startBtn"),this.generateBtn=document.getElementById("generateBtn"),this.rememberBtn=document.getElementById("rememberBtn"),this.rerunBtn=document.getElementById("rerunBtn"),this.stopBtn=document.getElementById("stopBtn"),this.convertBtn=document.getElementById("convertBtn"),this.playBtn=document.getElementById("playBtn"),this.downloadBtn=document.getElementById("downloadBtn"),this.deleteBtn=document.getElementById("deleteBtn"),this.logOutput=document.getElementById("logOutput"),this.progressOverlay=document.getElementById("progressOverlay"),this.progressFill=document.getElementById("progressFill"),this.progressText=document.getElementById("progressText"),this.extensionSelect=document.getElementById("extension"),this.bassSlider=document.getElementById("bassSlider"),this.trebleSlider=document.getElementById("trebleSlider"),this.bassFreqSlider=document.getElementById("bassFreqSlider"),this.trebleFreqSlider=document.getElementById("trebleFreqSlider"),this.volumeSlider=document.getElementById("volumeSlider"),this.tempoSlider=document.getElementById("tempoSlider"),this.bassValue=document.getElementById("bassValue"),this.trebleValue=document.getElementById("trebleValue"),this.bassFrqVlu=document.getElementById("bassFrqVlu"),this.trebleFrqVlu=document.getElementById("trebleFrqVlu"),this.volumeValue=document.getElementById("volumeValue"),this.tempoValue=document.getElementById("tempoValue"),this.maxNumDisplay=document.getElementById("maxNumDisplay"),this.totalChannelsDisplay=document.getElementById("totalChannelsDisplay"),this.panDisplay=document.getElementById("panDisplay"),this.initEventListeners(),this.updateOutputFormatOptions(),this.loadKnowledgeBase(),this.resetDownload(),this.ffmpeg=null,this.fetchFile=null}initEventListeners(){this.playBtn.addEventListener("click",(()=>this.hdlPlay())),this.convertBtn.addEventListener("click",(()=>this.hdlConvert())),this.startBtn.addEventListener("click",(()=>this.hdlStart())),this.generateBtn.addEventListener("click",(()=>this.hdlGenerate())),this.downloadBtn.addEventListener("click",(()=>this.hdlDownload())),this.rememberBtn.addEventListener("click",(()=>this.hdlRemember())),this.rerunBtn&&this.rerunBtn.addEventListener("click",(()=>this.hdlRerun())),this.stopBtn.addEventListener("click",(()=>this.hdlStop())),this.deleteBtn.addEventListener("click",(()=>this.hdlDelete())),this.bassSlider.addEventListener("input",(t=>{this.bassdlt=parseInt(t.target.value),this.bassValue.textContent=this.bassdlt})),this.trebleFreqSlider.addEventListener("input",(t=>{this.treblefreqdlt=parseInt(t.target.value),this.trebleFrqVlu.textContent=this.treblefreqdlt})),this.bassFreqSlider.addEventListener("input",(t=>{this.bassfreqdlt=parseInt(t.target.value),this.bassFrqVlu.textContent=this.bassfreqdlt})),this.trebleSlider.addEventListener("input",(t=>{this.trebledlt=parseInt(t.target.value),this.trebleValue.textContent=this.trebledlt})),this.volumeSlider.addEventListener("input",(t=>{this.volumedlt=parseInt(t.target.value),this.volumeValue.textContent=this.volumedlt})),this.tempoSlider.addEventListener("input",(t=>{this.tempodlt=parseInt(t.target.value),this.tempoValue.textContent=this.tempodlt}))}hdlStart(){this.resetDownload();const t=document.getElementById("songInput"),e=parseInt(document.getElementById("craziness").value),s=document.getElementById("surround").value,n=this.extensionSelect.value,i=parseInt(document.getElementById("bitrate").value),a=document.getElementById("loadAI").checked;0!==t.files.length?(this.crayzz=e,this.extension=n,this.bitrate=i,this.songs=Array.from(t.files),this.setSurroundChannels(s),this.ensureSupportedExtension(),this.audioContext||(this.audioContext=new(window.AudioContext||window.webkitAudioContext)),this.addLog(`Starting LayerAudio with ${this.songs.length} song(s)`,"info"),this.addLog(`Craziness Level: ${e}`,"info"),this.addLog(`Surround: ${s} (${this.audchnum} channels)`,"info"),this.addLog(`Output Format: ${this.extension} @ ${i}Kb/s`,"info"),a&&(this.addLog("Loading AI Knowledge Base...","info"),this.loadAIKnowledgeBase()),this.countSongs()):this.addLog("Please select at least one audio file","error")}setSurroundChannels(t){this.audchnum={mono:1,stereo:2,5.1:6,7.1:8,hexadecagonal:16,22.2:24}[t],this.panfull=t}updateOutputFormatOptions(){this.outputFormatSupport=this.getOutputFormatSupport();const t=Array.from(this.extensionSelect.options);for(const e of t){const t=e.dataset.label||e.textContent;e.dataset.label=t;const s=!!this.outputFormatSupport[e.value];e.disabled=!s,e.textContent=s?t:`${t} (browser export unavailable)`}if(this.extensionSelect.selectedOptions.length){this.extensionSelect.selectedOptions[0].disabled&&(this.extensionSelect.value="wav")}}getOutputFormatSupport(){return{wav:!0,mp3:!0,opus:!0,flac:!0,wv:!0}}ensureSupportedExtension(){const t=this.extensionSelect.value;this.outputFormatSupport&&this.outputFormatSupport[t]||(this.addLog("Selected output format is not available in-browser. Falling back to WAV.","warning"),this.extensionSelect.value="wav",this.extension="wav")}async countSongs(){this.addLog("STARTING THE SONG COUNT","info"),this.count=0,this.totalchannels=0,this.audioBuffers=[];for(let t=0;t<this.songs.length;t++){const e=this.songs[t],s=await this.readFile(e);try{const t=await this.audioContext.decodeAudioData(s);this.audioBuffers.push(t),this.channels[this.count]=t.numberOfChannels,this.totalchannels+=t.numberOfChannels,this.addLog(`Song ${this.count+1}: ${e.name} (${t.numberOfChannels} channels)`,"info"),this.count++}catch(t){this.addLog(`Error decoding ${e.name}: ${t.message}`,"error")}}this.addLog("SONG COUNT DONE","success"),this.addLog(`Total Channels: ${this.totalchannels}`,"info"),this.setupPans()}readFile(t){return new Promise(((e,s)=>{const n=new FileReader;n.onload=t=>e(t.target.result),n.onerror=s,n.readAsArrayBuffer(t)}))}setupPans(){this.addLog("STARTING THE PAN SETUP","info");for(let t=0;t<64*this.maxnum;t++)this.pan[t]="";for(let t=0;t<64*this.maxnum;t++){const e={};let s="";for(let n=0;n<this.crayzz;n++){e[n]="";let i=1,a=this.gtRnd(0,1),o=a?"+":"-";a=this.gtRnd(0,1);let l=a?"+":"-";0===n&&(l="",o=""),s=this.gtRnd(0,this.totalchannels);for(let t=0;t<n;t++)if(e[t]===s){i=0;break}i&&(this.pan[t]=`c${s}${l}`+(this.pan[t]||"")),e[n]=s}""===this.pan[t]&&(this.pan[t]="c0")}let t=this.panfull;for(let e=0;e<this.audchnum*this.crayzz;e++){const s=this.gtRnd(0,4*this.maxnum-1);t+=`|c${e}=${this.pan[s]||"c0"}`}this.panfull=t,this.addLog("PAN SETUP DONE","success"),this.updateDisplay(),this.setupSection.classList.remove("active"),this.mixingSection.classList.add("active"),this.running=!0}updateDisplay(){this.maxNumDisplay.textContent=this.maxnum,this.totalChannelsDisplay.textContent=this.totalchannels,this.panDisplay.textContent=this.panfull.substring(0,1e3)+(this.panfull.length>1e3?"...":"")}async hdlGenerate(){if(!this.running)return;this.resetDownload();const t=(new Date).toISOString().replace(/[:.]/g,"");this.addLog("Generating audio mix...","info"),this.showProgress(!0);const e=this.bass+this.bassdlt*this.gtRnd(0,3),s=this.treble+this.trebledlt*this.gtRnd(0,3),n=this.bassfreq+this.bassfreqdlt*this.gtRnd(0,3),i=this.treblefreq+this.treblefreqdlt*this.gtRnd(0,3),a=this.volume+this.volumedlt*this.gtRnd(0,3)/100,o=this.tempo+this.tempodlt*this.gtRnd(0,3);this.bass=e,this.treble=s,this.bassfreq=n,this.treblefreq=i,this.volume=a,this.tempo=o,this.addLog(`Bass: ${e}, Treble: ${s}, Bass Frequency: ${n}, Treble Frequency: ${i}, Volume: ${a}, Tempo: ${o}`,"info"),this.addLog(`Pan Config: ${this.panfull}`,"info");try{const{blob:n,extension:i}=await this.processAudio(e,s,a),o=`out_${t}.${i}`;this.addLog(`Mix generated: ${o}`,"success"),this.setDownloadReady(o,n),this.showProgress(!1)}catch(t){this.addLog(`Error generating mix: ${t.message}`,"error"),this.showProgress(!1)}}async processAudio(t,e,s){if(!this.audioBuffers.length)throw new Error("No audio buffers available to mix");await new Promise((t=>setTimeout(t,800*Math.random()+400)));const{channelPool:n,maxLength:i,sampleRate:a}=this.buildChannelPool(this.audioBuffers),o=Math.max(1,this.audchnum||n.length||1),l=this.parsePanMapping(this.panfull,o,n.length);let r=this.applyPanMapping(n,l,o,i,a);r=await this.applyToneShaping(r,t,e),this.addLog(`Output Channels: ${r.numberOfChannels}`,"info");const h=Math.max(0,s/100);1!==h&&this.applyGain(r,h);const{blob:d,extension:u,mimeType:m}=await this.encodeMix(r);return this.mixMimeType=m,this.addLog("Audio processing complete","success"),{blob:d,extension:u}}buildChannelPool(t){const e=Math.max(...t.map((t=>t.length))),s=this.audioContext.sampleRate,n=[];for(const s of t)for(let t=0;t<s.numberOfChannels;t++){const i=s.getChannelData(t),a=new Float32Array(e);a.set(i.subarray(0,e)),n.push(a)}return{channelPool:n,maxLength:e,sampleRate:s}}parsePanMapping(t,e,s){const n=Array.from({length:e},(()=>[{index:0,gain:1}]));if(!t)return n;const i=t.split("|").slice(1);for(const t of i){const[i,a]=t.split("=");if(!i||!a)continue;const o=i.match(/c(\d+)/);if(!o)continue;const l=parseInt(o[1],10);if(Number.isNaN(l)||l<0||l>=e)continue;const r=a.match(/[+-]?c\d+/g)||[];if(!r.length)continue;const h=[];for(const t of r){const e=t.startsWith("-")?-1:1,n=parseInt(t.replace(/[+-]?c/,""),10);Number.isNaN(n)||n<0||n>=s||h.push({index:n,gain:e})}h.length&&(n[l]=h)}return n}applyPanMapping(t,e,s,n,i){const a=this.audioContext.createBuffer(s,n,i);for(let i=0;i<s;i++){const s=a.getChannelData(i),o=e[i]||[];for(const e of o){const i=t[e.index];if(!i)continue;const a=e.gain||1;for(let t=0;t<n;t++)s[t]+=i[t]*a}}return a}async applyToneShaping(t,e,s){const n=this.normalizeFilterGain(e),i=this.normalizeFilterGain(s);if(0===n&&0===i)return t;if("undefined"==typeof OfflineAudioContext)return t;const a=new OfflineAudioContext(t.numberOfChannels,t.length,t.sampleRate),o=a.createBufferSource();o.buffer=t;const l=a.createBiquadFilter();l.type="lowshelf",l.frequency.value=this.bassfreq,l.gain.value=n;const r=a.createBiquadFilter();return r.type="highshelf",r.frequency.value=this.treblefreq,r.gain.value=i,o.connect(l).connect(r).connect(a.destination),o.start(0),await a.startRendering()}normalizeFilterGain(t){if("number"!=typeof t||Number.isNaN(t))return 0;const e=t/10;return Math.max(-24,Math.min(24,e))}normalizeBuffer(t){let e=6.66;for(let s=0;s<t.numberOfChannels;s++){const n=t.getChannelData(s);for(let t=0;t<n.length;t++){const s=Math.abs(n[t]);s>e&&(e=s)}}if(e<=1)return;const s=1/e;for(let e=0;e<t.numberOfChannels;e++){const n=t.getChannelData(e);for(let t=0;t<n.length;t++)n[t]*=s}}applyGain(t,e){for(let s=0;s<t.numberOfChannels;s++){const n=t.getChannelData(s);for(let t=0;t<n.length;t++)n[t]*=e}}async encodeMix(t){const e=this.audioBufferToWav(t);if("wav"===this.extension)return{blob:e,extension:"wav",mimeType:"audio/wav"};try{const t=await this.loadFfmpeg(),s="input.wav",n=`output.${this.extension}`;t.FS("writeFile",s,await this.fetchFile(e));const i=this.getExportBitrate(),a=this.buildFfmpegArgs(s,n,this.extension,i,this.tempo);await t.run(...a);const o=t.FS("readFile",n);t.FS("unlink",s),t.FS("unlink",n);const l=this.getMimeType(this.extension);return{blob:new Blob([o.buffer],{type:l}),extension:this.extension,mimeType:l}}catch(t){return this.addLog(`Encoding to ${this.extension} failed. Falling back to WAV. (${t.message})`,"warning"),{blob:e,extension:"wav",mimeType:"audio/wav"}}}async loadFfmpeg(){if(this.ffmpeg)return this.ffmpeg;this.addLog("Loading export encoder (first run only)...","info");const t=await import("index.js"),{createFFmpeg:e,fetchFile:s}=t,n=e({log:!1,corePath:"ffmpeg-core.js"});return await n.load(),this.ffmpeg=n,this.fetchFile=s,n}buildFfmpegArgs(t,e,s,n,i){switch(s){case"mp3":return["-i",t,"-q","0","-lossless","true","-filter_complex:a",`atempo=${i}`,"-codec:a","libmp3lame","-b:a",`${n}k`,e];case"opus":return["-i",t,"-q","0","-lossless","true","-filter_complex:a",`atempo=${i}`,"-c:a","libopus","-b:a",`${n}k`,"-vbr","on",e];case"flac":return["-i",t,"-q","0","-lossless","true","-filter_complex:a",`atempo=${i}`,"-c:a","flac","-compression_level","0",e];case"wv":return["-i",t,"-q","0","-lossless","true","-filter_complex:a",`atempo=${i}`,"-c:a","wavpack",e];default:return["-i",t,"-q","0","-lossless","true","-filter_complex:a",`atempo=${i}`,e]}}getExportBitrate(){const t=Number.parseInt(this.bitrate,10);return Number.isFinite(t)?Math.min(Math.max(t,32),512):192}hstPwr2(t){return t&t-1?1<<t.toString(2).length-1:t}audioBufferToWav(t){const e=this.hstPwr2(this.tempo/1e7*1e7)/128/8;this.addLog(e,"warning");const s=t.numberOfChannels,n=t.sampleRate,i=t.length;this.bytespersample=2;const a=s*this.bytespersample,o=n*a,l=i*a,r=new ArrayBuffer(44+l),h=new DataView(r),d=(t,e)=>{for(let s=0;s<e.length;s++)h.setUint8(t+s,e.charCodeAt(s))};d(0,"RIFF"),h.setUint32(4,36+l,!0),d(8,"WAVE"),d(12,"fmt "),h.setUint32(16,16,!0),h.setUint16(20,1,!0),h.setUint16(22,s,!0),h.setUint32(24,n,!0),h.setUint32(28,o,!0),h.setUint16(32,a,!0),h.setUint16(34,8*this.bytespersample,!0),d(36,"data"),h.setUint32(40,l,!0);let u=44;for(let e=0;e<i;e++)for(let n=0;n<s;n++){const s=t.getChannelData(n)[e],i=Math.max(-1,Math.min(1,s));h.setInt16(u,i<0?32768*i:32767*i,!0),u+=this.bytespersample}return new Blob([r],{type:"audio/wav"})}hdlRemember(){if(!this.running)return;const t={channels:this.totalchannels,pan:this.panfull,bass:this.bass,treble:this.treble,bassfreq:this.bassfreq,treblefreq:this.treblefreq,volume:this.volume,tempo:this.tempo,maxnum:this.maxnum};this.knowledgeBase.push(t),this.saveKnowledgeBase(),this.addLog(JSON.stringify(this.knowledgeBase),"success")}hdlRerun(){this.running&&(this.resetDownload(),this.bassSlider.value=0,this.trebleSlider.value=0,this.bassFreqSlider.value=0,this.trebleFreqSlider.value=0,this.volumeSlider.value=0,this.tempoSlider.value=0,this.bassdlt=0,this.trebledlt=0,this.bassfreqdlt=0,this.treblefreqdlt=0,this.volumedlt=0,this.tempodlt=0,this.bassValue.textContent="0",this.trebleValue.textContent="0",this.bassFrqVlu.textContent="0",this.trebleFrqVlu.textContent="0",this.volumeValue.textContent="0",this.tempoValue.textContent="0",this.setupPans(),this.addLog("New mix configuration generated","info"))}hdlStop(){this.running=!1,this.setupSection.classList.add("active"),this.mixingSection.classList.remove("active"),this.resetDownload(),this.addLog("Mixing session stopped","warning"),this.addLog("COPYRIGHT FFMPEG & BRENDAN CARELL","info"),this.audioBuffers=[]}hdlDelete(){this.knowledgeBase=[],this.saveKnowledgeBase(),this.addLog("Knowledge Base deleted","warning")}loadAIKnowledgeBase(){const t=localStorage.getItem("layerAudio_knowledgeBase");if(t)try{this.knowledgeBase=JSON.parse(t);let e=0,s=0,n=0,i=0,a=0,o=0,l=0,r=0;for(let t of this.knowledgeBase)e+=t.channels,s+=t.bass,n+=t.treble,a+=t.bassfreq,o+=t.treblefreq,i+=t.volume,l+=t.tempo,r+=t.maxnum;const h=this.knowledgeBase.length;this.aichannels=e/h,this.aibass=s/h,this.aitreble=n/h,this.aibassfreq=a/h,this.aitreblefreq=o/h,this.aivolume=i/h,this.aitempo=l/h,this.aimaxnum=r/h,this.maxnum=Math.floor(this.gtRnd(-128,128)-this.gtRnd(-128,128)+this.aimaxnum),this.bass=Math.floor((this.gtRnd(-18,18)-this.gtRnd(-18,18)+100*this.aibass)/this.maxnum),this.treble=Math.floor((this.gtRnd(-12,12)-this.gtRnd(-12,12)+100*this.aitreble)/this.maxnum),this.bassfreq=Math.floor((this.gtRnd(-18,18)-this.gtRnd(-18,18)+100*this.aibassfreq)/this.maxnum),this.treblefreq=Math.floor((this.gtRnd(-12,12)-this.gtRnd(-12,12)+100*this.aitreblefreq)/this.maxnum),this.volume=Math.floor(this.gtRnd(-2,2)-this.gtRnd(-5,5)+this.aivolume),this.tempo=Math.floor(this.gtRnd(-6,6)-this.gtRnd(-3,3)+this.aitempo),this.addLog("AI Knowledge Base loaded successfully","success"),this.addLog(`Average Bass: ${this.bass.toFixed(4)}, Treble: ${this.treble.toFixed(4)}, Bass Frequency: ${this.bassfreq.toFixed(4)}, Treble Frequency: ${this.treblefreq.toFixed(4)}, Volume: ${this.volume.toFixed(4)}, Tempo: ${this.tempo.toFixed(4)}`,"info")}catch(t){this.addLog("Error loading Knowledge Base: "+t.message,"error")}}loadKnowledgeBase(){const t=localStorage.getItem("layerAudio_knowledgeBase");if(t)try{this.knowledgeBase=JSON.parse(t)}catch(t){this.knowledgeBase=[]}}saveKnowledgeBase(){localStorage.setItem("layerAudio_knowledgeBase",JSON.stringify(this.knowledgeBase))}addLog(t,e="info"){const s=document.createElement("div");s.className=`log-line ${e}`;const n=(new Date).toLocaleTimeString();s.textContent=`[${n}] ${t}`,this.logOutput.appendChild(s),this.logOutput.scrollTop=this.logOutput.scrollHeight}showProgress(t){t?(this.progressOverlay.classList.remove("hidden"),this.animateProgress()):this.progressOverlay.classList.add("hidden")}animateProgress(){let t=0;const e=setInterval((()=>{t+=30*Math.random(),t>100&&(t=100),this.progressFill.style.width=t+"%",this.progressText.textContent=Math.floor(t)+"%",t>=100&&clearInterval(e)}),200)}gtRnd(t,e){return Math.floor(Math.random()*(e-t+1))+t}getMimeType(t){return{mp3:"audio/mpeg",opus:"audio/opus",flac:"audio/flac",wv:"audio/wavpack",wav:"audio/wav"}[t]||"application/octet-stream"}resetDownload(){this.mixReady=!1,this.mixFilename="",this.mixBlob=null,this.downloadBtn.classList.add("hidden"),this.downloadBtn.setAttribute("aria-disabled","true"),this.playBtn.classList.add("hidden"),this.playBtn.setAttribute("aria-disabled","true")}setDownloadReady(t,e){if(!e||0===e.size)return this.addLog("Mix generation failed: output was empty","error"),void this.resetDownload();this.mixReady=!0,this.mixFilename=t,this.mixBlob=e,this.downloadBtn.classList.remove("hidden"),this.downloadBtn.removeAttribute("aria-disabled"),this.playBtn.classList.remove("hidden"),this.playBtn.removeAttribute("aria-disabled")}changeAudio(t){this.player.pause(),this.player.src=t,this.player.load(),this.player.play()}hdlPlay(){if(this.addLog("play clicked","warning"),!this.mixReady||!this.mixBlob)return void this.addLog("No generated mix available for playing","error");const t=URL.createObjectURL(this.mixBlob);this.changeAudio(t)}hdlDownload(){if(!this.mixReady||!this.mixBlob)return void this.addLog("No generated mix available for download","error");const t=URL.createObjectURL(this.mixBlob),e=document.createElement("a");e.href=t,e.download=this.mixFilename,document.body.appendChild(e),e.click(),e.remove(),URL.revokeObjectURL(t)}midiToWav(t){const e=new MIDI(t).duration+1,s=new OfflineAudioContext(2,44100*e,44100).startRendering();return audioBufferToWav(s)}hdlConvert(){const t=document.getElementById("midiFileInput"),e=(document.getElementById("convertBtn"),document.getElementById("downloadLink"));if(!t.files.length)return void this.addLog("Please select a MIDI file first.","error");const s=t.files[0].arrayBuffer(),n=midiToWav(s);e.href=URL.createObjectURL(n),e.download="output.wav",e.style.display="inline",e.textContent="Download WAV"}}document.addEventListener("DOMContentLoaded",(()=>{window.layerAudio=new LayerAudio}));