Jazz-Soft.net

We make it sound!

Home » Examples » MIDI Console

MIDI Console

This example requires Jazz-Plugin v.1.2 or later.

How it works

Send messages to any or all connected MIDI devices and monitor their output.

Use <Up> and <Down> arrow keys to repeat previously sent messages; Use <Ctrl-number> for fast switch between the output devices; <Ctrl-0> selects all devices.

This demo dynamically creates as many instances of Jazz-Plugin as it is required to connect all available MIDI devices.

MidiPool.js describes the MidiPool object that keeps track of these instances.

Page source

<!DOCTYPE html>
<html>
<head>
<title>MIDI Console</title>
<script src="MidiPool.js"></script>
<style type="text/css">
#main {
 width:80em;
 border-collapse:collapse;
 font-family:Courier New, monospace;
 font-size:.75em;
}
#main, #main tr, #main td{
 margin:0;
 padding:0;
 border:0;
}
#msg {
 width:100%;
 height:32em;
 margin:0;
 padding:.2em;
 background-color:#eee;
 border:solid 1px #888;
 white-space:pre;
 font-family:Courier New, monospace;
 overflow:auto;
 display:block;
}
#msg span {
 color:#00f;
}
#inp {
 width:100%;
 margin:0;
 padding:.2em;
 background-color:#eee;
 border:solid 1px #888;
 color:#00f;
 font-family:Courier New, monospace;
}
</style>
</head>

<body>

<h1>MIDI Console</h1>

<table id=main><tr><!sorry for using table>
<td colspan=2><div id=msg></div></td>
</tr><tr>
<td id=cell><select id=sel></select></td>
<td><input id=inp onpaste='setTimeout(check_input,0);' value="90 3c 7f 40 7f 43 7f"></td>
</tr><tr>
<td colspan=2 align=right><button onmousedown='send_msg();'>Send</button></td>
</tr><tr>
<td colspan=2>
<input type=checkbox id=ign checked=true><label for=ign>Ignore clock messages</label>
<button onmousedown='play_all();' onmouseup='stop_all();'>Test note</button>
</td>
</tr></table>

<div>
<script><!--
var Pool;
var ins;
var outs;
var msgarea=document.getElementById('msg');
var sel=document.getElementById('sel');
var cell=document.getElementById('cell');
var inp=document.getElementById('inp');
var ign=document.getElementById('ign');
var namelen=13;
var hist=[];
var in_hist=0;
try{
 Pool=new MidiPool;
 ins=Pool.MidiInList();
 outs=Pool.MidiOutList();
 for(var i in outs) if(namelen<outs[i].length) namelen=outs[i].length;
 for(var i in ins) if(namelen<ins[i].length) namelen=ins[i].length;

 var allouts=outs.length>1?["All MIDI Outs"].concat(outs):outs;
 for(var i in allouts) sel[i]=new Option(allouts[i],allouts[i],i==0,i==0);
 var tmp=document.createElement('span');
 tmp.innerHTML=Array(namelen+6).join("=");;
 msgarea.appendChild(tmp);
 cell.style.width=tmp.offsetWidth+"px";
 sel.style.width=tmp.offsetWidth+"px";
 msgarea.removeChild(tmp);

 for(var i in outs) Pool.OpenMidiOut(outs[i]);
 for(var i in ins) Pool.OpenMidiIn(ins[i],function(name){return function(t,a){print_msg(name,a);};}(ins[i]));
}
catch(err){ alert(err);}
namelen+=2;

function play_all(){
 var note=60;
 for(var i in outs){ midi_out(outs[i],[144,note,0x7f]); note+=5;}
}
function stop_all(){
 var note=60;
 for(var i in outs){ midi_out(outs[i],[128,note,0]); note+=5;}
}
function midi_out(name,msg){
 Pool.MidiOut(name,msg);
 print_msg(name,msg,true);
}
function format(msg){
 var tmp=[];
 for(var i in msg) tmp[i]=(msg[i]<16?"0":"")+msg[i].toString(16);
 return tmp.join(" ");
}
function print_msg(name,msg,out){
 if(ign.checked && !out && (msg[0]==0xfe || msg[0]==0xf8)) return;
 var str=name+" ";
 if(out) str+="&lt;&lt;";
 str+=Array(namelen-name.length).join("=");
 if(!out) str+="&gt;&gt;";
 str+=" "+format(msg);
 if(out) str="<span>"+str+"</span>";
 msgarea.innerHTML=msgarea.innerHTML+str+"<br>";
 msgarea.scrollTop=msgarea.scrollHeight;
}
function send_msg(){
 var msg=[];
 var a=fix_input(inp.value).split(" ");
 for(var i in a){ var x=parseInt(a[i],16); if(!isNaN(x)) msg.push(x);}
 if(msg.length){
  if(sel.selectedIndex) midi_out(outs[sel.selectedIndex-1],msg);
  else for(var i in outs) midi_out(outs[i],msg);
  var str=format(msg);
  for(var i=0;i<hist.length;i++){
   if(hist[i]==str){
    hist.splice(i,1);
    i--;
   }
  }
  hist.push(str);
  in_hist=hist.length;
 }
 inp.value="";
 setTimeout(function(){inp.focus();},0);
}
function keydown(e){
 var e=window.event || e;
 if(e.altKey || e.ctrlKey) return;
 if(e.keyCode==38){ // arrow up
  if(in_hist>0){
   in_hist--;
   inp.value=hist[in_hist];
  }
 }
 if(e.keyCode==40){ // arrow down
  if(in_hist<hist.length){
   in_hist++;
   inp.value=in_hist==hist.length?"":hist[in_hist];
  }
 }
}
function keypress(e){
 var e=window.event || e;
 if(e.altKey) return;
 var a=e.which===undefined ? e.keyCode : e.which;
 if(!a) return;
 if(e.ctrlKey)
 { if(outs.length && a>=48 && a<58 && a-48<=outs.length) sel.selectedIndex=a-48;
   return;
 }
 if(a==13){ send_msg(); return;}
 if(a<32) return;
 if(" 0123456789abcdef".indexOf(String.fromCharCode(a).toLowerCase())==-1){
  if(e.preventDefault) e.preventDefault(); else e.returnValue=false;
  return;
 }
 setTimeout(check_input,0);
}
function check_input(){
 var str=fix_input(inp.value);
 if(str!=inp.value) inp.value=str;
}
function fix_input(s){
 var str="";
 var n=0;
 for(var i=0;i<s.length;i++){
  if(" 0123456789abcdef".indexOf(s[i].toLowerCase())==-1) continue;
  if(s[i]==' ') n=0; else n++;
  if(n>2){ str+=' '; n=1;}
  str+=s[i];
 }
 return str;
}
if(inp.addEventListener){ inp.addEventListener('keypress',keypress,false); inp.addEventListener('keydown',keydown,false);}
else if(inp.attachEvent){ inp.attachEvent('onkeypress',keypress); inp.attachEvent('onkeydown',keydown);}
--></script>
</div>

</body>
</html> 

MidiPool.js

// Jazz-Soft.net
// This code is totally free to copy, modify and distribute.

function MidiPool(){
 var place;
 var arr=[];
 var inputs={};
 var outputs={};
 if(arguments.length){
  if(arguments[0].isJazz){
   place=arguments[0].parentNode;
   arr[0]={plugin:arguments[0]};
  }
  else{
   try{ // if this is a good location to create plugins
    var tmp=create_plugin(arguments[0]);
    arr[0]={plugin:tmp};
    place=arguments[0];
   }
   catch(err){}
  }
 }
 if(place===undefined){ // otherwise create plugins at where the current script is
  var scripts=document.getElementsByTagName('script');
  place=scripts[scripts.length-1].parentNode;
 }
 if(!arr.length) arr[0]={plugin:create_plugin(place)};

 if(navigator.appName=='Microsoft Internet Explorer'){ document.onfocusin=onFocusIE; document.onfocusout=onBlurIE;}
 else{ window.onfocus=connectMidi; window.onblur=disconnectMidi;}

 function create_plugin(where){
  var obj=document.createElement('object');
  obj.classid="CLSID:1ACE1618-1C7D-4561-AEE1-34842AA85E90";
  if(!obj.isJazz) obj.type="audio/x-jazz";
  obj.style.visibility='hidden';
  obj.style.width='0px'; obj.style.height='0px';
  where.appendChild(obj);
  if(obj.isJazz) return obj;
  where.removeChild(obj);
  throw "Cannot create Jazz-Plugin";
 }
 function connectMidi(){
  try{
   for(i=0;i<arr.length;i++){
    if(arr[i].in){
     if(arr[i].func) arr[i].plugin.MidiInOpen(arr[i].in,arr[i].func);
     else arr[i].plugin.MidiInOpen(arr[i].in);
    }
    if(i && arr[i].out) arr[i].plugin.MidiOutOpen(arr[i].out);
   }
  }
  catch(err){res.innerHTML=res.innerHTML+' ERR: '+err;}
 }
 function disconnectMidi(){
  try{
   for(i=0;i<arr.length;i++){
    if(arr[i].in) arr[i].plugin.MidiInClose();
    if(i && arr[i].out) arr[i].plugin.MidiOutClose(); // don't close the default out
   }
  }
  catch(err){}
 }
 function onFocusIE(){
  active_element=document.activeElement;
  connectMidi();
 }
 var active_element;
 function onBlurIE(){
  if(active_element!=document.activeElement){ active_element=document.activeElement; return;}
  disconnectMidi();
 }

 this.MidiOutList=function(){ return arr[0].plugin.MidiOutList();}
 this.MidiInList=function(){ return arr[0].plugin.MidiInList();}
 this.MidiOut=function(name,msg){ if(outputs[name]) outputs[name].plugin.MidiOutLong(msg);}
 this.ClearMidiIn=function(name){ if(inputs[name]) inputs[name].plugin.ClearMidiIn();}
 this.QueryMidiIn=function(name){ if(inputs[name]) return inputs[name].plugin.QueryMidiIn();}
 this.OpenMidiOut=function(name){
  if(outputs[name]) return;
  var i;
  for(i=0;i<arr.length;i++) if(!arr[i].out) break;
  if(i==arr.length){
   arr[i]={plugin:create_plugin(place)};
  }
  arr[i].out=name;
  arr[i].plugin.MidiOutOpen(name);
  outputs[name]=arr[i];
 }
 this.OpenMidiIn=function(name,func){
  if(!inputs[name]){
   var i;
   for(i=0;i<arr.length;i++) if(!arr[i].in) break;
   if(i==arr.length){
    arr[i]={plugin:create_plugin(place)};
   }
   arr[i].in=name;
   inputs[name]=arr[i];
  }
  if(func) inputs[name].plugin.MidiInOpen(name,func); else inputs[name].plugin.MidiInOpen(name);
  inputs[name].func=func;
 }
}

See also