BasicMIDISocket 


In my hierarchy, MIDI sockets respond to note on and off messages. BasicMIDISocket is the simplest of these, in which you specify a note on function and a note off function. 


*new(channel, on_func, off_func) 


Channel: the port and channel this socket should listen to. It may be specified several ways: 


chan_num (simple integer): this number is the channel number; the port will be assumed 0

\omni: respond to any channel on port 0

nil: assume port 0, channel 0

[port_num, chan_num]: specify a port as well as channel. port_num can be the uid belonging to the port (see MIDIClient and MIDIEndPoint), or an integer index to the sources initialized by MIDIClient.

[port_num, \omni]: respond to any channel on this port 


on_func, off_func: your functions. They will be passed the note number and velocity as arguments. 


port_num here refers to an index into the MIDIPort.sources array, e.g., on my machine:


MIDIPort.init;


Sources: [ FastLane USB #2 : Port A, FastLane USB #2 : Port B, UltraLite : Midi Port ]

Destinations: [ FastLane USB #2 : Port A, FastLane USB #2 : Port B, UltraLite : Midi Port ]

MIDIPort


In this configuration,


port 0 = FastLane USB #2 : Port A

port 1 = FastLane USB #2 : Port B

port 2 = UltraLite : Midi Port



free 


Deactivate the socket and remove it from the hierarchy. 


Example: 


(

k = BasicMIDISocket(\omni,

{ arg num, vel; ["noteOn", num, vel].postln },

{ arg num, vel; "\t".post; ["noteOff", num, vel].postln; });

)


// try some notes on your keyboard

// when done:


k.free; // now the keyboard does nothing


Writing your own sockets


Extensibility was crucial for me in designing this framework. If you have particular types of objects you like to control by MIDI that need special features, you can easily write your own MIDI socket or MIDI controller classes. This section covers MIDI sockets. MIDI controllers are handled in the BasicMIDIControl help file.


All MIDI sockets should be subclasses of AbstractMIDISocket. The abstract class handles the details of initializing the MIDI channel and placing the socket into the hierarchy. The *new message is always in the format: new(channel, destination, args). *new should not be overridden in your subclass.


Any further initialization should be done in the method init. Init will receive any arguments after the channel number and destination given in the new call. For example, in BasicMIDISocket: 


init { arg offfunc;

onPlayer = destination;  // since destination is this, I simplify the usual syntax:

destination = this;   // new(channel, dest, args)  -- so you can say instead:

offPlayer = offfunc;   // new(channel, on_func, off_func) }


In this case, the socket is its own destination, so I do some swapping of variables to make the syntax cleaner. If you have a different destination, leave the destination variable alone.


AbstractMIDISocket-free removes the object from the hierarchy, then calls the clear method. 


clear {

onPlayer = offPlayer = nil;

}


The instance variable destination holds the object being played by the socket. It is set to the second argument to new() before your init method is called. 


The destination must respond to active with a Boolean which is true if the object being played is still in action and false if that object has been freed. When MIDIPort.update is called, it scans all the objects in the hierarchy and if .destination.active is false, that responder is freed. In the case of VoicerMIDISocket, which can create several MIDI controller objects in addition to the MIDI socket, the voicer (destination) calls MIDIPort.update when it's freed, and Voicer's active method tells MIDIPort that its MIDI objects can disappear. Your player should do this as well.


If the destination is this (as above), then your socket must implement active. From BasicMIDISocket:


active { ^onPlayer.notNil } // since I'm the destination, I must say if I'm active or not


Finally, the socket must implement noteOn and noteOff methods, which take the note number and velocity as arguments. From BasicMIDISocket:



noteOn { arg note, vel; onPlayer.value(note, vel) }

noteOff { arg note, vel; offPlayer.value(note, vel) }


BasicMIDISocket in its entirety:


BasicMIDISocket : AbstractMIDISocket {

var <>onPlayer, <>offPlayer;

init { arg offfunc;

onPlayer = destination;  // since destination is this, I simplify the usual syntax:

destination = this;   // new(channel, dest, args)  -- so you can say instead:

offPlayer = offfunc;   // new(channel, on_func, off_func)

}


clear {

onPlayer = offPlayer = nil;

}

active { ^onPlayer.notNil } // since I'm the destination, I must say if I'm active or not

noteOn { arg note, vel; onPlayer.value(note, vel) }

noteOff { arg note, vel; offPlayer.value(note, vel) }

}


Short and sweet.