Before opening a MIDI input port, you must first create a REXX GUI window using REXX GUI's GuiCreateWindow() function. Secondly, you pass that window's handle to RxMidiIO's MidiIoParams(). You also choose, and pass, a message number that you wish to use for MIDI events. Finally, you can open a MIDI input port using MidiIoOpenPort().

After you've done the above once, then you can do your REXX GUI message loop, calling GuiGetMsg(). When a MIDI message arrives at your input port, Reginald will call that window's WM_EXTRA event handler, passing it whatever message number you specified to MidiIoParams. (If you do not specify a message number, it defaults to 1124). The second arg passed return with RXWIND set to your window's ID, RXID = 'MIDI', and RXSUBID set to an "event ID" that you can pass to MidiIoInput() in order to retrieve the actual data bytes for that MIDI message. If MidiIoInput successfully retrieves the data bytes for you, it will return an empty string. Otherwise, it returns an error message.

Here then is a skeleton for setting up a REXX GUI window for MIDI input, opening the default MIDI In port, and then detecting whenever a MIDI message arrives at that port:

/*
GUIBEGIN
WINDOW , 54, 263, 257, 54, POPUP | CAPTION | SYSMENU | MINBOX | MAXBOX | THICK, , MIDI Input example
   FONT 8, 400, MS Shell Dlg
   TEXT 6, 7, 72, 8, , , , , Received MIDI event:
   TEXT 5, 45, 247, 8, , , InputEvent
   PUSH 140, 24, 40, 14, DEFAULT | TABSTOP, , RecordButton, ALT "R", &Record
DEND
GUIEND
*/

OPTIONS 'C_CALL'
NUMERIC DIGITS 10

/* Load and register functions in REXXGUI.DLL and RXMIDIIO.DLL. */
LIBRARY rexxgui, rxmidiio

GuiErr = 'SYNTAX'
GuiHeading = 1
MidiErr = 'SYNTAX'
MidiHeading = 1
GuiCreateWindow('NORMAL')

DO

   /* Associate our window with MIDI Input by passing the handle to
    * MidiIoParams(). If an error, SYNTAX is raised.
    */
   MidiIoParams(GuiWindow)

   /* Open the default MIDI In port. */
   MidiIoOpenPort(0, 'IN')

   CATCH SYNTAX
      CONDITION('M')
      RETURN
END

/* We're recording. */
Recording = 'START'

Again:
DO FOREVER
   GuiGetMsg()
   IF EXISTS('GuiObject') == 0 THEN DO
      IF EXISTS('GuiSignal') THEN DO
         NOP
      END
   END

   /* We have no Child Window Layout scripts, so we can skip any further checking
    * of GuiSignal and GuiObject.
    */

   CATCH HALT

   CATCH SYNTAX
      CONDITION('M')
      SIGNAL Again

   FINALLY
      /* Close the MIDI In port. */
      MidiIoClosePort('I')
	
      GuiDestroyWindow()
END
RETURN

/* Called by Reginald when the user clicks on the "Record" button. */
WM_CLICK_RecordButton:

   /* If we're currently recording, then stop. Otherwise, start. */
   IF Recording == 'STOP' THEN Temp = 'START'
   ELSE Temp = 'STOP'

   MidiIoRecord(Temp)

   /* Save the new mode. NOTE: If an error, SYNTAX was raised, and we've
    * jumped to the CATCH SYNTAX before we get here.
    */
   Recording = Temp

   RETURN

/* Called by Reginald when our window receives an event that REXX GUI itself
 * doesn't know about. REXX GUI does not know about MIDI events,
 * so it calls our "EXTRA" event handler. It passes two args, which are the
 * data for the event. The third arg is the message number. For an RxMidiIo
 * event, the default message number is 1124. The second arg is an event
 * ID that we pass to MidiIoInput to get the data bytes for the event.
 */
WM_EXTRA:
   /* Is it a message from RxMidiIo? */
   IF ARG(3) == 1124 THEN DO

      /* Get the actual data bytes in the stem variable 'MyStem'.
       * MyStem.0 will be a count of how many data bytes there are,
       * and MyStem.1 to myStem.xxx will be those data bytes. Here,
       * we just display them. NOTE: We pass the event ID.
       */
      MidiIoInput(ARG(2), 'MyStem')

      /* Display the data bytes. */
      DO i = 1 TO MyStem.0
         SAY MyStem.i
      END

   END

   /* Don't let Rexx Gui process this event. */
   RETURN ""

MIDIEvent variable

As you can see from the above, MidiIoInput() is used to retrieve the actual MIDI data bytes of a single MIDI message. If you supply a stem variable name, then MidiIoInput() stores each byte in its own compound variable using that stem, and sets a count of how many bytes have been stored. For example, if you supply a stem variable name of MyStem, then MyStem.1 will be the first data byte (ie, the MIDI status byte), MyStem.2 will be the second data byte (if there is a second byte), MyStem.3 will be the third data byte, etc. MyStem.0 will be a count of how many bytes have been stored (ie, how many compound variables have been set).

If preferred, you can omit passing any variable name to MidiIoInput(). In this case, MidiIoInput() will automatically use the stem variable MIDIEvent, just like it is used with RxMidi's MIDIGetEvent(). That is, the event number will be stored in MIDIEvent.!Type. The MIDI channel will be stored in MIDIEvent.!Channel. (This will be an empty string if the event has no channel). And the remaining data bytes will be stored in MIDIEvent.!Data1 and perhaps MIDIEvent.!Data2 according to the Types of events chart.

Here then is an alternate way to retrieve and display an incoming MIDI message:

WM_EXTRA:
   /* Is it a message from RxMidiIo? */
   IF ARG(3) == 1124 THEN DO

      /* Get the actual data bytes in the stem variable 'MIDIEvent'. */
      i = MidiIoInput(ARG(2))

      /* Display the bytes. */
      IF i \== "" THEN DO
         SAY "Type of event =" MIDIEvent.!Type

         /* For system exclusive, the data bytes are stored verbatim into MIDIEvent.Data1.
          * It's like using MIDISysex to retrieve the bytes, using the 'A' option. To
          * get each byte into REXX numeric format suitable for performing math on it,
          * or testing/displaying its value, we need to break off that byte and use
          * C2D() to convert it to REXX numeric format.
          */
         IF MIDIEvent.!Type == 240 THEN DO

            p = 1
            DO i /* Do all the data bytes. */

               byte = C2D(SUBSTR(MIDIEvent.!Data1, p, 1))  /* Break off and convert next byte. */
               SAY 'Data' p '=' byte

               p = p + 1            

            END /* LENGTH of sysex. */

         END

         /* All other types except SysEx may have a channel, and Data2, and are already
          * stored in a suitable format for REXX.
          */
         ELSE DO

            IF MIDIEvent.!Channel \== "" THEN SAY "MIDI Channel =" MIDIEvent.!Channel
            SAY "MIDI Data 1 =" MIDIEvent.!Data1
            IF MIDIEvent.!Data2 \== "" THEN SAY "MIDI Data 2 =" MIDIEvent.!Data2

         END

      END /* Display the bytes. */

   END

   /* Don't let Rexx Gui process this event. */
   RETURN ""

Inputting System Exclusive

In order to allow System Exclusive messages to be input, you must pass a second argument to MidiIoParams -- the desired size of a buffer to hold incoming messages. This must be at least as big as the largest single message you expect to receive. But, if a device will be sending Sysex messages faster than you can process them, it's advisable to specify a buffer size twice the size of the largest message (or even larger if MidiIoInput reports an error return about a buffer overrun).

For example, here we set a buffer size of 1024:

MidiIoParams(GuiWindow, 1024)

Filtering events

Normally, all incoming MIDI messages are received, except for Active Sense. But you can choose which messages you wish to be received. You must pass a third argument to MidiIoParams, which is a list of which types of events you wish to be received. Each event number is separated by a space. For example, here I receive only Note On (event number 90) and Note Off (event number 80):

MidiIoParams(GuiWindow, , '90 80')
You can also filter by channel. You must pass a fourth argument to MidiIoParams, which is a list of which channels you wish to be received. Each channel number is separated by a space. Here I receive only events on MIDI channels 1, 2, and 16:
MidiIoParams(GuiWindow, , , '1 2 16')
And you can combine the two filtering options. Here I receive only program change messages on channel 10:
MidiIoParams(GuiWindow, , '192', 10)
Rather than specifying which event types or channels to receive, you can specify which not to receive. Simply specify a \ as the first character. For example, here we choose to receive all types of events except Active Sense, MIDI Clock, and System Exclusive:
error = MidiIoParams(GuiWindow, , '\ 254 248 240')

Echoing events

If you desire, you can have certain received MIDI messages echoed (ie, retransmitted) to the computer's MIDI Out. This is useful if you are receiving messages from a controller, and wish to echo them to some MIDI sound module attached to MIDI Out. You must pass a fifth argument to MidiIoParams, which is a list of which types of events you wish to be echoed. Each event number is separated by a space. For example, here I echo only Note On (event number 90) and Note Off (event number 80):

error = MidiIoParams(GuiWindow, , , , '90 0')
Note that you may echo events that you have nevertheless set to be filtered (with the exception of System Exclusive. SysEx must not be filtered in order to be echoed).

So too, you can set which MIDI channels you wish to be echoed. You must pass a sixth argument to MidiIoParams, which is a list of which channels you wish to be echoed. Here we echo all events on channel 10.

error = MidiIoParams(GuiWindow, , , , , 10)

Start/Stop input

To temporarily stop the input of all MIDI messages, you can call MidiIoRecord(), passing either no arguments, or an argument of 'STOP'.

To subsequently resume input, call MidiIoRecord() with an argument of 'START' as so:

error = MidiIoRecord('START')