home *** CD-ROM | disk | FTP | other *** search
-
-
- 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 <infile >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.
-
-
-
- STATEMENT OF COPYRIGHT
-
- Copyright (c) 1991, Robin Davies. All Rights Reserved.
-
- You may use or modify this source code freely as long as the following
- conditions are met:
-
- (1) You may distribute object code derived from this work without
- displaying copyright notices of any kind. However, if this work or
- derivatives of this work are distributed in source form, this
- copyright notice must be left intact.
-
- If you wish to express your gratitute in any way (such as sending me
- a complimentary copy of *your* work), you may reach me at:
-
- Robin Davies
- 224 3rd Avenue
- Ottawa, Ontario
- Canada. K1S 2K3.
-
-
- 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: 73520.1736@compuserve.com
-
- 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().
-
-