Jazz-Soft.net

We make it sound!

Home » Documentation » JZZ.js » Modules » Custom Modules

How to create your own JZZ module

Please use the existing modules as templates.

Some details are clarified below.

Implementing custom MIDI-In / MIDI-Out ports

A module must allow both ways of opening ports:

                                            // MIDI-Out:
port = JZZ.synth.MySynth();                 // open port directly
                                            // or
JZZ.synth.MySynth.register('My Synth 1');   // register
JZZ.synth.MySynth.register('My Synth 2');   // (can register multiple instances)
port = JZZ().openMidiOut('My Synth 1');     // open the registered port

                                            // MIDI-In:
port = JZZ.input.MyGadget();                // open port directly
                                            // or
JZZ.input.MyGadget.register('My Gadget 1'); // register
JZZ.input.MyGadget.register('My Gadget 2'); // (can register multiple instances)
port = JZZ().openMidiIn('My Gadget 1');     // open the registered port

A module should implement the engine object that provides two functions:

Functions _openIn() and _openOut() can override the port's _info / _receive / _close members as explained here.

The port object passed to _openIn/Out() will normally be in the paused state. When the port is initialized and ready to go, call port._resume(), or, if an error occured, - port._crash().

Example

engine = {
  _info: function(name) { return { ... } },
  _openOut: function(port, name) {
    // ...
    port._info = this._info(name);
    port._receive = function(msg) { ... };
    port._close = function(msg) { ... };
    if (success)
      port._resume();
    else
      port._crash();
  }
};

You don't have to override _receive() for the MIDI-In ports, though, you may do it if you like! Call port._emit() whenever you need to emit a MIDI message:

myGadget.onKeyDown = function (note, velocity) {
  port._emit(JZZ.MIDI(0x90, note, velocity));
}

Finally, a module must define the port register/open functions as shown below:

JZZ.synth.MySynth = function(name) {
  // ...
  return JZZ.lib.openMidiOut(name, engine);
}
JZZ.synth.MySynth.register = function(name) {
  // ...
  return JZZ.lib.registerMidiOut(name, engine);
}

JZZ.input.MyGadget = function(name) {
  // ...
  return JZZ.lib.openMidiIn(name, engine);
}
JZZ.input.MyGadget.register = function(name) {
  // ...
  return JZZ.lib.registerMidiIn(name, engine);
}

Initializing ports synchronously

If no asynchronous calls required to initialize the port:

function init(port) {
  // do stuff...
  if (success) {
    port._resume();
  }
  else {
    port._crash();
  }
}

Initializing ports asynchronously

If an asynchronous call is required to initialize every port:

function init(port) {
  port._pause();  // wait until the asynchronous call returns
  // do stuff...
  asyncCall({
    onsuccess: function() {
      // do more stuff...
      port._resume();
    },
    onerror: function() {
      // do cleanup...
      port._crash();
    }
  );
}

If a single asynchronous call is required before initializing all ports:

var returned = false;
var success = false;
var waiting = [];

function finish(port) {
  if (success) {
    // do more stuff...
    port._resume();
  }
  else {
    // do cleanup...
    port._crash();
  }
}

function init(port) {
  if (returned) {
    finish(port);
    return;
  }
  port._pause();  // wait until the asynchronous call returns
  waiting.push(port);
  if (waiting.length == 1) {  // first time
    // do stuff...
    asyncCall({ onsuccess: onsuccess, onerror: onerror });
  }
}

function onerror() {
  returned = true;
  for (var i=0; i<waiting.length; i++) finish(waiting[i]);
}

function onsuccess() {
  returned = true;
  success = true;
  for (var i=0; i<waiting.length; i++) finish(waiting[i]);
}

See also