## MidiGenerator

The MidiGenerator emits explicitly specified Midi messages.

To use the MidiGenerator implementation, load midi.so and create an actor of type MidiGenerator.

### MidiGenerator

In addition to the messages understood by all actors, the MidiGenerator understands the following messages:

SetPitch`hActor pitch`
  Set default pitch. The argument `pitch` has a range of [0, 127] and defaults to 60.
SetAmp`hActor amplitude`
  Set default amplitude. The argument `amplitude` has a range of [0, 127] and defaults to 80.
SetChannel`hActor chan`
  Set default channel. The argument `chan` has a range of [1, 16] and defaults to 1.
SetDuration`hActor dur`
  Set default duration (in seconds). The argument `dur` has a range of [.00001, 3600] and defaults to 1.
SetCtrlNum`hActor ctrl`
  Set default control number. The argument `ctrl` has a range of [0, 15] and defaults to 0.
SetCtrlVal`hActor ctrlval`
  Set default controller value. The argument `ctrlval` has a range of [0, 127] and defaults to 0.
SetPatchNum`hActor patchnum`
  Set default patch number. The argument `patchnum` has a range of [0, 127] and defaults to 0.

### MidiGenerator handler messages

In addition to the messages understood by all handlers, the handler for the MidiGenerator understands the following messages. Defaults start off with the values specified below, but are in fact whatever the handler's actor's defaults are (possibly changed by sending one of the messages listed above).

In the list below, arguments in square brackets are optional. (For example, the possible argument strings to MidiNoteOn are: hSound; hSound pitch; hSound pitch ampl; hSound pitch ampl chan.) If an argument is omitted, the MidiGenerator handler takes the default value of that argument provided by the MidiGenerator actor.

MidiNoteOn`hSound [ pitch [ ampl [ chan ]]]`
  Send a note-on message.
  The argument `pitch` has a range of [0, 127] and defaults to 60.
  The argument `ampl` has a range of [0, 127] and defaults to 80.
  The argument `chan` has a range of [1, 16] and defaults to 1.
MidiNoteOff`hSound [ pitch [ chan ]]`
  Send a note-off message.
  The argument `pitch` has a range of [0, 127] and defaults to 60.
  The argument `chan` has a range of [1, 16] and defaults to 1.
MidiPlayNote`hSound [ pitch [ ampl [ chan [ dur ]]]]`
  Send note-on, then note-off message. (Currently under construction)
  The argument `pitch` has a range of [0, 127] and defaults to 60.
  The argument `ampl` has a range of [0, 127] and defaults to 80.
  The argument `chan` has a range of [1, 16] and defaults to 1.
  The argument `dur` has a range of [.00001, 3600] and defaults to 1.
MidiPitchBend`hSound [ pitchbend [ chan ]]`
  Send pitch-bend message.
  The argument `pitchbend` has a range of [-8192, 8192] and defaults to 0.
  The argument `chan` has a range of [1, 16] and defaults to 1.
MidiAllNotesOff`hSound`
  Send note-off message to all pitches and channels. (Currently under construction)
MidiCtrlChange`hSound [ ctrlval [ ctrl [ chan ]]]`
  Send control-change message (`MidiCtrlChange(ctrlval, ctrl, chan)` ).
  The argument `ctrlval` has a range of [0, 127] and defaults to 0.
  The argument `ctrl` has a range of [0, 15] and defaults to 0.
  The argument `chan` has a range of [1, 16] and defaults to 1.
MidiPatchChange`hSound [ patchnum [ chan ]]`
  Send patch-change message.
  The argument `patchnum` has a range of [0, 127] and defaults to 0.
  The argument `chan` has a range of [1, 16] and defaults to 1.
MidiGeneric`hSound [ x1, x2, x3... ]`
  Send an arbitrary array of integers `[ x1, x2, x3... ]` as a Midi message.

### Examples

`LoadDSO midi.so;
aGen = Create MidiGenerator;
sGen = BeginSound MidiGenerator;
aOut = Create MidiOutputActor;
sOut = BeginSound aOut SetInput sGen;
LoadDSO msgGroup.so;
`

Play individual notes from a message group:
the arguments of message group Beep represent pitch, amplitude, and channel number.

  `Beep = Create MessageGroup;
  AddMessage Beep MidiNoteOn sGen *0 *1 *2;
  `

Play individual short notes from a message group:
as above, but stop the note after half a second.

  `LoadDSO later.so;
  later = Create LaterActor;
  Beep = Create MessageGroup;
  AddMessage Beep MidiNoteOn sGen *0 *1 *2;
  AddMessage Beep AddMessage later 0.5 MidiNoteOff sGen *0 *2;
  `

  Or, more concisely:

  `Beep = Create MessageGroup;
  AddMessage Beep MidiPlayNote sGen *0 *1 *2 0.5;
  `

Adjust the pitch of a single instrument from a message group:
play a long note with loudness 80 (from 0 to 127) on channel 3, and change its pitch with the first argument to message group ChangePitch.

  `MidiNoteOn sGen 60 80 3;
  ChangePitch = Create MessageGroup;
  AddMessage ChangePitch MidiPitchBend sGen *0 3;
  `

Adjust several continuous controllers:
play a long note, and change 3 of its parameters, controllers number 13, 14, and 15, with the arguments to message group MorphSound.

  `MidiNoteOn sGen 60 80 3;
  MorphSound = Create MessageGroup;
  AddMessage MorphSound MidiCtrlChange sGen *0 13 3;
  AddMessage MorphSound MidiCtrlChange sGen *1 14 3;
  AddMessage MorphSound MidiCtrlChange sGen *2 15 3;
  `

Adjust continuous controllers of several notes:
play a C major chord with notes on channels 1,2,3; change controller number 7 of each one, with the arguments to message group MorphSound.

  `MidiNoteOn sGen 60 80 1; // C
  MidiNoteOn sGen 64 80 2; // E
  MidiNoteOn sGen 67 80 3; // G
  MorphSound = Create MessageGroup;
  AddMessage MorphSound MidiCtrlChange sGen *0 7 1;
  AddMessage MorphSound MidiCtrlChange sGen *1 7 2;
  AddMessage MorphSound MidiCtrlChange sGen *2 7 3;
  `

### Note to Developers

The analogy between sample-based audio and midi in VSS.

An output buffer contains 128 consecutive (8-channel) 32-bit float samples. Every actor which produces audio output sends audio to its output buffer and to VSS's main bus. (The latter can be muted with SetMute.) Output buffers can be used as sources for processor actors. The InputActor echoes the hardware input to its output buffer (and the main bus).

A midi output buffer is a list of midi messages, to be executed ASAP and in order, in parallel with audio output. It is implemented as an array of messages. A message is a 4-byte string. We allow at least 1000 messages per second. At the lowest sampling rate of 8 kHz, a 128-sample output buffer lasts .016 seconds, so the array need be no more than 16 messages long. We choose 20. Latency at 44 KHz is 2.9 msec; at 8 kHz, 16 msec; at 16 kHz, 8 msec. We recommend running VSS with a sampling rate of at least 16 kHz for best midi throughput.

Every actor which produces midi output sends midi to its output buffer, but not by default to the hardware midi output. Midi output buffers can be used as midi inputs for midi processors.

The MidiInputActor echoes the hardware midi input to its midi output buffer. The MidiOutputActor echoes its midi input to the hardware midi output.

"The" hardware midi input and output mean that VSS opens a single port (each) for midi input and output. This can be elaborated to multiple ports, later on. Midi I/O is normally done from the server, not from the client, again by analogy with regular audio. But if you want to do midi I/O on the client machine, have the client use the SGI midi libraries directly, and send messages to a MidiGenerator (and MidiClientActor, eventually).

A MidiClientActor would allow a VSS client to explicitly retrieve midi messages. Its internal midi buffer should be quite large, as it may accumulate a pile of messages before the client polls to retrieve them. Its handler understands the command "MidiReceive hSound;", which sends the midi events accumulated since the last MidiReceive back to the client in some format.