Dec 192017
 
C source code that provides basic MIDI message handling for IBM-PC systems equipped with a Roland MPU-401 or compatible MIDI interface card.
File MPUSR2.ZIP from The Programmer’s Corner in
Category C Source Code
C source code that provides basic MIDI message handling for IBM-PC systems equipped with a Roland MPU-401 or compatible MIDI interface card.
File Name File Size Zip Size Zip Type
CDOC.EXE 7188 4611 deflated
DELAYTST.C 1541 786 deflated
MAKEFILE.MAK 632 206 deflated
MIDI.C 34785 10705 deflated
MIDI.DOC 20015 7268 deflated
MIDI.H 4612 1784 deflated
MIDINAME.C 3158 952 deflated
MIDINAME.H 39 37 deflated
MPU.C 15701 4374 deflated
MPU.H 3732 1514 deflated
MPUREGS.H 3133 961 deflated
READTEST.C 1658 824 deflated
STD.H 730 365 deflated
WRITETST.C 1727 863 deflated

Download File MPUSR2.ZIP Here

Contents of the MIDI.DOC file




MPU-401 MIDI Interface Module v1.0

Copyright (c) 1991, Robin Davies.

DESCRIPTION

This module provides basic MIDI message handling for IBM-PC systems
equipped with a Roland MPU-401 or compatible MIDI interface card.

It provides interrupt driven Recording and playing and directly supports
most of the MPU-401 message filtering options.

Up to four MPU-401's can be driven simultaneously.

This module allows MIDI messages to be received and transmitted.

Received messages are automatically timestamped.

Transmitted messages may either be sent as scheduled messages (i.e. sent
at a specified time), or sent immediately.

Scheduled messages must be sent at least 240 ticks before they are
scheduled to be sent. There is no mechanism to slow down the transmission
process, so client applications must be careful to throttle themselves to
prevent all free memory from being used for midi messages. The best way
to do this is something like the following:

if (MidiMessagesPending(midiChannel) < 300) // or some arbitrary #
SendNextMidiMessage(channel);

This module has been compiled and tested under Turbo-Pascal 2.0. with
a variety of different programs, although it is far from completely
tested.

Three test programs, READTEST.C, WRITETST.C, and DELAYTST.C,
are included as sample programs.


FUNCTION SUMMARY

CreateMidiChannel -- Create a channel for sending and receiving
midi messages.
DestroyMidiChannel -- Destory the channel, and all resources used by
the channel (i.e. pending midi messages, turns off the MPU-401,
removes interrupt handlers, etc).
SetMidiOperatingMode -- Change the operating mode of a midi channel
(to record, play, record/play, or stop).

MidiStatus -- Returns the current error status of a midi channel.
MidiErrorString -- Returns an ASCII string describing the meaning of
an error code returned by MidiStatus.

AllocMidiMessage -- Allocate a midi message block (in preparation for
filling it in and sending it).
FreeMidiMessage -- Return a midi message block to the midi message
pool.
SetMidiMessage -- Fill a midi message block with data.
GetMidiMessageData -- Get a copy of the data in a message block.

SendMidiMessage -- Send a midi message immediately.
ScheduleMidiMessage -- Schedule a midi message for sending at a future
time.
ReceiveMidiMessage -- Get the next received midi message (or returns
NULL if no messages are waiting).

MidiMessagePending -- Returns the number of scheduled midi messages which
have not yet been sent.


DATA TYPES

The philosophy of this module is vaguely object oriented. The actual
contents of data structures are considered private to this module.
Client routines should have no need to access struture members directly
since access routines are provided to do so where appropriate. If you
really need to access a structure member, write an access routine in
this module to do so. This will minimize problems whith future versions
of this module.


MidiTimeT -- Time in ticks after playing or recording started.
Currently a long integer.

MidiMessageT -- The object which contains all data associated with
a midi message.

MidiChannelT -- Midi channel control block. Contains all information
associated with a midi communications channel.



HOW TO USE THIS MODULE

MIDI.H contains all typedefs, defines and function prototypes required
by clients of MIDI.C.

MIDI.C must be linked with the module MPU.C. The module MPU.C must be
compiled with register optimization disabled, and stack checking
disabled.

This module was compiled using Turbo C++ v1.0. It should work without
modification with Turbo C 2.0. It should work with Microsoft C equally
well, with minor tweaking, but I don't have a copy of MSC handy to do
the actual port. Feel free to chip in.

Three sample applications have been included: READTEST.EXE and WRITETST.EXE.
They may be built with the supplied MAKEFILE.MAK and Turbo MAKE.EXE.


ABOUT THE DOCUMENTATION

The documentation for this project was written using a simplified version
of Donald Knuth's literate programming style. The documentation was
written in the source code, and stripped out afterward using the utility
CDOC.EXE (included in this arc file).

Documentation is interwoven between actual source code by using enclosing
documentation in "ifdef DOC" and "endif" tag lines. The program CDOC.EXE
will filter out the documentation in a source file as follows:

CDOC.EXE outfile

The reason why this is done is to ensure that documentation accurately
reflects the current status of the code. If you are going to modify this
code, I would ask you to update the documentation in the source code(!)
when you do so. The HISTORY section of this document (below in the source
code) provides a good place for you to record the changes you made to
the sources.

If you follow this practice, then you will be able to generate up-to-date
documentation quickly and easily.

The MPU module also uses a more advanced form of literate programming
style which automatically inserts function prototypes into the
documentation, but I am unfortunately not able to distribute the program
which supports this, and am not willing to manually insert the
prototypes.

RELATED FILES

MIDI.H -- Contains defines and prototypes for clients of this module.

MPU.C -- This module must be linked with MPU.C.

MIDINAME.C .H -- Module which provides ascii translations of
midi messages.

READTEST.C -- A sample program which demonstrates usage of
the MIDI.C module. It provides an ASCII dump of all
messages read from the MIDI-IN port of the MPU-401.

WRITETST.C-- A sample program which demonstrates usage of the
MIDI.C module. It sends random notes to the MIDI-OUT port of
the MPU-401. Sounds kinda nice with bell-like patches.

RELATED DOCUMENTS

1)Midi Processing Unit MPU-401 Technical Reference Manual, version 1.5
(5/29/85), Roland Corporation, 1985. (Available directly from Roland for
a nominal fee).

BUGS

SYSEX messages, and Common messages may be sent as immediate messages
only. They may NOT be sent using SendScheduledMessage.

Due to the way that the MPU-401 works, System Common messages may or may
not work reliably. The MPU-401 handles most Common messages other than
SYSEX messages itself as part of the Record/Play sequence.

The parser looks like it needs some work. In particular, it will not
correctly parse MPU measure end marks.

NOTE: What follows is the statement of copyright that appeared in the
original post by Robin Davies. However, he has communicated to me the
desire to place this code in the public domain when reposting it with the
bug fixes. I'm not sure how to legally place a jointly authored work in
the public domain, so I suggest that you contact him about it before using
the code commercially. - Larry Troxler.

"
STATEMENT OF COPYRIGHT

Copyright (c) 1991, Robin Davies.

The author grants the rights to use or modify this code provided the
following conditions are met.

If this code is used for non-commercial purposes (i.e. FREEware -- Shareware
is considered commercial software for the purposes of this notice), you
may do so without restriction provided you send me a postcard (not a
letter, not e-mail, a postcard) -- Christmas cards are also acceptable --
saying how useful you found this code, and expressing your appreciation
for this useful little gem. If you distribute software which uses portions
of this code, or derivatives of this code, you must require that it
not be sold for more than $10 more than the cost of distribution.

If this code or derivatives of this code is used as part of a software
package which is offered for sale at a price that exceeds $10 more than
the cost of duplication, or is done as work for hire, then the
following condition must be met: you must send me a complete working copy
of each major version of any software package (and associated documentation)
which incorporates portions of this code, or derivatives thereof
in exchange for the right to use this code.

Alternate arrangements may be made by contacting me in person at:

Robin Davies
R.R.#1, Stouffville Sideroad,
Richmond Hill,
Ontario, Canada. L4C 4X7.

In any case, if you modify or redistribute the code, you must leave
this copyright notice intact.

"
(end of quote of original copyright notice. No longer applies -see above).

DISCLAIMER OF LIABILITY

If these programs, or programs based on this source code get up and
burn your house down in the middle of the night, I accept no
responsibility.

This source has not been fully tested. It may in fact have terrible bugs
in it still. It is provided "as-is", and without warranty, either express
or implied, and no representations are made as to its fitness for a
particular purpose. You have been warned, so don't come crying to me.

However, if you do find any bugs, do let me know, and I'll see what *we*
can do to fix them.

HISTORY

1/5/91 - Version 1.0 Posted on Compuserve for the first time.

5/6/92 - Larry Troxler
Compuserve: 73520,1736
Bix: ltroxler
Internet: [email protected]

I did some work on the scheduler. Also I
had to increase the timeout constants (see MPU.C), and it
appears the incoming time-stamps were missing.

- lt


PROTOTYPE

MidiMessageT *AllocMidiMessage(void);

DESCRIPTION

Allocates a Midi Message block.

Returns NULL if no more memory.


PROTOTYPE

MidiMessageT *ReceiveMidiMessage(MidiChannelT *channel) {

DESCRIPTION

Returns the next midi message.

RETURNS

Returns NULL if no message is ready to read.
Returns (MidiMessageT *)(-1) if error.

REVISIONS

5/6/92 by Larry Troxler
- Set time-stamp of message being returned.
- When an timer overflow was received, the time was not being advanced
by the correct amount.
- Do not send and overflow response to track requests from this routine.
Track requests are handled in the track request handler.

PROTOTYPE

void ScheduleMidiMessage(
MidiChannelT *channel,
int track,
MidiTimeT time,// Elapsed time in ticks since playback started
MidiMessageT *msg
);

DESCRIPTION

Sends a scheduled midi VOICE message. The supplied time is in ticks after the
start of playback.

Events may be scheduled BEFORE playback has actually started in order to
get ahead of the MPU.

Also note there is no throttling mechanism for the event queue. If you
call this routine too fast you will eventually run out of free memory
(used to allocate MidiMessageT blocks). You may want to hold off a bit
by checking to how far ahead of the Track time counter you are.

Messages are freed once they are transmitted. Once the message has been
scheduled, you may not access it again!

BUGS

Sysex messages and common messages may not be scheduled! Sysex messages
may only be sent directly.


PROTOTYPE

BOOL SendMidiMessage(MidiChannelT *channel,MidiMessageT *msg);

DESCRIPTION

Sends a message immediately. The message is freed before returning(!). Once
the message has been sent, you may not access it again.

RETURNS
YES if success.
NO if failure.
Call GetMidiStatus to receive error code.


PROTOTYPE

void SetMidiOperatingMode(
MidiChannelT *channel,
MpuOperatingModeT operating_mode
);

DESCRIPTION

Set current midi channel operating mode.

One of:

RECORD_MODE,PLAY_MODE,RECORDPLAY_MODE, STOP_MODE

RECORD_MODE: Allows receiving of timestamped midi messages and
sending of immediate midi messages.

PLAY_MODE: Allows sending of scheduled and immediate midi messages.

RECORDPLAY_MODE: Allows sending of scheduled and unscheduled midi messages,
and receiving of timestamped midi messages.

STOP_MODE: Stops sending and receiving of messages.

BUGS

CONTINUE should probably also be supported here, but isn't at the
present time.


PROTOTYPE

MidiChannelT *CreateMidiChannel(
int mpu_base_address, // Mpu base address (default 0x330)
int mpu_interrupt, // Mpu interrupt number (default 2)
int rx_buffersize, // Size of receive buffer (default 1024)
enum MpuOperatingModeT operating_mode, // RECORD_MODE, PLAY_MODE,RECORDPLAY_MODE,STOP_MODE
enum MpuClockT mpu_clock_source, // MPU_INTERNAL_CLOCK, MPU_MIDI_CLOCK
// or MPU_FSK_CLOCK
int tempo, // Beats per minute
enum MpuTimebaseT timebase, // Ticks per beat (see MpuTimebaseT in MPU.H)
int metronome_measure_length, // beats per measure, 0 -> metronome off
int mode, // See description
int midi_channel_mask, // bit n controls midi channel n+1
// bit = 0 -> pass trough without host intervention
// bit = 1 -> record/filter this midi channel
int tracks, // number of tracks (0 to 7) for scheduled midi messages
int *result // retcode placed here
);

DESCRIPTION

Create a MIDI channel.

mpu_base_address, mpu_interrupt, and rx_buffersize will default to
appropriate values if zero.

If you plan to send scheduled midi messages, it would probably be wise
to initially set the operating mode to STOP_MODE, schedule some messages,
and then set the MidiOperatingMode to PLAY_MODE once messages are ready to
be scheduled.

The mode parameter allows selection of messages to be received and and
passed through. Any of the following values (ORed together) may be specified:

MPU_VOICES_THRU -- pass all non-common messages directly from
MIDI-IN to MIDI-OUT. Note that voice messages
on channels which are masked are passed through
whether this mode option is specified or not.
MPU_EXCLUSIVE_THRU -- Pass through Sysex messages
MPU_REALTIME_THRU -- Pass through realtime (FA,FB,FC) messages
MPU_COMMON_THRU -- Pass through other Common (F2,F3,F6) messages

MPU_DEFAULT_THRU -- (MPU_VOICES_THRU)

MPU_RX_EXCLUSIVE -- Receive Sysex messages
MPU_RX_REALTIME -- Receive realtime (FA,FB,FC) messages
MPU_RX_BENDER -- Receive bender messages
MPU_RX_MODE -- receive mode messages
MPU_RX_COMMON -- Receive common (F2,F3,F6) messages

MPU_RX_DEFAULT -- 0. (Voice messages only).
MPU_RX_ALL -- Receive ALL messages.

midi_channel_mask selects midi channels for which messages should be
received. Note that if a channel is masked, messages are passed trough to
MIDI-OUT automatically. This is a problem with the MPU-401. There is no
way to prevent ALL messages from being passed through without actually
receiving them.

The MPU-401 supports up to 8 tracks for playback. The 8th track is
reserved for sending immediate midi messages. Scheduled messages may
be placed into any of these tracks. The maximum number of tracks which
will be used for playback should be set at create time. To be
perfectly honest, I can't think of a compelling reason to use more than
one track.

If an error occurs, the error code is placed into *result. This error
message may originate from either the MIDI module or the MPU module.
The MidiErrorString() routine will return an appropriate ASCII error message
for either class of error messages.

NOTES

You must call DestroyMidiChannel before exiting your program, since
interrupt handlers for the MPU-401 are dropped at Create time.

RETURNS

NULL if error, *retval <= error code.

BUGS

There are currently no provisions for count-in measures during record or
playback. This will (may) be remedied in future versions of this module.


PROTOTYPE

int DestroyMidiChannel(MidiChannelT *channel);

DESCRIPTION

Closes a midi channel, and deallocates all memory and resources used
by that channel. Removes the MPU-401 interrupt handler.

RETURNS

0 -> Success
non-zero = MidiErrorT error code.


PROTOTYPE

BOOL SetMidiMessage(
MidiMessageT *msg,
UCHAR midi_command,
UCHAR data1,
UCHAR data2,
unsigned int sysex_length,// Must be zero for non-SYSEX commands!
UCHAR *sysex_data
);


DESCRIPTION

Set the data of the supplied midi message.

Note that sysex data (if supplied) is copied into an in internal
memory block allocated from free store. The caller is responsible
for deallocating the SUPPLIED sysex data (if applicable).

For sysex messages, data1 and data2 are unused. sysex_data must
point to the first byte of sysex data following the sysex command.

The sequence of operations for sending a midi message:

msg = AllocMidiMessage();
SetMidiMessage(msg,cmd,data1,data2,0,NULL);

SendMidiMessage(midiChannel,msg);
or
SendScheduleMidiMessage(midiChannel,time,msg);

Note that a copy of the sysex data is made in heap memory, that will
be deallocated when the message is freed (or sent). This may pose a
problem for large sysex messages.

RETURNS

No -> insufficient memory for copy of sysex data.



PROTOTYPE

void GetMidiMessageData(
MidiMessageT *msg,
UCHAR *midi_cmd, // Receives midi command
UCHAR *data1, // Receives data1 (if not NULL)
UCHAR *data2 // Receives data2 (if not NULL)
);

DESCRIPTION

Get data for current midi message.

The sequence for reading a midi message:

msg = ReceiveMessage(channel);
if (msg == (MidiMessageT *)(-1)) {
handleMidiError(MidiStatus(channel));
return;
}
if (msg != NULL) {
GetMidiMessageData(msg,&cmd,&data1,&data2);
}


PROTOTYPE

int MidiStatus(MidiChannelT *channel);

DESCRIPTION

Returns the error status code of the specified Midi channel.

The only way to clear the error status is to Destroy the channel
and recreate it.

RETURNS

0 -> No error
non-zero = enum MidiErrorT.


PROTOTYPE

char *MidiErrorString(int error_code);

DESCRIPTION

Returns a string error message corresponding to the specified
(enum MidiErrorT or enum MpuErrorT) error code as returned by
either MidiStatus or CreateMidiChannel. Error codes originating
from either the MIDI module, or the MPU module are handled correctly.


PROTOTYPE

int MidiMessagesPending(MidiChannelT *channel);

DESCRIPTION

Returns the number of scheduled messages which have not been sent
to the MPU-401.

This function provides a useful technique for controlling the amount
of memory used by the midi messages queue:

if (MidiMessagesPending(channel) < 300) // or some arbitrary #
sendNextMidiMessage();


PROTOTYPE

void GetMidiMessageSysexData(
MidiMessageT *msg,
int *sysexLength,
UCHAR **sysexData// Filled if not null
);

DESCRIPTION

This routine returns the length and a pointer to the extra data
bytes sent or received with a sysex message.

The pointer remains valid until the message is sent or freed. (Clients
should NOT free the pointer themselves).

The first byte of sysex data is the byte which immediately follows the
Sysex (FF) midi command. The last byte of sysex data must be EOX_MSG (0xF7).
The contents of data1 and data2 as returned by GetMidiMessageData are
unspecified when a sysex command is sent or recieved.

sysexLength includes the EOX_MSG byte at the end of the sysex message.

BUGS

In truth, midi sysex messages may be terminated by ANY Midi status byte.

The current code only handles MIDI sysex messages terminated by EOX_MSG.
This is probably a fairly major ommission, although I have been
lucky enough not to have run into a machine which doesn't terminate
sysex messages with EOX_MSG. You have been warned.



PROTOTYPE

MidiTimeT GetMidiMessageReceiveTime(
MidiMessageT *msg
);

DESCRIPTION

Returns the time that the message was received by the MPU-401 in ticks
since recording started. Results are unspecified for messages which
haven't been obtained through ReceiveMidiMessage().



 December 19, 2017  Add comments

Leave a Reply