The MIDI Kit

Version 32, August 2000
© 1997-2000 McNabb Software Arts
San Francisco, CA
http://www.mcnabb.com/software/fantasia
michael@mcnabb.com

I. Overview

The MIDI Kit is a set of Java™ classes designed to serve as the basis for platform-independent and OS-independent real-time interactive MIDI applications. In the simplest case, the base classes may be used to build small, fixed configuration, stand-alone MIDI processing apps, similar to the included examples. More interestingly, the kit may be used to build a MIDI-processing network server, which may be programmed and configured dynamically by a client application running on the same computer, or any computer on the same network. The client could consist of a dedicated GUI application for MIDI processing or a larger application wanting to use the server for auxiliary MIDI processing. A full server application object (MIDIServerApp) is included, plus a set of Java proxy objects for use in building Java clients.

Unfortunately, the current versions of the Java platform do not include MIDI input and output primitives. Therefore, this function is currently handled through the use of native code interfaces. Note that this does not prevent a Java client application from being downloaded to a web browser running on another computer, as only the server need link in the native code.

It is very easy to extend the kit by merely subclassing MIDIProcessor or Performer.

Audio processing support is also now included, in the form of the AudioProcessor object and its subclasses. At this time sound handling is limited to sound file processing and playback. This area continues to be under development.
 

II. Design

The architecture of the MIDI Kit is inspired by the NeXT Music Kit architecture. It is built around the notion of a central EventLoop. Events of various types, including MIDIEvents, TimingEvents, and ControlEvents from the user interface, are created by separate i/o threads and placed on a central event queue (a FIFO). In the main thread, called the Main EventLoop, events are removed from the queue one at a time, and processed by their intended Processor object. Since all event processing is handled by a single thread, the processing code can dispense with the overhead of synchronization.

MIDIProcessors

The MIDIProcessor class is where the action is. Various subclasses of the MIDIProcessor class perform operations such as mapping incoming note values, playing a sequence, generating algorithmic data based on incoming notes, playing a sound, or sending notes on to MIDI output. One MIDIProcessor can forward MIDIEvents to other MIDIProcessors in a network.

MIDIProcessors can also declare and create input and output variables, objects called Vars, which operate via a variation of the java.util.Observable class. For a MIDIProcessor to send and/or receive control information from another MIDIProcessors, it binds one of its Vars to one of the other MIDIProcessors's Vars. Whenever one processor changes the value of its Var, all other Vars bound to it are automatically updated. For example, the VALUE output Var of a ConVal object can be used to update the TEMPO input Var of a Conductor object, so that MIDI control change messages will adjust the tempo. Bound Vars should be thought of as being the same Var. There is a built-in mechanism to prevent updates coming directly back to the Var which originates the update. Whether a Var is considered an "input" or "output" is determined solely by the Processor's use of it -- it is possible for one Var to both send and receive data.

Other event types include ControlEvents and ConnectionEvents, both of which are used to receive and process commands from external processes (such as the GUI), and TimingEvents. ControlEvents are used to update and inspect input and output Var values. ConnectionEvents are used to establish MIDI and Var connections between MIDIProcessors. TimingEvents are used to establish timed events using the Conductor object (a timer thread).

AudioProcessors

The AudioProcessor class serves as the foundation for audio processing capabilities. AudioEvents containing small buffers of sample frames are passed between AudioProcessor subclasses, which perform various operations on the buffers. The SndOut object provides playback capabilities and the SndFile object retrieves frames from a sound file.

Performers

Performer ia a special abstract subclass of MIDIProcessor which is designed to interact with a Conductor so as to allow subclasses to perform actions on a periodic or otherwise timed basis. Performers have an instance variable called nextPerform which indicates the desired delay until the next action needs to be performed by the perform() method. Internally, a timed event is scheduled with the Performer's Conductor. When the time reaches the time of the event, the Conductor places the event back on the main event loop, where it is received again by the Performer, which then calls perform() again, etc. In this fashion, MIDI sequences can be played, notes can be algorithmically generated, etc.

Conductors

The Conductor class is another special MIDIProcessor which also controls a timestamp-ordered event queue running in it's own thread. When the thread times out waiting for the earliest event in the queue, it pulls the event off the queue and places it on the main event loop's queue. The Conductor maintains two notions of time -- clock time and beat time. The basis for clock time is taken from the Time object (see below) in seconds. When a Conductor is started, the current time from the Time object is noted, and that becomes the reference time to compute clock time, which is at that point always 0. Beat time is time scaled by the Conductor's tempo, such that a tempo of 60 equals 1 beat per second (a beat here being always considered a quarter note). Since the tempo can be dynamically changing, beat time can advance faster or slower than clock time.

A default Conductor is automatically created to serve as the default Conductor for all Performers. This Conductor can be retrieved by the Conductor.defaultConductor() static method. You can, however, create multiple Conductors for groups of Performers running at different tempos.

Time

The Time static object is meant to serve as the global source of system time. It simply returns the time as provided by the TimeSource object it is provided, or the system clock if it has not been provided with another TimeSource. TimeSource is simply an interface indicating that an object can provide absolute time. Typically, MIDIInDevice or MIDIOutDevice would be an appropriate TimeSource to specify.

The following diagram shows the objects, events, and threads for a typical server application:

III. Extensibility

Another main design feature is ease of extensibility. A new MIDI processing object can be created just by subclassing MIDIProcessor or Performer, and overriding a couple of methods. New MIDIProcessors that are added to the com.msa.midi.processors package are automatically looked up and their class names, and input and output Var names, are made available to the client. The client can then instantiate and connect and use the new processors without knowing in advance that they were available.

MIDIProcessors can be as simple or as complicated as desired. A set of processors that perform simple arithmetic and logic operations would allow a GUI client to present a real-time graphic control programming language. A set of more elaborate MIDIProcessors could provide standard MIDI processing and sequencing functions for a music studio.

When implementing your own MIDIProcessors, it is important to avoid generating garbage, so that the Java garbage collector will not be invoked, causing processing delays. For example, when creating MIDIEvents, use the MIDIEvent.newEvent() and MIDIEvent.freeEvent() methods, which recycle events from a cache.

IV. API

V. Licensing

This library and program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License (com.msa.midi.server.*) and the GNU Library General Public License (everything else) as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This library and program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See What Is CopyLeft? for more details.

You are encouraged to suggest or implement improvements, additions, or fixes to the code. Please send such changes back to michael@mcnabb.com for consideration of inclusion in the standard code base, so that we all may benefit. I'm particularly interested in ports of the native code objects to other platforms. Thanks!

VI. Downloading

Please see the Fantasia main page for downloading links.


Java and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the U.S. and other countries