update to latest libraries

master
Michael Deal 2012-10-09 22:59:04 -07:00
parent 61f1a05ebe
commit eb21d1f743
31 changed files with 2935 additions and 1227 deletions

View File

@ -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>: &nbsp;Cleans up XML base64-requests for Web Audio API.</li>
<h3>Example(s): &nbsp;<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;

View File

@ -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>

View File

@ -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

507
inc/jsmidi.js Normal file
View 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 = {});

View File

@ -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
View 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
};
};
})();

View File

@ -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;
};

File diff suppressed because it is too large Load Diff

View File

@ -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,

View File

@ -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;
};

View File

@ -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 = [ ".&nbsp;&nbsp;","..&nbsp;","..." ].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
View 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 = [ ".&nbsp;&nbsp;","..&nbsp;","..." ].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);

View File

@ -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.

View File

@ -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
}
}

View File

@ -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);
}
};
};

View File

@ -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
}
}

View File

@ -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">

View File

@ -1,6 +1,6 @@
{
"name": "midi",
"version": "0.0.1",
"version": "0.0.2",
"description": "HTML5 midi player",
"author": "Michael Deal",
"scripts": {