You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
0x40-web/index.html

431 lines
18 KiB

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en" >
<!--
TODO:
Clean up the damn code
if you're not mon, don't read any further, it's pretty bad JS
Loading screen
Volume controls
Prettier ui
Song shuffle
Image pause
Fine tune blur
Keyboard shortcuts
Different colour palettes
External respacks
Change short blackout to beat length / 1.7
Blur into blackout? iunno
-->
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>0x40</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<link rel="stylesheet" href="style.css">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" height="0">
<filter id="blur" filterRes="1">
<feGaussianBlur stdDeviation="0 0">
<animate attributeName="stdDeviation" attributeType="XML"
begin="indefinite" end="indefinite" dur="0.1s"
fill="freeze" from="0 70" to="0 0"/>
<animate attributeName="stdDeviation" attributeType="XML"
begin="indefinite" end="indefinite" dur="0.1s"
fill="freeze" from="70 0" to="0 0"/>
</feGaussianBlur>
</filter>
</svg>
<!-- Won't work in the CSS without referencing index.html, yay -->
<style type='text/css'>
#waifu {
filter: url(#blur);
-webkit-filter: url(#blur);
}
</style>
<script type="text/javascript" src="0x40.js"></script>
<script type="text/javascript">
//debug
var noAutoPlay = false;
var leftToLoad = waifus.length;
var initialLoad = true;
var shortBlackout = false;
// Milliseconds!
var animationLoopTime = 50;
// Seconds!
var animationLookAhead = 0.2;
var tmpSong = {};
//--------------
// Audio Object
//--------------
var audio = {
buffer: {},
proceed: true,
songs: rgSongs,
current_song: null,
current_index: 0,
beat_pointer: 0,
}
audio.play = function() {
if(audio.current_song && audio.current_song._playing) {
return;
}
document.getElementById("songname").innerHTML = audio.songs[audio.current_index].name
document.getElementById("blackout").style.display = "none";
// stop() destroys the old, must recreate
var newSong = audio.context.createBufferSource()
audio.current_song = newSong;
newSong.buffer = audio.buffer[audio.current_index];
newSong.loop = true;
newSong.loopStart = audio.buffer[audio.current_index]._loopStart;
newSong.loopEnd = audio.buffer[audio.current_index].duration;
newSong._loopLength = audio.buffer[audio.current_index]._loopLength;
newSong.connect(audio.context.destination);
var songInfo = audio.songs[audio.current_index];
newSong._build = songInfo.buildUpRhythm || "";
newSong._beats = newSong._build + songInfo.rhythm;
audio.beat_pointer = -newSong._build.length;
newSong._loopWidth = newSong._loopLength / songInfo.rhythm.length;
newSong._buildWidth = newSong._build ? newSong.loopStart / newSong._build.length : 0;
newSong._beatWidth = newSong._buildWidth;
document.getElementById("beets").innerHTML = wrapBeats(0, 100);
document.getElementById("timer").innerHTML = "T=0x0000"
newSong.start(0);
// offset to after the build
newSong._startTime = audio.context.currentTime + newSong.loopStart;
newSong._playing = true;
}
audio.stop = function() {
if (audio.current_song && audio.current_song._playing) {
audio.current_song.stop();
audio.current_song._playing = false;
audio.current_song._startTime = 0;
}
}
audio.next = function() {
audio.current_index++;
audio.current_index %= audio.songs.length;
audio.playIndex(audio.current_index);
}
audio.prev = function() {
if(audio.current_index-- <= 0) {
audio.current_index = audio.songs.length - 1;
}
audio.playIndex(audio.current_index);
}
audio.playIndex = function(index) {
audio.current_index = index;
if(audio.buffer[index]) {
audio.stop();
audio.play();
} else {
loadSong(index);
}
}
// In seconds, relative to the loop start
function currentTime() {
var cur = audio.current_song;
var time = audio.context.currentTime - cur._startTime;
return time;// < 0 ? time : time % cur._loopLength;
}
function wrapBeats(start, length) {
var ret = '';
for(var i=start; i < start+length; i++) {
ret += getBeat(i);
}
return ret;
}
function loopBeat(index) {
return index %
(audio.current_song._beats.length-audio.current_song._build.length);
}
function getBeat(index) {
var cur = audio.current_song;
if(index < 0) {
return cur._beats.charAt(index + cur._build.length);
} else {
return cur._beats.charAt(loopBeat(index) + cur._build.length);
}
}
function timerLoop() {
// 60fps-ish
setTimeout(timerLoop, 16);
if(!audio.current_song || !audio.current_song._playing) {
return;
}
var now = currentTime();
if(now < 0)
return;
now %= audio.current_song._loopLength;
now = parseInt(now * 1000);
document.getElementById("timer").innerHTML = "T=0x" + pad(now.toString(16).toUpperCase(), 4);
}
function animationLoop() {
setTimeout(animationLoop, animationLoopTime);
var cur = audio.current_song;
if(!cur || !cur._playing)
return;
var now = currentTime();
var future = now + animationLookAhead;
if(audio.beat_pointer >=0)
cur._beatWidth = cur._loopWidth;
for(var beatTime = audio.beat_pointer * cur._beatWidth; beatTime < future;
beatTime = ++audio.beat_pointer * cur._beatWidth) {
var beat = getBeat(audio.beat_pointer);
var timeout = (beatTime - now)*1000;
// Either lagging behind or first note
if(timeout < 0)
timeout = 0;
setTimeout(
handleBeat, timeout,
beat, audio.beat_pointer, RequestNextColor(), nCurrentColor, cur);
}
}
function handleBeat(beat, bp, c, cc, current) {
// we have changed song since we were scheduled
if(current != audio.current_song)
return;
document.getElementById("beets").innerHTML = wrapBeats(bp+1, 100);
// I probably shouldn't have so much on one line
document.getElementById("beatCount").innerHTML = "B=0x" + pad(bp < 0 ? 0: loopBeat(bp).toString(16).toUpperCase(), 4);
if(beat != '.') {
if(beat == '|') {
shortBlackout = true;
document.getElementById("blackout").style.display = "block";
return;
}
if(beat == '+') {
document.getElementById("blackout").style.display = "block";
return;
} else {
document.getElementById("blackout").style.display = "none";
}
if(beat != ':' && beat != 'O' && beat != 'X') {
var waifu = GetRandomWaifu();
var waifuDiv = document.getElementsByClassName('waifuImg')[nCurrentWaifu];
current = document.getElementsByClassName('active');
if(current.length) {
current[0].style.display = "none";
current[0].className = "waifuImg";
}
waifuDiv.style.display = "block";
waifuDiv.className = "waifuImg active";
document.getElementById("waifuName").innerHTML = waifu.name;
}
if(beat != '*' && beat != 'X' && beat != 'O') {
document.getElementById("waifuColour").style.backgroundColor = c;
document.getElementById("colourName").innerHTML = colors[cc];
}
if(beat.toLowerCase() == 'o') {
$("#blur animate").get(1).beginElement();
}
if(beat.toLowerCase() == 'x') {
$("#blur animate").get(0).beginElement();
}
}
if(shortBlackout) {
document.getElementById("blackout").style.display = "none";
shortBlackout = false;
}
}
function loadSong(index) {
leftToLoad++;
tmpSong[0] = tmpSong[1] = null;
if(audio.songs[index].buildUp) {
leftToLoad++;
loadAudio(audio.songs[index].buildUp, index, 0);
}
loadAudio(audio.songs[index].file, index, 1);
}
function loadAudio(filename, index, tmpArray) {
var req = new XMLHttpRequest();
req.open('GET', filename, true);
req.responseType = 'arraybuffer';
req.onload = function() {
audio.context.decodeAudioData(
req.response,
function(buffer) {
tmpSong[tmpArray] = trimSilence(buffer);
if(!--leftToLoad) {
onFilesLoaded(index);
}
},
function() {
console.log('Error decoding audio "' + filename + '".');
}
);
};
req.send();
}
function onFilesLoaded(index) {
var buf;
// is there a buildup?
if(tmpSong[0]) {
buf = concatenateAudioBuffers(tmpSong[0],tmpSong[1]);
} else {
buf = tmpSong[1];
}
buf._loopStart = tmpSong[0] ? tmpSong[0].duration : 0;
buf._loopLength = buf.duration - buf._loopStart;
audio.buffer[index] = buf;
if(initialLoad) {
document.getElementById('waifu').className = "loaded";
initialLoad = false;
if(noAutoPlay)
return;
}
audio.playIndex(index);
}
// because MP3 is bad
function trimSilence(buffer) {
// how much silence we have
var minSilence = buffer.length;
var maxSilence = 0;
for(var i=0; i<buffer.numberOfChannels; i++) {
var tmp = buffer.getChannelData(i);
for(var j=0; j < tmp.length; j++) {
// end of silence
if(tmp[j] != 0) {
if(j < minSilence) {
minSilence = j;
}
break;
}
}
// because just padding 1 end isn't enough for this codec
for(var j=tmp.length-1; j >= 0 ; j--) {
if(tmp[j] != 0) {
if(j > maxSilence) {
maxSilence = j;
}
break;
}
}
}
// 1152 = one frame, makes the sync better because ID3 tags
// take up that space or some garbage
var ret = audio.context.createBuffer(buffer.numberOfChannels, maxSilence-minSilence-1152, buffer.sampleRate);
for(var i=0; i<buffer.numberOfChannels; i++) {
var oldBuf = buffer.getChannelData(i);
var newBuf = ret.getChannelData(i);
for(var j=0; j<ret.length; j++) {
newBuf[j] = oldBuf[minSilence + j + 1152];
}
}
return ret;
}
function concatenateAudioBuffers(buffer1, buffer2) {
if (!buffer1 || !buffer2) {
console.log("no buffers!");
return null;
}
if (buffer1.numberOfChannels != buffer2.numberOfChannels) {
console.log("number of channels is not the same!");
return null;
}
if (buffer1.sampleRate != buffer2.sampleRate) {
console.log("sample rates don't match!");
return null;
}
var tmp = audio.context.createBuffer(buffer1.numberOfChannels, buffer1.length + buffer2.length, buffer1.sampleRate);
for (var i=0; i<tmp.numberOfChannels; i++) {
var data = tmp.getChannelData(i);
data.set(buffer1.getChannelData(i));
data.set(buffer2.getChannelData(i),buffer1.length);
}
return tmp;
};
window.onload = function() {
// Check Web Audio API Support
try {
// More info at http://caniuse.com/#feat=audio-api
window.AudioContext = window.AudioContext || window.webkitAudioContext;
audio.context = new window.AudioContext();
} catch(e) {
audio.proceed = false;
alert('Web Audio API not supported in this browser.');
}
// preloads, don't update leftToLoad as that's up top
for(var waifu in waifus) {
o = document.createElement('img');
o.src = "images/" + waifus[waifu].file;
o.onload = function() {if(!--leftToLoad) onFilesLoaded(0);};
}
// actual images
var container = document.getElementById('waifu');
for(var waifu in waifus) {
o = document.createElement('div');
o.style.backgroundImage = 'url("images/' + waifus[waifu].file + '")';
o.className = "waifuImg";
container.appendChild(o);
}
var megumi = document.getElementsByClassName("waifuImg")[0];
megumi.style.display = "block";
megumi.className = "waifuImg active";
setTimeout( function() {
if (audio.proceed) {
audio.playIndex(0);
animationLoop();
timerLoop();
}
}, 400);
};
</script>
</head>
<body>
<div id="controls">
<a href="#" onclick="void(audio.stop());">stop</a>
<a href="#" onclick="void(audio.play());">play</a>
<a href="#" onclick="void(audio.prev());">prev</a>
<a href="#" onclick="void(audio.next());">next</a>
<div id="beatCount">B=0x0000</div>
<div id="timer">T=0x0000</div>
<div id="songname"></div>
<div id="beets"></div>
<div id="waifuName"></div>
<div id="colourName"></div>
</div>
<div id="blackout"></div>
<div id="waifu"></div>
<div id="waifuColour"></div>
</body>
</html>