├── README
├── examples
├── addconnection.html
├── audiolib.html
├── base64.html
├── configobject.html
├── css
│ ├── clearfix.css
│ ├── midibridge.css
│ └── reset.css
├── flash.html
├── flash
│ ├── Main.as
│ └── main.fla
├── getdevices.html
├── img
│ └── balloon.png
├── java
│ └── midiapplet-0.5.4.jar
├── js
│ ├── addconnection.js
│ ├── audiolib.js
│ ├── base64.js
│ ├── configobject.js
│ ├── flash.js
│ ├── getdevices.js
│ ├── patchpanel.js
│ ├── playmidifile.js
│ ├── playnote.js
│ ├── regularkeyboard.js
│ ├── soundfont64.js
│ └── timedmidievents.js
├── lib
│ ├── MIDIProgramSelector.js
│ ├── MidiBridge.js
│ ├── Slider.js
│ ├── audiolib.js
│ ├── chopin_opus18.mid.js
│ ├── midibridge-0.5.4.min.js
│ ├── soundfont-ogg.js
│ └── swfobject.js
├── patchpanel.html
├── php
│ ├── midiToBase64.php
│ └── tmp.mid
├── playmidifile.html
├── playnote.html
├── regularkeyboard.html
├── soundfont.html
├── swf
│ ├── expressinstall.swf
│ └── main.swf
├── timedmidievents.html
└── timesmidievents.html
├── index.html
├── java
└── midiapplet-0.5.4.jar
├── js
└── helloworld.js
├── lib
├── MidiBridge.js
└── midibridge-0.5.4.min.js
└── tests
├── img
└── balloon.png
├── js
├── play-midifile.js
└── setup-connections.js
├── lib
└── MIDIProgramSelector.js
└── php
├── midiToBase64.php
└── tmp.mid
/README:
--------------------------------------------------------------------------------
1 | NOTE: you might be interested in the newest version as well, see https://github.com/abudaan/midibridge
2 |
3 |
4 | The midibridge is a Javascript API for interacting with the midi devices on your computer.
5 |
6 | It provides methods for detecting the midi devices and for connecting these devices with each other.
7 |
8 | The midibridge itself is considered a midi device as well, so it can be connected to the detected devices.
9 |
10 | The midibridge can generate and send midi events to midi output devices, and receive midi events from midi input devices.
11 |
12 | The midibridge can also filter and alter midi events.
13 |
14 | Recording and playing back midi events with a sequencer will be added in later versions.
15 |
16 | A midi output device is a physical output port on your computer, a virtual output port or a software synthesizer. It can also be a sequencer, a file or a function.
17 |
18 | A midi input device is a physical or virtual input port, a sequencer, a file or a function.
19 |
20 | A midi device can be both in- and output. The midibridge itself for instance is both in- and output because it can send and receive midi events.
21 |
22 | The actual interaction with the midi devices on your computer is done by a Java applet. The midibridge automatically adds the applet to your webpage.
23 |
24 | The midibridge has no visual parts, it is 'headless' code. You could say the midibridge enables you to write a 'front-end' on top of the applet.
25 |
26 | Midi Devices -> Java Applet -> Javascript Midibridge API -> a GUI in Javascript, Flash, SVG, C# (Silverlight)
27 |
28 | Because the midibridge is written in native Javascript, you can use it conflict-free with any Javascript framework.
29 |
30 |
31 | The only files you need to get started are:
32 |
33 | /lib/midibridge-0.5.4.min.js
34 | /java/midiapplet.jar
35 |
36 |
37 | /lib/MidiBridge.js is the non-minified version of /lib/midibridge-0.5.4.min.js
38 |
39 | The index.html file shows you how you embed the midibridge in your html page; you can use this basic example as a starting point of your own code.
40 |
41 | In the /examples folder you will find the code examples that i have used in the quick start guide and the documentation on my blog, see:
42 |
43 | http://www.abumarkub.net/abublog/?p=399
44 |
45 |
--------------------------------------------------------------------------------
/examples/addconnection.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
midibridge loaded, play some keys on your MIDI keyboard!
";
101 | },
102 | data : function(midiEvent){
103 | playNote(midiEvent);
104 | }
105 | });
106 |
107 |
108 | }, false);
109 |
110 |
111 |
--------------------------------------------------------------------------------
/examples/js/timedmidievents.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Note for IE8 users: if you include MidiBridge.js in your html, the method addEventListener will be added to the window object.
3 | * In fact this method is just a wrapper around the attachEvent method.
4 | */
5 | window.addEventListener('load', function() {
6 |
7 | var contentDiv = document.getElementById("content");
8 | contentDiv.style.lineHeight = "1.6em";
9 |
10 | midiBridge.init({
11 | ready:function(){
12 | sendRandomEvent(0,100);
13 | }
14 | });
15 |
16 | //this function acts as a basic sequencer
17 | function sendTimedMIDIEvent(midiMessage,delay,callback){
18 | function send(midiMessage){
19 | midiBridge.sendMidiEvent(midiMessage.status, midiMessage.channel, midiMessage.data1, midiMessage.data2);
20 | contentDiv.innerHTML += "sending MIDI message " + midiMessage.toString() + " ";
21 | callback();
22 | }
23 | if(delay > 0){
24 | contentDiv.innerHTML += "delaying playback for " + delay + " ms ";
25 | setTimeout(function(){
26 | send(midiMessage);
27 | },delay);
28 | }else{
29 | send(midiMessage);
30 | }
31 | }
32 |
33 | //create a random note sequence
34 | function sendRandomEvent(currentNote,maxNotes){
35 | var delay, noteNumber, velocity,message;
36 | if(currentNote < maxNotes){
37 | delay = Math.floor((Math.random()*2000));//delay random between 0 an 1000 milliseconds
38 | noteNumber = Math.floor((Math.random()*87)+21);//noteNumber random between 21 (A0) an 108 (C8)
39 | velocity = Math.floor((Math.random()*127)+10);//velocity random betweeen 10 and 127
40 | message = {
41 | data1:noteNumber,
42 | data2:velocity,
43 | channel:1,
44 | status:midiBridge.NOTE_ON
45 | };
46 | //console.log(message);
47 | sendTimedMIDIEvent(new midiBridge.MidiMessage(message),delay,function(){
48 | //console.log(currentNote,"/",maxNotes);
49 | sendRandomEvent(++currentNote,maxNotes);
50 | });
51 | }
52 | }
53 | });
54 |
55 |
56 |
--------------------------------------------------------------------------------
/examples/lib/MIDIProgramSelector.js:
--------------------------------------------------------------------------------
1 | var abumarkub = abumarkub || {};
2 | abumarkub.ui = abumarkub.ui || {};
3 |
4 | abumarkub.ui.createMIDIProgramSelector = function(parentDiv,callback){
5 |
6 | var instruments = {
7 | 1 : "Acoustic Grand Piano",
8 | 2 : "Bright Acoustic Piano",
9 | 3 : "Electric Grand Piano",
10 | 4 : "Honky-tonk Piano",
11 | 5 : "Electric Piano 1",
12 | 6 : "Electric Piano 2",
13 | 7 : "Harpsichord",
14 | 8 : "Clavinet",
15 | 9 : "Celesta",
16 | 10 : "Glockenspiel",
17 | 11 : "Music Box",
18 | 12 : "Vibraphone",
19 | 13 : "Marimba",
20 | 14 : "Xylophone",
21 | 15 : "Tubular Bells",
22 | 16 : "Dulcimer",
23 | 17 : "Drawbar Organ",
24 | 18 : "Percussive Organ",
25 | 19 : "Rock Organ",
26 | 20 : "Church Organ",
27 | 21 : "Reed Organ",
28 | 22 : "Accordion",
29 | 23 : "Harmonica",
30 | 24 : "Tango Accordion",
31 | 25 : "Acoustic Guitar (nylon)",
32 | 26 : "Acoustic Guitar (steel)",
33 | 27 : "Electric Guitar (jazz)",
34 | 28 : "Electric Guitar (clean)",
35 | 29 : "Electric Guitar (muted)",
36 | 30 : "Overdriven Guitar",
37 | 31 : "Distortion Guitar",
38 | 32 : "Guitar Harmonics",
39 | 33 : "Acoustic Bass",
40 | 34 : "Electric Bass (finger)",
41 | 35 : "Electric Bass (pick)",
42 | 36 : "Fretless Bass",
43 | 37 : "Slap Bass 1",
44 | 38 : "Slap Bass 2",
45 | 39 : "Synth Bass 1",
46 | 40 : "Synth Bass 2",
47 | 41 : "Violin",
48 | 42 : "Viola",
49 | 43 : "Cello",
50 | 44 : "Contrabass",
51 | 45 : "Tremolo Strings",
52 | 46 : "Pizzicato Strings",
53 | 47 : "Orchestral Harp",
54 | 48 : "Timpani",
55 | 49 : "String Ensemble 1",
56 | 50 : "String Ensemble 2",
57 | 51 : "Synth Strings 1",
58 | 52 : "Synth Strings 2",
59 | 53 : "Choir Aahs",
60 | 54 : "Voice Oohs",
61 | 55 : "Synth Choir",
62 | 56 : "Orchestra Hit",
63 | 57 : "Trumpet",
64 | 58 : "Trombone",
65 | 59 : "Tuba",
66 | 60 : "Muted Trumpet",
67 | 61 : "French Horn",
68 | 62 : "Brass Section",
69 | 63 : "Synth Brass 1",
70 | 64 : "Synth Brass 2",
71 | 65 : "Soprano Sax",
72 | 66 : "Alto Sax",
73 | 67 : "Tenor Sax",
74 | 68 : "Baritone Sax",
75 | 69 : "Oboe",
76 | 70 : "English Horn",
77 | 71 : "Bassoon",
78 | 72 : "Clarinet",
79 | 73 : "Piccolo",
80 | 74 : "Flute",
81 | 75 : "Recorder",
82 | 76 : "Pan Flute",
83 | 77 : "Blown Bottle",
84 | 78 : "Shakuhachi",
85 | 79 : "Whistle",
86 | 80 : "Ocarina",
87 | 81 : "Lead 1 (square)",
88 | 82 : "Lead 2 (sawtooth)",
89 | 83 : "Lead 3 (calliope)",
90 | 84 : "Lead 4 (chiff)",
91 | 85 : "Lead 5 (charang)",
92 | 86 : "Lead 6 (voice)",
93 | 87 : "Lead 7 (fifths)",
94 | 88 : "Lead 8 (bass + lead)",
95 | 89 : "Pad 1 (new age)",
96 | 90 : "Pad 2 (warm)",
97 | 91 : "Pad 3 (polysynth)",
98 | 92 : "Pad 4 (choir)",
99 | 93 : "Pad 5 (bowed)",
100 | 94 : "Pad 6 (metallic)",
101 | 95 : "Pad 7 (halo)",
102 | 96 : "Pad 8 (sweep)",
103 | 97 : "FX 1 (rain)",
104 | 98 : "FX 2 (soundtrack)",
105 | 99 : "FX 3 (crystal)",
106 | 100 : "FX 4 (atmosphere)",
107 | 101 : "FX 5 (brightness)",
108 | 102 : "FX 6 (goblins)",
109 | 103 : "FX 7 (echoes)",
110 | 104 : "FX 8 (sci-fi)",
111 | 105 : "Sitar",
112 | 106 : "Banjo",
113 | 107 : "Shamisen",
114 | 108 : "Koto",
115 | 109 : "Kalimba",
116 | 110 : "Bagpipe",
117 | 111 : "Fiddle",
118 | 112 : "Shanai",
119 | 113 : "Tinkle Bell",
120 | 114 : "Agogo",
121 | 115 : "Steel Drums",
122 | 116 : "Woodblock",
123 | 117 : "Taiko Drum",
124 | 118 : "Melodic Tom",
125 | 119 : "Synth Drum",
126 | 120 : "Reverse Cymbal",
127 | 121 : "Guitar Fret Noise",
128 | 122 : "Breath Noise",
129 | 123 : "Seashore",
130 | 124 : "Bird Tweet",
131 | 125 : "Telephone Ring",
132 | 126 : "Helicopter",
133 | 127 : "Applause",
134 | 128 : "Gunshot"
135 | };
136 |
137 | var createOption = function(id, label) {
138 | var option = document.createElement("option");
139 | option.setAttribute("id", id);
140 | option.innerHTML = label;
141 | return option;
142 | }
143 |
144 | var progChange = document.createElement("select");
145 | for(var id in instruments){
146 | progChange.appendChild(createOption(id - 1, instruments[id]));
147 | }
148 | progChange.addEventListener("change", function(e) {
149 | var programId = progChange.options[progChange.selectedIndex].id;
150 | callback(programId);
151 | //midiBridge.sendMidiEvent(midiBridge.PROGRAM_CHANGE, 0, progChange.options[progChange.selectedIndex].id, 0);
152 | },false);
153 |
154 | parentDiv.appendChild(progChange);
155 |
156 | return {
157 | selectOption : function(index){
158 | progChange.selectedIndex = index;
159 | }
160 | }
161 | }
--------------------------------------------------------------------------------
/examples/lib/MidiBridge.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This version is supported by all browsers that support native JSON parsing:
3 | * - Firefox 3.5+
4 | * - Chrome 4.0+
5 | * - Safari 4.0+
6 | * - Opera 10.5+
7 | * - Internet Explorer 8.0+
8 | *
9 | * If you want this version to work with other browsers, you can use the JSON parsing methods of your favorite Javascript
10 | * framework (e.g. jQuery, Dojo, YUI, Mootools, etc.)
11 | *
12 | * Note for IE8 users: if you include MidiBridge.js (or preferably the minified version of it: midibridge-0.5.min.js) in your html,
13 | * the method addEventListener will be added to the window object. In fact this method is just a wrapper around the attachEvent method,
14 | * see code at the bottom of this file.
15 | */
16 |
17 | (function() {
18 |
19 | try {
20 | console.log("");
21 | } catch (e) {
22 | console = {
23 | 'log': function(args) {}
24 | };
25 | }
26 |
27 | var midiBridge = {
28 | NOTE_OFF : 0x80, //128
29 | NOTE_ON : 0x90, //144
30 | POLY_PRESSURE : 0xA0, //160
31 | CONTROL_CHANGE : 0xB0, //176
32 | PROGRAM_CHANGE : 0xC0, //192
33 | CHANNEL_PRESSURE : 0xD0, //208
34 | PITCH_BEND : 0xE0, //224
35 | SYSTEM_EXCLUSIVE : 0xF0, //240
36 | MIDI_TIMECODE : 241,
37 | SONG_POSITION : 242,
38 | SONG_SELECT : 243,
39 | TUNE_REQUEST : 246,
40 | EOX : 247,
41 | TIMING_CLOCK : 248,
42 | START : 250,
43 | CONTINUE : 251,
44 | STOP : 252,
45 | ACTIVE_SENSING : 254,
46 | SYSTEM_RESET : 255,
47 | NOTE_NAMES_SHARP : "sharp",
48 | NOTE_NAMES_FLAT : "flat",
49 | NOTE_NAMES_SOUNDFONT : "soundfont",
50 | NOTE_NAMES_ENHARMONIC_SHARP : "enh-sharp",
51 | NOTE_NAMES_ENHARMONIC_FLAT : "enh-flat"
52 |
53 | };
54 | //human readable representation of status byte midi data
55 | var status = [];
56 | status[0x80] = "NOTE OFF";
57 | status[0x90] = "NOTE ON";
58 | status[0xA0] = "POLY PRESSURE";//POLYPHONIC AFTERTOUCH
59 | status[0xB0] = "CONTROL CHANGE";
60 | status[0xC0] = "PROGRAM CHANGE";
61 | status[0xD0] = "CHANNEL PRESSURE";//AFTERTOUCH
62 | status[0xE0] = "PITCH BEND";
63 | status[0xF0] = "SYSTEM EXCLUSIVE";
64 | status[241] = "MIDI TIMECODE";
65 | status[242] = "SONG POSITION";
66 | status[243] = "SONG SELECT";
67 | status[244] = "RESERVED";
68 | status[245] = "RESERVED";
69 | status[246] = "TUNE REQUEST";
70 | status[247] = "EOX";
71 | status[248] = "TIMING CLOCK";
72 | status[249] = "RESERVED";
73 | status[250] = "START";
74 | status[251] = "CONTINUE";
75 | status[252] = "STOP";
76 | status[254] = "ACTIVE SENSING";
77 | status[255] = "SYSTEM RESET";
78 | //notenames in different modi
79 | var noteNames = {
80 | "sharp" : ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"],
81 | "flat" : ["C", "D♭", "D", "E♭", "E", "F", "G♭", "G", "A♭", "A", "B♭", "B"],
82 | "soundfont" : ["C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B"],
83 | "enh-sharp" : ["B#", "C#", "C##", "D#", "D##", "E#", "F#", "F##", "G#", "G##", "A#", "A##"],
84 | "enh-flat" : ["D♭♭", "D♭", "E♭♭", "E♭", "F♭", "G♭♭", "G♭", "A♭♭", "A♭", "B♭♭", "B♭", "C♭"]
85 | };
86 | //variable that holds a reference to the JSON parser method of your liking, defaults to native JSON parsing
87 | var parseJSON = JSON.parse;
88 | //method that gets called when midi note events arrive from the applet
89 | var ondata = null;
90 | var onerror = null;
91 | var onready = null;
92 | //the applet object
93 | var applet = null;
94 | var connectAllInputs = false;
95 | var connectFirstInput = false;
96 | var connectFirstOutput = false;
97 | var connectAllInputsToFirstOutput = true;
98 | var javaDir = "java";
99 | var devices = {};
100 | var debug = false;
101 | //these are the commands that the midibridge passes on to Javascript
102 | var midiCommands = {};
103 | var allCommands = [];
104 |
105 | midiBridge.version = "0.5.4";
106 | midiBridge.ready = false;
107 | midiBridge.noteNameModus = midiBridge.NOTE_NAMES_SHARP;
108 | var midiBridgeJar = "midiapplet-" + midiBridge.version + ".jar";
109 |
110 | /**
111 | * static method called to initialize the MidiBridge
112 | * possible arguments:
113 | * 1) callback [function] callback when midi data has arrived
114 | * 2) config object
115 | * - ready : [function] callback when midibridge is ready/initialized
116 | * - error : [function] callback in case of an error
117 | * - data : [function] callback when midi data has arrived
118 | * - connectAllInputs : [true,false] all found midi input devices get connected automatically
119 | * - connectFirstInput : [true,false] the first found midi input device gets connected automatically
120 | * - connectFirstOutput : [true,false] the first found midi output device gets connected automatically
121 | * - connectAllInputsToFirstOutput : [true,false] all found midi input devices will be automatically connected to the first found midi output device
122 | * - javaDir : [string] the folder where you store the midiapplet.jar on your webserver, defaults to "java"
123 | */
124 | midiBridge.init = function (arg) {
125 |
126 | for(var statusCode in status){
127 | var command = parseInt(statusCode);
128 | switch(command){
129 | case midiBridge.NOTE_OFF:
130 | case midiBridge.NOTE_ON:
131 | case midiBridge.POLY_PRESSURE: //POLYPHONIC AFTERTOUCH
132 | case midiBridge.CONTROL_CHANGE:
133 | case midiBridge.PROGRAM_CHANGE:
134 | case midiBridge.CHANNEL_PRESSURE: //AFTERTOUCH
135 | case midiBridge.PITCH_BEND:
136 | allCommands.push(command);
137 | for(var channel = 0; channel < 16; channel++){
138 | status[command + channel] = status[statusCode];
139 | }
140 | break;
141 | default:
142 | allCommands.push(command);
143 | break;
144 | }
145 | }
146 |
147 | //var args = Array.prototype.slice.call(arguments);
148 | if (typeof arg === "function") {
149 | ondata = arg;
150 | } else if (typeof arg === "object") {
151 | var config = arg;
152 | debug = config.debug;
153 | var commands = config.midiCommands || allCommands;
154 | midiCommands = {};
155 | for(var i = 0; i < commands.length; i++){
156 | command = commands[i];
157 | switch(command){
158 | case midiBridge.NOTE_OFF:
159 | case midiBridge.NOTE_ON:
160 | case midiBridge.POLY_PRESSURE: //POLYPHONIC AFTERTOUCH
161 | case midiBridge.CONTROL_CHANGE:
162 | case midiBridge.PROGRAM_CHANGE:
163 | case midiBridge.CHANNEL_PRESSURE: //AFTERTOUCH
164 | case midiBridge.PITCH_BEND:
165 | for(channel = 0; channel < 16; channel++){
166 | midiCommands[command + channel] = 1;
167 | }
168 | break;
169 | default:
170 | midiCommands[command] = 1;
171 | }
172 | }
173 | connectAllInputs = config.connectAllInputs === undefined ? connectAllInputs : config.connectAllInputs;
174 | connectFirstInput = config.connectFirstInput === undefined ? connectFirstInput : config.connectFirstInput;
175 | connectFirstOutput = config.connectFirstOutput === undefined ? connectFirstOutput : config.connectFirstOutput;
176 | connectAllInputsToFirstOutput = config.connectAllInputsToFirstOutput === undefined ? connectAllInputsToFirstOutput : config.connectAllInputsToFirstOutput;
177 | ondata = config.data;
178 | onready = config.ready;
179 | onerror = config.error;
180 |
181 | if(debug){
182 | console.log(connectFirstInput,connectFirstOutput,connectAllInputs,connectAllInputsToFirstOutput,parseJSON,midiCommands);
183 | }
184 | }
185 |
186 | /**
187 | * Very simple java plugin detection
188 | */
189 | if (!navigator.javaEnabled()) {
190 | if (onerror) {
191 | onerror("no java plugin found; install or enable the java plugin");
192 | } else {
193 | console.log("no java plugin found; install or enable the java plugin");
194 | }
195 | return;
196 | }
197 |
198 | /**
199 | * If you are using the JSON parse method of your favorite Javascript framework replace the following lines by only:
200 | *
201 | * loadJava();
202 | */
203 | try {
204 | if(parseJSON === undefined){
205 | console.log("supported browsers: Firefox 3.5+, Chrome 4.0+, Safari 4.0+, Opera 10.5+, Internet Explorer 8.0+");
206 | return;
207 | }
208 | loadJava();
209 | } catch(e) {
210 | if(onerror) {
211 | onerror("supported browsers: Firefox 3.5+, Chrome 4.0+, Safari 4.0+, Opera 10.5+, Internet Explorer 8.0+");
212 | } else {
213 | console.log("supported browsers: Firefox 3.5+, Chrome 4.0+, Safari 4.0+, Opera 10.5+, Internet Explorer 8.0+");
214 | }
215 | }
216 | };
217 |
218 | /**
219 | * static method called by the applet
220 | */
221 | midiBridge.msgFromJava = function(jsonString) {
222 | var data = parseJSON(jsonString);
223 | var msgId = data.msgId;
224 | //console.log(jsonString);
225 | //console.log(msgId);
226 | switch(msgId) {
227 | case "upgrade-java":
228 | if(onerror) {
229 | onerror("please upgrade your java plugin!");
230 | } else {
231 | console.log("please upgrade your java plugin!");
232 | }
233 | break;
234 | case "midi-started":
235 | getApplet();
236 | if(applet) {
237 | //console.log(applet.disconnectAll());
238 | devices = data.devices;
239 | midiBridge.ready = true;
240 | if(connectFirstOutput || connectAllInputsToFirstOutput){
241 | midiBridge.connectFirstOutput();
242 | }
243 | if(connectAllInputs || connectAllInputsToFirstOutput){
244 | midiBridge.connectAllInputs();
245 | }else if(connectFirstInput){
246 | midiBridge.connectFirstInput();
247 | }
248 | if(onready) {
249 | onready("midibridge started");
250 | }
251 | }
252 | //console.log("applet:",applet);
253 | break;
254 | case "midi-data":
255 | if(ondata) {
256 | //if(midiBridge.getStatus(data.status) === undefined){
257 | if(midiCommands[data.status] === undefined){
258 | if(debug){
259 | console.log("MIDI message intercepted", data.status, data.data1, data.data2, data.channel);
260 | }
261 | return;
262 | }
263 | ondata(new MidiMessage(data));
264 | }
265 | break;
266 | case "error":
267 | if(onerror) {
268 | onerror(data.code);
269 | }
270 | break;
271 | }
272 | };
273 |
274 | /**
275 | * Send a midi event from javascript to java
276 | * @param status : the midi status byte, e.g. NOTE ON, NOTE OFF, PITCH BEND and so on
277 | * @param channel : the midi channel that this event will be sent to 0 - 15
278 | * @param data1 : the midi note number
279 | * @param data2 : the second data byte, when the status byte is NOTE ON or NOTE OFF, data2 is the velocity
280 | */
281 | midiBridge.sendMidiEvent = function(status, channel, data1, data2) {
282 | if(checkIfReady()) {
283 | return parseJSON(applet.processMidiEvent(status, channel, data1, data2));
284 | }
285 | return false;
286 | };
287 |
288 | /**
289 | * Get the list of all currently connected midi devices
290 | */
291 | midiBridge.getDevices = function() {
292 | return devices;
293 | };
294 |
295 | /**
296 | * Refresh the list of all currently connected midi devices
297 | */
298 | midiBridge.refreshDevices = function() {
299 | if(checkIfReady()) {
300 | return parseJSON(applet.getDevices());
301 | }
302 | return false;
303 | };
304 |
305 | /**
306 | * Connect all found midi inputs to the midibridge right after the midibridge has been initialized
307 | */
308 | midiBridge.connectAllInputs = function() {
309 | if(checkIfReady()) {
310 | return parseJSON(applet.connectAllInputs());
311 | }
312 | return false;
313 | };
314 |
315 | /**
316 | * Connect the first found midi input to the midibridge right after the midibridge has been initialized
317 | */
318 | midiBridge.connectFirstInput = function() {
319 | if(checkIfReady()) {
320 | return parseJSON(applet.connectFirstInput());
321 | }
322 | return false;
323 | };
324 |
325 | /**
326 | * Connect the first found midi output to the midibridge right after the midibridge has been initialized
327 | */
328 | midiBridge.connectFirstOutput = function() {
329 | if(checkIfReady()) {
330 | return parseJSON(applet.connectFirstOutput());
331 | }
332 | return false;
333 | };
334 |
335 | /**
336 | * Connect the first found midi output to all connected midi inputs right after the midibridge has been initialized
337 | */
338 | midiBridge.connectAllInputsToFirstOutput = function() {
339 | if(checkIfReady()) {
340 | return parseJSON(applet.connectAllInputsToFirstOutput());
341 | }
342 | return false;
343 | };
344 |
345 | /**
346 | * Connect midi a midi input to the bridge, and/or a midi input to a midi output
347 | * @param midiInId : [int] id of the midi input that will be connected to the bridge, use the ids as retrieved by getDevices()
348 | * @param midiOutId : [int] optional, the id of the midi output that will be connected to the chosen midi input
349 | * @param filter : [array] an array containing status codes that will *not* be sent from the chosen midi input to the chosen midi output
350 | * e.g. if you supply the array [midiBridge.PITCH_BEND, midiBridge.POLY_PRESSURE], pitch bend and poly pressure midi messages will not be forwarded to the output
351 | */
352 | midiBridge.addConnection = function(midiInId, midiOutId, filter) {
353 | if(checkIfReady()) {
354 | midiOutId = midiOutId === undefined ? -1 : midiOutId;
355 | filter = filter === undefined ? [] : filter;
356 | return parseJSON(applet.addConnection(midiInId, midiOutId, filter));
357 | }
358 | return false;
359 | };
360 |
361 | /**
362 | * Remove a midi connection between between an input and the midibridge, and/or the given in- and output
363 | * @param midiIdIn : [int] the midi input
364 | * @param midiIdOut : [int] optional, the midi output
365 | */
366 | midiBridge.removeConnection = function(midiInId, midiOutId) {
367 | if(checkIfReady()) {
368 | return parseJSON(applet.removeConnection(midiInId, midiOutId));
369 | }
370 | return false;
371 | };
372 |
373 | /**
374 | * All previously setup midi connections will be disconnected
375 | */
376 | midiBridge.disconnectAll = function() {
377 | if(checkIfReady()) {
378 | return parseJSON(applet.disconnectAll());
379 | }
380 | return false;
381 | };
382 |
383 | midiBridge.loadBase64String = function(data){
384 | return parseJSON(applet.loadBase64String(data.replace(/data:audio\/mid[i]?;base64,/,"")));
385 | };
386 |
387 | midiBridge.playBase64String = function(data){
388 | return parseJSON(applet.playBase64String(data));
389 | };
390 |
391 | midiBridge.loadMidiFile = function(url){
392 | return parseJSON(applet.loadMidiFile(url));
393 | };
394 |
395 | midiBridge.playMidiFile = function(url){
396 | return parseJSON(applet.playMidiFile(url));
397 | };
398 |
399 | midiBridge.startSequencer = function(){
400 | applet.startSequencer();
401 | };
402 |
403 | midiBridge.pauseSequencer = function(){
404 | applet.pauseSequencer();
405 | };
406 |
407 | midiBridge.stopSequencer = function(){
408 | applet.stopSequencer();
409 | };
410 |
411 | midiBridge.closeSequencer = function(){
412 | applet.closeSequencer();
413 | };
414 |
415 | midiBridge.getSequencerPosition = function(){
416 | return applet.getSequencerPosition();
417 | };
418 |
419 | midiBridge.setSequencerPosition = function(pos){
420 | applet.setSequencerPosition(pos);
421 | };
422 |
423 | /**
424 | * Check if a midiBridge function is called before initialization
425 | */
426 | function checkIfReady(){
427 | if(!midiBridge.ready) {
428 | if(onerror) {
429 | onerror("midibridge not ready!");
430 | }
431 | return "midibridge not ready!";
432 | }
433 | return true;
434 | }
435 |
436 | /**
437 | * A div with the applet object is added to the body of your html document
438 | */
439 | function loadJava(){
440 | //console.log("loadJava");
441 | var javaDiv = document.createElement("div");
442 | javaDiv.setAttribute("id", "midibridge-java");
443 | var html = "";
444 |
445 | var ua = navigator.userAgent.toLowerCase();
446 | if(ua.indexOf("chrome") === -1){
447 | html += '';
455 | }else{
456 | html += '';
459 | }
460 |
461 | javaDiv.innerHTML = html;
462 | document.body.appendChild(javaDiv);
463 | }
464 |
465 | /**
466 | * class MidiMessage is used to wrap the midi note data that arrives from the applet
467 | */
468 | var MidiMessage = (function()//constructor
469 | {
470 | var _constructor = function(data) {
471 | this.command = data.command;
472 | this.channel = data.channel;
473 | this.data1 = data.data1;
474 | this.data2 = data.data2;
475 | this.status = data.status;
476 | this.noteName = midiBridge.getNoteName(this.data1, midiBridge.noteNameModus);
477 | this.statusCode = midiBridge.getStatus(this.status);
478 | this.microsecond = data.microsecond;
479 | this.time = midiBridge.getNiceTime(this.microsecond);
480 |
481 | if(this.command == midiBridge.NOTE_ON && this.data2 == 0){
482 | this.command = midiBridge.NOTE_OFF;
483 | this.status = this.command + this.channel;
484 | }
485 | };
486 |
487 | _constructor.prototype = {
488 | toString : function() {
489 | var s = "";
490 | s += this.noteName + " " + this.statusCode + " " + this.data1 + " " + this.data2 + " " + this.channel + " " + this.status;
491 | s += this.microsecond ? this.microsecond + " " + this.time : "";
492 | //console.log(s);
493 | return s;
494 | },
495 | toJSONString : function() {
496 | var s;
497 | if(this.microsecond){
498 | s= "{'notename':" + this.noteName + ", 'status':" + this.status + ", 'data1':" + this.data1 + ", 'data2':" + this.data2 + ", 'microsecond':" + this.microsecond + ", 'time':" + this.time + "}";
499 | }else{
500 | s= "{'notename':" + this.noteName + ", 'status':" + this.status + ", 'data1':" + this.data1 + ", 'data2':" + this.data2 + "}";
501 | }
502 | //console.log(s);
503 | return s;
504 | }
505 | };
506 |
507 | return _constructor;
508 | })();
509 |
510 | midiBridge.MidiMessage = MidiMessage;
511 |
512 | midiBridge.getNoteName = function(noteNumber, mode) {
513 |
514 | var octave = Math.floor(((noteNumber) / 12) - 1);
515 | var noteName = noteNames[mode][noteNumber % 12];
516 | return noteName + "" + octave;
517 | };
518 |
519 |
520 | midiBridge.getNoteNumber = function(noteName, octave) {
521 | var index = -1;
522 | noteName = noteName.toUpperCase();
523 | for(var key in noteNames) {
524 | var modus = noteNames[key];
525 | for(var i = 0, max = modus.length; i < max; i++) {
526 | if(modus[i] === noteName) {
527 | index = i;
528 | break;
529 | }
530 | }
531 | }
532 | if(index === -1) {
533 | return "invalid note name";
534 | }
535 | var noteNumber = (12 + index) + (octave * 12);
536 | return noteNumber;
537 | };
538 |
539 |
540 | midiBridge.getStatus = function($statusCode) {
541 | return status[$statusCode];
542 | };
543 |
544 | midiBridge.getNiceTime = function(microseconds)
545 | {
546 | //console.log(microseconds);
547 | var r = "",
548 | t = (microseconds / 1000 / 1000) >> 0,
549 | h = (t / (60 * 60)) >> 0,
550 | m = ((t % (60 * 60)) / 60) >> 0,
551 | s = t % (60),
552 | ms = (((microseconds /1000) - (h * 3600000) - (m * 60000) - (s * 1000)) + 0.5) >> 0;
553 |
554 | //console.log(t,h,m,s,ms);
555 |
556 | r += h > 0 ? h + ":" : "";
557 | r += h > 0 ? m < 10 ? "0" + m : m : m;
558 | r += ":";
559 | r += s < 10 ? "0" + s : s;
560 | r += ":";
561 | r += ms === 0 ? "000" : ms < 10 ? "00" + ms : ms < 100 ? "0" + ms : ms;
562 |
563 | return r;
564 | };
565 |
566 |
567 | function getApplet() {
568 | try {
569 | applet = midiBridge.getObject("midibridge-applet");
570 | } catch(e) {
571 | //console.log(e)
572 | //Firefox needs more time to initialize the Applet
573 | setTimeout(getApplet, 25);
574 | return;
575 | }
576 | }
577 |
578 | midiBridge.getObject = function(objectName) {
579 | var ua = navigator.userAgent.toLowerCase();
580 | //console.log(ua);
581 | if(ua.indexOf("msie") !== -1 || ua.indexOf("webkit") !== -1) {
582 | return window[objectName];
583 | } else {
584 | return document[objectName];
585 | }
586 | };
587 |
588 | //add addEventListener to IE8
589 | if(!window.addEventListener) {
590 | window.addEventListener = function($id, $callback, $bubble) {
591 | window.attachEvent('onload', $callback);
592 | };
593 | }
594 |
595 | window.midiBridge = midiBridge;
596 |
597 | })(window);
598 |
--------------------------------------------------------------------------------
/examples/lib/Slider.js:
--------------------------------------------------------------------------------
1 | var abumarkub = abumarkub || {};
2 | abumarkub.ui = abumarkub.ui || {};
3 |
4 | abumarkub.ui.Slider = function(id,w,h,min,max,initValue){
5 |
6 | var thumb;
7 | var thumbLabel;
8 | var track;
9 | var maxLength;
10 | var maxValue;
11 | var minValue;
12 | var percentage;
13 | var value;
14 | var offsetLeft;
15 |
16 | var listeners = {};
17 |
18 | track = document.getElementById(id);
19 | track.style.width = w + "px";
20 | track.style.height = h + "px";
21 | thumb = document.querySelector("#" + id + " > .thumb");
22 | thumbLabel = document.querySelector(".thumb > .label");
23 | thumb.style.top = "-26px";
24 | thumb.style.cursor = "pointer";
25 | track.style.cursor = "pointer";
26 | maxLength = w;
27 |
28 | maxValue = max;
29 | minValue = min;
30 |
31 | offsetLeft = track.offsetLeft;
32 | //console.log(thumb,track.offsetLeft)
33 |
34 | window.addEventListener("resize",function(){
35 | offsetLeft = track.offsetLeft;
36 | },false);
37 |
38 |
39 | initThumb(initValue ? initValue : minValue);
40 |
41 | track.addEventListener("mousedown",function(event){
42 | startDrag(event);
43 | },false);
44 |
45 |
46 |
47 | function startDrag(event){
48 |
49 | dispatchEvent("startDrag",value);
50 |
51 | track.addEventListener("mousemove",setThumb,false);
52 | track.addEventListener("mouseleave",stopDrag,false);
53 | track.addEventListener("mouseup",stopDrag,false);
54 |
55 | thumb.addEventListener("mouseup",stopDrag,false);
56 | thumb.addEventListener("mousedown",function(event){
57 | event.preventDefault();
58 | },false);
59 |
60 | setThumb(event);
61 | }
62 |
63 | function initThumb(value){
64 |
65 | var p = (value-minValue)/(maxValue - minValue);
66 | //log(p);
67 | thumb.style.left = (p * maxLength) + "px";
68 | thumbLabel.innerHTML = value;
69 | }
70 |
71 | function setThumb(event){
72 |
73 | var x = event.clientX - offsetLeft;
74 | //log(x);
75 | if(x >= 0 && x <= maxLength){
76 | thumb.style.left = x + "px";
77 | percentage = x/maxLength;
78 | //log(percentage);
79 | value = Math.round((percentage * (maxValue - minValue)) + minValue);
80 | //thumbLabel.innerHTML = value;
81 | thumbLabel.innerHTML = ((percentage * 100) >> 0) + "%";
82 | dispatchEvent("changed",value);
83 | }
84 | }
85 |
86 | function stopDrag(event){
87 |
88 | dispatchEvent("stopDrag",value);
89 |
90 | track.removeEventListener("mousemove",setThumb,false);
91 | track.removeEventListener("mouseleave",stopDrag);
92 | track.removeEventListener("mouseup",stopDrag);
93 | thumb.removeEventListener("mouseup",stopDrag);
94 | }
95 |
96 | function dispatchEvent(eventType,value){
97 | var currListeners = listeners[eventType];
98 | var i = 0;
99 | while(currListeners[i]){
100 | currListeners[i](value);
101 | i++;
102 | }
103 | }
104 |
105 | return {
106 | addEventListener: function(eventType,callback){
107 | if(!listeners[eventType]){
108 | listeners[eventType] = [];
109 | }
110 | listeners[eventType].push(callback);
111 | },
112 | setPercentage: function(percentage,dispatch){
113 | value = Math.round((percentage * (maxValue - minValue)) + minValue);
114 | //thumbLabel.innerHTML = value;
115 | thumbLabel.innerHTML = ((percentage * 100) >> 0) + "%";
116 | thumb.style.left = percentage * maxLength + "px";
117 | if(dispatch){
118 | dispatchEvent("changed",value);
119 | }
120 | },
121 | setRange: function(min,max){
122 | minValue = min;
123 | maxValue = max;
124 | }
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/examples/lib/audiolib.js:
--------------------------------------------------------------------------------
1 | [ There is also a native MIDI browser plugin available for OSX and Windows ! ]
2 |
3 |
4 | 1) Introduction
5 | 2) Quick start guide
6 | 3) Documentation
7 | 4) About midi out
8 | 5) Earlier versions
9 | 6) What about Flash?
10 | 7) Known issues
11 | 8) Forthcoming features
12 |
13 |
14 |
15 |
16 | Introduction
17 |
18 | The midibridge is a Javascript API for interacting with the midi devices on your computer.
19 |
20 | It provides methods for detecting the midi devices and for connecting these devices with each other.
21 |
22 | The midibridge itself is considered a midi device as well, so it can be connected to the detected devices.
23 |
24 | The midibridge can generate and send midi events to midi output devices, and receive midi events from midi input devices.
25 |
26 | The midibridge can also filter and alter midi events.
27 |
28 | Recording and playing back midi events with a sequencer will be added in later versions.
29 |
30 | A midi output device is a physical output port on your computer, a virtual output port or a software synthesizer. It can also be a sequencer, a file or a function.
31 |
32 | A midi input device is a physical or virtual input port, a sequencer, a file or a function.
33 |
34 | A midi device can be both in- and output. The midibridge itself for instance is both in- and output because it can send and receive midi events.
35 |
36 | The actual interaction with the midi devices on your computer is done by a Java applet. The midibridge automatically adds the applet to your webpage.
37 |
38 | The midibridge has no visual parts, it is 'headless' code. You could say the midibridge enables you to write a 'front-end' on top of the applet.
39 |
40 | Midi Devices -> Java Applet -> Javascript Midibridge API -> a GUI in Javascript, Flash, SVG, C# (Silverlight)
41 |
42 | Because the midibridge is written in native Javascript, you can use it conflict-free with any Javascript framework.
43 |
44 |
45 |
46 | Quick start guide
47 |
48 | 1) Download the zip file from Google Code.
49 |
50 | 2) In this zip file you'll find 2 files: a jar file and a Javascript file. On your webserver, put the jar file in a folder called "java" and the javascript file in the folder where you usually store your javascript libraries. Personally, i put the Javascript libraries that i use in a project in a "lib" folder, and the project specific javascript files in a folder "js".
51 |
52 | 3) Include the file midibridge-0.5.min.js to your webpage. If you use a Javascript framework (jQuery, YUI, Dojo, etc.), include it right after you've included the framework.
53 |
54 | 4) Start the midibridge. The page has to be fully loaded before you start the midibridge, so call the init function of the midibridge inside your onload handler:
55 | [code lang="js"]
56 |
57 | window.addEventListener('load', function() {
58 | midiBridge.init(function(midiEvent) {
59 | console.log(midiEvent);
60 | ;});
61 | }, false);
62 |
63 | [/code]
64 |
65 | 5) Done! If you have a midikeyboard connected to you computer, play some notes and you'll hear a piano sound. Also you will see that the midi events are printed to your console.
66 |
67 |
68 |
69 | Documentation
70 |
71 | By adding the midibridge to your html page, a global variable midiBridge is created. Below follows a list of the methods that you can call on the midiBridge object. There is also a bunch of handy static members that you use in your code:
72 |
73 | - init()
74 | - sendMidiEvent(status, channel, data1, data2)
75 | - getDevices()
76 | - refreshDevices()
77 | - connectAllInputs()
78 | - connectFirstInput()
79 | - connectFirstOutput()
80 | - connectAllInputsToFirstOutput()
81 | - addConnection(input,output,filter)
82 | - removeConnection(input,output)
83 | - disconnectAll()
84 | - getNoteName(midiNoteNumber,mode)
85 | - getNoteNumber(noteName,octave)
86 | - getStatus(statusCode)
87 |
88 | - loadBase64String(base64String)
89 | - playBase64String(base64String)
90 | - startSequencer()
91 | - pauseSequencer()
92 | - stopSequencer()
93 | - closeSequencer()
94 | - getSequencerPosition()
95 | - setSequencerPosition(microseconds)
96 | - getNiceTime(microseconds)
97 |
98 | - getObject(id)
99 | - MidiMessage(jsonString)
100 | - List of static members
101 |
102 |
103 |
104 | init()
105 |
106 | The init method of the midibridge adds an extra div to the body of your html page, and in this div the Java applet gets loaded. After the applet has loaded, it detects all currently connected midi in- and outputs. Then it automatically connects all midi inputs to the midibridge, and the midi output that is found first to all dectected midi inputs.
107 |
108 | This way you can start playing your midi keyboard directly after the midibridge is initialized. When you play, the callback method that you have passed as an argument to the init function will be called. In the example above, the incoming midi events are only printed to the console, but i'm sure you can come up with something more funky.
109 |
110 | You can also call the init method with a configuration object as argument, this object may contain one or all of the following keys:
111 |
ready : [function] callback function that gets called as soon as the midibridge is ready/initialized
112 |
113 |
error : [function] callback function that gets called in case of an error, for instance the user does not have a Java plugin or an outdated version or the user is using a not-supported browser
114 |
115 |
data : [function] callback that gets called when midi data has arrived
116 |
117 |
javaDir : [string] the folder where you store the midiapplet.jar on your webserver, defaults to "java"
118 |
119 |
connectFirstInput : [true,false] the first found midi input device gets connected automatically, defaults to false
120 |
121 |
connectAllInputs : [true,false] all found midi input devices get connected automatically, defaults to false
122 |
123 |
connectFirstOutput : [true,false] the first found midi output device gets connected automatically, defaults to false
124 |
125 |
connectAllInputsToFirstOutput : [true,false] all found midi input devices will be automatically connected to the first found midi output device, defaults to true
126 |
127 |
130 | In the example below all midi inputs get connected to the midibridge, but they will not be connected to the first midi output, i.e. you won't hear a sound when you play your keyboard(s).
131 |
132 | Also, all messages from the midibridge are printed to a div in the html page.
133 |
134 | [code lang="js"]
135 |
136 | window.addEventListener('load', function() {
137 |
138 | var contentDiv = document.getElementById("content");
139 | contentDiv.innerHTML += "midibridge says: ";
140 |
141 | midiBridge.init({
142 | connectAllInputs: true,
143 |
144 | ready: function(msg){
145 | contentDiv.innerHTML += msg + " ";
146 | },
147 | error: function(msg) {
148 | contentDiv.innerHTML += msg + " ";
149 | },
150 | data: function(midiEvent) {
151 | contentDiv.innerHTML += midiEvent + " ";
152 | }
153 | });
154 |
155 | }, false);
156 |
157 | [/code]
158 |
159 | You can check this example here.
160 |
161 |
162 |
163 | sendMidiEvent(status, channel, data1, data2)
164 |
165 | With this method you can send midi events to the applet. You can use this method for instance to make a vitual keyboard. Here is a code snippet to help you started:
166 | [code lang="js"]
167 |
168 | window.addEventListener('load', function() {
169 |
170 | midiBridge.init(function(midiEvent) {
171 | console.log(midiEvent);
172 | ;});
173 |
174 | var contentDiv = document.getElementById("content");
175 | contentDiv.innerHTML += "
play a note
";
176 | var playNote = document.getElementById("playAnote");
177 | playNote.style.textDecoration = "underline";
178 |
179 | playNote.addEventListener("click",function(){
180 | midiBridge.sendMidiEvent(midiBridge.NOTE_ON,1,80,100);
181 |
182 | }, false);
183 |
184 | [/code]
185 |
186 | Now if you click on "play a note", you should hear a note.
187 |
188 | You can check this example here.
189 |
190 |
191 |
192 | getDevices()
193 |
194 | This method returns a JSON object that contains all midi devices that were detected on your computer when the midibridge was initialized.
195 |
196 | [code lang="js"]
197 |
198 | window.addEventListener('load', function() {
199 |
200 | var contentDiv = document.getElementById("content");
201 |
202 | midiBridge.init({
203 | connectAllInputsToFirstOutput: false,
204 |
205 | ready: function(msg){
206 | var devices = midiBridge.getDevices();
207 | for(var i = 0, max = devices.length; i < max; i++) {
208 |
209 | var device = devices[i];
210 | var id = device.id;
211 | var type = device.type;
212 | var name = device.name;
213 | var descr = device.descr;
214 | var available = device.available;
215 | contentDiv.innerHTML += id + " : " + type+ " : " + name+ " : " + descr+ " : " + available + " ";
216 | }
217 | },
218 | });
219 | }, false);
220 |
221 | [/code]
222 |
223 |
224 | The parameter available shows whether the device is currently in use or not. For instance if your midi keyboard was connected to a softsynth at the time you started the midibridge, that midi keyboard will be listed but the parameter available will be set to false.
225 |
226 | You can check this example here.
227 |
228 |
229 |
230 | refreshDevices()
231 |
232 | You can use this method if your midi configuration has changed after you have started the midibridge. For instance if you connect a new midi device to your computer.
233 |
234 | If you call this method, all current connections between your midi devices between your midi inputs and the midibridge will be disconnected.
235 |
236 |
237 |
238 | connectAllInputs()
239 |
240 | By calling this method, all detected midi inputs will be connected to the midibridge. You can also achieve this if you call the init() method with a configuration object, and then set the key connectAllInputs to true.
241 |
242 |
243 |
244 | connectFirstInput()
245 |
246 | By calling this method, the first detected midi input will be connected to the midibridge. You can also achieve this if you call the init() method with a configuration object, and then set the key connectFirstInput to true.
247 |
248 | This method does not connect a midi output, so you can use this configuration for instance if your application only has a visual representation of the midi events.
249 |
250 | NOTE: Sometimes the first midi input is not the device that you want to connect. In this case use getDevices() to check what id has been assigned to the midi input that you want to connect to the midibridge, and establish the connection with addConnection(midiInputId,midiOutputId). This also applies to connectFirstOutput.
251 |
252 |
253 |
254 | connectFirstOutput()
255 |
256 | By calling this method, the first detected midi output will be connected to the midibridge. You can also achieve this if you call the init() method with a configuration object, and then set the key connectFirstOutput to true.
257 |
258 | This method does not connect a midi input, so you can use this configuration for instance if your application has a virtual keyboard, or if you attach the regular keyboard to the midibridge.
259 |
260 | [code lang="js"]
261 |
262 | window.addEventListener('load', function() {
263 |
264 | midiBridge.init({
265 | connectFirstOutput : true
266 | ready : function(msg) {
267 | connectKeyboard();
268 | },
269 | error : function(msg) {
270 | console.log(msg);
271 | }
272 | });
273 |
274 | var noteNumbers = {
275 | //white keys
276 | 65 : 48, //key a -> note c
277 | 83 : 50, //key s -> note d
278 | 68 : 52, //key d -> note e
279 | 70 : 53, //key f -> note f
280 | 71 : 55, //key g -> note g
281 | 72 : 57, //key h -> note a
282 | 74 : 59, //key j -> note b
283 | 75 : 60, //key k -> note c
284 | 76 : 62, //key l -> note d
285 | 186 : 64, //key ; -> note e
286 | 222 : 65, //key : -> note f
287 | //black keys
288 | 87 : 49, //key w -> note c#/d♭
289 | 69 : 51, //key e -> note d#/e♭
290 | 84 : 54, //key t -> note f#/g♭
291 | 89 : 56, //key y -> note g#/a♭
292 | 85 : 58, //key u -> note a#/b♭
293 | 79 : 61, //key o -> note c#/d♭
294 | 80 : 63 //key p -> note d#/e♭
295 | }
296 |
297 | var keysPressed = {};
298 |
299 | var connectKeyboard = function(){
300 |
301 | document.addEventListener("keydown", function(e) {
302 | if(e.which === 32) {
303 | midiBridge.sendMidiEvent(midiBridge.CONTROL_CHANGE, 1, 64, 127);
304 | } else if(noteNumbers[e.which] && !keysPressed[e.which]) {
305 | midiBridge.sendMidiEvent(midiBridge.NOTE_ON, 1, noteNumbers[e.which], 100);
306 | keysPressed[e.which] = true;
307 | }
308 | }, false);
309 |
310 |
311 | document.addEventListener("keyup", function(e) {
312 | if(e.which === 32) {
313 | midiBridge.sendMidiEvent(midiBridge.CONTROL_CHANGE, 1, 64, 0);
314 | } else if(noteNumbers[e.which]) {
315 | midiBridge.sendMidiEvent(midiBridge.NOTE_OFF, 1, noteNumbers[e.which], 0);
316 | keysPressed[e.which] = false;
317 | }
318 | }, false);
319 | };
320 |
321 | }, false);
322 |
323 |
324 |
325 | [/code]
326 |
327 | The spacebar is used for the sustainpedal. Pressing a sustainpedal is a control change event. The controller number of the sustainpedal is 64 and 127 means pedal down, 0 means pedal up.
328 |
329 | You can check this example here.
330 |
331 |
332 |
333 | connectAllInputsToFirstOutput()
334 |
335 | By calling this method, all detected midi inputs will be connected to the midibridge, and all inputs will also be connected to the first detected midi output.
336 |
337 | This is the default configuration of the midibridge, so you can also achieve this if you call the init() method with no arguments, or with a callback function as argument.
338 |
339 | It is interesting to know that the applet duplicates all midi events that arrive from your midi inputs (e.g. your midi keyboard). One event travels on to the midibridge and thus is available in Javascript. The other event travels to a midi out device if a midi output is connected to that midi input.
340 |
341 |
342 |
343 |
344 |
345 | addConnection(input,output,filter)
346 | You can set up a connection between any midi input and midi output device by passing the ids of the devices as arguments to this method. You can lookup the id of an device in the JSON object that is returned when you call getDevices() or refreshDevices().
347 |
348 | The id of the midi input and the midi output can also be set to -1. If you for instance only want to capture midi events in your application without connecting to an output device, you can set the id of the midi output to -1. If you set both the midi input and the midi outport to -1, nothing will happen.
349 |
350 | The code below shows you how you could implement functionality that allows the users to set up midi connections:
351 |
352 | [code lang="js"]
353 |
354 | window.addEventListener('load', function() {
355 |
356 | var contentDiv = document.getElementById("content");
357 | var currentMidiInputId = -1;
358 | var currentMidiOutputId = -1;
359 |
360 | //create a dropdown box for the midi inputs
361 | var midiInputs = document.createElement("select");
362 | midiInputs.setAttribute("id", "midi-in");
363 | contentDiv.appendChild(midiInputs);
364 | midiInputs.addEventListener("change", function(e) {
365 | var device = midiInputs.options[midiInputs.selectedIndex];
366 | currentMidiInputId = device.id;
367 | var result = midiBridge.addConnection(currentMidiInputId, currentMidiOutputId, [midiBridge.PITCH_BEND]);
368 | parseResult(result);
369 | }, false);
370 |
371 | //create a dropdown box for the midi outputs
372 | var midiOutputs = document.createElement("select");
373 | midiOutputs.setAttribute("id", "midi-out");
374 | contentDiv.appendChild(midiOutputs);
375 | midiOutputs.addEventListener("change", function(e) {
376 | var device = midiOutputs.options[midiOutputs.selectedIndex];
377 | currentMidiOutputId = device.id;
378 | var result = midiBridge.addConnection(currentMidiInputId, currentMidiOutputId, [midiBridge.PITCH_BEND]);
379 | parseResult(result);
380 | }, false);
381 |
382 | var devices;
383 |
384 | midiBridge.init({
385 | connectAllInputsToFirstOutput : false,
386 |
387 | ready : function(msg) {
388 | devices = midiBridge.getDevices();
389 | populateDropDownMenus()
390 | },
391 |
392 | error : function(msg) {
393 | contentDiv.innerHTML += msg + " ";
394 | },
395 |
396 | data : function(midiEvent) {
397 | contentDiv.innerHTML += midiEvent + " ";
398 | }
399 |
400 | });
401 |
402 | var populateDropDownMenus = function() {
403 |
404 | midiInputs.appendChild(createOption("-1", "choose a midi input"));
405 | midiOutputs.appendChild(createOption("-1", "choose a midi output"));
406 |
407 | for(var deviceId in devices) {
408 | var device = devices[deviceId];
409 | if(device.type === "input" && device.available === "true") {
410 | midiInputs.appendChild(createOption(device.id, device.name))
411 | } else if(device.type === "output" && device.available === "true") {
412 | midiOutputs.appendChild(createOption(device.id, device.name))
413 | }
414 | }
415 | }
416 |
417 | var createOption = function(id, label) {
418 | var option = document.createElement("option");
419 | option.setAttribute("id", id);
420 | option.innerHTML = label;
421 | return option;
422 | }
423 |
424 | var parseResult = function(data){
425 | contentDiv.innerHTML += data.msg + " : " + currentMidiInputId + " : " + currentMidiOutputId + "";
426 | }
427 |
428 | }, false);
429 |
430 |
431 |
432 | [/code]
433 |
434 | You can check this example here.
435 |
436 |
437 |
438 | removeConnection(input,output)
439 |
440 | You can remove a connection between a midi input and a midi output by passing their respective ids as arguments to this method.
441 |
442 |
443 |
444 | disconnectAll()
445 |
446 | This method does exactly what you would expect: it disconnects all current connections between midi in- and output and between your midi inputs and the midibridge
447 |
448 |
449 |
450 | getNoteName(midiNoteNumber,mode)
451 |
452 | Returns the name of the note based on the midi notenumber. Midi notenumbers are part of the midi standard and range between 0 and 127. The central C (261.626 Hz in 440 pitch) has notenumber 60.
453 |
454 | The parameter mode can be one of the following values:
455 |
midiBridge.NOTE_NAMES_SHARP : the black keys will be named sharps, so notenumber 61 will be called C#
456 |
midiBridge.NOTE_NAMES_FLAT : the black keys will be named flats, so notenumber 61 will be called D♭
457 |
midiBridge.NOTE_NAMES_ENHARMONIC_SHARP : all keys will be named sharps, so notenumber 60 will be called B#, notenumber 62 will be called C## (double sharp)
458 |
midiBridge.NOTE_NAMES_ENHARMONIC_FLAT : all keys will be named flats, notenumber 64 will be called F♭, notenumber 62 will be called E♭♭ (double flat)
459 |
460 |
461 |
462 |
463 | getNoteNumber(midiName,octave)
464 |
465 | Returns the midi notenumber based on the name of the note and the octave. Octave -1 is the lowest possible octave number, octave 9 the highest possible. A regular 88 key keyboard ranges between A-1 and C7.
466 |
467 |
468 |
469 | getStatus(statusCode)
470 |
471 | Returns a string representation of the status byte of the midi message. For instance midiBridge.getStatus(midiBridge.NOTE_ON) returns "NOTE ON".
472 |
473 |
474 |
475 | loadBase64String(base64String)
476 |
477 | Loads a base64 encoded MIDI file in the sequencer and return an object containing data about the loaded MIDI file.
478 |
479 |
480 |
microseconds: duration of the MIDI file in microseconds
481 |
ticklength: duration of the MIDI file in ticks
482 |
ticks: number of ticks in the MIDI file
483 |
484 |
485 |
486 | There are several ways of getting a MIDI file as base64 string into your application.
487 |
488 | 1) By using html5 drag and drop functionality with the File API, see this example.
489 |
490 | 2) By using the File API, see this example.
491 |
492 | 3) Store the MIDI file as a base64 string in a Javascript variable, see also this example. The Javascript file chopin_opus18.mid.js contains a global variable chopin_opus18 that contains the MIDI file as base64 string.
493 |
494 | 4) Since global variables are bad code practise, loading base64 MIDI files from a webserver is preferred over option 3. Encoding MIDI files on the server can be done with a single line of php code:
495 |
496 | [code lang="php"]
497 |
508 |
509 | [/code]
510 |
511 |
512 |
513 | playBase64String(base64String)
514 |
515 | Same as loadBase64String(base64String) but the file starts playing immediately.
516 |
517 |
518 |
519 | startSequencer()
520 |
521 | If a MIDI file is loaded the sequencer starts playing the file at the current position.
522 |
523 |
524 |
525 | pauseSequencer()
526 |
527 | Pauses the sequencer. Note: pause does not toggle, so you have to call startSequencer() to unpause the sequencer.
528 |
529 |
530 |
531 | stopSequencer()
532 |
533 | Stops the sequencer and rewinds sets the position of the file back to the start.
534 |
535 |
536 |
537 | closeSequencer()
538 |
539 | Stops the sequencers and then closes it. You have to call loadBase64String(base64String) or playBase64String(base64String) to open a sequencer again.
540 |
541 |
542 |
543 | getSequencerPosition()
544 |
545 | Gets the position of the MIDI file in the sequencer in microseconds.
546 |
547 |
548 |
549 | setSequencerPosition(microseconds)
550 |
551 | Sets the position of the MIDI file in the sequencer in microseconds.
552 |
553 |
554 |
555 | getNiceTime(microseconds)
556 |
557 | Converts microseconds to the time format m:ss:SSS.
558 |
559 | Typically used to display the sequencer position of the MIDI file.
560 |
561 |
562 |
563 | getObject(id)
564 |
565 | Returns a reference to the object in the html page whose id is specified. It is used for getting a reference to the applet, but you can also use it for getting a reference to any other type of object. For instance for getting the swf object if your application is built with Flash, see What about Flash?
566 |
567 |
568 |
569 | MidiMessage(jsonString)
570 |
571 | An internal class of the midibrigde that is used for storage and easy handling of the midi events that arrive from the applet. The applet sends midi events as JSON strings to the midibridge. The midibridge uses the native JSON.parse() method to parse this string into a JSON object.
572 |
573 | The MidiMessage class has 2 useful methods that you might need in your code toString() and toJSONString(). The first method is handy for printing the midi incoming events to a log, and the latter can be used if you want to send the midi event to Flash or another technology. See the offical JSON website for more information. Flash programmers might be interested in the JSON library of Mike Chambers.
574 |
575 |
576 |
577 | Static members
578 | Besides methods, there are also a bunch of very handy static members that you can use:
579 |
midiBridge.version : version of the midibridge as string
580 |
midiBridge.ready : set to true if the midiBridge has been initialized successfully, otherwise set to false
midiBridge.NOTE_NAMES_ENHARMONIC_SHARP : "enh-sharp" see getNoteNumber()
584 |
midiBridge.NOTE_NAMES_ENHARMONIC_FLAT : "enh-flat" see getNoteNumber()
585 |
midiBridge.NOTE_OFF : 0x80 (128)
586 |
midiBridge.NOTE_ON : 0x90 (144)
587 |
midiBridge.POLY_PRESSURE : 0xA0 (160)
588 |
midiBridge.CONTROL_CHANGE : 0xB0 (176)
589 |
midiBridge.PROGRAM_CHANGE : 0xC0 (192)
590 |
midiBridge.CHANNEL_PRESSURE : 0xD0 (208)
591 |
midiBridge.PITCH_BEND : 0xE0 (224)
592 |
midiBridge.SYSTEM_EXCLUSIVE : 0xF0 (240)
593 |
594 |
595 |
596 |
597 | About midi out
598 |
599 | If you're on Windows or Linux, the latency of the Java Sound Synthesizer makes it almost impossible to play. On Windows you can also choose the Microsoft GS Wavetable Synth and with some soundcards you may get a decent latency (i was told the Realtek AC97 perfoms pretty well).
600 |
601 | On a Mac you can just select a default midi synthesizer (Java Sound Synthesizer) and start playing with no noticeable latency.
602 |
603 | Latency is caused by both the drivers of your soundcard and the way your synthesizer works. Most modern softsynths hardly cause any latency, but even with the latest M-Audio pro cards you'll experience latency when using the Java Sound Synthesizer or the Microsoft GS Wavetable Synth.
604 |
605 | So we need to be able to connect to some real softsynths like Pianoteq or Lounge Lizard and for this we need a virtual midi driver.
606 |
607 | If you're on a Mac, you're lucky because such a thing is already installed on your machine. It is called IAC Driver and you'll find it if you open the Audio MIDI Setup in your Applications folder.
608 |
609 | If you are on Windows you can download LoopBe1 from nerds.de and Linux users can check VirMidi
610 |
611 | Below i'll give a brief explanation for every driver.
612 |
613 | - LoopBe
614 | - IAC
615 | - VirMidi
616 |
617 |
618 |
619 | LoopBe1
620 |
621 | Download it from nerds.de and run the installer. After the installation has finished LoopBe is up and running and will automatically start with Windows (if you don't want this, run msconfig and remove the LoopBe startup service).
622 |
623 | Now LoopBe Internal MIDI will be listed as both a midi input as well as a midi output when you call getDevices(). You can setup a connection betweein your favorite keyboard as input device and LoopBe Internal MIDI as output with addConnection().
624 |
625 | Now open your favorite softsynth and go to the midi settings and set your synth's midi input to LoopBe Internal MIDI. Here is a screendump of what this looks like in Pianteq:
626 |
627 |
628 |
629 | You should now be able to play your softsynth while midi data is passing thru the midibridge, and dependent on your soundcard's driver, with very low latency.
630 |
631 | Please notice that LoopBe1 is only free for non-commercial use. For commercial use you need to acquire a license after a 30-day evolution period. But for only € 11,90 inc VAT it's really a bargain. If you are willing to spend an extra 5 euro on top, i would recommend to buy LoopBe1 bigger brother LoopBe30, which gives you up to 30 virtual midi ports! Check here.
632 |
633 |
634 |
635 | IAC
636 |
637 | Open your Finder, go to menu Go -> Applications and scroll down till you've found a folder named Utilities. Open the folder Utilities and double click on Audio MIDI Setup. If you only see a window with Audio Devices, go to Window -> Show MIDI Window.
638 |
639 | In the window that subsequently opens, you should see an icon named IAC Driver. IAC stands for Inter-Application Communication, and that is exactly what it does.
640 |
641 | If the icon is greyed out double click it and check the box “Device is online” in the popup that appears. Now you should have a window like:
642 |
643 |
644 |
645 | Don't worry if looks a little different on your machine. You should see at least 1 port in the “Ports” part of the screen. If not, simply click the plus sign to add a port. I recommend to add a least 2 ports to the IAC Driver.
646 |
647 | Close this popup and the Audio MIDI Setup. Now "IAC Driver IAC Bus 1" (or something alike) will be listed as midi input when you call getDevices().
648 |
649 | Set up a connection between your favorite keyboard as input device and "IAC Driver IAC Bus 1" as output with addConnection(). Open your favorite softsynth and go to the midi settings and set your synth's midi input to "IAC Driver IAC Bus 1". Here is a screendump of what this looks like in Lounge Lizard:
650 |
651 |
652 |
653 | Now you can play your softsynth while midi data is passing thru the midibridge.
654 |
655 |
656 |
657 | VirMidi
658 |
659 | If you are using Ubuntu or Kubuntu, there is a thread about VirMidi on the Ubuntu forum
660 |
661 | Because snd-virmidi is a kernel module, you can simply load this module by typing
662 | sudo modprobe snd-virmidi
663 | on the command line.
664 |
665 | Now if you call getDevices(), you should see at least 4 additional devices listed.
666 |
667 | Set up a connection between your keyboard as input device and one of the virtual midi ports as output with addConnection(). Connect this output to the input of your favorite softsynth, for instance in Pianoteq this would look like:
668 |
669 |
670 |
671 | Now you can play your softsynth while midi data is passing thru the midibridge.
672 |
673 | Pianoteq is available for both 32 and 64 bits Linux, so if you want to try it yourself you can download a demo version over here.
674 |
675 |
676 |
677 | Earlier versions
678 |
679 | I started this project in the summer of 2008. Since then i have released 5 versions:
680 |
681 |
682 |
proof of concept : With dynamical sound generation, a chord finder, a color organ, midi learn functionality, a virtual keyboard and an adjustable pitch bend controller that acts upon the generated sound
683 |
version 2 : With all features of the proof of concept, but with an extra swf that sits between the applet and the application swf. This extra swf connects on one site via ExternalInternface to the applet, and on the other side via LocalConnection to the application swf.
684 |
version 3 : The in-between swf removed again, dynamical sound generation replaced by midi out. A very simple GUI: virtual keyboard, chord finder and pich bend controller removed, simple animation added in.
685 |
version 4 : FluidSynth softsynth added that allows you to use Soundfont files at choice. Also the midibridge is able to generate midi events itself. Virtual keyboard added again
686 |
version 5 : Same as version 4 but you can also export the code to a standalone AIR version.
687 |
688 |
689 |
690 | In my blogposts you can still find information about the earlier versions. This might be confusing and therefor i have started this page where you can find only up to date information about the latest, and thus featured release. Some of this information can also be found in various posts, but if it appears here as well it is still valid for the current release.
691 |
692 | With this version, the development of all earlier versions of the midibridge will be frozen. The reason for this is that with this new version i have redefined what the midibridge exactly is.
693 |
694 | In former version of the midibridge i have added too much GUI functionality. As explained in the introduction, the new midibridge provides only a compact set of methods that allows you to interact transparently with the midi devices on your computer, but leaves the GUI totally up to you.
695 |
696 | So therefor the new version has no control panel, virtual keyboard, midi learn functionality and so on. I have provided code examples for the basic features of the midibridge and i will add some more examples for more advanced features like sound generation soon. Also i might develop some configurable UI plugins for the midibridge (alike jQuery UI) somewhere in the future.
697 |
698 | Another reason for stripping down the midibridge to its core is that former versions were too much tied to Flash and Actionscript. In modern browsers Flash is no longer the only reasonable choice for creating compelling animations. This applies to dynamically generating sound as well.
699 |
700 | What's more, the latency of Mozilla's Audio Data API allows you to set the minimal audio buffer as low as 512 samples, which results in a latency of only 512/44100 ≈ 11.6 ms(!). In Flash the minimal buffer size is 2048 (recommended) which results in an almost un-playable latency of 2048/44100 ≈ 46ms.
701 |
702 |
703 | To summarize the benefits of the new approach:
704 |
705 | - the midibridge now only takes 5K of code
706 | - it makes it much easier to add the midibridge to your code
707 | - it gives you more control over what your website/application looks like
708 | - it does not impose a specific client side language on you
709 |
710 | The only downside is that the new version is not compatible with the earlier versions. However, you can still use it; the code is fully functional and remains available at GitHub and Google Code. You are encouraged to switch to the new version though.
711 |
712 | Code at GitHub (version 5):
713 |
714 | http://github.com/abudaan/javamidi
715 |
716 | http://github.com/abudaan/flashmidi
717 |
718 | Code at Google Code (version 5):
719 |
720 | http://code.google.com/p/miditoflash/downloads/
721 |
722 | As i mentioned above, the earlier version had both a web and an AIR version. The Air version uses NativeProcess to start a Java program on the commandline, and communicates with this program via its standard input and standard output.
723 |
724 | I am not yet sure what to do with the AIR version. You can use both Actionscript and Javascript in an AIR app, but i am actually looking for another way of creating a browser-less application. Any suggestions are welcome.
725 |
726 |
727 |
728 | What about Flash?
729 |
730 |
731 | The midibridge is fully accessible from Actionscript 3.0 if you use the ExternalInterface.
732 |
733 | You probably want to load Flash before you initialize the midibridge so you can show some loading animation. In Actionscript you have to test when the page has fully loaded and then call the init() function of the midibridge.
734 |
735 | First a Timer is set up to check if the midibridge object is null. As soon as the midibridge object exists, Flash creates callback handlers for Javascript: these are methods that can be called directly from Javascript.
736 |
737 | Flash also calls the global Javascript method callFromFlash(). You can name it anything you like btw. The first parameter determines what action of the midibridge is requested:
738 |
739 | [code lang="js"]
740 |
741 | package {
742 | import flash.display.Sprite;
743 | import flash.events.TimerEvent;
744 | import flash.external.ExternalInterface;
745 | import flash.utils.Timer;
746 |
747 | public class Main extends Sprite {
748 | private var _readyTimer:Timer = new Timer(1, 1);
749 |
750 | public function Main() {
751 | if(ExternalInterface.available) {
752 | _readyTimer.addEventListener(TimerEvent.TIMER, check);
753 | _readyTimer.start();
754 | } else {
755 | trace("ExternalInterface not avaible in this browser");
756 | }
757 | }
758 |
759 | public function midibridgeReady(msg:String):void {
760 | var jsonString:String = ExternalInterface.call("callFromFlash", "getDevices");
761 | trace(jsonString);
762 | }
763 |
764 | public function midibridgeError(msg:String):void {
765 | trace(msg);
766 | }
767 |
768 | public function midibridgeData(msg:String):void {
769 | trace(msg);
770 | }
771 |
772 | private function check(e:TimerEvent = null):void {
773 | try {
774 | if(ExternalInterface.call("midiBridge") !== null) {
775 | ExternalInterface.addCallback("midibridgeReady", midibridgeReady);
776 | ExternalInterface.addCallback("midibridgeError", midibridgeError);
777 | ExternalInterface.addCallback("midibridgeData", midibridgeData);
778 | ExternalInterface.call("callFromFlash", "start");
779 | _readyTimer.stop();
780 | } else {
781 | _readyTimer = new Timer(100, 1);
782 | _readyTimer.addEventListener(TimerEvent.TIMER, check);
783 | _readyTimer.start();
784 | }
785 | } catch(err1:SecurityError) {
786 | trace(err1.message);
787 | } catch(err2:Error) {
788 | trace(err2.message);
789 | }
790 | }
791 | }
792 | }
793 |
794 | [/code]
795 |
796 |
797 | Now back in Javascript, the call to callFromFlash() gets processed. The first parameter was "start" so the midibridge gets initialized. As you can see, the callback handlers of the midibridge get directly connected to the Javascript callback handlers of Flash. This way data and messages are routed from the midibridge to Flash:
798 |
799 | [code lang="js"]
800 |
801 | /**
802 | * You can only call global functions from Flash, therefor we declare a single global function that is used for all
803 | * communication with the Flashplayer.
804 | *
805 | */
806 | function callFromFlash() {
807 |
808 | /**
809 | * getObject is utility function of midiBridge, it is used to get the Applet object,
810 | * but it can also be used to get the swf object
811 | *
812 | * flashapp is the id of the swf object in the html page.
813 | */
814 | var flashObject = midiBridge.getObject("flashapp");
815 |
816 | /**
817 | * convert the arguments to a array, the first argument is the msgId.
818 | * the msgId is used to determine what the Flashplayer wants from the midibridge
819 | */
820 | var args = Array.prototype.slice.call(arguments);
821 | var msgId = args[0];
822 |
823 |
824 | switch(msgId) {
825 |
826 | case "start":
827 | midiBridge.init({
828 | connectAllInputsToFirstOutput : true,
829 |
830 | ready : function(msg) {
831 | flashObject.midibridgeReady(msg);
832 | return msg;
833 | },
834 |
835 | error : function(msg) {
836 | flashObject.midibridgeError(msg);
837 | },
838 |
839 | data : function(midiEvent) {
840 | flashObject.midibridgeData(midiEvent.toString());
841 | }
842 |
843 | });
844 | break;
845 |
846 | case "getDevices":
847 | return midiBridge.getDevices();
848 | break;
849 | }
850 |
851 | };
852 |
853 | [/code]
854 |
855 | You can check this example here.
856 |
857 |
858 |
859 | Known issues
860 |
861 | Currently the midibridge does not work with the Icedtea Java plugin on Linux.
862 |
863 |
864 |
865 | Forthcoming features
866 |
867 | In a future release i will add more functionality to the sequencer. The following methods will be implemented.
868 |
869 | getSequencerTickPosition();
870 | setSequencerTickPosition(ticks);
871 | recordMidi(file);
872 | setTempo(tempo);
873 |
874 | For more feature requests or other suggestions, please drop me a line!
875 |
876 |
--------------------------------------------------------------------------------
/examples/lib/midibridge-0.5.4.min.js:
--------------------------------------------------------------------------------
1 | (function(){function f(){return!b.ready?(g&&g("midibridge not ready!"),"midibridge not ready!"):!0}function u(){try{d=b.getObject("midibridge-applet")}catch(a){setTimeout(u,25)}}try{console.log("")}catch(y){console={log:function(){}}}var b={NOTE_OFF:128,NOTE_ON:144,POLY_PRESSURE:160,CONTROL_CHANGE:176,PROGRAM_CHANGE:192,CHANNEL_PRESSURE:208,PITCH_BEND:224,SYSTEM_EXCLUSIVE:240,MIDI_TIMECODE:241,SONG_POSITION:242,SONG_SELECT:243,TUNE_REQUEST:246,EOX:247,TIMING_CLOCK:248,START:250,CONTINUE:251,STOP:252,
2 | ACTIVE_SENSING:254,SYSTEM_RESET:255,NOTE_NAMES_SHARP:"sharp",NOTE_NAMES_FLAT:"flat",NOTE_NAMES_SOUNDFONT:"soundfont",NOTE_NAMES_ENHARMONIC_SHARP:"enh-sharp",NOTE_NAMES_ENHARMONIC_FLAT:"enh-flat"},c=[];c[128]="NOTE OFF";c[144]="NOTE ON";c[160]="POLY PRESSURE";c[176]="CONTROL CHANGE";c[192]="PROGRAM CHANGE";c[208]="CHANNEL PRESSURE";c[224]="PITCH BEND";c[240]="SYSTEM EXCLUSIVE";c[241]="MIDI TIMECODE";c[242]="SONG POSITION";c[243]="SONG SELECT";c[244]="RESERVED";c[245]="RESERVED";c[246]="TUNE REQUEST";
3 | c[247]="EOX";c[248]="TIMING CLOCK";c[249]="RESERVED";c[250]="START";c[251]="CONTINUE";c[252]="STOP";c[254]="ACTIVE SENSING";c[255]="SYSTEM RESET";var l={sharp:"C,C#,D,D#,E,F,F#,G,G#,A,A#,B".split(","),flat:"C,D♭,D,E♭,E,F,G♭,G,A♭,A,B♭,B".split(","),soundfont:"C,Db,D,Eb,E,F,Gb,G,Ab,A,Bb,B".split(","),"enh-sharp":"B#,C#,C##,D#,D##,E#,F#,F##,G#,G##,A#,A##".split(","),"enh-flat":"D♭♭,D♭,E♭♭,E♭,F♭,G♭♭,G♭,A♭♭,A♭,B♭♭,B♭,C♭".split(",")},
4 | e=JSON.parse,m=null,g=null,q=null,d=null,n=!1,o=!1,p=!1,i=!0,v={},r=!1,j={},s=[];b.version="0.5.4";b.ready=!1;b.noteNameModus=b.NOTE_NAMES_SHARP;var w="midiapplet-"+b.version+".jar";b.init=function(a){for(var k in c){var h=parseInt(k);switch(h){case b.NOTE_OFF:case b.NOTE_ON:case b.POLY_PRESSURE:case b.CONTROL_CHANGE:case b.PROGRAM_CHANGE:case b.CHANNEL_PRESSURE:case b.PITCH_BEND:s.push(h);for(var d=0;16>d;d++)c[h+d]=c[k];break;default:s.push(h)}}if("function"===typeof a)m=a;else if("object"===typeof a){r=
5 | a.debug;k=a.midiCommands||s;j={};for(var f=0;fd;d++)j[h+d]=1;break;default:j[h]=1}n=void 0===a.connectAllInputs?n:a.connectAllInputs;o=void 0===a.connectFirstInput?o:a.connectFirstInput;p=void 0===a.connectFirstOutput?p:a.connectFirstOutput;i=void 0===a.connectAllInputsToFirstOutput?i:a.connectAllInputsToFirstOutput;m=a.data;
6 | q=a.ready;g=a.error;r&&console.log(o,p,n,i,e,j)}if(navigator.javaEnabled())try{if(void 0===e)console.log("supported browsers: Firefox 3.5+, Chrome 4.0+, Safari 4.0+, Opera 10.5+, Internet Explorer 8.0+");else{var t=document.createElement("div");t.setAttribute("id","midibridge-java");a="";-1===navigator.userAgent.toLowerCase().indexOf("chrome")?(a=a+''):(a+='');t.innerHTML=a;document.body.appendChild(t)}}catch(l){g?g("supported browsers: Firefox 3.5+, Chrome 4.0+, Safari 4.0+, Opera 10.5+, Internet Explorer 8.0+"):console.log("supported browsers: Firefox 3.5+, Chrome 4.0+, Safari 4.0+, Opera 10.5+, Internet Explorer 8.0+")}else g?g("no java plugin found; install or enable the java plugin"):console.log("no java plugin found; install or enable the java plugin")};b.msgFromJava=function(a){a=e(a);switch(a.msgId){case "upgrade-java":g?
9 | g("please upgrade your java plugin!"):console.log("please upgrade your java plugin!");break;case "midi-started":u();d&&(v=a.devices,b.ready=!0,(p||i)&&b.connectFirstOutput(),n||i?b.connectAllInputs():o&&b.connectFirstInput(),q&&q("midibridge started"));break;case "midi-data":if(m){if(void 0===j[a.status]){r&&console.log("MIDI message intercepted",a.status,a.data1,a.data2,a.channel);break}m(new x(a))}break;case "error":g&&g(a.code)}};b.sendMidiEvent=function(a,b,c,g){return f()?e(d.processMidiEvent(a,
10 | b,c,g)):!1};b.getDevices=function(){return v};b.refreshDevices=function(){return f()?e(d.getDevices()):!1};b.connectAllInputs=function(){return f()?e(d.connectAllInputs()):!1};b.connectFirstInput=function(){return f()?e(d.connectFirstInput()):!1};b.connectFirstOutput=function(){return f()?e(d.connectFirstOutput()):!1};b.connectAllInputsToFirstOutput=function(){return f()?e(d.connectAllInputsToFirstOutput()):!1};b.addConnection=function(a,b,c){return f()?(c=void 0===c?[]:c,e(d.addConnection(a,void 0===
11 | b?-1:b,c))):!1};b.removeConnection=function(a,b){return f()?e(d.removeConnection(a,b)):!1};b.disconnectAll=function(){return f()?e(d.disconnectAll()):!1};b.loadBase64String=function(a){return e(d.loadBase64String(a.replace(/data:audio\/mid[i]?;base64,/,"")))};b.playBase64String=function(a){return e(d.playBase64String(a))};b.loadMidiFile=function(a){return e(d.loadMidiFile(a))};b.playMidiFile=function(a){return e(d.playMidiFile(a))};b.startSequencer=function(){d.startSequencer()};b.pauseSequencer=
12 | function(){d.pauseSequencer()};b.stopSequencer=function(){d.stopSequencer()};b.closeSequencer=function(){d.closeSequencer()};b.getSequencerPosition=function(){return d.getSequencerPosition()};b.setSequencerPosition=function(a){d.setSequencerPosition(a)};var x=function(){var a=function(a){this.command=a.command;this.channel=a.channel;this.data1=a.data1;this.data2=a.data2;this.status=a.status;this.noteName=b.getNoteName(this.data1,b.noteNameModus);this.statusCode=b.getStatus(this.status);this.microsecond=
13 | a.microsecond;this.time=b.getNiceTime(this.microsecond);this.command==b.NOTE_ON&&0==this.data2&&(this.command=b.NOTE_OFF,this.status=this.command+this.channel)};a.prototype={toString:function(){var a;a=""+(this.noteName+" "+this.statusCode+" "+this.data1+" "+this.data2+" "+this.channel+" "+this.status);return a+=this.microsecond?this.microsecond+" "+this.time:""},toJSONString:function(){return this.microsecond?"{'notename':"+this.noteName+", 'status':"+this.status+", 'data1':"+this.data1+", 'data2':"+
14 | this.data2+", 'microsecond':"+this.microsecond+", 'time':"+this.time+"}":"{'notename':"+this.noteName+", 'status':"+this.status+", 'data1':"+this.data1+", 'data2':"+this.data2+"}"}};return a}();b.MidiMessage=x;b.getNoteName=function(a,b){return l[b][a%12]+""+Math.floor(a/12-1)};b.getNoteNumber=function(a,b){var c=-1,a=a.toUpperCase(),d;for(d in l)for(var e=l[d],f=0,g=e.length;f>0,c=b/3600>>0,d=b%3600/60>>0,b=b%60,a=a/1E3-36E5*c-6E4*d-1E3*b+0.5>>0;return""+(0d?"0"+d:d:d)+":"+(10>b?"0"+b:b)+":"+(0===a?"000":10>a?"00"+a:100>a?"0"+a:a)};b.getObject=function(a){var b=navigator.userAgent.toLowerCase();return-1!==b.indexOf("msie")||-1!==b.indexOf("webkit")?window[a]:document[a]};window.addEventListener||(window.addEventListener=function(a,b){window.attachEvent("onload",b)});window.midiBridge=b})(window);
--------------------------------------------------------------------------------
/examples/lib/swfobject.js:
--------------------------------------------------------------------------------
1 | /* SWFObject v2.0
2 | Copyright (c) 2007 Geoff Stearns, Michael Williams, and Bobby van der Sluis
3 | This software is released under the MIT License
4 | */
5 | var swfobject=function(){var Z="undefined",P="object",B="Shockwave Flash",h="ShockwaveFlash.ShockwaveFlash",W="application/x-shockwave-flash",K="SWFObjectExprInst",G=window,g=document,N=navigator,f=[],H=[],Q=null,L=null,T=null,S=false,C=false;var a=function(){var l=typeof g.getElementById!=Z&&typeof g.getElementsByTagName!=Z&&typeof g.createElement!=Z&&typeof g.appendChild!=Z&&typeof g.replaceChild!=Z&&typeof g.removeChild!=Z&&typeof g.cloneNode!=Z,t=[0,0,0],n=null;if(typeof N.plugins!=Z&&typeof N.plugins[B]==P){n=N.plugins[B].description;if(n){n=n.replace(/^.*\s+(\S+\s+\S+$)/,"$1");t[0]=parseInt(n.replace(/^(.*)\..*$/,"$1"),10);t[1]=parseInt(n.replace(/^.*\.(.*)\s.*$/,"$1"),10);t[2]=/r/.test(n)?parseInt(n.replace(/^.*r(.*)$/,"$1"),10):0}}else{if(typeof G.ActiveXObject!=Z){var o=null,s=false;try{o=new ActiveXObject(h+".7")}catch(k){try{o=new ActiveXObject(h+".6");t=[6,0,21];o.AllowScriptAccess="always"}catch(k){if(t[0]==6){s=true}}if(!s){try{o=new ActiveXObject(h)}catch(k){}}}if(!s&&o){try{n=o.GetVariable("$version");if(n){n=n.split(" ")[1].split(",");t=[parseInt(n[0],10),parseInt(n[1],10),parseInt(n[2],10)]}}catch(k){}}}}var v=N.userAgent.toLowerCase(),j=N.platform.toLowerCase(),r=/webkit/.test(v)?parseFloat(v.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,i=false,q=j?/win/.test(j):/win/.test(v),m=j?/mac/.test(j):/mac/.test(v);/*@cc_on i=true;@if(@_win32)q=true;@elif(@_mac)m=true;@end@*/return{w3cdom:l,pv:t,webkit:r,ie:i,win:q,mac:m}}();var e=function(){if(!a.w3cdom){return }J(I);if(a.ie&&a.win){try{g.write(" -->
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |