update to latest libraries
This commit is contained in:
parent
61f1a05ebe
commit
eb21d1f743
31 changed files with 2935 additions and 1227 deletions
|
@ -4,26 +4,25 @@
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
<title>MIDI.js - Sequencing in Javascript.</title>
|
<title>MIDI.js - Sequencing in Javascript.</title>
|
||||||
<!-- soundfont.js css -->
|
<!-- soundfont.js css -->
|
||||||
<link href="./index.css" rel="stylesheet" type="text/css" />
|
<link href="./css/MIDIPlayer.css" rel="stylesheet" type="text/css" />
|
||||||
<!-- soundfont.js package -->
|
<!-- soundfont.js package -->
|
||||||
<script src="./js/Color.js" type="text/javascript"></script>
|
<script src="./js/Color/Space.js" type="text/javascript"></script>
|
||||||
<script src="./js/Event.js" type="text/javascript"></script>
|
<script src="./js/Event.js" type="text/javascript"></script>
|
||||||
<script src="./js/Event.Mouse.js" type="text/javascript"></script>
|
|
||||||
<script src="./js/DOMLoader.XMLHttp.js" type="text/javascript"></script>
|
<script src="./js/DOMLoader.XMLHttp.js" type="text/javascript"></script>
|
||||||
<script src="./js/DOMLoader.script.js" type="text/javascript"></script>
|
<script src="./js/DOMLoader.script.js" type="text/javascript"></script>
|
||||||
<script src="./js/MIDI.audioDetect.js" type="text/javascript"></script>
|
<script src="./js/MIDI/audioDetect.js" type="text/javascript"></script>
|
||||||
<script src="./js/MIDI.loadPlugin.js" type="text/javascript"></script>
|
<script src="./js/MIDI/loadPlugin.js" type="text/javascript"></script>
|
||||||
<script src="./js/MIDI.Plugin.js" type="text/javascript"></script>
|
<script src="./js/MIDI/Plugin.js" type="text/javascript"></script>
|
||||||
<script src="./js/MIDI.Player.js" type="text/javascript"></script>
|
<script src="./js/MIDI/Player.js" type="text/javascript"></script>
|
||||||
<script src="./js/MusicTheory.Synesthesia.js" type="text/javascript"></script>
|
<script src="./js/MusicTheory/Synesthesia.js" type="text/javascript"></script>
|
||||||
<script src="./js/Widgets.Loader.js" type="text/javascript"></script>
|
<script src="./js/Widgets/Loader.js" type="text/javascript"></script>
|
||||||
<!-- jasmid package -->
|
<!-- jasmid package -->
|
||||||
<script src="./js/lib/jasmid/stream.js"></script>
|
<script src="./inc/jasmid/stream.js"></script>
|
||||||
<script src="./js/lib/jasmid/midifile.js"></script>
|
<script src="./inc/jasmid/midifile.js"></script>
|
||||||
<script src="./js/lib/jasmid/replayer.js"></script>
|
<script src="./inc/jasmid/replayer.js"></script>
|
||||||
<!-- base64 packages -->
|
<!-- base64 packages -->
|
||||||
<script src="./js/VersionControl.Base64.js" type="text/javascript"></script>
|
<script src="./js/Polyfill/Base64.js" type="text/javascript"></script>
|
||||||
<script src="./js/lib/base64binary.js" type="text/javascript"></script>
|
<script src="./inc/base64binary.js" type="text/javascript"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<img src="./images/shiverMeTimbers.gif" style="position: fixed; top: 30px; left: 1300px; z-index: 4">
|
<img src="./images/shiverMeTimbers.gif" style="position: fixed; top: 30px; left: 1300px; z-index: 4">
|
||||||
|
@ -35,8 +34,8 @@
|
||||||
<div style="margin: 0 auto; width: 160px; float: right;">
|
<div style="margin: 0 auto; width: 160px; float: right;">
|
||||||
<input type="image" src="./images/pause.png" align="absmiddle" value="pause" onclick="pausePlayStop()" id="pausePlayStop">
|
<input type="image" src="./images/pause.png" align="absmiddle" value="pause" onclick="pausePlayStop()" id="pausePlayStop">
|
||||||
<input type="image" src="./images/stop.png" align="absmiddle" value="stop" onclick="pausePlayStop(true)">
|
<input type="image" src="./images/stop.png" align="absmiddle" value="stop" onclick="pausePlayStop(true)">
|
||||||
<input type="image" src="./images/backward.png" align="absmiddle" value="stop" onclick="getNextSong(-1);">
|
<input type="image" src="./images/backward.png" align="absmiddle" value="stop" onclick="player.getNextSong(-1);">
|
||||||
<input type="image" src="./images/forward.png" align="absmiddle" value="stop" onclick="getNextSong(+1);">
|
<input type="image" src="./images/forward.png" align="absmiddle" value="stop" onclick="player.getNextSong(+1);">
|
||||||
</div>
|
</div>
|
||||||
<div class="time-controls" style="float: left; margin: 0; position: relative; top: 5px;">
|
<div class="time-controls" style="float: left; margin: 0; position: relative; top: 5px;">
|
||||||
<span id="time1" class="time">0:00</span>
|
<span id="time1" class="time">0:00</span>
|
||||||
|
@ -127,11 +126,6 @@ MIDI.Player.setAnimation(function(data) {
|
||||||
<li><a href="http://blog.danguer.com/2011/10/24/base64-binary-decoding-in-javascript/">base642binary.js</a>: Cleans up XML base64-requests for Web Audio API.</li>
|
<li><a href="http://blog.danguer.com/2011/10/24/base64-binary-decoding-in-javascript/">base642binary.js</a>: Cleans up XML base64-requests for Web Audio API.</li>
|
||||||
<h3>Example(s): <a href="http://twitter.com/mudcube">Tweet</a> me your app to be included!</h3>
|
<h3>Example(s): <a href="http://twitter.com/mudcube">Tweet</a> me your app to be included!</h3>
|
||||||
|
|
||||||
<li class="indent square">
|
|
||||||
<a href="http://qiao.github.com/euphony/">Euphony</a><br>3D visualization of a piano with notes dropping from the sky.
|
|
||||||
<br>Coded by <a href="https://github.com/qiao/">Qiao</a>.</li>
|
|
||||||
<hr size=1 style="opacity: 0.25;">
|
|
||||||
|
|
||||||
<li class="indent square">
|
<li class="indent square">
|
||||||
<a href="http://mudcu.be/piano/">Color Piano</a><br>Learn piano songs without reading sheet music.
|
<a href="http://mudcu.be/piano/">Color Piano</a><br>Learn piano songs without reading sheet music.
|
||||||
<br>Coded by <a href="http://mudcu.be/">mud</a>.</li>
|
<br>Coded by <a href="http://mudcu.be/">mud</a>.</li>
|
||||||
|
@ -156,6 +150,7 @@ MIDI.Player.setAnimation(function(data) {
|
||||||
if (typeof(console) === "undefined") var console = { log: function() { } };
|
if (typeof(console) === "undefined") var console = { log: function() { } };
|
||||||
|
|
||||||
// Begin loading indication.
|
// Begin loading indication.
|
||||||
|
var player;
|
||||||
var loader = new widgets.Loader({
|
var loader = new widgets.Loader({
|
||||||
message: "loading: Soundfont..."
|
message: "loading: Soundfont..."
|
||||||
});
|
});
|
||||||
|
@ -212,7 +207,7 @@ Event.add(window, "load", function(event) {
|
||||||
var title = document.getElementById("title");
|
var title = document.getElementById("title");
|
||||||
title.innerHTML = "Sound being generated with " + MIDI.lang + ".";
|
title.innerHTML = "Sound being generated with " + MIDI.lang + ".";
|
||||||
// this sets up the MIDI.Player and gets things going...
|
// this sets up the MIDI.Player and gets things going...
|
||||||
var player = MIDI.Player;
|
player = MIDI.Player;
|
||||||
player.timeWarp = 1; // speed the song is played back
|
player.timeWarp = 1; // speed the song is played back
|
||||||
player.loadFile(song[songid++%3], player.start);
|
player.loadFile(song[songid++%3], player.start);
|
||||||
// control the piano keys colors
|
// control the piano keys colors
|
||||||
|
@ -221,7 +216,7 @@ Event.add(window, "load", function(event) {
|
||||||
var pianoKey = data.note - MIDI.pianoKeyOffset;
|
var pianoKey = data.note - MIDI.pianoKeyOffset;
|
||||||
var d = colorElements[pianoKey];
|
var d = colorElements[pianoKey];
|
||||||
if (data.message === 144) {
|
if (data.message === 144) {
|
||||||
d.style.background = "#" + colorMap[data.note-27].hex;
|
d.style.background = colorMap[data.note-27].hex;
|
||||||
d.style.color = "#fff";
|
d.style.color = "#fff";
|
||||||
} else {
|
} else {
|
||||||
d.style.background = "";
|
d.style.background = "";
|
||||||
|
@ -245,20 +240,16 @@ var MIDIPlayerPercentage = function(player) {
|
||||||
var capsule = document.getElementById("capsule");
|
var capsule = document.getElementById("capsule");
|
||||||
var timeCursor = document.getElementById("cursor");
|
var timeCursor = document.getElementById("cursor");
|
||||||
//
|
//
|
||||||
Event.drag({
|
Event.add(capsule, "drag", function (event, self) {
|
||||||
type: "absolute",
|
Event.cancel(event);
|
||||||
element: capsule,
|
player.currentTime = (self.x) / 420 * player.endTime;
|
||||||
callback: function (event, coords, state, self) {
|
|
||||||
var ab = abPos(capsule);
|
|
||||||
player.currentTime = (coords.x - ab.x) / 420 * player.endTime;
|
|
||||||
if (player.currentTime < 0) player.currentTime = 0;
|
if (player.currentTime < 0) player.currentTime = 0;
|
||||||
if (player.currentTime > player.endTime) player.currentTime = player.endTime;
|
if (player.currentTime > player.endTime) player.currentTime = player.endTime;
|
||||||
if (state === "down") {
|
if (self.state === "down") {
|
||||||
player.pause(true);
|
player.pause(true);
|
||||||
} else if (state === "up") {
|
} else if (self.state === "up") {
|
||||||
player.resume();
|
player.resume();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
//
|
//
|
||||||
function timeFormatting(n) {
|
function timeFormatting(n) {
|
||||||
|
@ -267,7 +258,7 @@ var MIDIPlayerPercentage = function(player) {
|
||||||
if (seconds.length == 1) seconds = "0" + seconds;
|
if (seconds.length == 1) seconds = "0" + seconds;
|
||||||
return minutes + ":" + seconds;
|
return minutes + ":" + seconds;
|
||||||
};
|
};
|
||||||
getNextSong = function(n) {
|
player.getNextSong = function(n) {
|
||||||
var id = Math.abs((songid += n) % song.length);
|
var id = Math.abs((songid += n) % song.length);
|
||||||
player.loadFile(song[id], player.start); // load MIDI
|
player.loadFile(song[id], player.start); // load MIDI
|
||||||
};
|
};
|
||||||
|
@ -314,7 +305,7 @@ var ColorSphereBackground = function() {
|
||||||
var onMouseMove = function(event) {
|
var onMouseMove = function(event) {
|
||||||
ctx.drawImage(theSphere, 0, 0);
|
ctx.drawImage(theSphere, 0, 0);
|
||||||
if (event) {
|
if (event) {
|
||||||
var coords = Event.coords(event);
|
var coords = Event.proxy.getCoord(event);
|
||||||
coords.x -= document.body.scrollLeft;
|
coords.x -= document.body.scrollLeft;
|
||||||
coords.y -= document.body.scrollTop;
|
coords.y -= document.body.scrollTop;
|
||||||
px = coords.x;
|
px = coords.x;
|
|
@ -2,19 +2,19 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<title>Whitney Music Box in HTML5</title>
|
<title>Whitney Music Box in HTML5</title>
|
||||||
<script src="./js/Color.js" type="text/javascript"></script>
|
<script src="./js/Color/Space.js" type="text/javascript"></script>
|
||||||
<script src="./js/Event.js" type="text/javascript"></script>
|
<script src="./js/Event.js" type="text/javascript"></script>
|
||||||
<script src="./js/DOMLoader.XMLHttp.js" type="text/javascript"></script>
|
<script src="./js/DOMLoader.XMLHttp.js" type="text/javascript"></script>
|
||||||
<script src="./js/DOMLoader.script.js" type="text/javascript"></script>
|
<script src="./js/DOMLoader.script.js" type="text/javascript"></script>
|
||||||
<script src="./js/MIDI.audioDetect.js" type="text/javascript"></script>
|
<script src="./js/MIDI/audioDetect.js" type="text/javascript"></script>
|
||||||
<script src="./js/MIDI.loadPlugin.js" type="text/javascript"></script>
|
<script src="./js/MIDI/loadPlugin.js" type="text/javascript"></script>
|
||||||
<script src="./js/MIDI.Plugin.js" type="text/javascript"></script>
|
<script src="./js/MIDI/Plugin.js" type="text/javascript"></script>
|
||||||
<script src="./js/MIDI.Player.js" type="text/javascript"></script>
|
<script src="./js/MIDI/Player.js" type="text/javascript"></script>
|
||||||
<script src="./js/MusicTheory.Synesthesia.js" type="text/javascript"></script>
|
<script src="./js/MusicTheory/Synesthesia.js" type="text/javascript"></script>
|
||||||
<script src="./js/VersionControl.Base64.js" type="text/javascript"></script>
|
<script src="./js/Widgets/Loader.js" type="text/javascript"></script>
|
||||||
<script src="./js/Widgets.Loader.js" type="text/javascript"></script>
|
|
||||||
<!-- base642binary package -->
|
<!-- base642binary package -->
|
||||||
<script src="./js/lib/base64binary.js" type="text/javascript"></script>
|
<script src="./js/Polyfill/Base64.js" type="text/javascript"></script>
|
||||||
|
<script src="./inc/base64binary.js" type="text/javascript"></script>
|
||||||
<!-- google fonts package -->
|
<!-- google fonts package -->
|
||||||
<link href="http://fonts.googleapis.com/css?family=Andada" rel="stylesheet" type="text/css" />
|
<link href="http://fonts.googleapis.com/css?family=Andada" rel="stylesheet" type="text/css" />
|
||||||
<style>
|
<style>
|
||||||
|
|
2
build.sh
2
build.sh
|
@ -5,7 +5,7 @@ OUT=build/MIDI.minimal.js
|
||||||
|
|
||||||
echo "//MIDI.js minimal Browserify wrapper" > $OUT
|
echo "//MIDI.js minimal Browserify wrapper" > $OUT
|
||||||
|
|
||||||
for file in js/DOMLoader.*.js js/VersionControl.Base64.js js/lib/base64binary.js js/MIDI.*.js
|
for file in js/DOMLoader.*.js js/Polyfill/Base64.js inc/base64binary.js js/MIDI.*.js
|
||||||
do
|
do
|
||||||
cat $file >> $OUT
|
cat $file >> $OUT
|
||||||
echo "" >> $OUT
|
echo "" >> $OUT
|
||||||
|
|
507
inc/jsmidi.js
Normal file
507
inc/jsmidi.js
Normal file
|
@ -0,0 +1,507 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
JSMIDI
|
||||||
|
---------
|
||||||
|
https://github.com/sergi/jsmidi
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function (window) {
|
||||||
|
|
||||||
|
var AP = Array.prototype;
|
||||||
|
|
||||||
|
// Create a mock console object to void undefined errors if the console object
|
||||||
|
// is not defined.
|
||||||
|
if (!window.console || !console.firebug) {
|
||||||
|
var names = ["log", "debug", "info", "warn", "error"];
|
||||||
|
|
||||||
|
window.console = {};
|
||||||
|
for (var i = 0; i < names.length; ++i) {
|
||||||
|
window.console[names[i]] = function() {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var DEFAULT_VOLUME = 90;
|
||||||
|
var DEFAULT_DURATION = 128;
|
||||||
|
var DEFAULT_CHANNEL = 0;
|
||||||
|
|
||||||
|
// These are the different values that compose a MID header. They are already
|
||||||
|
// expressed in their string form, so no useless conversion has to take place
|
||||||
|
// since they are constants.
|
||||||
|
|
||||||
|
var HDR_CHUNKID = "MThd";
|
||||||
|
var HDR_CHUNK_SIZE = "\x00\x00\x00\x06"; // Header size for SMF
|
||||||
|
var HDR_TYPE0 = "\x00\x00"; // Midi Type 0 id
|
||||||
|
var HDR_TYPE1 = "\x00\x01"; // Midi Type 1 id
|
||||||
|
var HDR_SPEED = "\x01\x90"; // Defaults to 128 ticks per beat
|
||||||
|
|
||||||
|
// Midi event codes
|
||||||
|
var EVT_NOTE_OFF = 0x8;
|
||||||
|
var EVT_NOTE_ON = 0x9;
|
||||||
|
var EVT_AFTER_TOUCH = 0xA;
|
||||||
|
var EVT_CONTROLLER = 0xB;
|
||||||
|
var EVT_PROGRAM_CHANGE = 0xC;
|
||||||
|
var EVT_CHANNEL_AFTERTOUCH = 0xD;
|
||||||
|
var EVT_PITCH_BEND = 0xE;
|
||||||
|
|
||||||
|
var META_SEQUENCE = 0x00;
|
||||||
|
var META_TEXT = 0x01;
|
||||||
|
var META_COPYRIGHT = 0x02;
|
||||||
|
var META_TRACK_NAME = 0x03;
|
||||||
|
var META_INSTRUMENT = 0x04;
|
||||||
|
var META_LYRIC = 0x05;
|
||||||
|
var META_MARKER = 0x06;
|
||||||
|
var META_CUE_POINT = 0x07;
|
||||||
|
var META_CHANNEL_PREFIX = 0x20;
|
||||||
|
var META_END_OF_TRACK = 0x2f;
|
||||||
|
var META_TEMPO = 0x51;
|
||||||
|
var META_SMPTE = 0x54;
|
||||||
|
var META_TIME_SIG = 0x58;
|
||||||
|
var META_KEY_SIG = 0x59;
|
||||||
|
var META_SEQ_EVENT = 0x7f;
|
||||||
|
|
||||||
|
// This is the conversion table from notes to its MIDI number. Provided for
|
||||||
|
// convenience, it is not used in this code.
|
||||||
|
var noteTable = { "G9": 0x7F, "Gb9": 0x7E, "F9": 0x7D, "E9": 0x7C, "Eb9": 0x7B,
|
||||||
|
"D9": 0x7A, "Db9": 0x79, "C9": 0x78, "B8": 0x77, "Bb8": 0x76, "A8": 0x75, "Ab8": 0x74,
|
||||||
|
"G8": 0x73, "Gb8": 0x72, "F8": 0x71, "E8": 0x70, "Eb8": 0x6F, "D8": 0x6E, "Db8": 0x6D,
|
||||||
|
"C8": 0x6C, "B7": 0x6B, "Bb7": 0x6A, "A7": 0x69, "Ab7": 0x68, "G7": 0x67, "Gb7": 0x66,
|
||||||
|
"F7": 0x65, "E7": 0x64, "Eb7": 0x63, "D7": 0x62, "Db7": 0x61, "C7": 0x60, "B6": 0x5F,
|
||||||
|
"Bb6": 0x5E, "A6": 0x5D, "Ab6": 0x5C, "G6": 0x5B, "Gb6": 0x5A, "F6": 0x59, "E6": 0x58,
|
||||||
|
"Eb6": 0x57, "D6": 0x56, "Db6": 0x55, "C6": 0x54, "B5": 0x53, "Bb5": 0x52, "A5": 0x51,
|
||||||
|
"Ab5": 0x50, "G5": 0x4F, "Gb5": 0x4E, "F5": 0x4D, "E5": 0x4C, "Eb5": 0x4B, "D5": 0x4A,
|
||||||
|
"Db5": 0x49, "C5": 0x48, "B4": 0x47, "Bb4": 0x46, "A4": 0x45, "Ab4": 0x44, "G4": 0x43,
|
||||||
|
"Gb4": 0x42, "F4": 0x41, "E4": 0x40, "Eb4": 0x3F, "D4": 0x3E, "Db4": 0x3D, "C4": 0x3C,
|
||||||
|
"B3": 0x3B, "Bb3": 0x3A, "A3": 0x39, "Ab3": 0x38, "G3": 0x37, "Gb3": 0x36, "F3": 0x35,
|
||||||
|
"E3": 0x34, "Eb3": 0x33, "D3": 0x32, "Db3": 0x31, "C3": 0x30, "B2": 0x2F, "Bb2": 0x2E,
|
||||||
|
"A2": 0x2D, "Ab2": 0x2C, "G2": 0x2B, "Gb2": 0x2A, "F2": 0x29, "E2": 0x28, "Eb2": 0x27,
|
||||||
|
"D2": 0x26, "Db2": 0x25, "C2": 0x24, "B1": 0x23, "Bb1": 0x22, "A1": 0x21, "Ab1": 0x20,
|
||||||
|
"G1": 0x1F, "Gb1": 0x1E, "F1": 0x1D, "E1": 0x1C, "Eb1": 0x1B, "D1": 0x1A, "Db1": 0x19,
|
||||||
|
"C1": 0x18, "B0": 0x17, "Bb0": 0x16, "A0": 0x15, "Ab0": 0x14, "G0": 0x13, "Gb0": 0x12,
|
||||||
|
"F0": 0x11, "E0": 0x10, "Eb0": 0x0F, "D0": 0x0E, "Db0": 0x0D, "C0": 0x0C };
|
||||||
|
|
||||||
|
// Helper functions
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Converts a string into an array of ASCII char codes for every character of
|
||||||
|
* the string.
|
||||||
|
*
|
||||||
|
* @param str {String} String to be converted
|
||||||
|
* @returns array with the charcode values of the string
|
||||||
|
*/
|
||||||
|
function StringToNumArray(str) {
|
||||||
|
return AP.map.call(str, function(str) {
|
||||||
|
return str.charCodeAt(0);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Converts an array of bytes to a string of hexadecimal characters. Prepares
|
||||||
|
* it to be converted into a base64 string.
|
||||||
|
*
|
||||||
|
* @param byteArray {Array} array of bytes that will be converted to a string
|
||||||
|
* @returns hexadecimal string
|
||||||
|
*/
|
||||||
|
function codes2Str(byteArray) {
|
||||||
|
return String.fromCharCode.apply(null, byteArray);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Converts a String of hexadecimal values to an array of bytes. It can also
|
||||||
|
* add remaining "0" nibbles in order to have enough bytes in the array as the
|
||||||
|
* |finalBytes| parameter.
|
||||||
|
*
|
||||||
|
* @param str {String} string of hexadecimal values e.g. "097B8A"
|
||||||
|
* @param finalBytes {Integer} Optional. The desired number of bytes that the returned array should contain
|
||||||
|
* @returns array of nibbles.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function str2Bytes(str, finalBytes) {
|
||||||
|
if (finalBytes) {
|
||||||
|
while ((str.length / 2) < finalBytes) { str = "0" + str; }
|
||||||
|
}
|
||||||
|
|
||||||
|
var bytes = [];
|
||||||
|
for (var i=str.length-1; i>=0; i = i-2) {
|
||||||
|
var chars = i === 0 ? str[i] : str[i-1] + str[i];
|
||||||
|
bytes.unshift(parseInt(chars, 16));
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
};
|
||||||
|
|
||||||
|
function isArray(obj) {
|
||||||
|
return !!(obj && obj.concat && obj.unshift && !obj.callee);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates number of ticks to MIDI timestamp format, returning an array of
|
||||||
|
* bytes with the time values. Midi has a very particular time to express time,
|
||||||
|
* take a good look at the spec before ever touching this function.
|
||||||
|
*
|
||||||
|
* @param ticks {Integer} Number of ticks to be translated
|
||||||
|
* @returns Array of bytes that form the MIDI time value
|
||||||
|
*/
|
||||||
|
var translateTickTime = function(ticks) {
|
||||||
|
var buffer = ticks & 0x7F;
|
||||||
|
|
||||||
|
while (ticks = ticks >> 7) {
|
||||||
|
buffer <<= 8;
|
||||||
|
buffer |= ((ticks & 0x7F) | 0x80);
|
||||||
|
}
|
||||||
|
|
||||||
|
var bList = [];
|
||||||
|
while (true) {
|
||||||
|
bList.push(buffer & 0xff);
|
||||||
|
|
||||||
|
if (buffer & 0x80) { buffer >>= 8; }
|
||||||
|
else { break; }
|
||||||
|
}
|
||||||
|
return bList;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is the function that assembles the MIDI file. It writes the
|
||||||
|
* necessary constants for the MIDI header and goes through all the tracks, appending
|
||||||
|
* their data to the final MIDI stream.
|
||||||
|
* It returns an object with the final values in hex and in base64, and with
|
||||||
|
* some useful methods to play an manipulate the resulting MIDI stream.
|
||||||
|
*
|
||||||
|
* @param config {Object} Configuration object. It contains the tracks, tempo
|
||||||
|
* and other values necessary to generate the MIDI stream.
|
||||||
|
*
|
||||||
|
* @returns An object with the hex and base64 resulting streams, as well as
|
||||||
|
* with some useful methods.
|
||||||
|
*/
|
||||||
|
var MidiWriter = function(config) {
|
||||||
|
if (config) {
|
||||||
|
var tracks = config.tracks || [];
|
||||||
|
// Number of tracks in hexadecimal
|
||||||
|
var tracksLength = tracks.length.toString(16);
|
||||||
|
|
||||||
|
// This variable will hold the whole midi stream and we will add every
|
||||||
|
// chunk of MIDI data to it in the next lines.
|
||||||
|
var hexMidi = HDR_CHUNKID + HDR_CHUNK_SIZE + HDR_TYPE0;
|
||||||
|
|
||||||
|
// Appends the number of tracks expressed in 2 bytes, as the MIDI
|
||||||
|
// standard requires.
|
||||||
|
hexMidi += codes2Str(str2Bytes(tracksLength, 2));
|
||||||
|
hexMidi += HDR_SPEED;
|
||||||
|
// Goes through the tracks appending the hex strings that compose them.
|
||||||
|
tracks.forEach(function(trk) { hexMidi += codes2Str(trk.toBytes()); });
|
||||||
|
|
||||||
|
return {
|
||||||
|
b64: btoa(hexMidi),
|
||||||
|
play: function() {
|
||||||
|
if (document) {
|
||||||
|
var embed = document.createElement("embed");
|
||||||
|
embed.setAttribute("src", "data:audio/midi;base64," + this.b64);
|
||||||
|
embed.setAttribute("type", "audio/midi");
|
||||||
|
document.body.appendChild(embed);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
save: function() {
|
||||||
|
window.open("data:audio/midi;base64," + this.b64,
|
||||||
|
"JSMidi generated output",
|
||||||
|
"resizable=yes,scrollbars=no,status=no");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new Error("No parameters have been passed to MidiWriter.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generic MidiEvent object. This object is used to create standard MIDI events
|
||||||
|
* (note Meta events nor SysEx events). It is passed a |params| object that may
|
||||||
|
* contain the keys time, type, channel, param1 and param2. Note that only the
|
||||||
|
* type, channel and param1 are strictly required. If the time is not provided,
|
||||||
|
* a time of 0 will be assumed.
|
||||||
|
*
|
||||||
|
* @param {object} params Object containing the properties of the event.
|
||||||
|
*/
|
||||||
|
var MidiEvent = function(params) {
|
||||||
|
if (params &&
|
||||||
|
(params.type !== null || params.type !== undefined) &&
|
||||||
|
(params.channel !== null || params.channel !== undefined) &&
|
||||||
|
(params.param1 !== null || params.param1 !== undefined)) {
|
||||||
|
this.setTime(params.time);
|
||||||
|
this.setType(params.type);
|
||||||
|
this.setChannel(params.channel);
|
||||||
|
this.setParam1(params.param1);
|
||||||
|
this.setParam2(params.param2);
|
||||||
|
} else {
|
||||||
|
throw new Error("Not enough parameters to create an event.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of events that form a note in MIDI. If the |sustained|
|
||||||
|
* parameter is not specified, it creates the noteOff event, which stops the
|
||||||
|
* note after it has been played, instead of keeping it playing.
|
||||||
|
*
|
||||||
|
* This method accepts two ways of expressing notes. The first one is a string,
|
||||||
|
* which will be looked up in the global |noteTable| but it will take the
|
||||||
|
* default values for pitch, channel, durtion and volume.
|
||||||
|
*
|
||||||
|
* If a note object is passed to the method instead, it should contain the properties
|
||||||
|
* channel, pitch, duration and volume, of which pitch is mandatory. In case the
|
||||||
|
* channel, the duration or the volume are not passed, default values will be
|
||||||
|
* used.
|
||||||
|
*
|
||||||
|
* @param note {object || String} Object with note properties or string
|
||||||
|
* @param sustained {Boolean} Whether the note has to end or keep playing
|
||||||
|
* @returns Array of events, with a maximum of two events (noteOn and noteOff)
|
||||||
|
*/
|
||||||
|
|
||||||
|
MidiEvent.createNote = function(note, sustained) {
|
||||||
|
if (!note) { throw new Error("Note not specified"); }
|
||||||
|
|
||||||
|
if (typeof note === "string") {
|
||||||
|
note = noteTable[note];
|
||||||
|
// The pitch is mandatory if the note object is used.
|
||||||
|
} else if (!note.pitch) {
|
||||||
|
throw new Error("The pitch is required in order to create a note.");
|
||||||
|
}
|
||||||
|
var events = [];
|
||||||
|
events.push(MidiEvent.noteOn(note));
|
||||||
|
|
||||||
|
// If there is a |sustained| parameter, the note will keep playing until
|
||||||
|
// a noteOff event is issued for it.
|
||||||
|
if (!sustained) {
|
||||||
|
// The noteOff event will be the one that is passed the actual duration
|
||||||
|
// value for the note, since it is the one that will stop playing the
|
||||||
|
// note at a particular time. If not specified it takes the default
|
||||||
|
// value for it.
|
||||||
|
// TODO: Is is good to have a default value for it?
|
||||||
|
events.push(MidiEvent.noteOff(note, note.duration || DEFAULT_DURATION));
|
||||||
|
}
|
||||||
|
|
||||||
|
return events;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an event of the type NOTE_ON taking the values passed and falling
|
||||||
|
* back to defaults if they are not specified.
|
||||||
|
*
|
||||||
|
* @param note {Note || String} Note object or string
|
||||||
|
* @param time {Number} Duration of the note in ticks
|
||||||
|
* @returns MIDI event with type NOTE_ON for the note specified
|
||||||
|
*/
|
||||||
|
MidiEvent.noteOn = function(note, duration) {
|
||||||
|
return new MidiEvent({
|
||||||
|
time: note.duration || duration || 0,
|
||||||
|
type: EVT_NOTE_ON,
|
||||||
|
channel: note.channel || DEFAULT_CHANNEL,
|
||||||
|
param1: note.pitch || note,
|
||||||
|
param2: note.volume || DEFAULT_VOLUME
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an event of the type NOTE_OFF taking the values passed and falling
|
||||||
|
* back to defaults if they are not specified.
|
||||||
|
*
|
||||||
|
* @param note {Note || String} Note object or string
|
||||||
|
* @param time {Number} Duration of the note in ticks
|
||||||
|
* @returns MIDI event with type NOTE_OFF for the note specified
|
||||||
|
*/
|
||||||
|
|
||||||
|
MidiEvent.noteOff = function(note, duration) {
|
||||||
|
return new MidiEvent({
|
||||||
|
time: note.duration || duration || 0,
|
||||||
|
type: EVT_NOTE_OFF,
|
||||||
|
channel: note.channel || DEFAULT_CHANNEL,
|
||||||
|
param1: note.pitch || note,
|
||||||
|
param2: note.volume || DEFAULT_VOLUME
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
MidiEvent.prototype = {
|
||||||
|
type: 0,
|
||||||
|
channel: 0,
|
||||||
|
time: 0,
|
||||||
|
setTime: function(ticks) {
|
||||||
|
// The 0x00 byte is always the last one. This is how Midi
|
||||||
|
// interpreters know that the time measure specification ends and the
|
||||||
|
// rest of the event signature starts.
|
||||||
|
|
||||||
|
this.time = translateTickTime(ticks || 0);
|
||||||
|
},
|
||||||
|
setType: function(type) {
|
||||||
|
if (type < EVT_NOTE_OFF || type > EVT_PITCH_BEND) {
|
||||||
|
throw new Error("Trying to set an unknown event: " + type);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.type = type;
|
||||||
|
},
|
||||||
|
setChannel: function(channel) {
|
||||||
|
if (channel < 0 || channel > 15) {
|
||||||
|
throw new Error("Channel is out of bounds.");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.channel = channel;
|
||||||
|
},
|
||||||
|
setParam1: function(p) {
|
||||||
|
this.param1 = p;
|
||||||
|
},
|
||||||
|
setParam2: function(p) {
|
||||||
|
this.param2 = p;
|
||||||
|
},
|
||||||
|
toBytes: function() {
|
||||||
|
var byteArray = [];
|
||||||
|
|
||||||
|
var typeChannelByte =
|
||||||
|
parseInt(this.type.toString(16) + this.channel.toString(16), 16);
|
||||||
|
|
||||||
|
byteArray.push.apply(byteArray, this.time);
|
||||||
|
byteArray.push(typeChannelByte);
|
||||||
|
byteArray.push(this.param1);
|
||||||
|
|
||||||
|
// Some events don't have a second parameter
|
||||||
|
if (this.param2 !== undefined && this.param2 !== null) {
|
||||||
|
byteArray.push(this.param2);
|
||||||
|
}
|
||||||
|
return byteArray;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var MetaEvent = function(params) {
|
||||||
|
if (params) {
|
||||||
|
this.setType(params.type);
|
||||||
|
this.setData(params.data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
MetaEvent.prototype = {
|
||||||
|
setType: function(t) {
|
||||||
|
this.type = t;
|
||||||
|
},
|
||||||
|
setData: function(d) {
|
||||||
|
this.data = d;
|
||||||
|
},
|
||||||
|
toBytes: function() {
|
||||||
|
if (!this.type || !this.data) {
|
||||||
|
throw new Error("Type or data for meta-event not specified.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var byteArray = [0xff, this.type];
|
||||||
|
|
||||||
|
// If data is an array, we assume that it contains several bytes. We
|
||||||
|
// apend them to byteArray.
|
||||||
|
if (isArray(this.data)) {
|
||||||
|
AP.push.apply(byteArray, this.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return byteArray;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var MidiTrack = function(cfg) {
|
||||||
|
this.events = [];
|
||||||
|
for (var p in cfg) {
|
||||||
|
if (cfg.hasOwnProperty(p)) {
|
||||||
|
// Get the setter for the property. The property is capitalized.
|
||||||
|
// Probably a try/catch should go here.
|
||||||
|
this["set" + p.charAt(0).toUpperCase() + p.substring(1)](cfg[p]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//"MTrk" Marks the start of the track data
|
||||||
|
MidiTrack.TRACK_START = [0x4d, 0x54, 0x72, 0x6b];
|
||||||
|
MidiTrack.TRACK_END = [0x0, 0xFF, 0x2F, 0x0];
|
||||||
|
|
||||||
|
MidiTrack.prototype = {
|
||||||
|
/*
|
||||||
|
* Adds an event to the track.
|
||||||
|
*
|
||||||
|
* @param event {MidiEvent} Event to add to the track
|
||||||
|
* @returns the track where the event has been added
|
||||||
|
*/
|
||||||
|
addEvent: function(event) {
|
||||||
|
this.events.push(event);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
setEvents: function(events) {
|
||||||
|
AP.push.apply(this.events, events);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
/*
|
||||||
|
* Adds a text meta-event to the track.
|
||||||
|
*
|
||||||
|
* @param type {Number} type of the text meta-event
|
||||||
|
* @param text {String} Optional. Text of the meta-event.
|
||||||
|
* @returns the track where the event ahs been added
|
||||||
|
*/
|
||||||
|
setText: function(type, text) {
|
||||||
|
// If the param text is not specified, it is assumed that a generic
|
||||||
|
// text is wanted and that the type parameter is the actual text to be
|
||||||
|
// used.
|
||||||
|
if (!text) {
|
||||||
|
type = META_TEXT;
|
||||||
|
text = type;
|
||||||
|
}
|
||||||
|
return this.addEvent(new MetaEvent({ type: type, data: text }));
|
||||||
|
},
|
||||||
|
// The following are setters for different kinds of text in MIDI, they all
|
||||||
|
// use the |setText| method as a proxy.
|
||||||
|
setCopyright: function(text) { return this.setText(META_COPYRIGHT, text); },
|
||||||
|
setTrackName: function(text) { return this.setText(META_TRACK_NAME, text); },
|
||||||
|
setInstrument: function(text) { return this.setText(META_INSTRUMENT, text); },
|
||||||
|
setLyric: function(text) { return this.setText(META_LYRIC, text); },
|
||||||
|
setMarker: function(text) { return this.setText(META_MARKER, text); },
|
||||||
|
setCuePoint: function(text) { return this.setText(META_CUE_POINT, text); },
|
||||||
|
|
||||||
|
setTempo: function(tempo) {
|
||||||
|
this.addEvent(new MetaEvent({ type: META_TEMPO, data: tempo }));
|
||||||
|
},
|
||||||
|
setTimeSig: function() {
|
||||||
|
// TBD
|
||||||
|
},
|
||||||
|
setKeySig: function() {
|
||||||
|
// TBD
|
||||||
|
},
|
||||||
|
|
||||||
|
toBytes: function() {
|
||||||
|
var trackLength = 0;
|
||||||
|
var eventBytes = [];
|
||||||
|
var startBytes = MidiTrack.TRACK_START;
|
||||||
|
var endBytes = MidiTrack.TRACK_END;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Adds the bytes of an event to the eventBytes array and add the
|
||||||
|
* amount of bytes to |trackLength|.
|
||||||
|
*
|
||||||
|
* @param event {MidiEvent} MIDI event we want the bytes from.
|
||||||
|
*/
|
||||||
|
var addEventBytes = function(event) {
|
||||||
|
var bytes = event.toBytes();
|
||||||
|
trackLength += bytes.length;
|
||||||
|
AP.push.apply(eventBytes, bytes);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.events.forEach(addEventBytes);
|
||||||
|
|
||||||
|
// Add the end-of-track bytes to the sum of bytes for the track, since
|
||||||
|
// they are counted (unlike the start-of-track ones).
|
||||||
|
trackLength += endBytes.length;
|
||||||
|
|
||||||
|
// Makes sure that track length will fill up 4 bytes with 0s in case
|
||||||
|
// the length is less than that (the usual case).
|
||||||
|
var lengthBytes = str2Bytes(trackLength.toString(16), 4);
|
||||||
|
|
||||||
|
return startBytes.concat(lengthBytes, eventBytes, endBytes);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.MidiWriter = MidiWriter;
|
||||||
|
window.MidiEvent = MidiEvent;
|
||||||
|
window.MetaEvent = MetaEvent;
|
||||||
|
window.MidiTrack = MidiTrack;
|
||||||
|
window.noteTable = noteTable;
|
||||||
|
|
||||||
|
})(jsmidi = {});
|
154
js/Color.js
154
js/Color.js
|
@ -1,154 +0,0 @@
|
||||||
/*
|
|
||||||
|
|
||||||
Color.Space : 0.3 : mudcu.be
|
|
||||||
-----------------------------
|
|
||||||
STRING <-> HEX <-> RGB <-> HSL
|
|
||||||
-----------------------------
|
|
||||||
var HEX = 0xFF0000;
|
|
||||||
var HSL = Color.Space(HEX, "HEX>RGB>HSL");
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (!window.Color) Color = {};
|
|
||||||
if (!window.Color.Space) Color.Space = {};
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
|
|
||||||
var DEG_RAD = Math.PI / 180;
|
|
||||||
var RAD_DEG = 1 / DEG_RAD;
|
|
||||||
|
|
||||||
var shortcuts = { };
|
|
||||||
var root = Color.Space = function(color, route) {
|
|
||||||
if (shortcuts[route]) {
|
|
||||||
route = shortcuts[route];
|
|
||||||
}
|
|
||||||
var arr = route.split(">");
|
|
||||||
var key = "";
|
|
||||||
for (var n = 0; n < arr.length; n ++) {
|
|
||||||
if (n > 1) {
|
|
||||||
key = key.split("_");
|
|
||||||
key.shift();
|
|
||||||
key = key.join("_");
|
|
||||||
}
|
|
||||||
key += (n == 0 ? "" : "_") + arr[n];
|
|
||||||
if (n > 0) color = root[key](color);
|
|
||||||
}
|
|
||||||
return color;
|
|
||||||
};
|
|
||||||
|
|
||||||
// STRING = 'FFFFFF' | 'FFFFFFFF'
|
|
||||||
|
|
||||||
root.STRING_HEX = function (o) {
|
|
||||||
return parseInt('0x' + o);
|
|
||||||
};
|
|
||||||
|
|
||||||
// HEX = 0x000000 -> 0xFFFFFF
|
|
||||||
|
|
||||||
root.HEX_STRING = function (o, maxLength) {
|
|
||||||
if (!maxLength) maxLength = 6;
|
|
||||||
if (!o) o = 0;
|
|
||||||
var z = o.toString(16);
|
|
||||||
// when string is lesser than maxLength
|
|
||||||
var n = z.length;
|
|
||||||
while (n < maxLength) {
|
|
||||||
z = '0' + z;
|
|
||||||
n++;
|
|
||||||
}
|
|
||||||
// when string is greater than maxLength
|
|
||||||
var n = z.length;
|
|
||||||
while (n > maxLength) {
|
|
||||||
z = z.substr(1);
|
|
||||||
n--;
|
|
||||||
}
|
|
||||||
return z;
|
|
||||||
};
|
|
||||||
|
|
||||||
root.HEX_RGB = function (o) {
|
|
||||||
return {
|
|
||||||
R: (o >> 16),
|
|
||||||
G: (o >> 8) & 0xFF,
|
|
||||||
B: o & 0xFF
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// RGB = R: Red / G: Green / B: Blue
|
|
||||||
|
|
||||||
root.RGB_HEX = function (o) {
|
|
||||||
if (o.R < 0) o.R = 0;
|
|
||||||
if (o.G < 0) o.G = 0;
|
|
||||||
if (o.B < 0) o.B = 0;
|
|
||||||
if (o.R > 255) o.R = 255;
|
|
||||||
if (o.G > 255) o.G = 255;
|
|
||||||
if (o.B > 255) o.B = 255;
|
|
||||||
return o.R << 16 | o.G << 8 | o.B;
|
|
||||||
};
|
|
||||||
|
|
||||||
root.RGB_HSL = function (o) { // RGB from 0 to 1
|
|
||||||
// http://www.easyrgb.com/index.php?X=MATH&H=18#text18
|
|
||||||
var _R = o.R / 255,
|
|
||||||
_G = o.G / 255,
|
|
||||||
_B = o.B / 255,
|
|
||||||
min = Math.min(_R, _G, _B),
|
|
||||||
max = Math.max(_R, _G, _B),
|
|
||||||
D = max - min,
|
|
||||||
H,
|
|
||||||
S,
|
|
||||||
L = (max + min) / 2;
|
|
||||||
if (D == 0) { // No chroma
|
|
||||||
H = 0;
|
|
||||||
S = 0;
|
|
||||||
} else { // Chromatic data
|
|
||||||
if (L < 0.5) S = D / (max + min);
|
|
||||||
else S = D / (2 - max - min);
|
|
||||||
var DR = (((max - _R) / 6) + (D / 2)) / D;
|
|
||||||
var DG = (((max - _G) / 6) + (D / 2)) / D;
|
|
||||||
var DB = (((max - _B) / 6) + (D / 2)) / D;
|
|
||||||
if (_R == max) H = DB - DG;
|
|
||||||
else if (_G == max) H = (1 / 3) + DR - DB;
|
|
||||||
else if (_B == max) H = (2 / 3) + DG - DR;
|
|
||||||
if (H < 0) H += 1;
|
|
||||||
if (H > 1) H -= 1;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
H: H * 360,
|
|
||||||
S: S * 100,
|
|
||||||
L: L * 100
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// HSL (1978) = H: Hue / S: Saturation / L: Lightess
|
|
||||||
|
|
||||||
root.HSL_RGB = function (o) {
|
|
||||||
// http://www.easyrgb.com/index.php?X=MATH&H=19
|
|
||||||
var H = o.H / 360,
|
|
||||||
S = o.S / 100,
|
|
||||||
L = o.L / 100,
|
|
||||||
R, G, B, _1, _2;
|
|
||||||
function Hue_2_RGB(v1, v2, vH) {
|
|
||||||
if (vH < 0) vH += 1;
|
|
||||||
if (vH > 1) vH -= 1;
|
|
||||||
if ((6 * vH) < 1) return v1 + (v2 - v1) * 6 * vH;
|
|
||||||
if ((2 * vH) < 1) return v2;
|
|
||||||
if ((3 * vH) < 2) return v1 + (v2 - v1) * ((2 / 3) - vH) * 6;
|
|
||||||
return v1;
|
|
||||||
}
|
|
||||||
if (S == 0) { // HSL from 0 to 1
|
|
||||||
R = L * 255;
|
|
||||||
G = L * 255;
|
|
||||||
B = L * 255;
|
|
||||||
} else {
|
|
||||||
if (L < 0.5) _2 = L * (1 + S);
|
|
||||||
else _2 = (L + S) - (S * L);
|
|
||||||
_1 = 2 * L - _2;
|
|
||||||
R = 255 * Hue_2_RGB(_1, _2, H + (1 / 3));
|
|
||||||
G = 255 * Hue_2_RGB(_1, _2, H);
|
|
||||||
B = 255 * Hue_2_RGB(_1, _2, H - (1 / 3));
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
R: R,
|
|
||||||
G: G,
|
|
||||||
B: B
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
|
439
js/Color/Space.js
Normal file
439
js/Color/Space.js
Normal file
|
@ -0,0 +1,439 @@
|
||||||
|
/*
|
||||||
|
----------------------------------------------------
|
||||||
|
Color Space : 1.2 : 2012.09.01 : http://mudcu.be
|
||||||
|
----------------------------------------------------
|
||||||
|
RGBA <-> HSLA <-> W3
|
||||||
|
RGBA <-> HSVA
|
||||||
|
RGBA <-> CMY <-> CMYK
|
||||||
|
RGBA <-> HEX24 <-> W3
|
||||||
|
RGBA <-> HEX32
|
||||||
|
RGBA <-> W3
|
||||||
|
----------------------------------------------------
|
||||||
|
Examples
|
||||||
|
----------------------------------------------------
|
||||||
|
Color.Space(0x99ff0000, "HEX32>RGBA>HSLA>W3"); // outputs "hsla(60,100%,17%,0.6)"
|
||||||
|
Color.Space(0xFF0000, "HEX24>RGB>HSL"); // convert hex24 to HSL object.
|
||||||
|
----------------------------------------------------
|
||||||
|
W3 values
|
||||||
|
----------------------------------------------------
|
||||||
|
rgb(255,0,0)
|
||||||
|
rgba(255,0,0,1)
|
||||||
|
rgb(100%,0%,0%)
|
||||||
|
rgba(100%,0%,0%,1)
|
||||||
|
hsl(120, 100%, 50%)
|
||||||
|
hsla(120, 100%, 50%, 1)
|
||||||
|
#000000
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (typeof(Color) === "undefined") Color = {};
|
||||||
|
if (typeof(Color.Space) === "undefined") Color.Space = {};
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
|
||||||
|
var functions = {
|
||||||
|
// holds generated cached conversion functions.
|
||||||
|
};
|
||||||
|
var shortcuts = {
|
||||||
|
"HEX24>HSL": "HEX24>RGB>HSL",
|
||||||
|
"HEX32>HSLA": "HEX32>RGBA>HSLA",
|
||||||
|
"HEX24>CMYK": "HEX24>RGB>CMY>CMYK",
|
||||||
|
"RGB>CMYK": "RGB>CMY>CMYK"
|
||||||
|
};
|
||||||
|
|
||||||
|
var root = Color.Space = function(color, route) {
|
||||||
|
if (shortcuts[route]) { // shortcut available
|
||||||
|
route = shortcuts[route];
|
||||||
|
}
|
||||||
|
var r = route.split(">");
|
||||||
|
// check whether color is an [], if so, convert to {}
|
||||||
|
if (typeof(color) === "object" && color[0] >= 0) { // array
|
||||||
|
var type = r[0];
|
||||||
|
var tmp = {};
|
||||||
|
for(var i = 0; i < type.length; i ++) {
|
||||||
|
var str = type.substr(i, 1);
|
||||||
|
tmp[str] = color[i];
|
||||||
|
}
|
||||||
|
color = tmp;
|
||||||
|
}
|
||||||
|
if (functions[route]) { // cached function available
|
||||||
|
return functions[route](color);
|
||||||
|
}
|
||||||
|
var f = "color";
|
||||||
|
for (var pos = 1, key = r[0]; pos < r.length; pos ++) {
|
||||||
|
if (pos > 1) { // recycle previous
|
||||||
|
key = key.substr(key.indexOf("_") + 1);
|
||||||
|
}
|
||||||
|
key += (pos === 0 ? "" : "_") + r[pos];
|
||||||
|
color = root[key](color);
|
||||||
|
f = "Color.Space."+key+"("+f+")";
|
||||||
|
}
|
||||||
|
|
||||||
|
functions[route] = eval("(function(color) { return "+f+" })");
|
||||||
|
return color;
|
||||||
|
};
|
||||||
|
|
||||||
|
// W3C - RGB + RGBA
|
||||||
|
|
||||||
|
root.RGB_W3 = function(o) {
|
||||||
|
return "rgb(" + (o.R >> 0) + "," + (o.G >> 0) + "," + (o.B >> 0) + ")";
|
||||||
|
};
|
||||||
|
|
||||||
|
root.RGBA_W3 = function(o) {
|
||||||
|
var alpha = typeof(o.A) === "number" ? o.A / 255 : 1;
|
||||||
|
return "rgba(" + (o.R >> 0) + "," + (o.G >> 0) + "," + (o.B >> 0) + "," + alpha + ")";
|
||||||
|
};
|
||||||
|
|
||||||
|
root.W3_RGB = function(o) {
|
||||||
|
var o = o.substr(4, o.length - 5).split(",");
|
||||||
|
return {
|
||||||
|
R: parseInt(o[0]),
|
||||||
|
G: parseInt(o[1]),
|
||||||
|
B: parseInt(o[2])
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
root.W3_RGBA = function(o) {
|
||||||
|
var o = o.substr(5, o.length - 6).split(",");
|
||||||
|
return {
|
||||||
|
R: parseInt(o[0]),
|
||||||
|
G: parseInt(o[1]),
|
||||||
|
B: parseInt(o[2]),
|
||||||
|
A: parseFloat(o[3]) * 255
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// W3C - HSL + HSLA
|
||||||
|
|
||||||
|
root.HSL_W3 = function(o) {
|
||||||
|
return "hsl(" + ((o.H + 0.5) >> 0) + "," + ((o.S + 0.5) >> 0) + "%," + ((o.L + 0.5) >> 0) + "%)";
|
||||||
|
};
|
||||||
|
|
||||||
|
root.HSLA_W3 = function(o) {
|
||||||
|
var alpha = typeof(o.A) === "number" ? o.A / 255 : 1;
|
||||||
|
return "hsla(" + ((o.H + 0.5) >> 0) + "," + ((o.S + 0.5) >> 0) + "%," + ((o.L + 0.5) >> 0) + "%," + alpha + ")";
|
||||||
|
};
|
||||||
|
|
||||||
|
root.W3_HSL = function(o) {
|
||||||
|
var o = o.substr(4, o.length - 5).split(",");
|
||||||
|
return {
|
||||||
|
H: parseInt(o[0]),
|
||||||
|
S: parseInt(o[1]),
|
||||||
|
L: parseInt(o[2])
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
root.W3_HSLA = function(o) {
|
||||||
|
var o = o.substr(5, o.length - 6).split(",");
|
||||||
|
return {
|
||||||
|
H: parseInt(o[0]),
|
||||||
|
S: parseInt(o[1]),
|
||||||
|
L: parseInt(o[2]),
|
||||||
|
A: parseFloat(o[3]) * 255
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// W3 HEX = "FFFFFF" | "FFFFFFFF"
|
||||||
|
|
||||||
|
root.W3_HEX =
|
||||||
|
root.W3_HEX24 = function (o) {
|
||||||
|
if (o.substr(0, 1) === "#") o = o.substr(1);
|
||||||
|
if (o.length === 3) o = o[0] + o[0] + o[1] + o[1] + o[2] + o[2];
|
||||||
|
return parseInt("0x" + o);
|
||||||
|
};
|
||||||
|
|
||||||
|
root.W3_HEX32 = function (o) {
|
||||||
|
if (o.substr(0, 1) === "#") o = o.substr(1);
|
||||||
|
if (o.length === 6) {
|
||||||
|
return parseInt("0xFF" + o);
|
||||||
|
} else {
|
||||||
|
return parseInt("0x" + o);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// HEX = 0x000000 -> 0xFFFFFF
|
||||||
|
|
||||||
|
root.HEX_W3 =
|
||||||
|
root.HEX24_W3 = function (o, maxLength) {
|
||||||
|
if (!maxLength) maxLength = 6;
|
||||||
|
if (!o) o = 0;
|
||||||
|
var z = o.toString(16);
|
||||||
|
// when string is lesser than maxLength
|
||||||
|
var n = z.length;
|
||||||
|
while (n < maxLength) {
|
||||||
|
z = "0" + z;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
// when string is greater than maxLength
|
||||||
|
var n = z.length;
|
||||||
|
while (n > maxLength) {
|
||||||
|
z = z.substr(1);
|
||||||
|
n--;
|
||||||
|
}
|
||||||
|
return "#" + z;
|
||||||
|
};
|
||||||
|
|
||||||
|
root.HEX32_W3 = function(o) {
|
||||||
|
return root.HEX_W3(o, 8);
|
||||||
|
};
|
||||||
|
|
||||||
|
root.HEX_RGB =
|
||||||
|
root.HEX24_RGB = function (o) {
|
||||||
|
return {
|
||||||
|
R: (o >> 16),
|
||||||
|
G: (o >> 8) & 0xFF,
|
||||||
|
B: o & 0xFF
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// HEX32 = 0x00000000 -> 0xFFFFFFFF
|
||||||
|
|
||||||
|
root.HEX32_RGBA = function (o) {
|
||||||
|
return {
|
||||||
|
R: o >>> 16 & 0xFF,
|
||||||
|
G: o >>> 8 & 0xFF,
|
||||||
|
B: o & 0xFF,
|
||||||
|
A: o >>> 24
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// RGBA = R: Red / G: Green / B: Blue / A: Alpha
|
||||||
|
|
||||||
|
root.RGBA_HEX32 = function (o) {
|
||||||
|
return (o.A << 24 | o.R << 16 | o.G << 8 | o.B) >>> 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// RGB = R: Red / G: Green / B: Blue
|
||||||
|
|
||||||
|
root.RGB_HEX24 =
|
||||||
|
root.RGB_HEX = function (o) {
|
||||||
|
if (o.R < 0) o.R = 0;
|
||||||
|
if (o.G < 0) o.G = 0;
|
||||||
|
if (o.B < 0) o.B = 0;
|
||||||
|
if (o.R > 255) o.R = 255;
|
||||||
|
if (o.G > 255) o.G = 255;
|
||||||
|
if (o.B > 255) o.B = 255;
|
||||||
|
return o.R << 16 | o.G << 8 | o.B;
|
||||||
|
};
|
||||||
|
|
||||||
|
root.RGB_CMY = function (o) {
|
||||||
|
return {
|
||||||
|
C: 1 - (o.R / 255),
|
||||||
|
M: 1 - (o.G / 255),
|
||||||
|
Y: 1 - (o.B / 255)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
root.RGBA_HSLA =
|
||||||
|
root.RGB_HSL = function (o) { // RGB from 0 to 1
|
||||||
|
var _R = o.R / 255,
|
||||||
|
_G = o.G / 255,
|
||||||
|
_B = o.B / 255,
|
||||||
|
min = Math.min(_R, _G, _B),
|
||||||
|
max = Math.max(_R, _G, _B),
|
||||||
|
D = max - min,
|
||||||
|
H,
|
||||||
|
S,
|
||||||
|
L = (max + min) / 2;
|
||||||
|
if (D === 0) { // No chroma
|
||||||
|
H = 0;
|
||||||
|
S = 0;
|
||||||
|
} else { // Chromatic data
|
||||||
|
if (L < 0.5) S = D / (max + min);
|
||||||
|
else S = D / (2 - max - min);
|
||||||
|
var DR = (((max - _R) / 6) + (D / 2)) / D;
|
||||||
|
var DG = (((max - _G) / 6) + (D / 2)) / D;
|
||||||
|
var DB = (((max - _B) / 6) + (D / 2)) / D;
|
||||||
|
if (_R === max) H = DB - DG;
|
||||||
|
else if (_G === max) H = (1 / 3) + DR - DB;
|
||||||
|
else if (_B === max) H = (2 / 3) + DG - DR;
|
||||||
|
if (H < 0) H += 1;
|
||||||
|
if (H > 1) H -= 1;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
H: H * 360,
|
||||||
|
S: S * 100,
|
||||||
|
L: L * 100,
|
||||||
|
A: o.A
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
root.RGBA_HSVA =
|
||||||
|
root.RGB_HSV = function (o) { //- RGB from 0 to 255
|
||||||
|
var _R = o.R / 255,
|
||||||
|
_G = o.G / 255,
|
||||||
|
_B = o.B / 255,
|
||||||
|
min = Math.min(_R, _G, _B),
|
||||||
|
max = Math.max(_R, _G, _B),
|
||||||
|
D = max - min,
|
||||||
|
H,
|
||||||
|
S,
|
||||||
|
V = max;
|
||||||
|
if (D === 0) { // No chroma
|
||||||
|
H = 0;
|
||||||
|
S = 0;
|
||||||
|
} else { // Chromatic data
|
||||||
|
S = D / max;
|
||||||
|
var DR = (((max - _R) / 6) + (D / 2)) / D;
|
||||||
|
var DG = (((max - _G) / 6) + (D / 2)) / D;
|
||||||
|
var DB = (((max - _B) / 6) + (D / 2)) / D;
|
||||||
|
if (_R === max) H = DB - DG;
|
||||||
|
else if (_G === max) H = (1 / 3) + DR - DB;
|
||||||
|
else if (_B === max) H = (2 / 3) + DG - DR;
|
||||||
|
if (H < 0) H += 1;
|
||||||
|
if (H > 1) H -= 1;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
H: H * 360,
|
||||||
|
S: S * 100,
|
||||||
|
V: V * 100,
|
||||||
|
A: o.A
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// CMY = C: Cyan / M: Magenta / Y: Yellow
|
||||||
|
|
||||||
|
root.CMY_RGB = function (o) {
|
||||||
|
return {
|
||||||
|
R: Math.max(0, (1 - o.C) * 255),
|
||||||
|
G: Math.max(0, (1 - o.M) * 255),
|
||||||
|
B: Math.max(0, (1 - o.Y) * 255)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
root.CMY_CMYK = function (o) {
|
||||||
|
var C = o.C;
|
||||||
|
var M = o.M;
|
||||||
|
var Y = o.Y;
|
||||||
|
var K = Math.min(Y, Math.min(M, Math.min(C, 1)));
|
||||||
|
C = Math.round((C - K) / (1 - K) * 100);
|
||||||
|
M = Math.round((M - K) / (1 - K) * 100);
|
||||||
|
Y = Math.round((Y - K) / (1 - K) * 100);
|
||||||
|
K = Math.round(K * 100);
|
||||||
|
return {
|
||||||
|
C: C,
|
||||||
|
M: M,
|
||||||
|
Y: Y,
|
||||||
|
K: K
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// CMYK = C: Cyan / M: Magenta / Y: Yellow / K: Key (black)
|
||||||
|
|
||||||
|
root.CMYK_CMY = function (o) {
|
||||||
|
return {
|
||||||
|
C: (o.C * (1 - o.K) + o.K),
|
||||||
|
M: (o.M * (1 - o.K) + o.K),
|
||||||
|
Y: (o.Y * (1 - o.K) + o.K)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// HSL (1978) = H: Hue / S: Saturation / L: Lightess
|
||||||
|
// en.wikipedia.org/wiki/HSL_and_HSV
|
||||||
|
|
||||||
|
root.HSLA_RGBA =
|
||||||
|
root.HSL_RGB = function (o) {
|
||||||
|
var H = o.H / 360;
|
||||||
|
var S = o.S / 100;
|
||||||
|
var L = o.L / 100;
|
||||||
|
var R, G, B;
|
||||||
|
var temp1, temp2, temp3;
|
||||||
|
if (S === 0) {
|
||||||
|
R = G = B = L;
|
||||||
|
} else {
|
||||||
|
if (L < 0.5) temp2 = L * (1 + S);
|
||||||
|
else temp2 = (L + S) - (S * L);
|
||||||
|
temp1 = 2 * L - temp2;
|
||||||
|
// calculate red
|
||||||
|
temp3 = H + (1 / 3);
|
||||||
|
if (temp3 < 0) temp3 += 1;
|
||||||
|
if (temp3 > 1) temp3 -= 1;
|
||||||
|
if ((6 * temp3) < 1) R = temp1 + (temp2 - temp1) * 6 * temp3;
|
||||||
|
else if ((2 * temp3) < 1) R = temp2;
|
||||||
|
else if ((3 * temp3) < 2) R = temp1 + (temp2 - temp1) * ((2 / 3) - temp3) * 6;
|
||||||
|
else R = temp1;
|
||||||
|
// calculate green
|
||||||
|
temp3 = H;
|
||||||
|
if (temp3 < 0) temp3 += 1;
|
||||||
|
if (temp3 > 1) temp3 -= 1;
|
||||||
|
if ((6 * temp3) < 1) G = temp1 + (temp2 - temp1) * 6 * temp3;
|
||||||
|
else if ((2 * temp3) < 1) G = temp2;
|
||||||
|
else if ((3 * temp3) < 2) G = temp1 + (temp2 - temp1) * ((2 / 3) - temp3) * 6;
|
||||||
|
else G = temp1;
|
||||||
|
// calculate blue
|
||||||
|
temp3 = H - (1 / 3);
|
||||||
|
if (temp3 < 0) temp3 += 1;
|
||||||
|
if (temp3 > 1) temp3 -= 1;
|
||||||
|
if ((6 * temp3) < 1) B = temp1 + (temp2 - temp1) * 6 * temp3;
|
||||||
|
else if ((2 * temp3) < 1) B = temp2;
|
||||||
|
else if ((3 * temp3) < 2) B = temp1 + (temp2 - temp1) * ((2 / 3) - temp3) * 6;
|
||||||
|
else B = temp1;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
R: R * 255,
|
||||||
|
G: G * 255,
|
||||||
|
B: B * 255,
|
||||||
|
A: o.A
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// HSV (1978) = H: Hue / S: Saturation / V: Value
|
||||||
|
// en.wikipedia.org/wiki/HSL_and_HSV
|
||||||
|
|
||||||
|
root.HSVA_RGBA =
|
||||||
|
root.HSV_RGB = function (o) {
|
||||||
|
var H = o.H / 360;
|
||||||
|
var S = o.S / 100;
|
||||||
|
var V = o.V / 100;
|
||||||
|
var R, G, B;
|
||||||
|
if (S === 0) {
|
||||||
|
R = G = B = Math.round(V * 255);
|
||||||
|
} else {
|
||||||
|
if (H >= 1) H = 0;
|
||||||
|
H = 6 * H;
|
||||||
|
D = H - Math.floor(H);
|
||||||
|
A = Math.round(255 * V * (1 - S));
|
||||||
|
B = Math.round(255 * V * (1 - (S * D)));
|
||||||
|
C = Math.round(255 * V * (1 - (S * (1 - D))));
|
||||||
|
V = Math.round(255 * V);
|
||||||
|
switch (Math.floor(H)) {
|
||||||
|
case 0:
|
||||||
|
R = V;
|
||||||
|
G = C;
|
||||||
|
B = A;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
R = B;
|
||||||
|
G = V;
|
||||||
|
B = A;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
R = A;
|
||||||
|
G = V;
|
||||||
|
B = C;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
R = A;
|
||||||
|
G = B;
|
||||||
|
B = V;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
R = C;
|
||||||
|
G = A;
|
||||||
|
B = V;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
R = V;
|
||||||
|
G = A;
|
||||||
|
B = B;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
R: R,
|
||||||
|
G: G,
|
||||||
|
B: B,
|
||||||
|
A: o.A
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
})();
|
|
@ -1,155 +0,0 @@
|
||||||
/*
|
|
||||||
|
|
||||||
Event.Mouse : 0.3.1 : mudcu.be
|
|
||||||
-------------------------------------
|
|
||||||
Event.add(document, "mousedown", function(event) {
|
|
||||||
Event.drag({
|
|
||||||
type: "absolute",
|
|
||||||
event: event,
|
|
||||||
element: document,
|
|
||||||
callback: function (event, coords, state, self) {
|
|
||||||
Event.stopPropagation(event);
|
|
||||||
Event.preventDefault(event);
|
|
||||||
console.log(coords);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// this does the same thing
|
|
||||||
Event.drag({
|
|
||||||
type: "absolute",
|
|
||||||
element: document,
|
|
||||||
callback: function (event, coords, state, self) {
|
|
||||||
console.log(coords);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/// easier mousewheel events
|
|
||||||
Event.mousewheel(window, function(event, state, wheelData, self) {
|
|
||||||
self.stop.prevent.remove();
|
|
||||||
});
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (typeof(Event) === "undefined") var Event = {};
|
|
||||||
|
|
||||||
Event.drag =
|
|
||||||
Event.dragElement = function(props) {
|
|
||||||
var el = props.element || document.body;
|
|
||||||
var doc = el.ownerDocument; // could be within an iframe
|
|
||||||
if (typeof(props.event) === "undefined") { // create event
|
|
||||||
Event.add(el, "mousedown", function(event) {
|
|
||||||
props.event = event;
|
|
||||||
Event.dragElement(props);
|
|
||||||
Event.preventDefault(event);
|
|
||||||
Event.stopPropagation(event);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// functions accessible externally
|
|
||||||
var self = {
|
|
||||||
cancel: function() {
|
|
||||||
Event.remove(doc, "mousemove", mouseMove);
|
|
||||||
Event.remove(doc, "mouseup", mouseUp);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// event move
|
|
||||||
var mouseMove = function (event, state) {
|
|
||||||
if (typeof(state) === "undefined") state = "move";
|
|
||||||
var coord = Event.coords(event);
|
|
||||||
switch (props.type) {
|
|
||||||
case "move": // move
|
|
||||||
props.callback(event, {
|
|
||||||
x: coord.x + oX - eX,
|
|
||||||
y: coord.y + oY - eY
|
|
||||||
}, state, self);
|
|
||||||
break;
|
|
||||||
case "difference": // relative, from position within element
|
|
||||||
props.callback(event, {
|
|
||||||
x: coord.x - oX,
|
|
||||||
y: coord.y - oY
|
|
||||||
}, state, self);
|
|
||||||
break;
|
|
||||||
case "relative": // eveything is relative from origin
|
|
||||||
props.callback(event, {
|
|
||||||
x: coord.x - eX,
|
|
||||||
y: coord.y - eY
|
|
||||||
}, state, self);
|
|
||||||
break;
|
|
||||||
default: // "absolute", origin is 0x0
|
|
||||||
props.callback(event, {
|
|
||||||
x: coord.x,
|
|
||||||
y: coord.y
|
|
||||||
}, state, self);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// event up
|
|
||||||
var mouseUp = function(event) {
|
|
||||||
self.cancel();
|
|
||||||
mouseMove(event, "up");
|
|
||||||
};
|
|
||||||
// current element position
|
|
||||||
var origin = abPos(el);
|
|
||||||
var oX = origin.x;
|
|
||||||
var oY = origin.y;
|
|
||||||
// current mouse position
|
|
||||||
var event = props.event;
|
|
||||||
var coord = Event.coords(event);
|
|
||||||
var eX = coord.x;
|
|
||||||
var eY = coord.y;
|
|
||||||
// events
|
|
||||||
Event.add(doc, "mousemove", mouseMove);
|
|
||||||
Event.add(doc, "mouseup", mouseUp);
|
|
||||||
mouseMove(event, "down"); // run mouse-down
|
|
||||||
//
|
|
||||||
return self;
|
|
||||||
};
|
|
||||||
|
|
||||||
Event.coords = (function() {
|
|
||||||
if (window.ActiveXObject) {
|
|
||||||
return function(event) {
|
|
||||||
return {
|
|
||||||
x: event.clientX + document.documentElement.scrollLeft,
|
|
||||||
y: event.clientY + document.documentElement.scrollTop
|
|
||||||
};
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return function(event) {
|
|
||||||
return {
|
|
||||||
x: event.pageX,
|
|
||||||
y: event.pageY
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
//////////////// MouseWheel ////////////////
|
|
||||||
|
|
||||||
Event.mousewheel = function(target, listener, timeout) {
|
|
||||||
var interval = 0;
|
|
||||||
var self = Event(target, "mousewheel", function(event) {
|
|
||||||
event = event || window.event;
|
|
||||||
var wheelData = event.detail ? event.detail * -1 : event.wheelDelta / 40;
|
|
||||||
listener(event, "wheel", wheelData);
|
|
||||||
window.clearInterval(interval);
|
|
||||||
interval = window.setInterval(function() {
|
|
||||||
window.clearInterval(interval);
|
|
||||||
listener(event, "wheelup", wheelData, self);
|
|
||||||
}, timeout || 150);
|
|
||||||
});
|
|
||||||
return self;
|
|
||||||
};
|
|
||||||
|
|
||||||
///// DOM.absPos
|
|
||||||
|
|
||||||
var abPos = function(o) {
|
|
||||||
o = typeof(o) === 'object' ? o : document.getElementById(o);
|
|
||||||
var offset = { x: 0, y: 0 };
|
|
||||||
while(o != null) {
|
|
||||||
offset.x += o.offsetLeft;
|
|
||||||
offset.y += o.offsetTop;
|
|
||||||
o = o.offsetParent;
|
|
||||||
};
|
|
||||||
return offset;
|
|
||||||
};
|
|
1837
js/Event.js
1837
js/Event.js
File diff suppressed because it is too large
Load diff
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
MIDI.Player : 0.3
|
MIDI.Player : 0.3
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
https://github.com/mudx/MIDI.js
|
https://github.com/mudcube/MIDI.js
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
requires jasmid
|
requires jasmid
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ root.clearAnimation = function() {
|
||||||
|
|
||||||
root.setAnimation = function(config) {
|
root.setAnimation = function(config) {
|
||||||
var callback = (typeof(config) === "function") ? config : config.callback;
|
var callback = (typeof(config) === "function") ? config : config.callback;
|
||||||
var delay = config.delay || 24;
|
var interval = config.interval || 30;
|
||||||
var currentTime = 0;
|
var currentTime = 0;
|
||||||
var tOurTime = 0;
|
var tOurTime = 0;
|
||||||
var tTheirTime = 0;
|
var tTheirTime = 0;
|
||||||
|
@ -65,14 +65,14 @@ root.setAnimation = function(config) {
|
||||||
root.interval = window.setInterval(function () {
|
root.interval = window.setInterval(function () {
|
||||||
if (root.endTime === 0) return;
|
if (root.endTime === 0) return;
|
||||||
if (root.playing) {
|
if (root.playing) {
|
||||||
currentTime = (tTheirTime == root.currentTime) ? tOurTime-(new Date()).getTime() : 0;
|
currentTime = (tTheirTime === root.currentTime) ? tOurTime - (new Date).getTime() : 0;
|
||||||
if (root.currentTime === 0) {
|
if (root.currentTime === 0) {
|
||||||
currentTime = 0;
|
currentTime = 0;
|
||||||
} else {
|
} else {
|
||||||
currentTime = root.currentTime - currentTime;
|
currentTime = root.currentTime - currentTime;
|
||||||
}
|
}
|
||||||
if (tTheirTime != root.currentTime) {
|
if (tTheirTime !== root.currentTime) {
|
||||||
tOurTime = (new Date()).getTime();
|
tOurTime = (new Date).getTime();
|
||||||
tTheirTime = root.currentTime;
|
tTheirTime = root.currentTime;
|
||||||
}
|
}
|
||||||
} else { // paused
|
} else { // paused
|
||||||
|
@ -91,7 +91,7 @@ root.setAnimation = function(config) {
|
||||||
end: t2,
|
end: t2,
|
||||||
events: noteRegistrar
|
events: noteRegistrar
|
||||||
});
|
});
|
||||||
}, delay);
|
}, interval);
|
||||||
};
|
};
|
||||||
|
|
||||||
// helpers
|
// helpers
|
||||||
|
@ -111,6 +111,10 @@ root.loadFile = function (file, callback) {
|
||||||
if (callback) callback(data);
|
if (callback) callback(data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
///
|
||||||
|
var title = file.split(" - ")[1] || file;
|
||||||
|
document.getElementById("playback-title").innerHTML = title.replace(".mid","");
|
||||||
|
///
|
||||||
var fetch = new XMLHttpRequest();
|
var fetch = new XMLHttpRequest();
|
||||||
fetch.open('GET', file);
|
fetch.open('GET', file);
|
||||||
fetch.overrideMimeType("text/plain; charset=x-user-defined");
|
fetch.overrideMimeType("text/plain; charset=x-user-defined");
|
||||||
|
@ -140,8 +144,7 @@ var startTime = 0; // to measure time elapse
|
||||||
var noteRegistrar = {}; // get event for requested note
|
var noteRegistrar = {}; // get event for requested note
|
||||||
var onMidiEvent = undefined; // listener callback
|
var onMidiEvent = undefined; // listener callback
|
||||||
var scheduleTracking = function (channel, note, currentTime, offset, message, velocity) {
|
var scheduleTracking = function (channel, note, currentTime, offset, message, velocity) {
|
||||||
var interval = window.setInterval(function () {
|
var interval = window.setTimeout(function () {
|
||||||
window.clearInterval(interval);
|
|
||||||
var data = {
|
var data = {
|
||||||
channel: channel,
|
channel: channel,
|
||||||
note: note,
|
note: note,
|
|
@ -225,7 +225,11 @@ if (typeof(MusicTheory.Synesthesia) === "undefined") MusicTheory.Synesthesia = {
|
||||||
9: [ 29, 94, 52 ],
|
9: [ 29, 94, 52 ],
|
||||||
10: [ 62, 78, 74 ],
|
10: [ 62, 78, 74 ],
|
||||||
11: [ 60, 90, 60 ]
|
11: [ 60, 90, 60 ]
|
||||||
}
|
},
|
||||||
|
'Circle of Fifths (2012)': {
|
||||||
|
ref: "Stuart Wheatman", // http://www.valleysfamilychurch.org/
|
||||||
|
english: [],
|
||||||
|
data: ['#122400', '#2E002E', '#002914', '#470000', '#002142', '#2E2E00', '#290052', '#003D00', '#520029', '#003D3D', '#522900', '#000080', '#244700', '#570057', '#004D26', '#7A0000', '#003B75', '#4C4D00', '#47008F', '#006100', '#850042', '#005C5C', '#804000', '#0000C7', '#366B00', '#80007F', '#00753B', '#B80000', '#0057AD', '#6B6B00', '#6600CC', '#008A00', '#B8005C', '#007F80', '#B35900', '#2424FF', '#478F00', '#AD00AD', '#00994D', '#F00000', '#0073E6', '#8F8F00', '#8A14FF', '#00AD00', '#EB0075', '#00A3A3', '#E07000', '#6B6BFF', '#5CB800', '#DB00DB', '#00C261', '#FF5757', '#3399FF', '#ADAD00', '#B56BFF', '#00D600', '#FF57AB', '#00C7C7', '#FF9124', '#9999FF', '#6EDB00', '#FF29FF', '#00E070', '#FF9999', '#7ABDFF', '#D1D100', '#D1A3FF', '#00FA00', '#FFA3D1', '#00E5E6', '#FFC285', '#C2C2FF', '#80FF00', '#FFA8FF', '#00E070', '#FFCCCC', '#C2E0FF', '#F0F000', '#EBD6FF', '#ADFFAD', '#FFD6EB', '#8AFFFF', '#FFEBD6', '#EBEBFF', '#E0FFC2', '#FFEBFF', '#E5FFF2', '#FFF5F5'] }
|
||||||
};
|
};
|
||||||
|
|
||||||
root.map = function(type) {
|
root.map = function(type) {
|
||||||
|
@ -240,19 +244,29 @@ if (typeof(MusicTheory.Synesthesia) === "undefined") MusicTheory.Synesthesia = {
|
||||||
var syn = root.data;
|
var syn = root.data;
|
||||||
var colors = syn[type] || syn["D. D. Jameson (1844)"];
|
var colors = syn[type] || syn["D. D. Jameson (1844)"];
|
||||||
for (var note = 0; note <= 88; note ++) { // creates mapping for 88 notes
|
for (var note = 0; note <= 88; note ++) { // creates mapping for 88 notes
|
||||||
|
if (colors.data) {
|
||||||
|
data[note] = {
|
||||||
|
hsl: colors.data[note],
|
||||||
|
hex: colors.data[note]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
var clr = colors[(note + 9) % 12];
|
var clr = colors[(note + 9) % 12];
|
||||||
if (clr[0] == clr[1] && clr[1] == clr[2]) {
|
var H = clr.H || clr[0];
|
||||||
|
var S = clr.S || clr[1];
|
||||||
|
var L = clr.L || clr[2];
|
||||||
|
if (H == S && S == L) {
|
||||||
clr = blend(parray, colors[(note + 10) % 12]);
|
clr = blend(parray, colors[(note + 10) % 12]);
|
||||||
}
|
}
|
||||||
var amount = clr[2] / 10;
|
var amount = L / 10;
|
||||||
var octave = note / 12 >> 0;
|
var octave = note / 12 >> 0;
|
||||||
var octaveLum = clr[2] + amount * octave - 3 * amount; // map luminance to octave
|
var octaveLum = L + amount * octave - 3 * amount; // map luminance to octave
|
||||||
data[note] = {
|
data[note] = {
|
||||||
hsl: 'hsla(' + clr[0] + ',' + clr[1] + '%,' + octaveLum + '%, 1)',
|
hsl: 'hsla(' + H + ',' + S + '%,' + octaveLum + '%, 1)',
|
||||||
hex: Color.Space({H:clr[0], S:clr[1], L:octaveLum}, "HSL>RGB>HEX>STRING")
|
hex: Color.Space({H:H, S:S, L:octaveLum}, "HSL>RGB>HEX>W3")
|
||||||
};
|
};
|
||||||
var parray = clr;
|
var parray = clr;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,205 +0,0 @@
|
||||||
/*
|
|
||||||
var loader = new widgets.Loader({ message: "loading: New loading message..." });
|
|
||||||
-----
|
|
||||||
var loader = new widgets.Loader({
|
|
||||||
id: "loader",
|
|
||||||
bars: 12,
|
|
||||||
radius: 20,
|
|
||||||
lineWidth: 3,
|
|
||||||
lineHeight: 10
|
|
||||||
});
|
|
||||||
loader.stop();
|
|
||||||
|
|
||||||
loader.message("loading: New loading message...");
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (typeof(widgets) === "undefined") widgets = {};
|
|
||||||
|
|
||||||
(function(root) {
|
|
||||||
|
|
||||||
var PI = Math.PI;
|
|
||||||
var defaultConfig = {
|
|
||||||
id: "loader",
|
|
||||||
bars: 12,
|
|
||||||
radius: 0,
|
|
||||||
lineWidth: 20,
|
|
||||||
lineHeight: 70
|
|
||||||
};
|
|
||||||
|
|
||||||
var getWindowSize = function() {
|
|
||||||
if (window.innerWidth && window.innerHeight) {
|
|
||||||
var width = window.innerWidth;
|
|
||||||
var height = window.innerHeight;
|
|
||||||
} else if (document.body && document.body.offsetWidth) {
|
|
||||||
var width = window.innerWidth = document.body.offsetWidth;
|
|
||||||
var height = window.innerHeight = document.body.offsetHeight;
|
|
||||||
} else if (document.compatMode === 'CSS1Compat' && document.documentElement && document.documentElement.offsetWidth) {
|
|
||||||
var width = window.innerWidth = document.documentElement.offsetWidth;
|
|
||||||
var height = window.innerHeight = document.documentElement.offsetHeight;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
width: width,
|
|
||||||
height: height
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
root.Loader = function (config) {
|
|
||||||
var that = this;
|
|
||||||
if (!document.body) return;
|
|
||||||
if (!config) config = {};
|
|
||||||
for (var key in defaultConfig) {
|
|
||||||
if (typeof(config[key]) === "undefined") {
|
|
||||||
config[key] = defaultConfig[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//
|
|
||||||
var canvas = document.getElementById(config.id);
|
|
||||||
if (!canvas) {
|
|
||||||
var div = document.createElement("div");
|
|
||||||
div.style.cssText = "color: #fff; pointer-events: none; -webkit-transition-property: opacity; -webkit-transition-duration: .5s; position: fixed; left: 0; top: 0; width: 100%; height: 100%; z-index: 10000; background: rgba(0,0,0,0.5); opacity: 1";
|
|
||||||
if (config.message) {
|
|
||||||
var span = document.createElement("span");
|
|
||||||
span.style.cssText = "font-family: courier; opacity: 1; display: inline-block;background: rgba(0,0,0,0.65); border-radius: 10px; padding: 10px; width: 200px; text-align: center; position: absolute; z-index: 1000;";
|
|
||||||
div.appendChild(span);
|
|
||||||
that.span = span;
|
|
||||||
}
|
|
||||||
var canvas = document.createElement("canvas");
|
|
||||||
document.body.appendChild(canvas);
|
|
||||||
canvas.id = config.id;
|
|
||||||
canvas.style.cssText = "opacity: 1; position: absolute; z-index: 1000;";
|
|
||||||
div.appendChild(canvas);
|
|
||||||
document.body.appendChild(div);
|
|
||||||
} else {
|
|
||||||
that.span = canvas.parentNode.getElementsByTagName("span")[0];
|
|
||||||
}
|
|
||||||
//
|
|
||||||
var max = config.lineHeight + 20;
|
|
||||||
var size = max * 2 + config.radius;
|
|
||||||
var windowSize = getWindowSize();
|
|
||||||
var width = windowSize.width - size;
|
|
||||||
var height = windowSize.height - size;
|
|
||||||
canvas.width = size;
|
|
||||||
canvas.height = size;
|
|
||||||
canvas.style.left = (width / 2) + "px";
|
|
||||||
canvas.style.top = (height / 2) + "px";
|
|
||||||
if (config.message) {
|
|
||||||
that.span.style.left = ((width + size) / 2 - that.span.offsetWidth/2) + "px";
|
|
||||||
that.span.style.top = (height / 2 + size - 10) + "px";
|
|
||||||
}
|
|
||||||
var that = this;
|
|
||||||
var interval = 0;
|
|
||||||
var offset = 0;
|
|
||||||
var delay = config.delay;
|
|
||||||
var bars = config.bars;
|
|
||||||
var radius = config.radius;
|
|
||||||
var ctx = canvas.getContext('2d');
|
|
||||||
ctx.globalCompositeOperation = "lighter";
|
|
||||||
ctx.shadowOffsetX = 1;
|
|
||||||
ctx.shadowOffsetY = 1;
|
|
||||||
ctx.shadowBlur = 1;
|
|
||||||
ctx.shadowColor = 'rgba(0, 0, 0, 0.5)';
|
|
||||||
//
|
|
||||||
function animate() {
|
|
||||||
var windowSize = getWindowSize();
|
|
||||||
var width = windowSize.width - size;
|
|
||||||
var height = windowSize.height - size;
|
|
||||||
//
|
|
||||||
canvas.style.left = (width / 2) + "px";
|
|
||||||
canvas.style.top = (height / 2) + "px";
|
|
||||||
if (config.message) {
|
|
||||||
that.span.style.left = ((width + size) / 2 - that.span.offsetWidth/2) + "px";
|
|
||||||
that.span.style.top = (height / 2 + size - 10) + "px";
|
|
||||||
}
|
|
||||||
//
|
|
||||||
ctx.save();
|
|
||||||
ctx.clearRect(0, 0, size, size);
|
|
||||||
ctx.translate(size / 2, size / 2);
|
|
||||||
var hues = 360 - 360 / bars;
|
|
||||||
for (var i = 0; i < bars; i++) {
|
|
||||||
var angle = (i / bars * 2 * PI) + offset;
|
|
||||||
ctx.save();
|
|
||||||
ctx.translate(radius * Math.sin(-angle), radius * Math.cos(-angle));
|
|
||||||
ctx.rotate(angle);
|
|
||||||
// round-rect properties
|
|
||||||
var x = -config.lineWidth / 2;
|
|
||||||
var y = 0;
|
|
||||||
var width = config.lineWidth;
|
|
||||||
var height = config.lineHeight;
|
|
||||||
var curve = width / 2;
|
|
||||||
// round-rect path
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(x + curve, y);
|
|
||||||
ctx.lineTo(x + width - curve, y);
|
|
||||||
ctx.quadraticCurveTo(x + width, y, x + width, y + curve);
|
|
||||||
ctx.lineTo(x + width, y + height - curve);
|
|
||||||
ctx.quadraticCurveTo(x + width, y + height, x + width - curve, y + height);
|
|
||||||
ctx.lineTo(x + curve, y + height);
|
|
||||||
ctx.quadraticCurveTo(x, y + height, x, y + height - curve);
|
|
||||||
ctx.lineTo(x, y + curve);
|
|
||||||
ctx.quadraticCurveTo(x, y, x + curve, y);
|
|
||||||
// round-rect fill
|
|
||||||
var hue = ((i / (bars - 1)) * hues);
|
|
||||||
ctx.fillStyle = "hsla(" + hue + ", 100%, 50%, 0.85)";
|
|
||||||
ctx.fill();
|
|
||||||
ctx.restore();
|
|
||||||
}
|
|
||||||
ctx.restore();
|
|
||||||
offset += 0.07;
|
|
||||||
//
|
|
||||||
if (config.messageAnimate) {
|
|
||||||
var iteration = offset / 0.07 >> 0;
|
|
||||||
if (iteration % 10 === 0) {
|
|
||||||
var length = config.messageAnimate.length;
|
|
||||||
var n = iteration / 10 % length;
|
|
||||||
that.span.innerHTML = config.message + config.messageAnimate[n];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//
|
|
||||||
this.message = function(message) {
|
|
||||||
if (!interval) this.start();
|
|
||||||
config.message = message;
|
|
||||||
if (message.substr(-3) === "...") {
|
|
||||||
config.message = message.substr(0, message.length - 3);
|
|
||||||
config.messageAnimate = [ ". ",".. ","..." ].reverse();
|
|
||||||
} else {
|
|
||||||
config.messageAnimate = false;
|
|
||||||
}
|
|
||||||
that.span.innerHTML = config.message + (config.messageAnimate[0] || "");
|
|
||||||
};
|
|
||||||
//
|
|
||||||
this.stop = function () {
|
|
||||||
if (interval) {
|
|
||||||
window.clearInterval(interval);
|
|
||||||
interval = 0;
|
|
||||||
}
|
|
||||||
if (canvas && canvas.style) {
|
|
||||||
canvas.parentNode.style.opacity = 0;
|
|
||||||
window.setTimeout(function () {
|
|
||||||
canvas.parentNode.style.display = "none";
|
|
||||||
ctx.clearRect(0, 0, size, size);
|
|
||||||
}, 500);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//
|
|
||||||
this.start = function (max) {
|
|
||||||
if (interval) return;
|
|
||||||
canvas.parentNode.style.top = document.body.scrollTop + "px";
|
|
||||||
canvas.parentNode.style.opacity = 1;
|
|
||||||
canvas.parentNode.style.display = "block";
|
|
||||||
canvas.style.left = (width / 2) + "px";
|
|
||||||
canvas.style.top = (height / 2) + "px";
|
|
||||||
if (!config.delay) animate();
|
|
||||||
interval = window.setInterval(animate, 30);
|
|
||||||
if (config.message) {
|
|
||||||
this.message(config.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//
|
|
||||||
this.start(30 * 1000);
|
|
||||||
//
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
})(widgets);
|
|
250
js/Widgets/Loader.js
Normal file
250
js/Widgets/Loader.js
Normal file
|
@ -0,0 +1,250 @@
|
||||||
|
/*
|
||||||
|
----------------------------------------------------
|
||||||
|
Loader.js : 0.3 : 2012/04/12
|
||||||
|
----------------------------------------------------
|
||||||
|
https://github.com/mudcube/Loader.js
|
||||||
|
----------------------------------------------------
|
||||||
|
var loader = new widgets.Loader({ message: "loading: New loading message..." });
|
||||||
|
----------------------------------------------------
|
||||||
|
var loader = new widgets.Loader({
|
||||||
|
id: "loader",
|
||||||
|
bars: 12,
|
||||||
|
radius: 0,
|
||||||
|
lineWidth: 20,
|
||||||
|
lineHeight: 70,
|
||||||
|
background: "rgba(0,0,0,0.5)",
|
||||||
|
message: "loading...",
|
||||||
|
callback: function() {
|
||||||
|
// call function once loader has started
|
||||||
|
}
|
||||||
|
});
|
||||||
|
loader.stop();
|
||||||
|
----------------------------------------------------
|
||||||
|
loader.message("loading: New loading message...", function() {
|
||||||
|
// call function once loader has started
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (typeof(widgets) === "undefined") var widgets = {};
|
||||||
|
|
||||||
|
widgets.Loader = (function(root) {
|
||||||
|
|
||||||
|
var PI = Math.PI;
|
||||||
|
var defaultConfig = {
|
||||||
|
id: "loader",
|
||||||
|
bars: 12,
|
||||||
|
radius: 0,
|
||||||
|
lineWidth: 20,
|
||||||
|
lineHeight: 70,
|
||||||
|
display: true
|
||||||
|
};
|
||||||
|
|
||||||
|
var getWindowSize = function() {
|
||||||
|
if (window.innerWidth && window.innerHeight) {
|
||||||
|
var width = window.innerWidth;
|
||||||
|
var height = window.innerHeight;
|
||||||
|
} else if (document.compatMode === 'CSS1Compat' && document.documentElement && document.documentElement.offsetWidth) {
|
||||||
|
var width = document.documentElement.offsetWidth;
|
||||||
|
var height = document.documentElement.offsetHeight;
|
||||||
|
} else if (document.body && document.body.offsetWidth) {
|
||||||
|
var width = document.body.offsetWidth;
|
||||||
|
var height = document.body.offsetHeight;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
width: width,
|
||||||
|
height: height
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return function (conf) {
|
||||||
|
var that = this;
|
||||||
|
if (!document.createElement("canvas").getContext) return;
|
||||||
|
var that = this;
|
||||||
|
if (!document.body) return;
|
||||||
|
if (typeof(conf) === "string") conf = { message: conf };
|
||||||
|
if (typeof(conf) === "undefined") conf = {};
|
||||||
|
if (typeof(conf) === "boolean") conf = { display: false };
|
||||||
|
for (var key in defaultConfig) {
|
||||||
|
if (typeof(conf[key]) === "undefined") {
|
||||||
|
conf[key] = defaultConfig[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//
|
||||||
|
function centerSpan() {
|
||||||
|
if (conf.message) {
|
||||||
|
var windowSize = getWindowSize();
|
||||||
|
var width = windowSize.width - size;
|
||||||
|
var height = windowSize.height - size;
|
||||||
|
that.span.style.left = ((width + size) / 2 - that.span.offsetWidth/2) + "px";
|
||||||
|
that.span.style.top = (height / 2 + size - 10) + "px";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
///
|
||||||
|
var canvas = document.getElementById(conf.id);
|
||||||
|
var timeout = 1;
|
||||||
|
if (!canvas) {
|
||||||
|
var div = document.createElement("div");
|
||||||
|
var span = document.createElement("span");
|
||||||
|
div.appendChild(span);
|
||||||
|
that.span = span;
|
||||||
|
that.div = div;
|
||||||
|
var canvas = document.createElement("canvas");
|
||||||
|
document.body.appendChild(canvas);
|
||||||
|
canvas.id = conf.id;
|
||||||
|
canvas.style.cssText = "opacity: 1; position: absolute; z-index: 10000;";
|
||||||
|
div.appendChild(canvas);
|
||||||
|
document.body.appendChild(div);
|
||||||
|
} else {
|
||||||
|
that.span = canvas.parentNode.getElementsByTagName("span")[0];
|
||||||
|
}
|
||||||
|
//
|
||||||
|
var delay = conf.delay;
|
||||||
|
var bars = conf.bars;
|
||||||
|
var radius = conf.radius;
|
||||||
|
var max = conf.lineHeight + 20;
|
||||||
|
var size = max * 2 + conf.radius * 2;
|
||||||
|
var windowSize = getWindowSize();
|
||||||
|
var width = windowSize.width - size;
|
||||||
|
var height = windowSize.height - size;
|
||||||
|
///
|
||||||
|
canvas.width = size;
|
||||||
|
canvas.height = size;
|
||||||
|
canvas.style.left = (width / 2) + "px";
|
||||||
|
canvas.style.top = (height / 2) + "px";
|
||||||
|
///
|
||||||
|
centerSpan();
|
||||||
|
///
|
||||||
|
var offset = 0;
|
||||||
|
var ctx = canvas.getContext('2d');
|
||||||
|
ctx.globalCompositeOperation = "lighter";
|
||||||
|
ctx.shadowOffsetX = 1;
|
||||||
|
ctx.shadowOffsetY = 1;
|
||||||
|
ctx.shadowBlur = 1;
|
||||||
|
ctx.shadowColor = 'rgba(0, 0, 0, 0.5)';
|
||||||
|
//
|
||||||
|
function animate() {
|
||||||
|
var windowSize = getWindowSize();
|
||||||
|
var width = windowSize.width - size;
|
||||||
|
var height = windowSize.height - size;
|
||||||
|
//
|
||||||
|
canvas.style.left = (width / 2) + "px";
|
||||||
|
canvas.style.top = (height / 2) + "px";
|
||||||
|
centerSpan();
|
||||||
|
//
|
||||||
|
ctx.save();
|
||||||
|
ctx.clearRect(0, 0, size, size);
|
||||||
|
ctx.translate(size / 2, size / 2);
|
||||||
|
var hues = 360 - 360 / bars;
|
||||||
|
for (var i = 0; i < bars; i++) {
|
||||||
|
var angle = (i / bars * 2 * PI) + offset;
|
||||||
|
ctx.save();
|
||||||
|
ctx.translate(radius * Math.sin(-angle), radius * Math.cos(-angle));
|
||||||
|
ctx.rotate(angle);
|
||||||
|
// round-rect properties
|
||||||
|
var x = -conf.lineWidth / 2;
|
||||||
|
var y = 0;
|
||||||
|
var width = conf.lineWidth;
|
||||||
|
var height = conf.lineHeight;
|
||||||
|
var curve = width / 2;
|
||||||
|
// round-rect path
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x + curve, y);
|
||||||
|
ctx.lineTo(x + width - curve, y);
|
||||||
|
ctx.quadraticCurveTo(x + width, y, x + width, y + curve);
|
||||||
|
ctx.lineTo(x + width, y + height - curve);
|
||||||
|
ctx.quadraticCurveTo(x + width, y + height, x + width - curve, y + height);
|
||||||
|
ctx.lineTo(x + curve, y + height);
|
||||||
|
ctx.quadraticCurveTo(x, y + height, x, y + height - curve);
|
||||||
|
ctx.lineTo(x, y + curve);
|
||||||
|
ctx.quadraticCurveTo(x, y, x + curve, y);
|
||||||
|
// round-rect fill
|
||||||
|
var hue = ((i / (bars - 1)) * hues);
|
||||||
|
ctx.fillStyle = "hsla(" + hue + ", 100%, 50%, 0.85)";
|
||||||
|
ctx.fill();
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
ctx.restore();
|
||||||
|
offset += 0.07;
|
||||||
|
//
|
||||||
|
if (conf.messageAnimate) {
|
||||||
|
var iteration = offset / 0.07 >> 0;
|
||||||
|
if (iteration % 10 === 0) {
|
||||||
|
var length = conf.messageAnimate.length;
|
||||||
|
var n = iteration / 10 % length;
|
||||||
|
that.span.innerHTML = conf.message + conf.messageAnimate[n];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.css = function() {
|
||||||
|
var background = conf.background ? conf.background : "rgba(0,0,0,0.65)";
|
||||||
|
span.style.cssText = "font-family: monospace; font-size: 14px; opacity: 1; display: none; background: "+background+"; border-radius: 10px; padding: 10px; width: 200px; text-align: center; position: absolute; z-index: 10000;";
|
||||||
|
div.style.cssText = "color: #fff; -webkit-transition-property: opacity; -webkit-transition-duration: " + timeout + "s; position: fixed; left: 0; top: 0; width: 100%; height: 100%; z-index: 100000; opacity: 0; display: none";
|
||||||
|
if (this.stopPropagation) {
|
||||||
|
div.style.cssText += "background: rgba(0,0,0,0.25);";
|
||||||
|
} else {
|
||||||
|
div.style.cssText += "pointer-events: none;";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.stop = function () {
|
||||||
|
setTimeout(function() {
|
||||||
|
window.clearInterval(that.interval);
|
||||||
|
delete that.interval;
|
||||||
|
}, 50);
|
||||||
|
if (canvas && canvas.style) {
|
||||||
|
div.style.cssText += "pointer-events: none;";
|
||||||
|
canvas.parentNode.style.opacity = 0;
|
||||||
|
window.setTimeout(function () {
|
||||||
|
that.stopPropagation = false;
|
||||||
|
canvas.parentNode.style.display = "none";
|
||||||
|
ctx.clearRect(0, 0, size, size);
|
||||||
|
}, timeout * 1000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.start = function (callback) {
|
||||||
|
this.css();
|
||||||
|
var windowSize = getWindowSize();
|
||||||
|
var width = windowSize.width - size;
|
||||||
|
var height = windowSize.height - size;
|
||||||
|
canvas.parentNode.style.opacity = 1;
|
||||||
|
canvas.parentNode.style.display = "block";
|
||||||
|
that.span.style.display = conf.message ? "block" : "none";
|
||||||
|
if (conf.background) that.div.style.background = conf.backgrond;
|
||||||
|
canvas.style.left = (width / 2) + "px";
|
||||||
|
canvas.style.top = (height / 2) + "px";
|
||||||
|
if (!conf.delay) animate();
|
||||||
|
window.clearInterval(this.interval);
|
||||||
|
this.interval = window.setInterval(animate, 30);
|
||||||
|
if (conf.message) {
|
||||||
|
compileMessage(conf.message, callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.message = function(message, callback) {
|
||||||
|
conf.message = message;
|
||||||
|
if (!this.interval) return this.start(callback);
|
||||||
|
if (conf.background) that.span.style.background = conf.backgrond;
|
||||||
|
compileMessage(conf.message, callback);
|
||||||
|
that.span.style.display = conf.message ? "block" : "none";
|
||||||
|
};
|
||||||
|
var compileMessage = function(message, callback) {
|
||||||
|
if (message.substr(-3) === "...") {
|
||||||
|
conf.message = message.substr(0, message.length - 3);
|
||||||
|
conf.messageAnimate = [ ". ",".. ","..." ].reverse();
|
||||||
|
that.span.innerHTML = conf.message + conf.messageAnimate[0];
|
||||||
|
} else {
|
||||||
|
conf.messageAnimate = false;
|
||||||
|
that.span.innerHTML = conf.message;
|
||||||
|
}
|
||||||
|
if (callback) {
|
||||||
|
setTimeout(callback, 50);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//
|
||||||
|
if (conf.display === false) return this;
|
||||||
|
//
|
||||||
|
this.css();
|
||||||
|
this.start();
|
||||||
|
//
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
})(widgets);
|
|
@ -1,24 +0,0 @@
|
||||||
Copyright (c) 2010, Matt Westcott & Ben Firshman
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
* The names of its contributors may not be used to endorse or promote products
|
|
||||||
derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
||||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
@ -1,238 +0,0 @@
|
||||||
/*
|
|
||||||
class to parse the .mid file format
|
|
||||||
(depends on stream.js)
|
|
||||||
*/
|
|
||||||
function MidiFile(data) {
|
|
||||||
function readChunk(stream) {
|
|
||||||
var id = stream.read(4);
|
|
||||||
var length = stream.readInt32();
|
|
||||||
return {
|
|
||||||
'id': id,
|
|
||||||
'length': length,
|
|
||||||
'data': stream.read(length)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var lastEventTypeByte;
|
|
||||||
|
|
||||||
function readEvent(stream) {
|
|
||||||
var event = {};
|
|
||||||
event.deltaTime = stream.readVarInt();
|
|
||||||
var eventTypeByte = stream.readInt8();
|
|
||||||
if ((eventTypeByte & 0xf0) == 0xf0) {
|
|
||||||
/* system / meta event */
|
|
||||||
if (eventTypeByte == 0xff) {
|
|
||||||
/* meta event */
|
|
||||||
event.type = 'meta';
|
|
||||||
var subtypeByte = stream.readInt8();
|
|
||||||
var length = stream.readVarInt();
|
|
||||||
switch(subtypeByte) {
|
|
||||||
case 0x00:
|
|
||||||
event.subtype = 'sequenceNumber';
|
|
||||||
if (length != 2) throw "Expected length for sequenceNumber event is 2, got " + length;
|
|
||||||
event.number = stream.readInt16();
|
|
||||||
return event;
|
|
||||||
case 0x01:
|
|
||||||
event.subtype = 'text';
|
|
||||||
event.text = stream.read(length);
|
|
||||||
return event;
|
|
||||||
case 0x02:
|
|
||||||
event.subtype = 'copyrightNotice';
|
|
||||||
event.text = stream.read(length);
|
|
||||||
return event;
|
|
||||||
case 0x03:
|
|
||||||
event.subtype = 'trackName';
|
|
||||||
event.text = stream.read(length);
|
|
||||||
return event;
|
|
||||||
case 0x04:
|
|
||||||
event.subtype = 'instrumentName';
|
|
||||||
event.text = stream.read(length);
|
|
||||||
return event;
|
|
||||||
case 0x05:
|
|
||||||
event.subtype = 'lyrics';
|
|
||||||
event.text = stream.read(length);
|
|
||||||
return event;
|
|
||||||
case 0x06:
|
|
||||||
event.subtype = 'marker';
|
|
||||||
event.text = stream.read(length);
|
|
||||||
return event;
|
|
||||||
case 0x07:
|
|
||||||
event.subtype = 'cuePoint';
|
|
||||||
event.text = stream.read(length);
|
|
||||||
return event;
|
|
||||||
case 0x20:
|
|
||||||
event.subtype = 'midiChannelPrefix';
|
|
||||||
if (length != 1) throw "Expected length for midiChannelPrefix event is 1, got " + length;
|
|
||||||
event.channel = stream.readInt8();
|
|
||||||
return event;
|
|
||||||
case 0x2f:
|
|
||||||
event.subtype = 'endOfTrack';
|
|
||||||
if (length != 0) throw "Expected length for endOfTrack event is 0, got " + length;
|
|
||||||
return event;
|
|
||||||
case 0x51:
|
|
||||||
event.subtype = 'setTempo';
|
|
||||||
if (length != 3) throw "Expected length for setTempo event is 3, got " + length;
|
|
||||||
event.microsecondsPerBeat = (
|
|
||||||
(stream.readInt8() << 16)
|
|
||||||
+ (stream.readInt8() << 8)
|
|
||||||
+ stream.readInt8()
|
|
||||||
)
|
|
||||||
return event;
|
|
||||||
case 0x54:
|
|
||||||
event.subtype = 'smpteOffset';
|
|
||||||
if (length != 5) throw "Expected length for smpteOffset event is 5, got " + length;
|
|
||||||
var hourByte = stream.readInt8();
|
|
||||||
event.frameRate = {
|
|
||||||
0x00: 24, 0x20: 25, 0x40: 29, 0x60: 30
|
|
||||||
}[hourByte & 0x60];
|
|
||||||
event.hour = hourByte & 0x1f;
|
|
||||||
event.min = stream.readInt8();
|
|
||||||
event.sec = stream.readInt8();
|
|
||||||
event.frame = stream.readInt8();
|
|
||||||
event.subframe = stream.readInt8();
|
|
||||||
return event;
|
|
||||||
case 0x58:
|
|
||||||
event.subtype = 'timeSignature';
|
|
||||||
if (length != 4) throw "Expected length for timeSignature event is 4, got " + length;
|
|
||||||
event.numerator = stream.readInt8();
|
|
||||||
event.denominator = Math.pow(2, stream.readInt8());
|
|
||||||
event.metronome = stream.readInt8();
|
|
||||||
event.thirtyseconds = stream.readInt8();
|
|
||||||
return event;
|
|
||||||
case 0x59:
|
|
||||||
event.subtype = 'keySignature';
|
|
||||||
if (length != 2) throw "Expected length for keySignature event is 2, got " + length;
|
|
||||||
event.key = stream.readInt8();
|
|
||||||
event.scale = stream.readInt8();
|
|
||||||
return event;
|
|
||||||
case 0x7f:
|
|
||||||
event.subtype = 'sequencerSpecific';
|
|
||||||
event.data = stream.read(length);
|
|
||||||
return event;
|
|
||||||
default:
|
|
||||||
// console.log("Unrecognised meta event subtype: " + subtypeByte);
|
|
||||||
event.subtype = 'unknown'
|
|
||||||
event.data = stream.read(length);
|
|
||||||
return event;
|
|
||||||
}
|
|
||||||
event.data = stream.read(length);
|
|
||||||
return event;
|
|
||||||
} else if (eventTypeByte == 0xf0) {
|
|
||||||
event.type = 'sysEx';
|
|
||||||
var length = stream.readVarInt();
|
|
||||||
event.data = stream.read(length);
|
|
||||||
return event;
|
|
||||||
} else if (eventTypeByte == 0xf7) {
|
|
||||||
event.type = 'dividedSysEx';
|
|
||||||
var length = stream.readVarInt();
|
|
||||||
event.data = stream.read(length);
|
|
||||||
return event;
|
|
||||||
} else {
|
|
||||||
throw "Unrecognised MIDI event type byte: " + eventTypeByte;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* channel event */
|
|
||||||
var param1;
|
|
||||||
if ((eventTypeByte & 0x80) == 0) {
|
|
||||||
/* running status - reuse lastEventTypeByte as the event type.
|
|
||||||
eventTypeByte is actually the first parameter
|
|
||||||
*/
|
|
||||||
param1 = eventTypeByte;
|
|
||||||
eventTypeByte = lastEventTypeByte;
|
|
||||||
} else {
|
|
||||||
param1 = stream.readInt8();
|
|
||||||
lastEventTypeByte = eventTypeByte;
|
|
||||||
}
|
|
||||||
var eventType = eventTypeByte >> 4;
|
|
||||||
event.channel = eventTypeByte & 0x0f;
|
|
||||||
event.type = 'channel';
|
|
||||||
switch (eventType) {
|
|
||||||
case 0x08:
|
|
||||||
event.subtype = 'noteOff';
|
|
||||||
event.noteNumber = param1;
|
|
||||||
event.velocity = stream.readInt8();
|
|
||||||
return event;
|
|
||||||
case 0x09:
|
|
||||||
event.noteNumber = param1;
|
|
||||||
event.velocity = stream.readInt8();
|
|
||||||
if (event.velocity == 0) {
|
|
||||||
event.subtype = 'noteOff';
|
|
||||||
} else {
|
|
||||||
event.subtype = 'noteOn';
|
|
||||||
}
|
|
||||||
return event;
|
|
||||||
case 0x0a:
|
|
||||||
event.subtype = 'noteAftertouch';
|
|
||||||
event.noteNumber = param1;
|
|
||||||
event.amount = stream.readInt8();
|
|
||||||
return event;
|
|
||||||
case 0x0b:
|
|
||||||
event.subtype = 'controller';
|
|
||||||
event.controllerType = param1;
|
|
||||||
event.value = stream.readInt8();
|
|
||||||
return event;
|
|
||||||
case 0x0c:
|
|
||||||
event.subtype = 'programChange';
|
|
||||||
event.programNumber = param1;
|
|
||||||
return event;
|
|
||||||
case 0x0d:
|
|
||||||
event.subtype = 'channelAftertouch';
|
|
||||||
event.amount = param1;
|
|
||||||
return event;
|
|
||||||
case 0x0e:
|
|
||||||
event.subtype = 'pitchBend';
|
|
||||||
event.value = param1 + (stream.readInt8() << 7);
|
|
||||||
return event;
|
|
||||||
default:
|
|
||||||
throw "Unrecognised MIDI event type: " + eventType
|
|
||||||
/*
|
|
||||||
console.log("Unrecognised MIDI event type: " + eventType);
|
|
||||||
stream.readInt8();
|
|
||||||
event.subtype = 'unknown';
|
|
||||||
return event;
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stream = Stream(data);
|
|
||||||
var headerChunk = readChunk(stream);
|
|
||||||
if (headerChunk.id != 'MThd' || headerChunk.length != 6) {
|
|
||||||
throw "Bad .mid file - header not found";
|
|
||||||
}
|
|
||||||
var headerStream = Stream(headerChunk.data);
|
|
||||||
var formatType = headerStream.readInt16();
|
|
||||||
var trackCount = headerStream.readInt16();
|
|
||||||
var timeDivision = headerStream.readInt16();
|
|
||||||
|
|
||||||
if (timeDivision & 0x8000) {
|
|
||||||
throw "Expressing time division in SMTPE frames is not supported yet"
|
|
||||||
} else {
|
|
||||||
ticksPerBeat = timeDivision;
|
|
||||||
}
|
|
||||||
|
|
||||||
var header = {
|
|
||||||
'formatType': formatType,
|
|
||||||
'trackCount': trackCount,
|
|
||||||
'ticksPerBeat': ticksPerBeat
|
|
||||||
}
|
|
||||||
var tracks = [];
|
|
||||||
for (var i = 0; i < header.trackCount; i++) {
|
|
||||||
tracks[i] = [];
|
|
||||||
var trackChunk = readChunk(stream);
|
|
||||||
if (trackChunk.id != 'MTrk') {
|
|
||||||
throw "Unexpected chunk - expected MTrk, got "+ trackChunk.id;
|
|
||||||
}
|
|
||||||
var trackStream = Stream(trackChunk.data);
|
|
||||||
while (!trackStream.eof()) {
|
|
||||||
var event = readEvent(trackStream);
|
|
||||||
tracks[i].push(event);
|
|
||||||
//console.log(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
'header': header,
|
|
||||||
'tracks': tracks
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
var clone = function (o) {
|
|
||||||
if (typeof o != 'object') return (o);
|
|
||||||
if (o == null) return (o);
|
|
||||||
var ret = (typeof o.length == 'number') ? [] : {};
|
|
||||||
for (var key in o) ret[key] = clone(o[key]);
|
|
||||||
return ret;
|
|
||||||
};
|
|
||||||
|
|
||||||
function Replayer(midiFile, timeWarp, eventProcessor) {
|
|
||||||
var trackStates = [];
|
|
||||||
var beatsPerMinute = 120;
|
|
||||||
var ticksPerBeat = midiFile.header.ticksPerBeat;
|
|
||||||
for (var i = 0; i < midiFile.tracks.length; i++) {
|
|
||||||
trackStates[i] = {
|
|
||||||
"nextEventIndex": 0,
|
|
||||||
"ticksToNextEvent": (
|
|
||||||
midiFile.tracks[i].length ? midiFile.tracks[i][0].deltaTime : null
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
function getNextEvent() {
|
|
||||||
var ticksToNextEvent = null;
|
|
||||||
var nextEventTrack = null;
|
|
||||||
var nextEventIndex = null;
|
|
||||||
for (var i = 0; i < trackStates.length; i++) {
|
|
||||||
if (trackStates[i].ticksToNextEvent != null && (ticksToNextEvent == null || trackStates[i].ticksToNextEvent < ticksToNextEvent)) {
|
|
||||||
ticksToNextEvent = trackStates[i].ticksToNextEvent;
|
|
||||||
nextEventTrack = i;
|
|
||||||
nextEventIndex = trackStates[i].nextEventIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (nextEventTrack != null) {
|
|
||||||
/// consume event from that track
|
|
||||||
var nextEvent = midiFile.tracks[nextEventTrack][nextEventIndex];
|
|
||||||
if (midiFile.tracks[nextEventTrack][nextEventIndex + 1]) {
|
|
||||||
trackStates[nextEventTrack].ticksToNextEvent += midiFile.tracks[nextEventTrack][nextEventIndex + 1].deltaTime;
|
|
||||||
} else {
|
|
||||||
trackStates[nextEventTrack].ticksToNextEvent = null;
|
|
||||||
}
|
|
||||||
trackStates[nextEventTrack].nextEventIndex += 1;
|
|
||||||
/// advance timings on all tracks by ticksToNextEvent
|
|
||||||
for (var i = 0; i < trackStates.length; i++) {
|
|
||||||
if (trackStates[i].ticksToNextEvent != null) {
|
|
||||||
trackStates[i].ticksToNextEvent -= ticksToNextEvent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
"ticksToEvent": ticksToNextEvent,
|
|
||||||
"event": nextEvent,
|
|
||||||
"track": nextEventTrack
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//
|
|
||||||
var midiEvent;
|
|
||||||
var temporal = [];
|
|
||||||
//
|
|
||||||
function processEvents() {
|
|
||||||
function processNext() {
|
|
||||||
if (midiEvent.ticksToEvent > 0) {
|
|
||||||
var beatsToGenerate = midiEvent.ticksToEvent / ticksPerBeat;
|
|
||||||
var secondsToGenerate = beatsToGenerate / (beatsPerMinute / 60);
|
|
||||||
}
|
|
||||||
var time = (secondsToGenerate * 1000 * timeWarp) || 0;
|
|
||||||
temporal.push([ midiEvent, time]);
|
|
||||||
midiEvent = getNextEvent();
|
|
||||||
};
|
|
||||||
//
|
|
||||||
if (midiEvent = getNextEvent()) {
|
|
||||||
while(midiEvent) processNext(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
processEvents();
|
|
||||||
return {
|
|
||||||
"getData": function() {
|
|
||||||
return clone(temporal);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -1,68 +0,0 @@
|
||||||
/* Wrapper for accessing strings through sequential reads */
|
|
||||||
function Stream(str) {
|
|
||||||
var position = 0;
|
|
||||||
|
|
||||||
function read(length) {
|
|
||||||
var result = str.substr(position, length);
|
|
||||||
position += length;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read a big-endian 32-bit integer */
|
|
||||||
function readInt32() {
|
|
||||||
var result = (
|
|
||||||
(str.charCodeAt(position) << 24)
|
|
||||||
+ (str.charCodeAt(position + 1) << 16)
|
|
||||||
+ (str.charCodeAt(position + 2) << 8)
|
|
||||||
+ str.charCodeAt(position + 3));
|
|
||||||
position += 4;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read a big-endian 16-bit integer */
|
|
||||||
function readInt16() {
|
|
||||||
var result = (
|
|
||||||
(str.charCodeAt(position) << 8)
|
|
||||||
+ str.charCodeAt(position + 1));
|
|
||||||
position += 2;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read an 8-bit integer */
|
|
||||||
function readInt8() {
|
|
||||||
var result = str.charCodeAt(position);
|
|
||||||
position += 1;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function eof() {
|
|
||||||
return position >= str.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* read a MIDI-style variable-length integer
|
|
||||||
(big-endian value in groups of 7 bits,
|
|
||||||
with top bit set to signify that another byte follows)
|
|
||||||
*/
|
|
||||||
function readVarInt() {
|
|
||||||
var result = 0;
|
|
||||||
while (true) {
|
|
||||||
var b = readInt8();
|
|
||||||
if (b & 0x80) {
|
|
||||||
result += (b & 0x7f);
|
|
||||||
result <<= 7;
|
|
||||||
} else {
|
|
||||||
/* b is the last byte */
|
|
||||||
return result + b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
'eof': eof,
|
|
||||||
'read': read,
|
|
||||||
'readInt32': readInt32,
|
|
||||||
'readInt16': readInt16,
|
|
||||||
'readInt8': readInt8,
|
|
||||||
'readVarInt': readVarInt
|
|
||||||
}
|
|
||||||
}
|
|
12
minimal.html
12
minimal.html
|
@ -3,13 +3,13 @@
|
||||||
<head>
|
<head>
|
||||||
<script src="./js/DOMLoader.XMLHttp.js" type="text/javascript"></script>
|
<script src="./js/DOMLoader.XMLHttp.js" type="text/javascript"></script>
|
||||||
<script src="./js/DOMLoader.script.js" type="text/javascript"></script>
|
<script src="./js/DOMLoader.script.js" type="text/javascript"></script>
|
||||||
<script src="./js/MIDI.audioDetect.js" type="text/javascript"></script>
|
<script src="./js/MIDI/audioDetect.js" type="text/javascript"></script>
|
||||||
<script src="./js/MIDI.loadPlugin.js" type="text/javascript"></script>
|
<script src="./js/MIDI/loadPlugin.js" type="text/javascript"></script>
|
||||||
<script src="./js/MIDI.Plugin.js" type="text/javascript"></script>
|
<script src="./js/MIDI/Plugin.js" type="text/javascript"></script>
|
||||||
<script src="./js/MIDI.Player.js" type="text/javascript"></script>
|
<script src="./js/MIDI/Player.js" type="text/javascript"></script>
|
||||||
<!-- base64 packages -->
|
<!-- base64 packages -->
|
||||||
<script src="./js/VersionControl.Base64.js" type="text/javascript"></script>
|
<script src="./js/Polyfill/Base64.js" type="text/javascript"></script>
|
||||||
<script src="./js/lib/base64binary.js" type="text/javascript"></script>
|
<script src="./inc/base64binary.js" type="text/javascript"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "midi",
|
"name": "midi",
|
||||||
"version": "0.0.1",
|
"version": "0.0.2",
|
||||||
"description": "HTML5 midi player",
|
"description": "HTML5 midi player",
|
||||||
"author": "Michael Deal",
|
"author": "Michael Deal",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue