~firefoxmp3
37 itemsDownload ./*

..
1.mp3
10.mp3
11.mp3
12.mp3
13.mp3
14.mp3
15.mp3
16.mp3
17.mp3
18.mp3
19.mp3
2.mp3
20.mp3
21.mp3
22.mp3
23.mp3
24.mp3
25.mp3
26.mp3
27.mp3
28.mp3
29.mp3
3.mp3
30.mp3
31.mp3
32.mp3
33.mp3
34.mp3
4.mp3
5.mp3
6.mp3
7.mp3
8.mp3
9.mp3
index.html
spaceship.png
stars-transparent.html


mp3index.html
36 KB• 12•  7 hours ago•  DownloadRawClose
7 hours ago•  12

{}
<!DOCTYPE html>
<!--

    Author:        Twily  2015 - 2025
    Website:       http://twily.info/

    AI Generated music album, (no lora), using MusicGen from
    hugginface run locally on home computer for experimenting.

    Delivered in a very nostalgic inspired Winamp, (re)designed
    by myself. Help from grok to setup visualizer and equalizer.

-->
<html>
<head>
<title>Twily AI Music</title>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
<meta name='viewport' content='width=device-width,initial-scale=1'>

<!--<link id="favicon" rel="shortcut icon" href="http://twily.info/favicon.ico" />-->
<!--<link rel="stylesheet" type="text/css" href="./css/style.css?v=1" />-->
<style type="text/css">
html,body {
    width: 100%; height: 100%; margin: 0; padding: 0; overflow: auto;
    font-family: "Droid Sans", "Liberation Sans", "DejaVu Sans", "Segoe UI", Sans;
    font-size: 12pt; font-weight: bold;
    background: #02161c; color: #AAABAD;
    user-select: none;
}
* { box-sizing: border-box; }
*:focus { outline: none !important; }

a:link, a:visited { color: #606163; text-decoration: none; }
a:hover, a:active { color: #0f0; }

.tbl { display: table; width: 100%; }
.tr { display: table-row; }
.td { display: table-cell; vertical-align: middle; /*border: 1px solid transparent; box-shadow: inset 0 0 2px 2px #000;*/ }

.sidebar { width: 28px; min-width: 28px; }

/*
#wrap { width: 100%; height: 100%; display: flex; position: absolute; z-index: 9; }
#wrap #box { padding: 128px; background: #111113; margin: auto; display: inline-block; position: relative; }*/

.brd { border-top: 1px solid #620; }

.frm {
    width: 100%; height: 100%;
    position: relative;
    margin: 0; padding: 0;
}
#backdrop {
    position: fixed; top: 0; left: 0; z-index: 1;
    width: 100%; height: 100%;
}
#above {
    position: absolute; top: 0; left: 0; z-index: 10000;
    width: 100%; height: 100%;
}
#backlay {
    position: fixed; top: 0; left: 0; z-index: 2;
    width: 100%; height: 100%;
    background: transparent;
    background: radial-gradient(ellipse at 50%, rgba(26,72,68,.25), rgba(44,53,70,.25) 50%, transparent 100%);
    /*background: radial-gradient(ellipse at 50%, transparent, rgba(35,52,84,.25) 50%, rgba(52,112,114,.25) 100%);*/
}

.td label:nth-child(1) {
    margin-right: 10px;
}

.playing.selected::before {
    content: "►";
    /*content: "♬"▶;*/
}
.track.selected {
    color: #0f8;
}

#triangle.on::before {
    font-size: 22pt; color: #0f0;
    content: "►";
    position: absolute; z-index: 100;
}

.pad18 {
    padding: 4px;
}

#player {
    border: 1px solid #620;
    border-radius: 2px;
    width: 100%;
    max-width: 600px;
    text-align: left;
}

.btn {
    border-right: 1px solid #2f303c;
    border-bottom: 1px solid #2f303c;
    border-left: 1px solid #747cd3;
    border-top: 1px solid #747cd3;
    background: #525793;
    color: #0f0;
    -webkit-appearance: none; border-radius: 2px; cursor: pointer;
}
.btn:hover {
    background: #5f5ea9;
}
.btn:active, .btn.tog {
    border-top: 1px solid #2f303c;
    border-left: 1px solid #2f303c;
    border-right: 1px solid #747cd3;
    border-bottom: 1px solid #747cd3;
    background: #3e4270;
}

#bar {
    /*position: fixed; bottom: 0; left: 0;*/
    display: block; height: 7px; width: 100%;
    background: #525793;
    background: linear-gradient(0deg, #2d3a7e, #4d2f95);
    border-top: 1px solid #2f303c;
    border-left: 1px solid #2f303c;
    border-right: 1px solid #747cd3;
    border-bottom: 1px solid #747cd3;
}
#progress {
    display: block; height: 100%; width: 0%;
    background: #0f0;
    background: linear-gradient(0deg, #01af01, #00ff2c);
}
#lbloop { display: none; }
#lbshuf { display: none; }

.center { text-align: center; }
.grey {
    padding-left: 6px;
    color: #666;
    font-size: 8pt;
    font-family: monospace;
}

#vol {
    /*position: fixed; bottom: 0; left: 0;*/
    display: inline-block; height: 7px; width: 100px;
    background: #525793;
    background: linear-gradient(0deg, #2d3a7e, #4d2f95);
    border-top: 1px solid #2f303c;
    border-left: 1px solid #2f303c;
    border-right: 1px solid #747cd3;
    border-bottom: 1px solid #747cd3;
}
#level {
    display: block; height: 100%; width: 100%;
    background: #0f0;
    background: linear-gradient(0deg, #01af01, #00ff2c);
}

.eqbar {
    /*position: fixed; bottom: 0; left: 0;*/
    position: relative;
    display: inline-block; height: 60px; width: 7px;
    background: #525793;
    background: linear-gradient(270deg, #2d3a7e, #4d2f95);
    border-top: 1px solid #2f303c;
    border-left: 1px solid #2f303c;
    border-right: 1px solid #747cd3;
    border-bottom: 1px solid #747cd3;
}
.eqlevel {
    position: absolute; bottom: 0;
    display: block; height: 100%; width: 100%;
    background: #0f0;
    background: linear-gradient(270deg, #01af01, #00ff2c);
}
#eq0 { height: 50%; }
#eq1 { height: 50%; }
#eq2 { height: 50%; }
#eq3 { height: 50%; }
#eq4 { height: 50%; }
#eq5 { height: 50%; }
#eq6 { height: 50%; }
#eq7 { height: 50%; }
#eq8 { height: 50%; }
#eq9 { height: 50%; }

#display {
    display: block;
    background: #1f1f1f;
    border: 1px solid #363; border-radius: 2px;
    width:100%; height: 100%;
    position: relative;
}
#time {
    font-size: 22pt;
    font-family: monospace;
    color: #0f0; /*background: #333;*/
    float: right;
    padding: 4px;
    position: relative; z-index: 100;
}
#title {
    background: #06070a;
    color: #0f0;
    border: 1px solid #026; border-radius: 2px;
    /*position: relative;*/
}
#inner {
    width: 100%;
}
#visualizer {
    background: transparent;
    width: 100%; height: 69%;
    position: absolute; bottom: 0;
    z-index: 1;
}

#spaceship {
    position: fixed; top: 0; left: 0;
    width: 100%; height: 100%;
    /*background: url('./spaceship.png') no-repeat center center transparent;
    background-size: cover;*/
    background: transparent;
    visibility: hidden;
    opacity: 0; /* start hidden - preload async */
    z-index: 5;
}

.lnkbtn {
    padding: 8px;
    border: 1px solid #aaa;
    border-radius: 10px;
}
#btn_ship {
    position: fixed;
    bottom: 20px; right: 20px;
    z-index: 10025;
}
#btn_dir {
    position: fixed;
    bottom: 20px; left: 20px;
    z-index: 10025;
}
</style>

<!--<script type="text/javascript" src="./js/main.js?v=1"></script>-->
<script type="text/javascript">
var $=function(id) { return document.getElementById(id); };
var lerp=function(a,b,t) { return a + t * ( b - a ) };
var clamp=function(val,min,max,) { return Math.min(Math.max(val,min), max) };

var playing=false;
var idxPlaying=1;
var idxText="1. ";

var volume=100;
var idxCurrentTime=0;
var idxDuration=0;

function playpause() {
    if(!playing) {
        play(idxPlaying);
    } else {
        clearplay();
    }
}
function prev() {
    var elms=$('player').getElementsByClassName('playing');
    if(loopon) {
        play(idxPlaying);
        return;
    } else if(shufon) {
        var n=-1;
        while(n==-1 || n==idxPlaying) {
            n=rndMinMax(0,elms.length-1);
        }
        play(n);
        return;
    }

    if(idxCurrentTime>1) {
        // rewind song
        play(idxPlaying);
    } else {
        if(idxPlaying-1 <= 0) {
            // rewind list
            play(elms.length);
        } else {
            // prev
            play(idxPlaying-1);
        }
    }
}

function rndMinMax(min,max) { // min and max included
  return Math.floor(Math.random()*(max-min+1)+min);
}
function next() {
    var elms=$('player').getElementsByClassName('playing');
    if(loopon) {
        play(idxPlaying);
        return;
    } else if(shufon) {
        var n=-1;
        while(n==-1 || n==idxPlaying) {
            n=rndMinMax(0,elms.length-1);
        }
        play(n);
        return;
    }

    if(idxPlaying-1 >= elms.length-1) {
        // rewind
        play(1);
    } else {
        // next
        play(idxPlaying+1);
    }
}
var shufon=false;
function shuftog() {
    shufon=!shufon;

    if(shufon) {
        if(loopon) {
            looptog();
        }
        $('lbshuf').style.display="inline-block";
        $('btnshuftog').className="btn tog";
    } else {
        $('lbshuf').style.display="none";
        $('btnshuftog').className="btn";
    }
}
var loopon=false;
function looptog() {
    loopon=!loopon;

    if(loopon) {
        if(shufon) {
            shuftog();
        }
        $('lbloop').style.display="inline-block";
        $('btnlooptog').className="btn tog";
    } else {
        $('lbloop').style.display="none";
        $('btnlooptog').className="btn";
    }
}
var eqon=false;
function eqtog() {
    eqon=!eqon;

    if(eqon) {
        $('btneqtog').className="btn tog";
        $('eqfrm').style.display="block";
        $('eqbrd').style.border="";
    } else {
        $('btneqtog').className="btn";
        $('eqfrm').style.display="none";
        $('eqbrd').style.border="none";
    }
}
var liston=false;
function listtog() {
    liston=!liston;

    if(liston) {
        $('btnlisttog').className="btn tog";
        $('listfrm').style.display="block";
        $('listbrd').style.border="";
    } else {
        $('btnlisttog').className="btn";
        $('listfrm').style.display="none";
        $('listbrd').style.border="none";
    }
}

// set colors here
var mColor=[ // [color1],[color2] = [inner],[outer]
    //[255,0,0],[0,255,0], // red,green 
    //[255,255,0],[255,0,255], // yellow,pink
    //[0,255,255],[0,0,255] // cyan,blue
    [165,49,83],[44,53,70], // stage0 red,blue
    [56,164,83],[75,75,25], // stage1 green,yellow
    [26,72,68],[143,49,165] // stage2 cyan,violet
];

var mStage=0;
var mLapse=0; // 0-100, once 100 mStage++ or reset
var color1=[0,0,0];
var color2=[0,0,0];
var pos0=0;
var pos1=1;
var alpha=0.0;
var wW,wH,wH2,bH,bH2;
var bgAspect=15/10; // 16/9

//var fps=12; // tick
var speed=1; // percentage per tick
//var alT;
function animloop() {
    //return;
    //clearTimeout(alT);

    mLapse+=speed;
    if(mLapse>=100) {
        mLapse=0;
        mStage++;

        if(mStage>2) mStage=0;
        //console.log("new stage = "+mStage);
    } 
    pos0=mStage*2; // skip every other for multi array
    pos1=pos0+2;
    alpha=mLapse/100;
    if(pos1>2*2) pos1=0;

    //console.log("pos0:"+pos0+", pos1:"+pos1);

    color1[0]=lerp(mColor[pos0][0],mColor[pos1][0],alpha);
    color1[1]=lerp(mColor[pos0][1],mColor[pos1][1],alpha);
    color1[2]=lerp(mColor[pos0][2],mColor[pos1][2],alpha);

    color2[0]=lerp(mColor[pos0+1][0],mColor[pos1+1][0],alpha);
    color2[1]=lerp(mColor[pos0+1][1],mColor[pos1+1][1],alpha);
    color2[2]=lerp(mColor[pos0+1][2],mColor[pos1+1][2],alpha);
    
    $('backlay').style.background="radial-gradient(ellipse at 50%, rgba("+color1.toString()+",0.25), rgba("+color2.toString()+",.25) 50%, transparent 100%)";

    $('backlay').style.height=bH+"px"; // width is 100% always
    $('backlay').style.top=(wH2 - bH2)+"px";

    //alT=setTimeout(function() { animloop(); }, 1000/fps);
}


var kAT;
var fps=12;
//var keepAliveRunning=false;
var switchNext=-1; // automatic for end of song unless loop on
var updateTime=0;
function keepAlive() {
    clearTimeout(kAT);
    //keepAliveRunning=true;

    if(playing) {
        idxCurrentTime=$('myAudio').currentTime;
        //idxDuration//
        //console.log(idxCurrentTime);
        // seekpos = percentage * duration / 100 = time
        var iPercent = idxCurrentTime*100/idxDuration;
        iPercent=clamp(iPercent,0,100);
        $('progress').style.width=iPercent+"%";

        if(switchNext>-1) {
            switchNext--;
            //console.log("ready switch "+switchNext);
            if(switchNext==0) {
                // switch now
                switchNext=-1;
                //console.log("switching next done")
                next();
            }
        } else if((idxDuration - idxCurrentTime <= 1) && !loopon) {
            switchNext=fps;
        }

        if(updateTime<=0) {
            var cmin=0;
            var csec=parseInt(idxCurrentTime);
            while(csec>=60) {
                csec-=60;
                cmin++;
            }
            var strcsec=(csec<10)?"0"+csec:csec;
            var strcmin=(cmin<10)?"0"+cmin:cmin;

            $('time').innerHTML=strcmin+":"+strcsec;
            updateTime=fps; // 1 per fps
        } else {
            updateTime--;
        }

        //$('visualizer').width = $('visualizer').clientWidth;
        //$('visualizer').height = $('visualizer').clientHeight;
    }
    
    animloop(); // always update bg colors

    //if(playing) {
        kAT=setTimeout(function() { keepAlive(); },1000/fps);
    //}
    //keepAliveRunning=false;

    if(shipOn && shipOpa<100) {
        shipOpa+=3;
        if(shipOpa>=100) {
            shipOpa=100;
        }
        if(lastShipOpa==0) {
            $('spaceship').style.visibility="visible";
        }
    } else if(!shipOn && shipOpa>0) {
        shipOpa-=3;
        if(shipOpa<=0) {
            shipOpa=0;
            $('spaceship').style.visibility="hidden";
        }
    }
    if(lastShipOpa!=shipOpa) { 
        $('spaceship').style.opacity=(shipOpa/100);
        lastShipOpa=shipOpa;
    }
}
var cache=2;
var t="";
function play(idx) {
    //alert(idx);
    var reloading=true;
    if(idx==idxPlaying && !playing) {
        reloading=false;
    } else {
        switchNext=-1;
    }
    idxPlaying=idx;

    var elms=$('player').getElementsByClassName('playing');
    for(var i=0;i<elms.length;i++) {
        var c=i+1;
        if(c==idx) {
            elms[i].className="playing selected";
            $('aud'+c).className="track selected";
            idxText=c+". "+$('aud'+c).getElementsByClassName('td')[2].innerHTML; // idx|name
            t=$('aud'+c).getElementsByClassName('mp3name')[0].value;
        } else {
            elms[i].className="playing";
            $('aud'+c).className="track";
        }
    }

    playing=true;
    $('btnplaypause').value="∎";
    $('triangle').className="triangle on";

    if(reloading) {
        //$('myAudio').src=idx+".mp3"; // need t, not c
        $('myAudio').src=t+".mp3?c="+cache;
    }
    $('inner').innerHTML=idxText;
    $('myAudio').play();

    if(!audioContextInitiated) {
        createAudioContext();
    }

    if(eqloaded==1 || eqtrig==1) {
        updateeq();
        eqloaded=2;
    }
    //if(!keepAliveRunning) keepAlive();
}
var initClear=0;
function clearplay() {
    var elms=$('player').getElementsByClassName('playing');
    for(var i=0;i<elms.length;i++) {
        var c=i+1;
        if(c==idxPlaying) {
            elms[i].className="playing"; // not playing
            $('aud'+c).className="track selected";
            idxText=c+". "+$('aud'+c).getElementsByClassName('td')[2].innerHTML; // idx|name
            t=$('aud'+c).getElementsByClassName('mp3name')[0].value;
        } else {
            elms[i].className="playing";
            $('aud'+c).className="track";
        }
    }

    playing=false;
    $('btnplaypause').value="►";
    $('triangle').className="triangle";

    if(initClear==0) {
        $('myAudio').src=t+".mp3?c="+cache;
        initClear=1;
    }
    $('inner').innerHTML=idxText;
    $('myAudio').pause();
}

var plX=0;
var plY=0;
function mousehandle(e) {
    e=event || window.event;

    var lX=e.clientX || e.targetTouches[0].pageX;
    var lY=e.clientY || e.targetTouches[0].pageY;

    //var sY=window.scrollY;
    //var sX=window.scrollX;

    plX=lX;
    plY=lY;

    // update on move (and hold)
    if(seeklock) {
        seekpos();
    } else if(vollock) {
        setvol();
    } else if(eqlock!=-1) {
        seteq(eqlock);
    }
}

var seeklock=false;
var vollock=false;
var eqlock=-1;

function unlockHold() {
    seeklock=false;
    vollock=false;
    eqlock=-1;
}

function lockseek() {
    seeklock=true;

    seekpos(); // initial
}
function lockvol() {
    vollock=true;

    setvol(); // initial
}
function lockeq(x) {
    eqlock=x;
    
    seteq(eqlock); // initial
}

function seekpos() {
    var elm=$('bar').getBoundingClientRect();

    var ilX=plX - elm.left;
    var ilY=plY - elm.top;
    var iPercent = ilX * 100 / elm.width; // 0 - 100
    //alert("mouseX = "+ilX+" mouseY = "+ilY+" percent = "+iPercent);
    iPercent=clamp(iPercent,0,100);

    $('progress').style.width = iPercent+"%";

    var seekpos = iPercent * idxDuration / 100;
    $('myAudio').currentTime = seekpos;
}

function setvol() {
    var elm=$('vol').getBoundingClientRect();

    var ilX=plX - elm.left;
    var ilY=plY - elm.top;
    var iPercent = ilX * 100 / elm.width; // 0 - 100
    //alert("mouseX = "+ilX+" mouseY = "+ilY+" percent = "+iPercent);
    iPercent=clamp(iPercent,0,100);

    $('level').style.width = iPercent+"%";

    volume = parseInt(iPercent);

    $('myAudio').volume = volume/100;

    localStorage.setItem("volume",iPercent);
}

function loadvol() {
    if(localStorage.getItem("volume")) {
        var iPercent=localStorage.getItem("volume")

        $('level').style.width = iPercent+"%";

        volume = parseInt(iPercent);

        $('myAudio').volume = volume/100;
    }
}

var ryg=[
    [1,175,1],[0,255,44], // green
    [175,175,0],[255,255,0], // yellow
    [175,50,0],[255,50,0] // red
];
var rcolor1=[0,0,0];
var rcolor2=[0,0,0];
var arpha=0.0;
function coloreq(x,iPercent) {
    // lerp [0][0-2] to [2][0-2] and [1][0-2] to [3][0-2] 0-50
    // lerp [2][0-2] to [4][0-2] and [3][0-2] to [5][0-2] 50-100
    var ridx=0;
    arpha=iPercent/50;
    if(iPercent>50) {
        ridx=2;
        arpha-=1.0;
    }
    rcolor1[0]=lerp(ryg[ridx][0],ryg[ridx+2][0],arpha);
    rcolor1[1]=lerp(ryg[ridx][1],ryg[ridx+2][1],arpha);
    rcolor1[2]=lerp(ryg[ridx][2],ryg[ridx+2][2],arpha);

    rcolor2[0]=lerp(ryg[ridx+1][0],ryg[ridx+3][0],arpha);
    rcolor2[1]=lerp(ryg[ridx+1][1],ryg[ridx+3][1],arpha);
    rcolor2[2]=lerp(ryg[ridx+1][2],ryg[ridx+3][2],arpha);
    //
    $('eq'+x).style.background="linear-gradient(270deg, rgb("+rcolor1+"), rgb("+rcolor2+"))";
    //console.log("arpha = "+arpha+", ");
    //console.log("rcolor1="+rcolor1.toString()+" rcolor2="+rcolor2.toString());
}

function seteq(x) {
    var elm=$('eq'+x).parentNode.getBoundingClientRect();

    var ilX=plX - elm.left;
    var ilY=plY - elm.top;
    var iPercent = 100 - (ilY * 100 / elm.height); // 0 - 100
    //alert("mouseX = "+ilX+" mouseY = "+ilY+" percent = "+iPercent);
    iPercent=clamp(iPercent,0,100);

    $('eq'+x).style.height = iPercent+"%";

    coloreq(x,iPercent);

    // convert 0 - 100 iPercent to
    // -20 to 0 to 20 where 0 = 50
    audioFilters[x][1] = parseFloat((iPercent * .4) - 20);

    //volume = parseInt(iPercent);

    //$('myAudio').volume = volume/100;
    updateeq();
    saveeq();
}
var eqpreset=[
    [50,50,50,50,50,50,50,50,50,50], // flat 
    [42,52,58,38,50,40,30,50,46,40], // preset1
    [45,60,83,45,35,30,30,70,80,50], // preset2
    [36,45,65,80,70,70,85,80,50,44], // preset3
    [35,48,65,75,86,86,75,65,48,35], // preset4
];

function loadpreset(x) {
    loadeq(x);
}

var eqloaded=0;
var eqtrig=0;
function updateeq() {
    eqtrig=1;
    if(eqloaded==0) {
        eqloaded=1;
    } else if(eqloaded==1) {
        if(!audioContextInitiated) {
            createAudioContext();
        }
        eqloaded=2;
    } else if(eqloaded==2) {
        eqtrig=0;
        for(var i=0;i<audioFilters.length;i++) {
            audioFilters[i][2].gain.value=audioFilters[i][1];
            //console.log("update gain eq "+i+" to "+audioFilters[i][1]);
        }
    }
}

function saveeq() {
    var arr=[];
    var iPercent=0;
    for(var i=0;i<audioFilters.length;i++) {
        iPercent=(audioFilters[i][1]+20)*2.5; // realign and scale
        iPercent=Math.floor(clamp(iPercent,0,100));
        arr.push(iPercent);
        //console.log("convert "+audioFilters[i][1]+" to "+iPercent);
    }
    //console.log(JSON.stringify(arr));
    localStorage.setItem("eqdata",JSON.stringify(arr));
}

function loadeq(f=-1) {
    var arr;
    var iPercent=0;
    if(localStorage.getItem("eqdata") && f==-1) {
        var data=localStorage.getItem("eqdata")
        arr=JSON.parse(data);
    } else {
        if(f==-1) f=1; // default load

        arr=eqpreset[f];
    }
    for(var i=0;i<arr.length;i++) {
        iPercent=arr[i];
        $('eq'+i).style.height = iPercent+"%";
        
        coloreq(i,iPercent);

        audioFilters[i][1] = parseFloat((iPercent * .4) - 20);
        //console.log("loading eq"+i+" = "+iPercent);
    }

    if(eqloaded==1 && f==-1) {
        eqloaded=1;
    } else {
        updateeq();
        saveeq();
    }
}

function dl() {
    //alert(idxPlaying+".mp3");
    downloadResource('//twily.info/s/firefox/mp3/'+t+".mp3","twily-album1-track"+idxPlaying+".mp3");
}

function forceDownload(blob, filename) {
  var a = document.createElement('a');
  a.download = filename;
  a.href = blob;
  // For Firefox https://stackoverflow.com/a/32226068
  document.body.appendChild(a);
  a.click();
  a.remove();
}

// Current blob size limit is around 500MB for browsers
function downloadResource(url, filename) {
  if (!filename) filename = url.split('\\').pop().split('/').pop();
  fetch(url, {
      headers: new Headers({
        'Origin': location.origin
      }),
      mode: 'cors'
    })
    .then(response => response.blob())
    .then(blob => {
      let blobUrl = window.URL.createObjectURL(blob);
      forceDownload(blobUrl, filename);
    })
    .catch(e => console.error(e));
}

var audioFilters=[ // hz,db,obj
    [60,0.0,null],
    [170,0.0,null],
    [310,0.0,null],
    [600,0.0,null],
    [1000,0.0,null],
    [3000,0.0,null],
    [6000,0.0,null],
    [12000,0.0,null],
    [14000,0.0,null],
    [16000,0.0,null]
];

var audioContextInitiated=false;
var audioContext, source, analyser, bufferLength, dataArray;
var canvas,canvasCtx;
function createAudioContext() {
    audioContextInitiated=true;

    audioContext = new (window.AudioContext || window.webkitAudioContext)();
    
    // Create a source node from the audio element
    source = audioContext.createMediaElementSource($('myAudio'));
    
    // Create an AnalyserNode for audio analysis
    analyser = audioContext.createAnalyser();
    analyser.fftSize = 128; // Number of samples for FFT (higher = more detail)
    bufferLength = analyser.frequencyBinCount; // Half of fftSize
    dataArray = new Uint8Array(bufferLength); // Array to store frequency data
    
    for(var i=0;i<audioFilters.length;i++) {
        audioFilters[i][2] = audioContext.createBiquadFilter();
        var type="lowshelf";
        if(i>6) {
            type="highshelf";
        } else if(i>3) {
            type="peaking";
        }
        audioFilters[i][2].type=type;
        audioFilters[i][2].frequency.value=audioFilters[i][0];
    }
    updateeq();
    
    // Connect the audio source to the analyser and then to the output (speakers)
    source.connect(analyser);

    analyser.connect(audioFilters[0][2]);
    for(var i=0;i<audioFilters.length-1;i++) {
        audioFilters[i][2].connect(audioFilters[(i+1)][2]);
    }
    //analyser.connect(audioContext.destination);
    audioFilters[9][2].connect(audioContext.destination);

    canvas = $('visualizer');
    canvasCtx = canvas.getContext("2d"); 
    // Start the visualization loop
    draw();
}

// Visualization function
var barWidth,barHeight;
var grad,red,green;
function draw() {
  // Schedule the next frame
  requestAnimationFrame(draw);

  // Get the current frequency data
  analyser.getByteFrequencyData(dataArray);

  // Clear the canvas
  canvasCtx.clearRect(0, 0, canvas.width, canvas.height);
  //console.log(canvas.width+"x"+canvas.height);

  // Calculate bar width for the spectrum
  barWidth = ((canvas.width - 3) / bufferLength) * 2.5; // -3 adjust right edge last bar
  let x = 0;

  // Draw bars for each frequency bin
  for (let i = 0; i < bufferLength; i++) {
    barHeight = dataArray[i]; // Amplitude value (0-255)

    //grad = canvasCtx.createLinearGradient(0, 0, 0, barHeight);
    grad = canvasCtx.createLinearGradient(0, canvas.height / 4, 0, canvas.height);
    red=barHeight-50;
    green=255-red;
    grad.addColorStop(0, "rgb(255,50,0)");
    grad.addColorStop(0.5, "rgb(255,255,0)");
    grad.addColorStop(1, "rgb(0,127,0)");

    //canvasCtx.fillStyle = `rgb(${barHeight + 100}, 50, 50)`; // Dynamic color
    canvasCtx.fillStyle = grad;
    canvasCtx.fillRect(x, canvas.height - barHeight / 2, barWidth, barHeight / 2);
    x += barWidth + 1; // Move to the next bar with a small gap
  }
}

function newSize() {
    wW=window.innerWidth;
    wH=window.innerHeight;

    wH2=wH/2;
    
    //bW=wW;
    bH=wW * bgAspect;

    bH2 = bH/2;
}

function init() {
    $('eqbrd').style.border="none"; // default off
    listtog(); // default on
    
    $('myAudio').addEventListener('loadedmetadata',function() {
        idxDuration = $('myAudio').duration;
        //console.log(idxDuration);
    });
    idxPlaying=1;
    clearplay();

    preloadSpaceship();

    newSize();
    keepAlive();

    loadvol();
    loadeq();
}

var shipOpa=0;
var lastShipOpa=0;
var shipOn=false;
var shipLoaded=false;
function toggle_direction() {
    $('star_frm').src=$('star_frm').src;
}
function toggle_ship() {
    if(!shipLoaded) return;
    shipOn=!shipOn;

    if(shipOn) {
        $('btn_ship').innerHTML="[ Float through space ]";
    } else {
        $('btn_ship').innerHTML="[ Ride spaceship ]";
    }
}
async function preloadSpaceship() {
    var img=new Image();
    img.onload=function() {
        shipLoaded=true;
        $('spaceship').style.background="url("+this.src+") no-repeat center center transparent";
        $('spaceship').style.backgroundSize="cover";
        toggle_ship();
    }
    img.src="./spaceship.png";
}
</script>
</head>
<body onload="init();" onmousemove="mousehandle(event);" onmouseup="unlockHold();" ontouchmove="mousehandle(event);" ontouchend="unlockHold();">


<div id="backdrop">
<iframe src="stars-transparent.html" frameborder="0" class="frm" id="star_frm"></iframe>
</div>

<div id="spaceship"></div>
<div id="backlay"></div>

<!-- Audio element with controls and a sample audio file -->
<audio loop id="myAudio">
  <source src="1.mp3" type="audio/mpeg">
  Your browser does not support the audio element.
</audio>

<div id="above">
<div class="tbl" style="height: 100%;">
    <div class="tr" style="height: 50px;">
        <div class="td" style="text-align: center; vertical-align: middle;">
            Twily AI Album #1
        </div>
    </div>
    <div class="tr">
        <div class="td">
        
        <div class="tbl">
            <div class="tr">
                <div class="td sidebar">
                    &nbsp;
                </div>
                <div class="td">

<center>
<div id="player">

<div class="tbl">
<div class="tr">
<div class="td" style="background: rgba(25,32,70,1.0); background: linear-gradient(0deg, #192046, #1e275c); /*#46192e, #172158);*/ border-radius: 2px;">


<!-- player -->
<div class="tbl" style="height: 100%;">
<div class="tr">
<div class="td" style="width: 150px; height: 100%; /* ff fix */">

<!-- player left -->
<div class="pad18" style="height: 100%;">
<div id="display">
<span id="triangle"></span>
<span id="time">00:00</span>
<!-- Canvas for visualization -->
<canvas id="visualizer"></canvas>
</div>
</div>

</div>
<div class="td">

<!-- player right -->

<div class="pad18">
<!--<div id="title"><marquee><div id="inner">1. </div></marquee></div>-->
<div id="title"><div id="inner">1. </div></div>
<br />
<div id="bar" onmousedown="lockseek();" ontouchstart="lockseek();">
<div id="progress"></div>
</div>
<br />
<div id="controls">
    <input type="button" class="btn" value="⊲" onclick="prev();" />
    <input type="button" class="btn" value="►" id="btnplaypause" onclick="playpause();" />
    <input type="button" class="btn" value="⊳" onclick="next();" />
    <input type="button" class="btn" value="≠" id="btnshuftog" onclick="shuftog();" /><label class="grey" id="lbshuf">Random</label>
    <input type="button" class="btn" value="∞" id="btnlooptog" onclick="looptog();" /><label class="grey" id="lbloop">Loop one</label>
&nbsp;
    <input type="button" class="btn" value="⏏" onclick="dl();" title="Download" alt="Download" />
<div style="float: right;">
<input type="button" class="btn" value="≣" id="btnlisttog" onclick="listtog();" />
<input type="button" class="btn" value="~" id="btneqtog" onclick="eqtog();" />
<label id="lbvol">Vol:</label><div id="vol" onmousedown="lockvol();" ontouchstart="lockvol();">
<div id="level"></div>
</div>
</div>
<div style="clear:both;"></div>
</div>
</div>

</div>
</div>
</div>


</div>
</div>
<div class="tr">
<div class="td brd" id="eqbrd" style="background: rgba(25,32,70,1.0); background: linear-gradient(0deg, #1e275c, #192046); /*#46192e, #172158);*/ border-radius: 2px;">

<div class="pad18" id="eqfrm" style="display: none;">
<!--Equalizer
<br />-->

<center>
<div class="tbl">
<div class="tr">
<div class="td" style="vertical-align: top; text-align: left;">
<!-- left -->
<input type="button" class="btn" value="flat" onclick="loadpreset(0);" />
<br />
</div>
<div class="td">

<div class="tbl" style="width: 75%;"> <!-- db from +20 to 0 to -20 -->
<div class="tr">
<div class="td" style="width: 9%;">

<div class="tbl" style="height: 100%;text-align: right;">
<div class="tr">
<div class="td" style="vertical-align: top; padding-bottom: 6px;">
<label class="grey">+20</label>
</div>
</div>
<div class="tr">
<div class="td">
<label class="grey">-</label>
</div>
</div>
<div class="tr">
<div class="td" style="vertical-align: bottom; padding-top: 6px;">
<label class="grey">-20</label>
</div>
</div>
</div>

</div>
<div class="td" style="width: 9%;">
<div class="eqbar" onmousedown="lockeq(0);" ontouchstart="lockeq(0);">
<div class="eqlevel" id="eq0"></div>
</div>
</div>
<div class="td" style="width: 9%;">
<div class="eqbar" onmousedown="lockeq(1);" ontouchstart="lockeq(1);">
<div class="eqlevel" id="eq1"></div>
</div>
</div>
<div class="td" style="width: 9%;">
<div class="eqbar" onmousedown="lockeq(2);" ontouchstart="lockeq(2);">
<div class="eqlevel" id="eq2"></div>
</div>
</div>
<div class="td" style="width: 9%;">
<div class="eqbar" onmousedown="lockeq(3);" ontouchstart="lockeq(3);">
<div class="eqlevel" id="eq3"></div>
</div>
</div>
<div class="td" style="width: 9%;">
<div class="eqbar" onmousedown="lockeq(4);" ontouchstart="lockeq(4);">
<div class="eqlevel" id="eq4"></div>
</div>
</div>
<div class="td" style="width: 9%;">
<div class="eqbar" onmousedown="lockeq(5);" ontouchstart="lockeq(5);">
<div class="eqlevel" id="eq5"></div>
</div>
</div>
<div class="td" style="width: 9%;">
<div class="eqbar" onmousedown="lockeq(6);" ontouchstart="lockeq(6);">
<div class="eqlevel" id="eq6"></div>
</div>
</div>
<div class="td" style="width: 9%;">
<div class="eqbar" onmousedown="lockeq(7);" ontouchstart="lockeq(7);">
<div class="eqlevel" id="eq7"></div>
</div>
</div>
<div class="td" style="width: 9%;">
<div class="eqbar" onmousedown="lockeq(8);" ontouchstart="lockeq(8);">
<div class="eqlevel" id="eq8"></div>
</div>
</div>
<div class="td" style="width: 9%;">
<div class="eqbar" onmousedown="lockeq(9);" ontouchstart="lockeq(9);">
<div class="eqlevel" id="eq9"></div>
</div>
</div>
</div>
<div class="tr">
<div class="td center"><label class="grey">&nbsp;</label></div>
<div class="td center"><label class="grey">60</label></div>
<div class="td center"><label class="grey">170</label></div>
<div class="td center"><label class="grey">310</label></div>
<div class="td center"><label class="grey">600</label></div>
<div class="td center"><label class="grey">1K</label></div>
<div class="td center"><label class="grey">3K</label></div>
<div class="td center"><label class="grey">6K</label></div>
<div class="td center"><label class="grey">12K</label></div>
<div class="td center"><label class="grey">14K</label></div>
<div class="td center"><label class="grey">16K</label></div>
</div>
</div>

</div>
<div class="td" style="vertical-align: top; text-align: right;">
<!-- right -->
<input type="button" class="btn" value="preset1" onclick="loadpreset(1);" />
<br />
<input type="button" class="btn" value="preset2" onclick="loadpreset(2);" />
<br />
<input type="button" class="btn" value="preset3" onclick="loadpreset(3);" />
<br />
<input type="button" class="btn" value="preset4" onclick="loadpreset(4);" />
<br />
</div>
</div>
</div>
</center>

</div><!--pad18-->

</div>
</div>
<div class="tr">
<div class="td brd" id="listbrd" style="background: rgba(16,20,38,.5);">

<!-- list -->

<div class="pad18" id="listfrm">

</div>

</div>
</div>
</div>

</div> <!-- end player -->
</center>


                </div>
                <div class="td sidebar">
                    &nbsp;
                </div>
            </div>
        </div>
        
        </div>
    </div>
    <div class="tr" style="height: 50px;">
        <div class="td" style="text-align: center; vertical-align: middle;">
<div id="jswarning">This website requires JavaScript to function</div>
<br />
    Runtime: <span id="runtime"></span>
<br />
<br />
            (produced with MusicGen~)
<br />
<br />
            (ɔ) Twily 2025
<br />
<br />
        </div>
    </div>
</div>
</div>

<a href="javascript:toggle_ship();" target="_self" id="btn_ship" class="lnkbtn">[ Loading spaceship ]</a>
<a href="javascript:toggle_direction();" target="_self" id="btn_dir" class="lnkbtn">[ Change direction ]</a>

<script type="text/javascript">
$('jswarning').style.display="none";

var tracks=[
    "1",90,
    "2",90,
    "3",90,
    "4",90,
    "5",90,
    "6",90,
    "7",90,
    "8",90,
    "9",90,
    "10",90,
    "11",80,
    "12",80,
    "13",80,
    "14",80,
    "15",80,
    "16",90,
    "17",90,
    "18",90,
    "19",90,
    "20",90,
    "21",90,
    "22",90,
    "23",90,
    "24",90,
    "25",90,
    "26",90,
    "27",90,
    "28",90,
    "29",90,
    "30",90,
    "31",90,
    "32",90,
    "33",90,
    "34",90
];
var c=0;
var playmins=0;
var playsecs=0;
var maxtry=0;
for(var i=0;i<tracks.length;i+=2) {
    c++;
    let t=tracks[i];
    let secs=tracks[i+1];
    playsecs+=secs;
    playing="";
    //if(c==1) {
    //    playing=" selected";
    //}
    maxtry=1000;
    var mins=0;
    while(secs>=60 && maxtry>0) {
        secs-=60;
        mins++;
        maxtry--;
        //console.log('maxtry1: '+maxtry);
    }

    let html="<a href=\"javascript:play("+c+");\" class=\"track\" id=\"aud"+c+"\">";
    html+="<div class=\"tbl\">";
    html+="<div class=\"tr\">";
    html+="<div class=\"td\" style=\"width: 24px;\">";
    html+="<span class=\"playing"+playing+"\">";
    html+="</div>";
    html+="<div class=\"td\" style=\"width: 48px;\">";
    html+="#"+c+"";
    html+="</div>";
    html+="<div class=\"td\">";
    html+="Track"+t+"";
    html+="</div>";
    html+="<div class=\"td\" style=\"width: 50px;\">";
    html+=""+mins+":"+secs+"";
    html+="</div>\n";
    html+="</div>";
    html+="</div>";
    html+="<input type=\"hidden\" class=\"mp3name\" value=\""+t+"\" />";
    html+="</a>\n\n";
    $('listfrm').insertAdjacentHTML('beforeend',html);
}
maxtry=1000;
while(playsecs>60 && maxtry>0) {
    playmins++;
    playsecs-=60;
    maxtry--;
    //console.log('maxtry2: '+maxtry);
}
$('runtime').insertAdjacentHTML('beforeend',playmins+" min "+playsecs+" sec");

var rzT;
window.addEventListener("resize",function() {
    clearTimeout(rzT);
    rzT=setTimeout(function() { newSize(); }, 100);
});

// grok visualizer implement

//Adapting for Volume
//
//To visualize volume instead, you can use time-domain data:
//
//Replace getByteFrequencyData(dataArray) with getFloatTimeDomainData(timeDataArray) (where timeDataArray is a Float32Array of length analyser.fftSize).
//
//
//
//Calculate the root mean square (RMS) to estimate volume:
//
//javascript
//
//
//const timeDataArray = new Float32Array(analyser.fftSize);
//analyser.getFloatTimeDomainData(timeDataArray);
//let sum = 0;
//for (let i = 0; i < timeDataArray.length; i++) {
//  sum += timeDataArray[i] * timeDataArray[i];
//}
//const rms = Math.sqrt(sum / timeDataArray.length); // Volume level
//
//
//Use rms to adjust the height of a single bar or scale a waveform.
//
//Notes on Pitch
//
//Extracting pitch directly is more complex and requires pitch
// detection algorithms (e.g., autocorrelation or finding the 
// dominant frequency from the frequency data). The frequency 
// spectrum provided by getByteFrequencyData gives raw frequency
// amplitudes, which can hint at pitch but don’t directly isolate 
// it for complex sounds. For a simple visualization, the spectrum i
// s often sufficient, as pitch relates to frequency content.

</script>

</body>
</html>

Top
©twily.info 2013 - 2025
twily at twily dot info



2 461 127 visits
... ^ v