In order to write out MIDI events to a particular port (ie, device), you need to first open that port. Then, you can subsequently call a function such as MidiIoOutShort() which (immediately) outputs MIDI data to that port. To input MIDI data, you need to first associate a REXX GUI window with MIDI input, and then open a MIDI input port. Subsequently, you can call MidiIoInput() to read a MIDI message whenever your REXX GUI window is informed of the arrival of that message.

After you're done outputting to a port (and have no further use for it), you must close that port.

Think of a MIDI port like a file. You open it, you read or write to it, and then you close it.


Opening the default MIDI port for output

How does your script choose a MIDI device for output? There are several different approaches you can take, depending upon how fancy and flexible you want your script to be.

Recall that Windows maintains separate lists of the ports for inputting MIDI data, and the ports for outputting MIDI data. For output, there is also the MIDI Mapper. It's not hardwired to any specific MIDI output/playback device. Rather, MIDI Mapper is an output port that sort of doles out MIDI events (you send to it) to one or more other MIDI Out ports in the list. (ie, MIDI Mapper is a MIDI Out port that resends MIDI events to other MIDI Out ports in the list). So what MIDI port is the MIDI Mapper attached to? Well, that depends upon the settings that the user has made in Control Panel's Multimedia MIDI page. This Control Panel utility lets him route MIDI Mapper to a single MIDI Out port (ie, one of the other MIDI Out ports in Windows' list). Or, he can use the "Custom configuration" setup (not available in Windows 2000/NT) to split up MIDI Mapper's 16 MIDI channels among several of the other MIDI ports, for example, he could set all MIDI events on channel 1 to go to the built-in wavetable module on his Creative Labs sound card, and all MIDI events on channel 2 to go to the built-in wavetable on his Turtle Beach sound card. So when using the MIDI Mapper, although your script outputs to only one "port", it actually supports having the various MIDI channels going to different ports (which the user may desire for more polyphony or because some cards are better suited for certain sounds, etc). Plus, the "Add new Instrument" feature (not available in Windows earlier than Windows 95) allows the user to apply Instrument Definition Files thus remapping your script's MIDI output even more, for example, to make non-General MIDI instruments conform to General MIDI.

When you use the MIDI Mapper for output, think of the Multimedia utility's "MIDI" page as becoming the "MIDI Setup" dialog for your own application. Whichever way the user set that page up is where your calls to MIDIOutShort() and MIDIOutLong() get routed. So, by opening the MIDI Mapper, you use the "default MIDI Out" setup.

You call the function MidiIoOpenPort() once to open a port. The MIDI Mapper has a Port ID of 0. So you can open MIDI Mapper (for MIDI output), as so:

OPTIONS "C_CALL"

/* Open the MIDI Mapper */
err = MidiIoOpenPort(0)

/* Did it open successfully? */
IF err == "" THEN DO

   /* Here you may send MIDI events to that port. */

END

/* It failed to open. */
ELSE SAY err
Note that if you do not pass any Port ID to MidiIoOpenPort(), then it will open MIDI Mapper by default. So, you may instead do:

OPTIONS "C_CALL"

/* Open the MIDI Mapper */
err = MidiIoOpenPort()

/* Did it open successfully? */
IF err == "" THEN DO

   /* Here you may send MIDI events to that port. */

END

/* It failed to open. */
ELSE SAY err
One drawback with MIDI Mapper is that it does impose an extra layer of software processing upon your MIDI output. If the user never enables the "Custom configuration", then all MIDI data ends up going to one port anyway, so you gain nothing here (and lose a little efficiency).


For a MIDI input port, your default should open port 0. You use MidiIoOpenPort() to open a MIDI input port, but must pass a second argument of 'IN'. Here's an example of opening the default MIDI input port:
OPTIONS "C_CALL"

/* Open the default input port */
err = MidiIoOpenPort(0, 'IN')

/* Did it open successfully? */
IF err == "" THEN DO

   /* Here you may call RXMSG() to wait for MIDI messages to arrive. */

END

/* It failed to open. */
ELSE SAY err


The most flexible way to choose a MIDI port

The most flexible way would be to present the user with all of the names in the list of MIDI Output ports and let him choose which one he wants.

RxMidiIO has a function that you can call to determine how many port names are in the list of ports for outputting/playing MIDI data. This function is called MidiIoPortInfo(). Using the COUNT option, this returns the number of ports in the list (but does not count MIDI Mapper). Remember that the Port IDs start with 1 and increment. So if Windows says that there are 3 ports in the list, then you know that their Port IDs are 1, 2, and 3 respectively. You then use one of these Port IDs with MidiIoPortInfo() to get further information about that specific port, and MIDIOpenPort() to open that port. For example, MidiIoPortInfo() can return the name of a specific port, and what sort of other features it has. You pass the Port ID of the port about which you want to get information.

Here then is an example of going through the list of MIDI Out ports, and printing the name of each one:

OPTIONS "C_CALL"

/* Determine how many MIDI Out ports */
count = MidiIoPortInfo('COUNT')

/* Enumerate each port's name, starting with MIDI Mapper */
IF count > 0 THEN DO

   DO portID = 0 TO count

      MidiIoPortInfo('NAME', portID) /* You can omit the option arg for 'NAME', if desired */

   END

END

/* Must be no MIDI Out ports in this computer. */
ELSE SAY "There are no MIDI Out ports!"

To enumerate input ports, you must specify the option 'IN' to MidiIoPortInfo(), as so:

OPTIONS "C_CALL"

/* Determine how many MIDI In ports */
count = MidiIoPortInfo('COUNT|IN')

/* Enumerate each port's name, starting with 1 */
IF count > 0 THEN DO

   DO portID = 1 TO count

      MidiIoPortInfo('NAME|IN', portID)

   END

END

/* Must be no MIDI In ports in this computer. */
ELSE SAY "There are no MIDI In ports!"

Closing a MIDI port

After you're done outputting events to a MIDI port, and no longer wish to do any more output with it, then you should call MidiIoClosePort() to close it, as so:

/* Close the currently open output port */
err = MidiIoClosePort()

/* Did it close successfully? */
IF err \== "" THEN SAY err
You can subsequently open another port, or open the same one again.


To close an input port, you must specify the option 'IN' to MidiIoClose(), as so:

/* Close the currently open input port */
err = MidiIoClosePort('IN')

Information about a port

MidiIoPortInfo() can return information about a port such as its name, how many multi-timbral parts it supports, its polyphony, etc.