The modMessage Entry Point for MIDI Output Drivers

A MIDI output driver must have an entry-point function named modMessage to process messages sent by MMSYSTEM. The syntax of modMessage is as follows:

DWORD modMessage(uDeviceID, uMsg, dwUser, dwParam1, dwParam2)

The uDeviceID parameter is a UINT specifying an ID for the target device. Device IDs are sequential, starting at zero and ending at a value equal to one less than the number of devices the driver supports.

The uMsg parameter is a UINT specifying the message being sent to the driver. The driver should return MMSYSERR_NOTSUPPORTED if it does not support the specified message.

The dwUser parameter is a DWORD of instance information for use by the driver. The driver should fill this location with its instance data (for example, a far pointer to a data structure). For any other messages, the instance data is returned to the driver. Drivers supporting multiple clients can use this instance data to keep track of which client is associated with the message.

The dwParam1 and dwParam2 parameters are message-dependent.

The low-order word of the return value specifies a message-dependent return value or error code. The high-order word should be zero.

Synchronous Versus Asynchronous Message Processing

MIDI output device drivers can process the MODM_DATA and MODM_LONGDATA messages either synchronously or asynchronously. If a driver processes these messages synchronously, then it waits until all the data has been sent to the hardware before returning from the modMessage function. An asynchronous driver puts the data in a queue and returns immediately. The data is then sent to the hardware in the background.

Whether a driver is written to be synchronous or asynchronous usually depends on the hardware being supported. An asynchronous driver is desirable because it lets foreground applications run while it processes data; however, some hardware may not support asynchronous data transfer. The Sound Blaster sample driver and the corresponding code examples in this chapter process the MODM_DATA and MODM_LONGDATA messages synchronously.

Interrupt-Time and Reentrancy Considerations for modMessage

The midiOutShortMsg and midiOutLongMsg functions can be called by applications (and by device drivers) at interrupt time. Thus, the modMessage entry-point function in a MIDI output device driver can be accessed at interrupt time and must be written to be reentrant for the MODM_DATA and MODM_LONGDATA messages.

Note:

For internal MIDI synthesizer drivers, the interrupt-time reception of the MODM_DATA and MODM_LONGDATA messages has an additional consequence: The driver cannot access the disk at interrupt-time to load patch data if it receives a MIDI program-change request.

An example of the reentrancy situation is illustrated by an application that takes incoming MIDI events and sends them to a MIDI output port. The application's low-level callback function for MIDI input receives a MIDM_DATA message, accompanied by the incoming MIDI data. The callback function calls midiOutShortMsg to send the data to a MIDI output port. The modMessage entry-point function in the output port driver is called with a MODM_DATA message. This all happens at interrupt time. While modMessage is processing the MODM_DATA message, another MIDI event arrives at the input port, and the process starts again, with modMessage being reentered with another MODM_DATA message.

To handle being called at interrupt time, the modMessage entry-point function must follow these guidelines:

modMessage must reside in a FIXED code segment. Any functions called during processing of the MODM_DATA and MODM_LONGDATA messages must also be in a FIXED code segment.

modMessage can access only data in FIXED data segments when processing the MODM_DATA and MODM_LONGDATA messages.

One way for synchronous drivers to be reentrant is to detect when modMessage is reentered and return a MIDIERR_NOTREADY error. The following example code fragment illustrates how the Sound Blaster driver processes the MODM_DATA and MODM_LONGDATA messages:

.

.

.

case MODM_DATA:

/* Make sure we're not being reentered

*/

wMidiOutEntered++;

{

if ( wMidiOutEntered != 1 )

{

dwReturn = MIDIERR_NOTREADY;

}

else

{

/* Send short message out MIDI port

*/

modSendShortMsg( dwParam1 );

dwReturn = 0L;

}

}

wMidiOutEntered--;

return ( dwReturn );

case MODM_LONGDATA:

/* Make sure we're not being reentered

*/

wMidiOutEntered++;

{

if ( wMidiOutEntered != 1 )

{

dwReturn = MIDIERR_NOTREADY;

}

/* Send long message out MIDI port

*/

else

dwReturn = modSendLongData( (LPMIDIHDR)dwParam1 );

}

wMidiOutEntered--;

return ( dwReturn );

Note:

A better way for a synchronous driver to handle being reentered is for the driver to maintain a small buffer to hold data received when it's reentered, instead of returning MIDIERR_NOTREADY. Before the driver returns from the original modMessage call (the one that was reentered), it makes sure that all data in this buffer is sent to the hardware.