update to latest libraries
parent
61f1a05ebe
commit
eb21d1f743
|
@ -4,26 +4,25 @@
|
|||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>MIDI.js - Sequencing in Javascript.</title>
|
||||
<!-- 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 -->
|
||||
<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.Mouse.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/MIDI.audioDetect.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.Player.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/MIDI/audioDetect.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/Player.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>
|
||||
<!-- jasmid package -->
|
||||
<script src="./js/lib/jasmid/stream.js"></script>
|
||||
<script src="./js/lib/jasmid/midifile.js"></script>
|
||||
<script src="./js/lib/jasmid/replayer.js"></script>
|
||||
<script src="./inc/jasmid/stream.js"></script>
|
||||
<script src="./inc/jasmid/midifile.js"></script>
|
||||
<script src="./inc/jasmid/replayer.js"></script>
|
||||
<!-- base64 packages -->
|
||||
<script src="./js/VersionControl.Base64.js" type="text/javascript"></script>
|
||||
<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>
|
||||
</head>
|
||||
<body>
|
||||
<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;">
|
||||
<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/backward.png" align="absmiddle" value="stop" onclick="getNextSong(-1);">
|
||||
<input type="image" src="./images/forward.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="player.getNextSong(+1);">
|
||||
</div>
|
||||
<div class="time-controls" style="float: left; margin: 0; position: relative; top: 5px;">
|
||||
<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>
|
||||
<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">
|
||||
<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>
|
||||
|
@ -156,6 +150,7 @@ MIDI.Player.setAnimation(function(data) {
|
|||
if (typeof(console) === "undefined") var console = { log: function() { } };
|
||||
|
||||
// Begin loading indication.
|
||||
var player;
|
||||
var loader = new widgets.Loader({
|
||||
message: "loading: Soundfont..."
|
||||
});
|
||||
|
@ -212,7 +207,7 @@ Event.add(window, "load", function(event) {
|
|||
var title = document.getElementById("title");
|
||||
title.innerHTML = "Sound being generated with " + MIDI.lang + ".";
|
||||
// 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.loadFile(song[songid++%3], player.start);
|
||||
// control the piano keys colors
|
||||
|
@ -221,7 +216,7 @@ Event.add(window, "load", function(event) {
|
|||
var pianoKey = data.note - MIDI.pianoKeyOffset;
|
||||
var d = colorElements[pianoKey];
|
||||
if (data.message === 144) {
|
||||
d.style.background = "#" + colorMap[data.note-27].hex;
|
||||
d.style.background = colorMap[data.note-27].hex;
|
||||
d.style.color = "#fff";
|
||||
} else {
|
||||
d.style.background = "";
|
||||
|
@ -245,19 +240,15 @@ var MIDIPlayerPercentage = function(player) {
|
|||
var capsule = document.getElementById("capsule");
|
||||
var timeCursor = document.getElementById("cursor");
|
||||
//
|
||||
Event.drag({
|
||||
type: "absolute",
|
||||
element: capsule,
|
||||
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 > player.endTime) player.currentTime = player.endTime;
|
||||
if (state === "down") {
|
||||
player.pause(true);
|
||||
} else if (state === "up") {
|
||||
player.resume();
|
||||
}
|
||||
Event.add(capsule, "drag", function (event, self) {
|
||||
Event.cancel(event);
|
||||
player.currentTime = (self.x) / 420 * player.endTime;
|
||||
if (player.currentTime < 0) player.currentTime = 0;
|
||||
if (player.currentTime > player.endTime) player.currentTime = player.endTime;
|
||||
if (self.state === "down") {
|
||||
player.pause(true);
|
||||
} else if (self.state === "up") {
|
||||
player.resume();
|
||||
}
|
||||
});
|
||||
//
|
||||
|
@ -267,7 +258,7 @@ var MIDIPlayerPercentage = function(player) {
|
|||
if (seconds.length == 1) seconds = "0" + seconds;
|
||||
return minutes + ":" + seconds;
|
||||
};
|
||||
getNextSong = function(n) {
|
||||
player.getNextSong = function(n) {
|
||||
var id = Math.abs((songid += n) % song.length);
|
||||
player.loadFile(song[id], player.start); // load MIDI
|
||||
};
|
||||
|
@ -314,7 +305,7 @@ var ColorSphereBackground = function() {
|
|||
var onMouseMove = function(event) {
|
||||
ctx.drawImage(theSphere, 0, 0);
|
||||
if (event) {
|
||||
var coords = Event.coords(event);
|
||||
var coords = Event.proxy.getCoord(event);
|
||||
coords.x -= document.body.scrollLeft;
|
||||
coords.y -= document.body.scrollTop;
|
||||
px = coords.x;
|
|
@ -2,19 +2,19 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<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/DOMLoader.XMLHttp.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.loadPlugin.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/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/MIDI/audioDetect.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/Player.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>
|
||||
<!-- 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 -->
|
||||
<link href="http://fonts.googleapis.com/css?family=Andada" rel="stylesheet" type="text/css" />
|
||||
<style>
|
||||
|
|
2
build.sh
2
build.sh
|
@ -5,7 +5,7 @@ OUT=build/MIDI.minimal.js
|
|||
|
||||
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
|
||||
cat $file >> $OUT
|
||||
echo "" >> $OUT
|
||||
|
|
|
@ -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
|
||||
};
|
||||
};
|
||||
|
||||
})();
|
|
@ -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;
|
||||
};
|
1877
js/Event.js
1877
js/Event.js
File diff suppressed because it is too large
Load Diff
|
@ -2,7 +2,7 @@
|
|||
|
||||
MIDI.Player : 0.3
|
||||
-------------------------------------
|
||||
https://github.com/mudx/MIDI.js
|
||||
https://github.com/mudcube/MIDI.js
|
||||
-------------------------------------
|
||||
requires jasmid
|
||||
|
||||
|
@ -56,23 +56,23 @@ root.clearAnimation = function() {
|
|||
|
||||
root.setAnimation = function(config) {
|
||||
var callback = (typeof(config) === "function") ? config : config.callback;
|
||||
var delay = config.delay || 24;
|
||||
var interval = config.interval || 30;
|
||||
var currentTime = 0;
|
||||
var tOurTime = 0;
|
||||
var tTheirTime = 0;
|
||||
//
|
||||
root.clearAnimation();
|
||||
root.interval = window.setInterval(function (){
|
||||
root.interval = window.setInterval(function () {
|
||||
if (root.endTime === 0) return;
|
||||
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) {
|
||||
currentTime = 0;
|
||||
} else {
|
||||
currentTime = root.currentTime - currentTime;
|
||||
}
|
||||
if (tTheirTime != root.currentTime) {
|
||||
tOurTime = (new Date()).getTime();
|
||||
if (tTheirTime !== root.currentTime) {
|
||||
tOurTime = (new Date).getTime();
|
||||
tTheirTime = root.currentTime;
|
||||
}
|
||||
} else { // paused
|
||||
|
@ -91,7 +91,7 @@ root.setAnimation = function(config) {
|
|||
end: t2,
|
||||
events: noteRegistrar
|
||||
});
|
||||
}, delay);
|
||||
}, interval);
|
||||
};
|
||||
|
||||
// helpers
|
||||
|
@ -111,6 +111,10 @@ root.loadFile = function (file, callback) {
|
|||
if (callback) callback(data);
|
||||
return;
|
||||
}
|
||||
///
|
||||
var title = file.split(" - ")[1] || file;
|
||||
document.getElementById("playback-title").innerHTML = title.replace(".mid","");
|
||||
///
|
||||
var fetch = new XMLHttpRequest();
|
||||
fetch.open('GET', file);
|
||||
fetch.overrideMimeType("text/plain; charset=x-user-defined");
|
||||
|
@ -123,7 +127,7 @@ root.loadFile = function (file, callback) {
|
|||
for (var z = 0; z < mx; z++) {
|
||||
ff[z] = scc(t.charCodeAt(z) & 255);
|
||||
}
|
||||
var data = ff.join("");
|
||||
var data = ff.join("");
|
||||
root.currentData = data;
|
||||
root.loadMidiFile();
|
||||
if (callback) callback(data);
|
||||
|
@ -140,8 +144,7 @@ var startTime = 0; // to measure time elapse
|
|||
var noteRegistrar = {}; // get event for requested note
|
||||
var onMidiEvent = undefined; // listener callback
|
||||
var scheduleTracking = function (channel, note, currentTime, offset, message, velocity) {
|
||||
var interval = window.setInterval(function () {
|
||||
window.clearInterval(interval);
|
||||
var interval = window.setTimeout(function () {
|
||||
var data = {
|
||||
channel: channel,
|
||||
note: note,
|
|
@ -225,7 +225,11 @@ if (typeof(MusicTheory.Synesthesia) === "undefined") MusicTheory.Synesthesia = {
|
|||
9: [ 29, 94, 52 ],
|
||||
10: [ 62, 78, 74 ],
|
||||
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) {
|
||||
|
@ -240,18 +244,28 @@ if (typeof(MusicTheory.Synesthesia) === "undefined") MusicTheory.Synesthesia = {
|
|||
var syn = root.data;
|
||||
var colors = syn[type] || syn["D. D. Jameson (1844)"];
|
||||
for (var note = 0; note <= 88; note ++) { // creates mapping for 88 notes
|
||||
var clr = colors[(note + 9) % 12];
|
||||
if (clr[0] == clr[1] && clr[1] == clr[2]) {
|
||||
clr = blend(parray, colors[(note + 10) % 12]);
|
||||
}
|
||||
var amount = clr[2] / 10;
|
||||
var octave = note / 12 >> 0;
|
||||
var octaveLum = clr[2] + amount * octave - 3 * amount; // map luminance to octave
|
||||
data[note] = {
|
||||
hsl: 'hsla(' + clr[0] + ',' + clr[1] + '%,' + octaveLum + '%, 1)',
|
||||
hex: Color.Space({H:clr[0], S:clr[1], L:octaveLum}, "HSL>RGB>HEX>STRING")
|
||||
};
|
||||
var parray = clr;
|
||||
if (colors.data) {
|
||||
data[note] = {
|
||||
hsl: colors.data[note],
|
||||
hex: colors.data[note]
|
||||
}
|
||||
} else {
|
||||
var clr = colors[(note + 9) % 12];
|
||||
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]);
|
||||
}
|
||||
var amount = L / 10;
|
||||
var octave = note / 12 >> 0;
|
||||
var octaveLum = L + amount * octave - 3 * amount; // map luminance to octave
|
||||
data[note] = {
|
||||
hsl: 'hsla(' + H + ',' + S + '%,' + octaveLum + '%, 1)',
|
||||
hex: Color.Space({H:H, S:S, L:octaveLum}, "HSL>RGB>HEX>W3")
|
||||
};
|
||||
var parray = clr;
|
||||
}
|
||||
}
|
||||
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);
|
|
@ -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>
|
||||
<script src="./js/DOMLoader.XMLHttp.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.loadPlugin.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/audioDetect.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/Player.js" type="text/javascript"></script>
|
||||
<!-- base64 packages -->
|
||||
<script src="./js/VersionControl.Base64.js" type="text/javascript"></script>
|
||||
<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>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "midi",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.2",
|
||||
"description": "HTML5 midi player",
|
||||
"author": "Michael Deal",
|
||||
"scripts": {
|
||||
|
|
Loading…
Reference in New Issue