├── 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 | 4 | midi bridge 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /examples/audiolib.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | midi bridge 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 | 16 | 17 | -------------------------------------------------------------------------------- /examples/base64.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | midi bridge 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /examples/configobject.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | midi bridge 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /examples/css/clearfix.css: -------------------------------------------------------------------------------- 1 | .clearfix:after { 2 | content: "."; 3 | display: block; 4 | clear: both; 5 | visibility: hidden; 6 | line-height: 0; 7 | height: 0; 8 | } 9 | 10 | .clearfix { 11 | display: inline-block; 12 | } 13 | 14 | html[xmlns] .clearfix { 15 | display: block; 16 | } 17 | 18 | * html .clearfix { 19 | 20 | height: 1%; 21 | } 22 | -------------------------------------------------------------------------------- /examples/css/midibridge.css: -------------------------------------------------------------------------------- 1 | html{ 2 | font-family: Arial, Verdana, sans-serif; 3 | font-size: 14px; 4 | } 5 | #content{ 6 | width:700px; 7 | margin: 0 auto; 8 | } 9 | h1{ 10 | font-size:15px; 11 | } 12 | #info{ 13 | margin: 5px 3px 40px 3px; 14 | } 15 | #dropbox{ 16 | width:500px; 17 | height:120px; 18 | line-height: 120px; 19 | text-align: center; 20 | font-size: 17px; 21 | font-weight: bold; 22 | border: 1px dotted #333; 23 | margin: 10px 0px; 24 | } 25 | #controls{ 26 | margin:10px 0px; 27 | height:20px; 28 | } 29 | #console{ 30 | background-color: #000; 31 | font-family: Courier, Monaco, monospace; 32 | font-size: 12px; 33 | color: #00ff00; 34 | border:solid 1px #000; 35 | padding: 4px; 36 | margin: 2px 0px 2px 0px; 37 | width:492px; 38 | height:25px; 39 | line-height:25px; 40 | overflow: auto; 41 | clear:left; 42 | } 43 | #console div{ 44 | 45 | } 46 | .button{ 47 | float: left; 48 | border: 1px solid #333; 49 | padding:2px; 50 | margin: 0px 4px; 51 | cursor: pointer; 52 | } 53 | .button:hover{ 54 | background-color: #fff; 55 | } 56 | .label{ 57 | font-size: 13px; 58 | font-weight: bold; 59 | } 60 | .value{ 61 | font-size: 13px; 62 | background-color: #fff; 63 | padding:2px; 64 | } 65 | select{ 66 | float:left; 67 | margin:-1px 0px; 68 | } 69 | .slider{ 70 | border: #00A0B8 solid 1px; 71 | border-radius: 3px; 72 | background-color: white; 73 | float:left; 74 | /*margin-left: 20px;*/ 75 | } 76 | 77 | .slider-label{ 78 | margin: -7px 13px 0px 10px; 79 | font-family: Verdana,Arial,_sans; 80 | font-size: 12px; 81 | color: #333333; 82 | float:left; 83 | } 84 | 85 | .thumb{ 86 | background-image: url('../img/balloon.png'); 87 | background-repeat: no-repeat; 88 | position:relative; 89 | width:34px; 90 | height:28px; 91 | margin-left: -17px; 92 | text-align: center; 93 | } 94 | 95 | .thumb .label{ 96 | padding-top: 4px; 97 | font-family: monospace; 98 | color: #333333; 99 | font-size: 13px; 100 | } 101 | -------------------------------------------------------------------------------- /examples/css/reset.css: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | /* http://meyerweb.com/eric/thoughts/2007/05/01/reset-reloaded/ */ 3 | 4 | html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, 5 | a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, 6 | small, strike, strong, sub, sup, tt, var,dl, dt, dd, ol, ul, li, fieldset, form, label, legend, 7 | table, caption, tbody, tfoot, thead, tr, th, td { 8 | margin: 0; 9 | padding: 0; 10 | border: 0; 11 | outline: 0; 12 | font-weight: inherit; 13 | font-style: inherit; 14 | font-size: 100%; 15 | font-family: inherit; 16 | vertical-align: baseline; 17 | } 18 | /* remember to define focus styles! */ 19 | :focus { 20 | outline: 0; 21 | } 22 | body { 23 | line-height: 1; 24 | color: black; 25 | background: #dddddd; 26 | } 27 | ol, ul { 28 | list-style: none; 29 | } 30 | /* tables still need 'cellspacing="0"' in the markup */ 31 | table { 32 | border-collapse: separate; 33 | border-spacing: 0; 34 | } 35 | caption, th, td { 36 | text-align: left; 37 | font-weight: normal; 38 | } 39 | blockquote:before, blockquote:after, 40 | q:before, q:after { 41 | content: ""; 42 | } 43 | blockquote, q { 44 | quotes: "" ""; 45 | } -------------------------------------------------------------------------------- /examples/flash.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | midi bridge 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 |
25 | 28 |
29 | 30 | 31 | -------------------------------------------------------------------------------- /examples/flash/Main.as: -------------------------------------------------------------------------------- 1 | package { 2 | import flash.display.Sprite; 3 | import flash.events.TimerEvent; 4 | import flash.external.ExternalInterface; 5 | import flash.text.TextField; 6 | import flash.utils.Timer; 7 | 8 | [SWF(backgroundColor="#ffffff", width="230", height="800")] 9 | 10 | public class Main extends Sprite { 11 | private var _readyTimer:Timer = new Timer(1, 1); 12 | private var _output:TextField = new TextField(); 13 | 14 | public function Main() { 15 | 16 | _output.width = 210; 17 | _output.height = 780; 18 | _output.border = true; 19 | _output.x = 10; 20 | _output.y = 10; 21 | addChild(_output); 22 | 23 | if(ExternalInterface.available) { 24 | _readyTimer.addEventListener(TimerEvent.TIMER, check); 25 | _readyTimer.start(); 26 | } else { 27 | trace("ExternalInterface not avaible in this browser"); 28 | } 29 | } 30 | 31 | public function midibridgeReady(msg:String):void { 32 | var jsonString:String = ExternalInterface.call("callFromFlash", "getDevices"); 33 | trace(jsonString); 34 | } 35 | 36 | public function midibridgeError(msg:String):void { 37 | trace(msg); 38 | } 39 | 40 | //incoming MIDI data from the Midibridge 41 | public function midibridgeData(msg:String):void { 42 | //parse String to midiMessage object so you can use the MIDI data more convenient 43 | var data:Array = msg.split(" "); 44 | var midiMessage:Object; 45 | if(data.length === 5){ 46 | midiMessage = { 47 | noteName:data[0], 48 | commandName:data[1], 49 | data1:data[2],//usually MIDI note number 50 | data2:data[3],//usually velocity 51 | command:data[4]//command codes like 144, 128 etc. 52 | } 53 | }else if(data.length === 6){ 54 | midiMessage = { 55 | noteName:data[0], 56 | commandName:data[1] + "_" + data[2],//NOTE_ON, NOTE_OFF, etc. 57 | data1:data[3],//usually MIDI note number 58 | data2:data[4],//usually velocity 59 | command:data[5]//command codes like 144, 128 etc. 60 | } 61 | } 62 | //filter all incoming midi messages; add only the command codes of the midi messages you're interested in 63 | //currently: note on and note off 64 | switch(midiMessage.command){ 65 | case "128"://note off 66 | case "144"://note on 67 | yourAwesomeAnimationFunction(midiMessage); 68 | break; 69 | default: 70 | return; 71 | 72 | } 73 | } 74 | 75 | public function yourAwesomeAnimationFunction(midiMessage:Object){ 76 | var noteNumber:uint = parseInt(midiMessage.data1); 77 | var velocity:uint = parseInt(midiMessage.data2); 78 | _output.appendText("" + midiMessage.noteName + " : " + noteNumber + " : " + velocity + " : " + midiMessage.command + "\n"); 79 | _output.scrollV = _output.maxScrollH; 80 | } 81 | 82 | //check if the document is loaded 83 | private function check(e:TimerEvent = null):void { 84 | try { 85 | if(ExternalInterface.call("midiBridge") !== null) { 86 | ExternalInterface.addCallback("midibridgeReady", midibridgeReady); 87 | ExternalInterface.addCallback("midibridgeError", midibridgeError); 88 | ExternalInterface.addCallback("midibridgeData", midibridgeData); 89 | ExternalInterface.call("callFromFlash", "start"); 90 | _readyTimer.stop(); 91 | } else { 92 | _readyTimer = new Timer(100, 1); 93 | _readyTimer.addEventListener(TimerEvent.TIMER, check); 94 | _readyTimer.start(); 95 | } 96 | } catch(err1:SecurityError) { 97 | trace(err1.message); 98 | } catch(err2:Error) { 99 | trace(err2.message); 100 | } 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /examples/flash/main.fla: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abudaan/midibridge-js/1a75ca1784575716acae25321a44ffef27cd29a3/examples/flash/main.fla -------------------------------------------------------------------------------- /examples/getdevices.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | midi bridge 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /examples/img/balloon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abudaan/midibridge-js/1a75ca1784575716acae25321a44ffef27cd29a3/examples/img/balloon.png -------------------------------------------------------------------------------- /examples/java/midiapplet-0.5.4.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abudaan/midibridge-js/1a75ca1784575716acae25321a44ffef27cd29a3/examples/java/midiapplet-0.5.4.jar -------------------------------------------------------------------------------- /examples/js/addconnection.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 | 9 | midiBridge.init({ connectAllInputsToFirstOutput: false, 10 | ready: function(msg){ 11 | contentDiv.innerHTML += msg + "
"; 12 | 13 | var devices = midiBridge.getDevices(); 14 | for (var i = 0, max = devices.length; i < max; i++) { 15 | 16 | var device = devices[i]; 17 | var id = device.id; 18 | var name = device.name; 19 | var type = device.type; 20 | //console.log(id,type,name); 21 | console.log(device); 22 | } 23 | /* 24 | * create a connection between MIDI input 6 and MIDI output 7 25 | * adjust this according to your system! 26 | */ 27 | midiBridge.addConnection(6,7,[midiBridge.PITCH_BEND]); 28 | }, 29 | error: function(msg) { 30 | contentDiv.innerHTML += msg + "
"; 31 | }, 32 | data: function(midiEvent) { 33 | contentDiv.innerHTML += midiEvent + "
"; 34 | } 35 | }); 36 | 37 | 38 | }, false); 39 | -------------------------------------------------------------------------------- /examples/js/audiolib.js: -------------------------------------------------------------------------------- 1 | window.addEventListener('load', function() { 2 | 3 | var dev,osc; 4 | var frequencies = {}; 5 | var noteNames = ["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"]; 6 | var pitch = 440; 7 | var content = document.getElementById("content"); 8 | 9 | const INIT_MIDINUMBER = 69; //Note A4 has midinumber 69 10 | const LOWEST_N = -69; //equals Midi NoteNumber 0 ~ 8 Hz 11 | const HIGHEST_N = 58; //equals Midi NoteNumber 127 ~ 12500 Hz 12 | const LOWEST_TEMP = 415.3; 13 | const HIGHEST_TEMP = 466.16; 14 | 15 | function shiftIndex(index,range,shift){ 16 | shift = (index + shift) >= range ? shift - range : shift; 17 | return (index + shift); 18 | } 19 | 20 | for(var i = LOWEST_N;i < HIGHEST_N; i++){ 21 | var frequency = Math.round((Math.pow(2,i/12) * pitch) * 100) / 100; 22 | 23 | var octave = Math.floor(((i-3)/12) + 5); //octaves are calculated from note C and A440 is in octave 4 and the lowest note is in octave -1 (midinumber 0) 24 | 25 | var noteIndex = i%12; 26 | noteIndex = noteIndex < 0 ? noteIndex + 12 : noteIndex; 27 | 28 | var shiftedIndex = shiftIndex(noteIndex,12,9); 29 | var noteName = noteNames[shiftedIndex] + "" + octave; 30 | 31 | //var noteCode = octave + "" + noteIndex; 32 | //var midiNumber = INIT_MIDINUMBER + i; 33 | 34 | //console.log(i,"=",noteName, noteCode, midiNumber,frequency); 35 | 36 | frequencies[noteName] = frequency; 37 | } 38 | 39 | 40 | dev = audioLib.AudioDevice(function(buffer, channelCount){ 41 | if(osc){ 42 | osc.append(buffer, channelCount); 43 | } 44 | }, 2); 45 | dev.sampleRate = 44100; 46 | 47 | 48 | midiBridge.init({ 49 | connectAllInputs : true, 50 | ready : function(msg) { 51 | content.innerHTML = "

midibridge loaded, play some notes!


(You should have a midi keyboard connected to your computer)"; 52 | }, 53 | error : function(msg) { 54 | console.log(msg); 55 | }, 56 | data : function(midiEvent) { 57 | var noteName = midiEvent.noteName; 58 | //console.log(noteName,frequencies[noteName]); 59 | if(midiEvent.status == midiBridge.NOTE_ON){ 60 | //var volume = midiEvent.data2/127; 61 | osc = audioLib.Oscillator(dev.sampleRate, frequencies[noteName]); 62 | }else{ 63 | osc = null; 64 | } 65 | } 66 | }); 67 | 68 | },false); -------------------------------------------------------------------------------- /examples/js/base64.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 | var position; 9 | var midiProgramSelector; 10 | 11 | var ua = navigator.userAgent.toLowerCase(); 12 | if(ua.indexOf("chrome") === -1){ 13 | contentDiv.style.marginTop = "20px"; 14 | contentDiv.style.lineHeight = "30px"; 15 | contentDiv.style.padding = "10px"; 16 | contentDiv.style.fontSize = "15px"; 17 | contentDiv.style.textAlign = "center"; 18 | contentDiv.style.backgroundColor = "#fff"; 19 | contentDiv.style.border = "#f00 dotted 3px"; 20 | contentDiv.innerHTML = "This sample uses the File API and the html5 Slider,
so you can only view this sample in Google Chrome." 21 | return; 22 | } 23 | 24 | var dropbox = document.createElement("div"); 25 | dropbox.id = "dropbox"; 26 | dropbox.innerHTML = "drop your midifile here"; 27 | contentDiv.appendChild(dropbox); 28 | 29 | var info = document.createElement("div"); 30 | info.id = "info"; 31 | info.innerHTML = "no midi file loaded"; 32 | contentDiv.appendChild(info); 33 | 34 | var controls = document.createElement("div"); 35 | controls.id = "controls"; 36 | controls.className = "clearfix"; 37 | contentDiv.appendChild(controls); 38 | 39 | var btnPlay = document.createElement("div"); 40 | btnPlay.innerHTML = "PLAY"; 41 | btnPlay.className = "button"; 42 | controls.appendChild(btnPlay); 43 | btnPlay.addEventListener("click", function(){ 44 | midiBridge.startSequencer(); 45 | }, false); 46 | 47 | var btnPause = document.createElement("div"); 48 | btnPause.innerHTML = "PAUSE"; 49 | btnPause.className = "button"; 50 | controls.appendChild(btnPause); 51 | btnPause.addEventListener("click", function(){ 52 | midiBridge.pauseSequencer(); 53 | }, false); 54 | 55 | var btnStop = document.createElement("div"); 56 | btnStop.innerHTML = "STOP"; 57 | btnStop.className = "button"; 58 | controls.appendChild(btnStop); 59 | btnStop.addEventListener("click", function(){ 60 | midiBridge.stopSequencer(); 61 | output.innerHTML = ""; 62 | }, false); 63 | 64 | midiProgramSelector = abumarkub.ui.createMIDIProgramSelector(controls,function(programId){ 65 | midiBridge.sendMidiEvent(midiBridge.PROGRAM_CHANGE, 0, programId, 0) 66 | }); 67 | 68 | var output = document.createElement("div"); 69 | output.id = "console"; 70 | output.innerHTML = ""; 71 | contentDiv.appendChild(output); 72 | 73 | var slider = document.createElement("input"); 74 | slider.setAttribute("type", "range"); 75 | slider.setAttribute("min", "0"); 76 | slider.setAttribute("step", "1"); 77 | slider.setAttribute("value", "0"); 78 | slider.style.width = "500px"; 79 | contentDiv.appendChild(slider); 80 | slider.addEventListener("change",function(e){ 81 | //console.log(e.target.value * 1000); 82 | midiBridge.setSequencerPosition((e.target.value * 1000).toString())//value in microseconds as String! 83 | },false); 84 | 85 | dropbox.addEventListener("dragenter", function(e) { 86 | e.stopPropagation(); 87 | e.preventDefault(); 88 | }, false); 89 | 90 | dropbox.addEventListener("dragover", function(e) { 91 | e.stopPropagation(); 92 | e.preventDefault(); 93 | }, false); 94 | 95 | dropbox.addEventListener("drop", function(e) { 96 | e.stopPropagation(); 97 | e.preventDefault(); 98 | 99 | var dt = e.dataTransfer; 100 | var files = dt.files; 101 | var file = files[files.length-1]; 102 | info.innerHTML = "loading: " + file.name; 103 | output.innerHTML = ""; 104 | midiProgramSelector.selectOption(0); 105 | 106 | var reader = new FileReader(); 107 | reader.onload = function(e) { 108 | //console.log(e.target.result); 109 | var data = midiBridge.loadBase64String(e.target.result.replace("data:audio/mid;base64,","")); 110 | slider.setAttribute("value", "0"); 111 | slider.setAttribute("max", (data.microseconds/1000) >> 0); 112 | info.innerHTML = "file: " + file.name + " "; 113 | info.innerHTML += "length:" + midiBridge.getNiceTime(data.microseconds) + " "; 114 | info.innerHTML += "ticks:" + data.ticks + " "; 115 | info.innerHTML += "position:0:00:000"; 116 | position = info.querySelector("#position"); 117 | }; 118 | reader.readAsDataURL(file); 119 | }, false); 120 | 121 | 122 | midiBridge.init({ 123 | connectAllInputsToFirstOutput : false, 124 | connectFirstOutput : true, 125 | 126 | ready : function(msg) { 127 | }, 128 | 129 | error : function(msg) { 130 | contentDiv.innerHTML += msg + "
"; 131 | }, 132 | 133 | data : function(midiEvent) { 134 | output.innerHTML = midiEvent + "
"; 135 | output.scrollTop = output.scrollHeight; 136 | slider.setAttribute("value", (midiEvent.microsecond/1000) >> 0); 137 | position.innerHTML = midiEvent.time; 138 | //console.log(midiEvent,midiEvent.microsecond); 139 | } 140 | }); 141 | 142 | }, false); 143 | 144 | 145 | -------------------------------------------------------------------------------- /examples/js/configobject.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.innerHTML += "content:
"; 9 | 10 | midiBridge.init({ 11 | 12 | connectAllInputs: true, 13 | 14 | ready: function(msg){ 15 | contentDiv.innerHTML += msg + "
"; 16 | }, 17 | error: function(msg) { 18 | contentDiv.innerHTML += msg + "
"; 19 | }, 20 | data: function(midiEvent) { 21 | contentDiv.innerHTML += midiEvent + "
"; 22 | } 23 | }); 24 | 25 | }, false); 26 | -------------------------------------------------------------------------------- /examples/js/flash.js: -------------------------------------------------------------------------------- 1 | /** 2 | * You can only call global functions from Flash, therefor we declare a single global function that is used for all 3 | * communication with the Flashplayer. 4 | * 5 | */ 6 | function callFromFlash() { 7 | 8 | /** 9 | * getObject is utility function of midiBridge, it is used to get the Applet object, 10 | * but it can also be used to get the swf object 11 | * 12 | * flashapp is the id of the swf object in the html page. 13 | */ 14 | var flashObject = midiBridge.getObject("flashapp"); 15 | 16 | /** 17 | * convert the arguments to a array, the first argument is the msgId. 18 | * the msgId is used to determine what the Flashplayer wants from the midibridge 19 | */ 20 | var args = Array.prototype.slice.call(arguments); 21 | var msgId = args[0]; 22 | 23 | //console.log(args); 24 | 25 | switch(msgId) { 26 | 27 | case "start": 28 | midiBridge.init({ 29 | connectAllInputsToFirstOutput : true, 30 | 31 | ready : function(msg) { 32 | flashObject.midibridgeReady(msg); 33 | }, 34 | 35 | error : function(msg) { 36 | flashObject.midibridgeError(msg); 37 | }, 38 | 39 | data : function(midiEvent) { 40 | // flashObject.midibridgeData(midiEvent.toJSONString()); 41 | flashObject.midibridgeData(midiEvent.toString()); 42 | } 43 | 44 | }); 45 | return null; 46 | break; 47 | 48 | case "getDevices": 49 | return midiBridge.getDevices(); 50 | break; 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /examples/js/getdevices.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 | 9 | midiBridge.init({ 10 | 11 | connectAllInputsToFirstOutput: false, 12 | 13 | ready: function(msg){ 14 | //contentDiv.innerHTML += msg + "
"; 15 | 16 | var devices = midiBridge.getDevices(); 17 | for(var i = 0, max = devices.length; i < max; i++) { 18 | 19 | var device = devices[i]; 20 | var id = device.id; 21 | var type = device.type; 22 | var name = device.name; 23 | var descr = device.descr; 24 | var available = device.available; 25 | contentDiv.innerHTML += id + " : " + type+ " : " + name+ " : " + descr+ " : " + available + "
"; 26 | } 27 | 28 | }, 29 | error: function(msg) { 30 | contentDiv.innerHTML += msg + "
"; 31 | }, 32 | data: function(midiEvent) { 33 | contentDiv.innerHTML += midiEvent + "
"; 34 | } 35 | }); 36 | 37 | 38 | }, false); 39 | -------------------------------------------------------------------------------- /examples/js/patchpanel.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 | var currentMidiInputId = -1; 9 | var currentMidiOutputId = -1; 10 | 11 | //create a dropdown box for the midi inputs 12 | var midiInputs = document.createElement("select"); 13 | midiInputs.setAttribute("id", "midi-in"); 14 | contentDiv.appendChild(midiInputs); 15 | midiInputs.addEventListener("change", function(e) { 16 | clearCurrentConnection(); 17 | var device = midiInputs.options[midiInputs.selectedIndex]; 18 | currentMidiInputId = device.id; 19 | //console.log(currentMidiInputId, currentMidiOutputId); 20 | var result = midiBridge.addConnection(currentMidiInputId, currentMidiOutputId, [midiBridge.PITCH_BEND]); 21 | parseResult(result); 22 | }, false); 23 | 24 | //create a dropdown box for the midi outputs 25 | var midiOutputs = document.createElement("select"); 26 | midiOutputs.setAttribute("id", "midi-out"); 27 | contentDiv.appendChild(midiOutputs); 28 | midiOutputs.addEventListener("change", function(e) { 29 | clearCurrentConnection(); 30 | var device = midiOutputs.options[midiOutputs.selectedIndex]; 31 | currentMidiOutputId = device.id; 32 | //console.log(currentMidiInputId, currentMidiOutputId); 33 | var result = midiBridge.addConnection(currentMidiInputId, currentMidiOutputId, [midiBridge.PITCH_BEND]); 34 | parseResult(result); 35 | }, false); 36 | 37 | var clearCurrentConnection = function(){ 38 | 39 | if(currentMidiInputId === -1 && currentMidiOutputId === -1){ 40 | return; 41 | } 42 | midiBridge.removeConnection(currentMidiInputId,currentMidiOutputId); 43 | } 44 | 45 | var midiDataOutput = document.createElement("div"); 46 | midiDataOutput.style['clear'] = "left"; 47 | midiDataOutput.style['float'] = "left"; 48 | //midiDataOutput.style['height'] = "400px"; 49 | //midiDataOutput.style['overflow'] = "auto"; 50 | contentDiv.appendChild(midiDataOutput); 51 | 52 | var devices; 53 | 54 | midiBridge.init({ 55 | connectAllInputsToFirstOutput : false, 56 | //debug : true, 57 | //midiCommands : [midiBridge.NOTE_ON,midiBridge.NOTE_OFF], 58 | 59 | ready : function(msg) { 60 | devices = midiBridge.getDevices(); 61 | populateDropDownMenus() 62 | }, 63 | 64 | error : function(msg) { 65 | midiDataOutput.innerHTML += msg + "
"; 66 | }, 67 | 68 | data : function(midiEvent) { 69 | midiDataOutput.innerHTML += midiEvent + "
"; 70 | } 71 | 72 | }); 73 | 74 | var populateDropDownMenus = function() { 75 | 76 | midiInputs.appendChild(createOption("-1", "choose a midi input")); 77 | midiOutputs.appendChild(createOption("-1", "choose a midi output")); 78 | 79 | for(var deviceId in devices) { 80 | var device = devices[deviceId]; 81 | if(device.type === "input" && device.available === "true") { 82 | midiInputs.appendChild(createOption(device.id, device.name)) 83 | } else if(device.type === "output" && device.available === "true") { 84 | midiOutputs.appendChild(createOption(device.id, device.name)) 85 | } 86 | } 87 | } 88 | 89 | var createOption = function(id, label) { 90 | var option = document.createElement("option"); 91 | option.setAttribute("id", id); 92 | option.innerHTML = label; 93 | return option; 94 | } 95 | 96 | var parseResult = function(data) { 97 | //contentDiv.innerHTML += data.msg + " : " + currentMidiInputId + " : " + currentMidiOutputId + "
"; 98 | } 99 | 100 | }, false); 101 | -------------------------------------------------------------------------------- /examples/js/playmidifile.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 | 6 | window.addEventListener('load', function() { 7 | 8 | //"use strict"; 9 | 10 | var contentDiv = document.getElementById("content"), 11 | uploadUrl = "php/midiToBase64.php", 12 | fileName = "Chopin Opus18", 13 | chooseFile, fileDuration, controls, sliderDiv, thumbDiv, labelDiv, btnPlay, btnPause, btnStop, slider, output, info, position; 14 | 15 | chooseFile = document.createElement("input"); 16 | chooseFile.setAttribute("type", "file"); 17 | chooseFile.setAttribute("onchange", "alert(this.value)"); 18 | contentDiv.appendChild(chooseFile); 19 | 20 | controls = document.createElement("div"); 21 | controls.id = "controls"; 22 | controls.className = "clearfix"; 23 | contentDiv.appendChild(controls); 24 | 25 | btnPlay = document.createElement("div"); 26 | btnPlay.innerHTML = "PLAY"; 27 | btnPlay.className = "button"; 28 | controls.appendChild(btnPlay); 29 | btnPlay.addEventListener("click", function() { 30 | midiBridge.startSequencer(); 31 | }, false); 32 | 33 | btnPause = document.createElement("div"); 34 | btnPause.innerHTML = "PAUSE"; 35 | btnPause.className = "button"; 36 | controls.appendChild(btnPause); 37 | btnPause.addEventListener("click", function() { 38 | midiBridge.pauseSequencer(); 39 | }, false); 40 | 41 | btnStop = document.createElement("div"); 42 | btnStop.innerHTML = "STOP"; 43 | btnStop.className = "button"; 44 | controls.appendChild(btnStop); 45 | btnStop.addEventListener("click", function() { 46 | midiBridge.stopSequencer(); 47 | slider.setPercentage(0); 48 | info.getElementsByTagName("span")[7].innerHTML = "0:00:000"; 49 | output.innerHTML = ""; 50 | }, false); 51 | 52 | output = document.createElement("div"); 53 | output.id = "console"; 54 | output.innerHTML = ""; 55 | contentDiv.appendChild(output); 56 | 57 | info = document.createElement("div"); 58 | info.id = "info"; 59 | info.innerHTML = "no midi file loaded"; 60 | contentDiv.appendChild(info); 61 | 62 | 63 | sliderDiv = document.createElement("div"); 64 | sliderDiv.setAttribute("id", "position"); 65 | sliderDiv.className = "slider"; 66 | contentDiv.appendChild(sliderDiv); 67 | 68 | thumbDiv = document.createElement("div"); 69 | thumbDiv.className = "thumb"; 70 | sliderDiv.appendChild(thumbDiv); 71 | 72 | labelDiv = document.createElement("div"); 73 | labelDiv.className = "label"; 74 | thumbDiv.appendChild(labelDiv); 75 | 76 | slider = new abumarkub.ui.Slider("position", 500, 8, 0, 100, 0); 77 | slider.addEventListener("changed", function(value) { 78 | midiBridge.setSequencerPosition((value * 1000).toString()) //value in microseconds as String! 79 | //console.log(value); 80 | }); 81 | 82 | slider.addEventListener("startDrag", function(value) { 83 | midiBridge.pauseSequencer(); 84 | }); 85 | 86 | slider.addEventListener("stopDrag", function(value) { 87 | midiBridge.startSequencer() 88 | }); 89 | 90 | abumarkub.ui.createMIDIProgramSelector(controls,function(programId){ 91 | midiBridge.sendMidiEvent(midiBridge.PROGRAM_CHANGE, 0, programId, 0) 92 | }); 93 | 94 | /* 95 | slider = document.createElement("input"); 96 | slider.setAttribute("type", "range"); 97 | slider.setAttribute("min", "0"); 98 | slider.setAttribute("step", "1"); 99 | slider.setAttribute("value", "0"); 100 | slider.style.width = "500px"; 101 | contentDiv.appendChild(slider); 102 | slider.addEventListener("change",function(e){ 103 | //console.log(e.target.value * 1000); 104 | midiBridge.setSequencerPosition((e.target.value * 1000).toString())//value in microseconds as String! 105 | },false); 106 | */ 107 | 108 | function loadMIDIFile(base64data) { 109 | var data = midiBridge.loadBase64String(base64data); 110 | //console.log(data); 111 | //slider.setAttribute("value", "0"); 112 | //slider.setAttribute("max", (data.microseconds/1000) >> 0); 113 | info.innerHTML = "file: " + fileName + " "; 114 | info.innerHTML += "length:" + midiBridge.getNiceTime(data.microseconds) + " "; 115 | info.innerHTML += "ticks:" + data.ticks + " "; 116 | info.innerHTML += "position:0:00:000"; 117 | 118 | fileDuration = data.microseconds/1000; 119 | slider.setRange(0, fileDuration); 120 | 121 | position = info.querySelector("#position"); 122 | } 123 | 124 | chooseFile.onchange = function(e) { 125 | 126 | e.preventDefault(); 127 | var file; 128 | if (chooseFile.files) { 129 | file = chooseFile.files[0]; 130 | } else { 131 | //console.log(chooseFile.value); 132 | file = chooseFile.value; 133 | } 134 | 135 | fileName = file.name; 136 | 137 | if (fileName === undefined) { 138 | info.style.color = "#f00"; 139 | info.innerHTML = "uploading files not supported natively; use a library like jQuery"; 140 | return; 141 | } 142 | 143 | info.innerHTML = "loading: " + fileName; 144 | output.innerHTML = ""; 145 | 146 | 147 | if (typeof window.FileReader === 'undefined') { 148 | //console.log("via server"); 149 | var request = new XMLHttpRequest(); 150 | request.addEventListener("readystatechange", function(e) { 151 | if (request.readyState == 4 && request.status === 200) { 152 | //console.log(e); 153 | loadMIDIFile(e.target.response); 154 | } 155 | }); 156 | 157 | request.open('POST', uploadUrl, true); 158 | request.setRequestHeader("Cache-Control", "no-cache"); 159 | request.setRequestHeader("X-Requested-With", "XMLHttpRequest"); 160 | request.setRequestHeader("X-File-Name", fileName); 161 | request.send(file); 162 | 163 | 164 | } else { 165 | var reader = new FileReader(); 166 | 167 | reader.onerror = function(error) { 168 | //console.log("error", error) 169 | } 170 | 171 | reader.onload = function(e) { 172 | loadMIDIFile(e.target.result); 173 | }; 174 | 175 | reader.readAsDataURL(file); 176 | } 177 | }; 178 | 179 | midiBridge.init({ 180 | connectAllInputsToFirstOutput: false, 181 | connectFirstOutput: true, 182 | //debug:true, 183 | //midiCommands:[midiBridge.NOTE_OFF,midiBridge.NOTE_ON], 184 | 185 | ready: function(msg) { 186 | loadMIDIFile(chopin_opus18); 187 | }, 188 | 189 | error: function(msg) { 190 | contentDiv.innerHTML += msg + "
"; 191 | }, 192 | 193 | data: function(midiEvent) { 194 | output.innerHTML = midiEvent + "
"; 195 | output.scrollTop = output.scrollHeight; 196 | //slider.setAttribute("value", (midiEvent.microsecond/1000) >> 0); 197 | slider.setPercentage(((midiEvent.microsecond / 1000) >> 0) / (fileDuration), false); 198 | position.innerHTML = midiEvent.time; 199 | //console.log(midiEvent.channel); 200 | } 201 | }); 202 | }, false); 203 | -------------------------------------------------------------------------------- /examples/js/playnote.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 | 9 | midiBridge.init(function(midiEvent) { 10 | contentDiv.innerHTML += midiEvent + "
"; 11 | }); 12 | 13 | contentDiv.innerHTML += "
play a note
"; 14 | var playNote = document.getElementById("playAnote"); 15 | playNote.style.textDecoration = "underline"; 16 | 17 | playNote.addEventListener("click",function(){ 18 | midiBridge.sendMidiEvent(midiBridge.NOTE_ON,1,84,100); 19 | },false); 20 | 21 | }, false); 22 | -------------------------------------------------------------------------------- /examples/js/regularkeyboard.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 | midiBridge.init({ 8 | 9 | connectFirstOutput : true, 10 | 11 | ready : function(msg) { 12 | connectKeyboard(); 13 | }, 14 | error : function(msg) { 15 | console.log(msg); 16 | } 17 | }); 18 | 19 | var noteNumbers = { 20 | //white keys 21 | 65 : 48, //key a -> note c 22 | 83 : 50, //key s -> note d 23 | 68 : 52, //key d -> note e 24 | 70 : 53, //key f -> note f 25 | 71 : 55, //key g -> note g 26 | 72 : 57, //key h -> note a 27 | 74 : 59, //key j -> note b 28 | 75 : 60, //key k -> note c 29 | 76 : 62, //key l -> note d 30 | 186 : 64, //key ; -> note e 31 | 222 : 65, //key : -> note f 32 | //black keys 33 | 87 : 49, //key w -> note c#/d♭ 34 | 69 : 51, //key e -> note d#/e♭ 35 | 84 : 54, //key t -> note f#/g♭ 36 | 89 : 56, //key y -> note g#/a♭ 37 | 85 : 58, //key u -> note a#/b♭ 38 | 79 : 61, //key o -> note c#/d♭ 39 | 80 : 63 //key p -> note d#/e♭ 40 | } 41 | 42 | var keysPressed = {}; 43 | 44 | var connectKeyboard = function(){ 45 | document.addEventListener("keydown", function(e) { 46 | //console.log(e, e.which, e.which.toString(), noteNumbers[e.which.toString()]); 47 | if(e.which === 32) { 48 | midiBridge.sendMidiEvent(midiBridge.CONTROL_CHANGE, 1, 64, 127); 49 | } else if(noteNumbers[e.which] && !keysPressed[e.which]) { 50 | midiBridge.sendMidiEvent(midiBridge.NOTE_ON, 1, noteNumbers[e.which], 100); 51 | keysPressed[e.which] = true; 52 | } 53 | }, false); 54 | 55 | 56 | document.addEventListener("keyup", function(e) { 57 | if(e.which === 32) { 58 | midiBridge.sendMidiEvent(midiBridge.CONTROL_CHANGE, 1, 64, 0); 59 | } else if(noteNumbers[e.which]) { 60 | midiBridge.sendMidiEvent(midiBridge.NOTE_OFF, 1, noteNumbers[e.which], 0); 61 | keysPressed[e.which] = false; 62 | } 63 | }, false); 64 | 65 | document.getElementById("content").innerHTML = "Play some keys on your regular computer keyboard!"; 66 | } 67 | 68 | }, false); 69 | -------------------------------------------------------------------------------- /examples/js/soundfont64.js: -------------------------------------------------------------------------------- 1 | window.addEventListener('load', function() { 2 | 3 | 4 | var notesOn = {}; 5 | var content = document.getElementById("content"); 6 | 7 | for(var noteName in SoundFont){ 8 | /* 9 | var note = document.createElement("audio"); 10 | note.setAttribute("preload","auto"); 11 | note.setAttribute("src",SoundFont[noteName]); 12 | notesOn[noteName] = note; 13 | content.appendChild(note); 14 | */ 15 | var note = new Audio(SoundFont[noteName]); 16 | notesOn[noteName] = note; 17 | } 18 | 19 | 20 | var playNote = function(midiEvent){ 21 | //console.log(midiEvent); 22 | var noteName = midiEvent.noteName; 23 | var note = notesOn[noteName]; 24 | //console.log(noteName); 25 | if(midiEvent.status == midiBridge.NOTE_ON){ 26 | if(note){ 27 | //note.currentTime = 0; 28 | //console.log(note.currentTime); 29 | note.volume = midiEvent.data2/127; 30 | console.log(note.volume); 31 | note.play(); 32 | } 33 | }else{ 34 | if(note){ 35 | note.pause(); 36 | note.currentTime = 0; 37 | } 38 | } 39 | } 40 | 41 | 42 | var noteNumbers = { 43 | //white keys 44 | 65 : 48, //key a -> note c 45 | 83 : 50, //key s -> note d 46 | 68 : 52, //key d -> note e 47 | 70 : 53, //key f -> note f 48 | 71 : 55, //key g -> note g 49 | 72 : 57, //key h -> note a 50 | 74 : 59, //key j -> note b 51 | 75 : 60, //key k -> note c 52 | 76 : 62, //key l -> note d 53 | 186 : 64, //key ; -> note e 54 | 222 : 65, //key : -> note f 55 | //black keys 56 | 87 : 49, //key w -> note c#/d♭ 57 | 69 : 51, //key e -> note d#/e♭ 58 | 84 : 54, //key t -> note f#/g♭ 59 | 89 : 56, //key y -> note g#/a♭ 60 | 85 : 58, //key u -> note a#/b♭ 61 | 79 : 61, //key o -> note c#/d♭ 62 | 80 : 63 //key p -> note d#/e♭ 63 | } 64 | 65 | var keysPressed = {}; 66 | 67 | /* 68 | * should be integrated into the midibridge 69 | */ 70 | var connectKeyboard = function(){ 71 | document.addEventListener("keydown", function(e) { 72 | //console.log(e, e.which, e.which.toString(), noteNumbers[e.which.toString()]); 73 | if(e.which === 32) { 74 | midiBridge.sendMidiEvent(midiBridge.CONTROL_CHANGE, 1, 64, 127); 75 | } else if(noteNumbers[e.which] && !keysPressed[e.which]) { 76 | //midiBridge.sendMidiEvent(midiBridge.NOTE_ON, 1, noteNumbers[e.which], 100); 77 | playNote(new midiBridge.MidiMessage(midiBridge.NOTE_ON, 1, noteNumbers[e.which], 100)); 78 | keysPressed[e.which] = true; 79 | } 80 | }, false); 81 | 82 | 83 | document.addEventListener("keyup", function(e) { 84 | if(e.which === 32) { 85 | midiBridge.sendMidiEvent(midiBridge.CONTROL_CHANGE, 1, 64, 0); 86 | } else if(noteNumbers[e.which]) { 87 | //midiBridge.sendMidiEvent(midiBridge.NOTE_OFF, 1, noteNumbers[e.which], 0); 88 | playNote(new midiBridge.MidiMessage(midiBridge.NOTE_OFF, 1, noteNumbers[e.which], 0)); 89 | keysPressed[e.which] = false; 90 | } 91 | }, false); 92 | } 93 | 94 | midiBridge.noteNameModus = midiBridge.NOTE_NAMES_SOUNDFONT; 95 | 96 | midiBridge.init({ 97 | connectAllInputs : true, 98 | ready : function(){ 99 | connectKeyboard(); 100 | content.innerHTML = "

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 += ''; 448 | html += ''; 449 | html += ''; 450 | html += ''; 451 | html += ''; 452 | html += ''; 453 | html += 'Your browser needs the Java plugin to use the midibridge. You can download it here'; 454 | html += ''; 455 | }else{ 456 | html += ''; 457 | html += ''; 458 | 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 | 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 | 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 | 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 | 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 | Pianoteq LoopBe Abumarkub midibridge 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 | IAC-Driver-Abumarkub-midibridge 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 | Lounge Lizard IAC input Abumarkub midibridge 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 | VirMidi Kubuntu Pianoteq midibridge abumarkub 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 | 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+='Your browser needs the Java plugin to use the midibridge. You can download it here'):(a+='', 8 | 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(" 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /examples/php/midiToBase64.php: -------------------------------------------------------------------------------- 1 | 16 | -------------------------------------------------------------------------------- /examples/php/tmp.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abudaan/midibridge-js/1a75ca1784575716acae25321a44ffef27cd29a3/examples/php/tmp.mid -------------------------------------------------------------------------------- /examples/playmidifile.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | midi bridge 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /examples/playnote.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | midi bridge 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /examples/regularkeyboard.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | midi bridge 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /examples/soundfont.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | midi bridge 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /examples/swf/expressinstall.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abudaan/midibridge-js/1a75ca1784575716acae25321a44ffef27cd29a3/examples/swf/expressinstall.swf -------------------------------------------------------------------------------- /examples/swf/main.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abudaan/midibridge-js/1a75ca1784575716acae25321a44ffef27cd29a3/examples/swf/main.swf -------------------------------------------------------------------------------- /examples/timedmidievents.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | midi bridge 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /examples/timesmidievents.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | midi bridge 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | midi bridge basic example 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /java/midiapplet-0.5.4.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abudaan/midibridge-js/1a75ca1784575716acae25321a44ffef27cd29a3/java/midiapplet-0.5.4.jar -------------------------------------------------------------------------------- /js/helloworld.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 | 6 | //a very basic example that shows how to embed an use the midibridge 7 | window.addEventListener('load', function() { 8 | 9 | var contentDiv = document.getElementById("content"); 10 | 11 | midiBridge.init({ 12 | ready: function(){ 13 | contentDiv.innerHTML += "midi bridge loaded
"; 14 | },data: function(midiEvent){ 15 | contentDiv.innerHTML += midiEvent + "
"; 16 | } 17 | }); 18 | 19 | }, false); 20 | -------------------------------------------------------------------------------- /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 += ''; 448 | html += ''; 449 | html += ''; 450 | html += ''; 451 | html += ''; 452 | html += ''; 453 | html += 'Your browser needs the Java plugin to use the midibridge. You can download it here'; 454 | html += ''; 455 | }else{ 456 | html += ''; 457 | html += ''; 458 | 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 | -------------------------------------------------------------------------------- /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+='Your browser needs the Java plugin to use the midibridge. You can download it here'):(a+='', 8 | 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); -------------------------------------------------------------------------------- /tests/img/balloon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abudaan/midibridge-js/1a75ca1784575716acae25321a44ffef27cd29a3/tests/img/balloon.png -------------------------------------------------------------------------------- /tests/js/play-midifile.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 | 6 | window.addEventListener('load', function() { 7 | 8 | //"use strict"; 9 | 10 | var contentDiv = document.getElementById("content"), 11 | uploadUrl = "php/midiToBase64.php", 12 | fileName = "Chopin Opus18", 13 | chooseFile, fileDuration, controls, sliderDiv, thumbDiv, labelDiv, btnPlay, btnPause, btnStop, slider, output, info, position; 14 | 15 | chooseFile = document.createElement("input"); 16 | chooseFile.setAttribute("type", "file"); 17 | chooseFile.setAttribute("onchange", "alert(this.value)"); 18 | contentDiv.appendChild(chooseFile); 19 | 20 | controls = document.createElement("div"); 21 | controls.id = "controls"; 22 | controls.className = "clearfix"; 23 | contentDiv.appendChild(controls); 24 | 25 | btnPlay = document.createElement("div"); 26 | btnPlay.innerHTML = "PLAY"; 27 | btnPlay.className = "button"; 28 | controls.appendChild(btnPlay); 29 | btnPlay.addEventListener("click", function() { 30 | midiBridge.startSequencer(); 31 | }, false); 32 | 33 | btnPause = document.createElement("div"); 34 | btnPause.innerHTML = "PAUSE"; 35 | btnPause.className = "button"; 36 | controls.appendChild(btnPause); 37 | btnPause.addEventListener("click", function() { 38 | midiBridge.pauseSequencer(); 39 | }, false); 40 | 41 | btnStop = document.createElement("div"); 42 | btnStop.innerHTML = "STOP"; 43 | btnStop.className = "button"; 44 | controls.appendChild(btnStop); 45 | btnStop.addEventListener("click", function() { 46 | midiBridge.stopSequencer(); 47 | slider.setPercentage(0); 48 | info.getElementsByTagName("span")[7].innerHTML = "0:00:000"; 49 | output.innerHTML = ""; 50 | }, false); 51 | 52 | output = document.createElement("div"); 53 | output.id = "console"; 54 | output.innerHTML = ""; 55 | contentDiv.appendChild(output); 56 | 57 | info = document.createElement("div"); 58 | info.id = "info"; 59 | info.innerHTML = "no midi file loaded"; 60 | contentDiv.appendChild(info); 61 | 62 | 63 | sliderDiv = document.createElement("div"); 64 | sliderDiv.setAttribute("id", "position"); 65 | sliderDiv.className = "slider"; 66 | contentDiv.appendChild(sliderDiv); 67 | 68 | thumbDiv = document.createElement("div"); 69 | thumbDiv.className = "thumb"; 70 | sliderDiv.appendChild(thumbDiv); 71 | 72 | labelDiv = document.createElement("div"); 73 | labelDiv.className = "label"; 74 | thumbDiv.appendChild(labelDiv); 75 | 76 | slider = new abumarkub.ui.Slider("position", 500, 8, 0, 100, 0); 77 | slider.addEventListener("changed", function(value) { 78 | midiBridge.setSequencerPosition((value * 1000).toString()) //value in microseconds as String! 79 | //console.log(value); 80 | }); 81 | 82 | slider.addEventListener("startDrag", function(value) { 83 | midiBridge.pauseSequencer(); 84 | }); 85 | 86 | slider.addEventListener("stopDrag", function(value) { 87 | midiBridge.startSequencer() 88 | }); 89 | 90 | abumarkub.ui.createMIDIProgramSelector(controls,function(programId){ 91 | midiBridge.sendMidiEvent(midiBridge.PROGRAM_CHANGE, 0, programId, 0) 92 | }); 93 | 94 | /* 95 | slider = document.createElement("input"); 96 | slider.setAttribute("type", "range"); 97 | slider.setAttribute("min", "0"); 98 | slider.setAttribute("step", "1"); 99 | slider.setAttribute("value", "0"); 100 | slider.style.width = "500px"; 101 | contentDiv.appendChild(slider); 102 | slider.addEventListener("change",function(e){ 103 | //console.log(e.target.value * 1000); 104 | midiBridge.setSequencerPosition((e.target.value * 1000).toString())//value in microseconds as String! 105 | },false); 106 | */ 107 | 108 | function loadMIDIFile(base64data) { 109 | var data = midiBridge.loadBase64String(base64data); 110 | //console.log(data); 111 | //slider.setAttribute("value", "0"); 112 | //slider.setAttribute("max", (data.microseconds/1000) >> 0); 113 | info.innerHTML = "file: " + fileName + " "; 114 | info.innerHTML += "length:" + midiBridge.getNiceTime(data.microseconds) + " "; 115 | info.innerHTML += "ticks:" + data.ticks + " "; 116 | info.innerHTML += "position:0:00:000"; 117 | 118 | fileDuration = data.microseconds/1000; 119 | slider.setRange(0, fileDuration); 120 | 121 | position = info.querySelector("#position"); 122 | } 123 | 124 | chooseFile.onchange = function(e) { 125 | 126 | e.preventDefault(); 127 | var file; 128 | if (chooseFile.files) { 129 | file = chooseFile.files[0]; 130 | } else { 131 | //console.log(chooseFile.value); 132 | file = chooseFile.value; 133 | } 134 | 135 | fileName = file.name; 136 | 137 | if (fileName === undefined) { 138 | info.style.color = "#f00"; 139 | info.innerHTML = "uploading files not supported natively; use a library like jQuery"; 140 | return; 141 | } 142 | 143 | info.innerHTML = "loading: " + fileName; 144 | output.innerHTML = ""; 145 | 146 | 147 | if (typeof window.FileReader === 'undefined') { 148 | //console.log("via server"); 149 | var request = new XMLHttpRequest(); 150 | request.addEventListener("readystatechange", function(e) { 151 | if (request.readyState == 4 && request.status === 200) { 152 | //console.log(e); 153 | loadMIDIFile(e.target.response); 154 | } 155 | }); 156 | 157 | request.open('POST', uploadUrl, true); 158 | request.setRequestHeader("Cache-Control", "no-cache"); 159 | request.setRequestHeader("X-Requested-With", "XMLHttpRequest"); 160 | request.setRequestHeader("X-File-Name", fileName); 161 | request.send(file); 162 | 163 | 164 | } else { 165 | var reader = new FileReader(); 166 | 167 | reader.onerror = function(error) { 168 | //console.log("error", error) 169 | } 170 | 171 | reader.onload = function(e) { 172 | loadMIDIFile(e.target.result); 173 | }; 174 | 175 | reader.readAsDataURL(file); 176 | } 177 | }; 178 | 179 | midiBridge.init({ 180 | connectAllInputsToFirstOutput: false, 181 | connectFirstOutput: true, 182 | //debug:true, 183 | //midiCommands:[midiBridge.NOTE_OFF,midiBridge.NOTE_ON], 184 | 185 | ready: function(msg) { 186 | loadMIDIFile(chopin_opus18); 187 | }, 188 | 189 | error: function(msg) { 190 | contentDiv.innerHTML += msg + "
"; 191 | }, 192 | 193 | data: function(midiEvent) { 194 | output.innerHTML = midiEvent + "
"; 195 | output.scrollTop = output.scrollHeight; 196 | //slider.setAttribute("value", (midiEvent.microsecond/1000) >> 0); 197 | slider.setPercentage(((midiEvent.microsecond / 1000) >> 0) / (fileDuration), false); 198 | position.innerHTML = midiEvent.time; 199 | //console.log(midiEvent.channel); 200 | } 201 | }); 202 | }, false); 203 | -------------------------------------------------------------------------------- /tests/js/setup-connections.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 | var currentMidiInputId = -1; 9 | var currentMidiOutputId = -1; 10 | 11 | //create a dropdown box for the midi inputs 12 | var midiInputs = document.createElement("select"); 13 | midiInputs.setAttribute("id", "midi-in"); 14 | contentDiv.appendChild(midiInputs); 15 | midiInputs.addEventListener("change", function(e) { 16 | clearCurrentConnection(); 17 | var device = midiInputs.options[midiInputs.selectedIndex]; 18 | currentMidiInputId = device.id; 19 | //console.log(currentMidiInputId, currentMidiOutputId); 20 | var result = midiBridge.addConnection(currentMidiInputId, currentMidiOutputId, [midiBridge.PITCH_BEND]); 21 | parseResult(result); 22 | }, false); 23 | 24 | //create a dropdown box for the midi outputs 25 | var midiOutputs = document.createElement("select"); 26 | midiOutputs.setAttribute("id", "midi-out"); 27 | contentDiv.appendChild(midiOutputs); 28 | midiOutputs.addEventListener("change", function(e) { 29 | clearCurrentConnection(); 30 | var device = midiOutputs.options[midiOutputs.selectedIndex]; 31 | currentMidiOutputId = device.id; 32 | //console.log(currentMidiInputId, currentMidiOutputId); 33 | var result = midiBridge.addConnection(currentMidiInputId, currentMidiOutputId, [midiBridge.PITCH_BEND]); 34 | parseResult(result); 35 | }, false); 36 | 37 | var clearCurrentConnection = function(){ 38 | 39 | if(currentMidiInputId === -1 && currentMidiOutputId === -1){ 40 | return; 41 | } 42 | midiBridge.removeConnection(currentMidiInputId,currentMidiOutputId); 43 | } 44 | 45 | var midiDataOutput = document.createElement("div"); 46 | midiDataOutput.style['clear'] = "left"; 47 | midiDataOutput.style['float'] = "left"; 48 | //midiDataOutput.style['height'] = "400px"; 49 | //midiDataOutput.style['overflow'] = "auto"; 50 | contentDiv.appendChild(midiDataOutput); 51 | 52 | var devices; 53 | 54 | midiBridge.init({ 55 | connectAllInputsToFirstOutput : false, 56 | //debug : true, 57 | //midiCommands : [midiBridge.NOTE_ON,midiBridge.NOTE_OFF], 58 | 59 | ready : function(msg) { 60 | devices = midiBridge.getDevices(); 61 | populateDropDownMenus() 62 | }, 63 | 64 | error : function(msg) { 65 | midiDataOutput.innerHTML += msg + "
"; 66 | }, 67 | 68 | data : function(midiEvent) { 69 | midiDataOutput.innerHTML += midiEvent + "
"; 70 | } 71 | 72 | }); 73 | 74 | var populateDropDownMenus = function() { 75 | 76 | midiInputs.appendChild(createOption("-1", "choose a midi input")); 77 | midiOutputs.appendChild(createOption("-1", "choose a midi output")); 78 | 79 | for(var deviceId in devices) { 80 | var device = devices[deviceId]; 81 | if(device.type === "input" && device.available === "true") { 82 | midiInputs.appendChild(createOption(device.id, device.name)) 83 | } else if(device.type === "output" && device.available === "true") { 84 | midiOutputs.appendChild(createOption(device.id, device.name)) 85 | } 86 | } 87 | } 88 | 89 | var createOption = function(id, label) { 90 | var option = document.createElement("option"); 91 | option.setAttribute("id", id); 92 | option.innerHTML = label; 93 | return option; 94 | } 95 | 96 | var parseResult = function(data) { 97 | //contentDiv.innerHTML += data.msg + " : " + currentMidiInputId + " : " + currentMidiOutputId + "
"; 98 | } 99 | 100 | }, false); 101 | -------------------------------------------------------------------------------- /tests/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 | } -------------------------------------------------------------------------------- /tests/php/midiToBase64.php: -------------------------------------------------------------------------------- 1 | 16 | -------------------------------------------------------------------------------- /tests/php/tmp.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abudaan/midibridge-js/1a75ca1784575716acae25321a44ffef27cd29a3/tests/php/tmp.mid --------------------------------------------------------------------------------