home *** CD-ROM | disk | FTP | other *** search
/ Audio 4.94 - Over 11,000 Files / audio-11000.iso / msdos / midi / mpusr2 / midi.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-07-06  |  33.1 KB  |  1,226 lines

  1.  
  2. /*
  3.     MIDI.C -- Midi Function interface
  4.  
  5. */
  6. #ifdef DOC
  7.  
  8. MPU-401 MIDI Interface Module v1.0
  9.  
  10. Copyright (c) 1991, Robin Davies.
  11.  
  12. DESCRIPTION
  13.  
  14. This module provides basic MIDI message handling for IBM-PC systems
  15. equipped with a Roland MPU-401 or compatible MIDI interface card.
  16.  
  17. It provides interrupt driven Recording and playing and directly supports
  18. most of the MPU-401 message filtering options.
  19.  
  20. Up to four MPU-401's can be driven simultaneously.
  21.  
  22. This module allows MIDI messages to be received and transmitted.
  23.  
  24. Received messages are automatically timestamped.
  25.  
  26. Transmitted messages may either be sent as scheduled messages (i.e. sent
  27. at a specified time), or sent immediately.
  28.  
  29. Scheduled messages must be sent at least 240 ticks before they are 
  30. scheduled to be sent. There is no mechanism to slow down the transmission
  31. process, so client applications must be careful to throttle themselves to 
  32. prevent all free memory from being used for midi messages. The best way 
  33. to do this is something like the following:
  34.  
  35.     if (MidiMessagesPending(midiChannel) < 300) // or some arbitrary #
  36.         SendNextMidiMessage(channel);
  37.  
  38. This module has been compiled and tested under Turbo-Pascal 2.0. with 
  39. a variety of different programs, although it is far from completely 
  40. tested.
  41.  
  42. Three test programs, READTEST.C, WRITETST.C, and DELAYTST.C,
  43. are included as sample programs.
  44.  
  45.  
  46. FUNCTION SUMMARY
  47.     
  48.     CreateMidiChannel -- Create a channel for sending and receiving 
  49.                 midi messages.
  50.     DestroyMidiChannel -- Destory the channel, and all resources used by
  51.                 the channel (i.e. pending midi messages, turns off the MPU-401,
  52.                 removes interrupt handlers, etc).
  53.     SetMidiOperatingMode -- Change the operating mode of a midi channel 
  54.                 (to record, play, record/play, or stop).
  55.  
  56.     MidiStatus -- Returns the current error status of a midi channel.
  57.     MidiErrorString -- Returns an ASCII string describing the meaning of
  58.                 an error code returned by MidiStatus.
  59.  
  60.     AllocMidiMessage -- Allocate a midi message block (in preparation for
  61.                 filling it in and sending it).
  62.     FreeMidiMessage -- Return a midi message block to the midi message
  63.                 pool.
  64.     SetMidiMessage -- Fill a midi message block with data.
  65.     GetMidiMessageData -- Get a copy of the data in a message block.
  66.  
  67.     SendMidiMessage -- Send a midi message immediately.
  68.     ScheduleMidiMessage -- Schedule a midi message for sending at a future
  69.                     time.
  70.     ReceiveMidiMessage -- Get the next received midi message (or returns 
  71.                     NULL if no messages are waiting).
  72.     
  73.     MidiMessagePending -- Returns the number of scheduled midi messages which
  74.                 have not yet been sent.
  75.  
  76.  
  77. DATA TYPES
  78.  
  79. The philosophy of this module is vaguely object oriented. The actual 
  80. contents of data structures are considered private to this module. 
  81. Client routines should have no need to access struture members directly
  82. since access routines are provided to do so where appropriate. If you
  83. really need to access a structure member, write an access routine in 
  84. this module to do so. This will minimize problems whith future versions
  85. of this module.
  86.  
  87.  
  88.     MidiTimeT -- Time in ticks after playing or recording started.
  89.                     Currently a long integer.
  90.  
  91.     MidiMessageT -- The object which contains all data associated with 
  92.                 a midi message.
  93.  
  94.     MidiChannelT -- Midi channel control block. Contains all information
  95.                 associated with a midi communications channel.
  96.  
  97.         
  98.  
  99. HOW TO USE THIS MODULE
  100.  
  101. MIDI.H contains all typedefs, defines and function prototypes required 
  102. by clients of MIDI.C.
  103.  
  104. MIDI.C must be linked with the module MPU.C. The module MPU.C must be 
  105. compiled with register optimization disabled, and stack checking 
  106. disabled.
  107.  
  108. This module was compiled using Turbo C++ v1.0. It should work without 
  109. modification with Turbo C 2.0. It should work with Microsoft C equally 
  110. well, with minor tweaking, but I don't have a copy of MSC handy to do 
  111. the actual port. Feel free to chip in.
  112.  
  113. Three sample applications have been included: READTEST.EXE and WRITETST.EXE.
  114. They may be built with the supplied MAKEFILE.MAK and Turbo MAKE.EXE.
  115.  
  116.  
  117. ABOUT THE DOCUMENTATION
  118.  
  119. The documentation for this project was written using a simplified version
  120. of Donald Knuth's literate programming style. The documentation was 
  121. written in the source code, and stripped out afterward using the utility
  122. CDOC.EXE (included in this arc file).
  123.  
  124. Documentation is interwoven between actual source code by using enclosing
  125. documentation in "ifdef DOC" and "endif" tag lines. The program CDOC.EXE
  126. will filter out the documentation in a source file as follows:
  127.         
  128.     CDOC.EXE <infile >outfile
  129.  
  130. The reason why this is done is to ensure that documentation accurately
  131. reflects the current status of the code. If you are going to modify this
  132. code, I would ask you to update the documentation in the source code(!) 
  133. when you do so. The HISTORY section of this document (below in the source
  134. code) provides a good place for you to record the changes you made to 
  135. the sources.
  136.  
  137. If you follow this practice, then you will be able to generate up-to-date
  138. documentation quickly and easily. 
  139.  
  140. The MPU module also uses a more advanced form of literate programming 
  141. style which automatically inserts function prototypes into the 
  142. documentation, but I am unfortunately not able to distribute the program
  143. which supports this, and am not willing to manually insert the 
  144. prototypes.
  145.  
  146. RELATED FILES
  147.  
  148.     MIDI.H -- Contains defines and prototypes for clients of this module.
  149.  
  150.     MPU.C -- This module must be linked with MPU.C.
  151.  
  152.     MIDINAME.C .H -- Module which provides ascii translations of 
  153.                 midi messages.
  154.  
  155.     READTEST.C -- A sample program which demonstrates usage of 
  156.             the MIDI.C module. It provides an ASCII dump of all 
  157.             messages read from the MIDI-IN port of the MPU-401.
  158.  
  159.     WRITETST.C    -- A sample program which demonstrates usage of the 
  160.             MIDI.C module. It sends random notes to the MIDI-OUT port of 
  161.             the MPU-401. Sounds kinda nice with bell-like patches.
  162.  
  163. RELATED DOCUMENTS
  164.  
  165. 1)    Midi Processing Unit MPU-401 Technical Reference Manual, version 1.5
  166.     (5/29/85), Roland Corporation, 1985. (Available directly from Roland for
  167.     a nominal fee).
  168.  
  169. BUGS
  170.  
  171. SYSEX messages, and Common messages  may be sent as immediate messages 
  172. only. They may NOT be sent using SendScheduledMessage.
  173.  
  174. Due to the way that the MPU-401 works, System Common messages may or may 
  175. not work reliably. The MPU-401 handles most Common messages other than 
  176. SYSEX messages itself as part of the Record/Play sequence.
  177.  
  178. The parser looks like it needs some work. In particular, it will not
  179. correctly parse MPU measure end marks.
  180.  
  181.  
  182. STATEMENT OF COPYRIGHT 
  183.  
  184. Copyright (c) 1991, Robin Davies. All Rights Reserved.
  185.  
  186. You may use or modify this source code freely as long as the following
  187. conditions are met:
  188.  
  189. (1) You may distribute object code derived from this work without
  190. displaying copyright notices of any kind. However, if this work or
  191. derivatives of this work are distributed in source form, this
  192. copyright notice must be left intact.
  193.  
  194. If you wish to express your gratitute in any way (such as sending me
  195. a complimentary copy of *your* work), you may reach me at:
  196.  
  197.     Robin Davies
  198.     224 3rd Avenue
  199.     Ottawa, Ontario
  200.     Canada. K1S 2K3.
  201.  
  202.  
  203. DISCLAIMER OF LIABILITY
  204.  
  205. If these programs, or programs based on this source code get up and
  206. burn your house down in the middle of the night, I accept no 
  207. responsibility. 
  208.  
  209. This source has not been fully tested. It may in fact have terrible bugs 
  210. in it still. It is provided "as-is", and without warranty, either express 
  211. or implied, and no representations are made as to its fitness for a 
  212. particular purpose. You have been warned, so don't come crying to me. 
  213.  
  214. However, if you do find any bugs, do let me know, and I'll see what *we*
  215. can do to fix them.
  216.  
  217. If you find anything else wrong, or have suggestions for improvements,
  218. please contact me at 73520,1736, since Robin (the author of the original
  219. post) has indicated that he is not currently using these sources.
  220.  
  221.     - Larry Troxler
  222.  
  223.  
  224. HISTORY
  225.  
  226. 1/5/91 - Version 1.0 Posted on Compuserve for the first time.    
  227.  
  228. 5/6/92 - Larry Troxler
  229.             Compuserve: 73520,1736
  230.             Bix:  ltroxler
  231.             Internet: 73520.1736@compuserve.com
  232.  
  233.             I did some work on the scheduler. Also I
  234.             had to increase the timeout constants (see MPU.C), and it
  235.             appears the incoming time-stamps were missing.
  236.  
  237.             - lt
  238.  
  239. 06Jul93 - Robin Davies
  240.  
  241.         Officially ammended the copyright notice. Taken from
  242.         MPUSR2.ARC on Compuserve, repackaged as MPUSR2.ZIP,
  243.         and posted on internet at ftp.ircam.fr. A copy of the
  244.         ammended copyright notice was forwarded to
  245.         Larry Troxler.
  246.  
  247.  
  248.  
  249. #endif
  250.  
  251.  
  252.  
  253. #include <stdio.h>
  254. #include <stdlib.h>
  255. #include <mem.h>
  256. #include <dos.h>
  257.  
  258. #include "std.h"
  259. #include "mpuregs.h"
  260. #include "mpu.h"
  261.  
  262. #define MIDI_PRIVATES
  263. #include "midi.h"
  264.  
  265. // Manifest constants
  266.  
  267. #define MIDI_MESSAGE_ALLOC 100 /* Number of MidiMessageT's to alloc at one time */
  268. #define INITIAL_SYSEX_BLOCK_LENGTH 1024 /* Initial SYSEX block length */
  269.  
  270. BYTE MidiCommandLength[128] = {
  271.     3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3,  // 80
  272.     3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3,  // 90
  273.     3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3,  // A0
  274.     3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3,  // B0
  275.  
  276.     2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2,  // C0
  277.     2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2,  // D0
  278.  
  279.     3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3,  // E0
  280.  
  281.     1,0,3,2, 0,0,1,0, 0,0,1,1, 1,0,0,0   // F0
  282. };
  283.  
  284. #ifdef PDOC
  285.  
  286. DESCRIPTION
  287.  
  288. This routine deallocates sysex data memory which has been sent using
  289. midi command scheduling. The problem is that sysex data can't be deallocated
  290. from the interrupt service routine. Therefore a free chain is built by
  291. the interrupt service routine so that the memory can later be deallocated
  292. under more controlled circumstances.
  293.  
  294. All of this is moot since SYSEX data can't currently be sent through
  295. scheduled midi commands. (I can't figure out what to do if the
  296. SYSEX message should collide when the REQUEST_TO_SEND_SYSEX_DATA_CMD
  297. command is send).
  298.  
  299. I know there *is* a way to handle scheduled sysex data (interestingly
  300. Cakewalk won't). However, for the meantime, this code is left, since
  301. it may well be required eventually.
  302.  
  303. You may also wan't to take a look at the rather peculiar, but peculiarly
  304. ANSI redefinitions of calloc and malloc in MIDI.H which also relate to
  305. this problem.
  306.  
  307. -rd
  308.  
  309. #endif
  310. typedef struct FreeSysexDataT {
  311. struct FreeSysexDataT *next;
  312. } FreeSysexDataT;
  313.  
  314. FreeSysexDataT *FreeSysexData = NULL;
  315.  
  316. void ReclaimSysexMemory(void) {
  317.     FreeSysexDataT *sysexData;
  318.     disable();
  319.     while (FreeSysexData) {
  320.         sysexData = FreeSysexData;
  321.         FreeSysexData = FreeSysexData->next;
  322.         enable();
  323.         free(sysexData);
  324.         disable();
  325.     }
  326.     enable();
  327.  
  328. }
  329.  
  330. #ifdef DOC
  331. PROTOTYPE
  332.  
  333. MidiMessageT *AllocMidiMessage(void);
  334.  
  335. DESCRIPTION
  336.  
  337. Allocates a Midi Message block.
  338.  
  339. Returns NULL if no more memory.
  340. #endif
  341.  
  342.  
  343.  
  344. MidiMessageT *FreeMidiMessages = NULL;
  345.  
  346. MidiMessageT *MidiMessageBlock = NULL;
  347. int MidiMessagesInBlock = 0;
  348.  
  349. MidiMessageT *AllocMidiMessage(void) {
  350.     MidiMessageT *msg;
  351.  
  352.     disable(); /* Warning: Interrupt may modify free chain -rd */
  353.     if (FreeMidiMessages) {
  354.         msg = FreeMidiMessages;
  355.         FreeMidiMessages = FreeMidiMessages->next;
  356.         enable();
  357.         msg->sysexLength = 0;
  358.         return msg;
  359.     }
  360.     enable();
  361.  
  362.     if (MidiMessagesInBlock == 0) {
  363.         MidiMessageBlock = calloc(MIDI_MESSAGE_ALLOC,sizeof(MidiMessageT));
  364.         if (MidiMessageBlock == NULL)
  365.             return NULL;
  366.         MidiMessagesInBlock = MIDI_MESSAGE_ALLOC;
  367.     }
  368.     --MidiMessagesInBlock;
  369.     return MidiMessageBlock++;
  370. }
  371.  
  372. #ifdef PDOC
  373.  
  374. DESCRIPTION
  375.  
  376. Free a MidiMessageT. This version is for INTERNAL USE ONLY. It is 
  377. callable from an interrupt service routine. Instead of freeing SYSEX data 
  378. the data blocks are inserted into a free list in order that they can be 
  379. freed later under more controlled circumstances.
  380.  
  381. #endif
  382.  
  383.  
  384.  
  385. PRIVATE void _FreeMidiMessage(MidiMessageT *msg) {
  386.     FreeSysexDataT *sysex_data;
  387.  
  388.     if (msg->sysexLength != 0) {
  389.         sysex_data = (void *) (msg->sysexData);
  390.         disable();
  391.         sysex_data->next = FreeSysexData;
  392.         FreeSysexData = sysex_data;
  393.         enable();
  394.     }
  395.     disable();        /* Warning: interrupt may modify free chain! -rd */
  396.     msg->next = FreeMidiMessages;
  397.     FreeMidiMessages = msg;
  398.     enable();
  399. }
  400.  
  401. void FreeMidiMessage(MidiMessageT *msg) {
  402.     if (msg->sysexLength != 0) {
  403.         free(msg->sysexData);
  404.     }
  405.     disable();        /* Warning: interrupt may modify free chain! -rd */
  406.     msg->next = FreeMidiMessages;
  407.     FreeMidiMessages = msg;
  408.     enable();
  409. }
  410.  
  411. #ifdef DOC
  412.  
  413. PROTOTYPE
  414.  
  415. MidiMessageT *ReceiveMidiMessage(MidiChannelT *channel) {
  416.  
  417. DESCRIPTION
  418.  
  419. Returns the next midi message.
  420.  
  421. RETURNS
  422.  
  423. Returns NULL if no message is ready to read.
  424. Returns (MidiMessageT *)(-1) if error.
  425.  
  426. REVISIONS
  427.  
  428. 5/6/92 by Larry Troxler
  429.  - Set time-stamp of message being returned.
  430.  - When an timer overflow was received, the time was not being advanced
  431.     by the correct amount.
  432.  - Do not send and overflow response to track requests from this routine.
  433.     Track requests are handled in the track request handler.
  434. #endif
  435.  
  436. MidiMessageT *ReceiveMidiMessage(MidiChannelT *channel) {
  437.     int data;
  438.     int cmd;
  439.     MpuPortT *mpu;
  440.     MidiMessageT *msg;
  441.     int i;
  442.     int tick;
  443.     int cmd_length;
  444.  
  445.     mpu = channel->mpu_port;
  446.  
  447.     if (!DataReady(mpu)) {
  448.         return NULL;
  449.     }
  450.     data = ReadData(mpu);
  451.     if (data == -1)
  452.         return (MidiMessageT *)(-1);
  453.  
  454.     if (IsMpuMessageNumber(data)) {
  455.         switch (data) {
  456.         case TIMING_OVERFLOW_MSG:
  457.             mpu->currentTime += 240;
  458.             break;
  459.         case SYSTEM_MESSAGE_MSG:
  460.             cmd = ReadData(mpu);
  461.             if (cmd == -1)
  462.                 return (MidiMessageT *)(-1);
  463.             tick = 0;
  464.             goto HandleMidiCommand;
  465.         default:
  466.             break;
  467.         }
  468.         return NULL;
  469.     } else {
  470.         // We have a midi command!
  471.         tick = data;
  472.         mpu->currentTime += tick;
  473.         cmd = ReadData(mpu);
  474.         if (cmd == -1)
  475.             return (MidiMessageT *)(-1);
  476.         if (cmd >= 0xF0) {
  477.             return NULL;
  478.         } else {
  479. HandleMidiCommand:
  480.             if (cmd < 0x80) // is it a running status message?
  481.             {
  482.                 UngetData(mpu,cmd);
  483.                 cmd = mpu->lastCommand;
  484.             } else {
  485.                 mpu->lastCommand = cmd;
  486.             }
  487.             if (cmd < 0x80)
  488.                 cmd_length = 1; /* We're in trouble */
  489.             else
  490.                 cmd_length = MidiCommandLength[cmd-0x80];
  491.             msg = AllocMidiMessage();
  492.             if (msg == NULL) {
  493.                 channel->errno = MIDI_ENOMEM;
  494.                 return (MidiMessageT *)(-1);
  495.             }
  496.             msg->midiCommand = cmd;
  497.             --cmd_length;
  498.             for (i = 0; i < cmd_length; ++i) {
  499.                 msg->midiData[i] = data = ReadData(mpu);
  500.                 if (data == -1)
  501.                     return (MidiMessageT *) (-1);
  502.             }
  503.             if (cmd == SYSEX_MSG) {
  504.                 unsigned int sysex_block_size;
  505.                 unsigned int sysex_length;
  506.                 UCHAR *sysex_data,*sysex_ptr;
  507.                 //Ohboy! Allocate a trial block. We'll grow it if we have to.
  508.                 // We'll shrink it when we're done.
  509.                 sysex_length = 0;
  510.                 sysex_block_size = INITIAL_SYSEX_BLOCK_LENGTH;
  511.                 sysex_data = calloc(1,sysex_block_size);
  512.                 if (sysex_data == NULL) {
  513.                     channel->errno = MIDI_ENOMEM;
  514.                     return (MidiMessageT *)(-1);
  515.                 }
  516.                 sysex_ptr = sysex_data;
  517.  
  518.                 while (data = ReadData(mpu), data != -1 && data != 0xF7)
  519.                 {
  520.                     *sysex_ptr++ = data;
  521.                     if (++sysex_length >= sysex_block_size) {
  522.                         sysex_block_size += INITIAL_SYSEX_BLOCK_LENGTH;
  523.                         sysex_data = realloc(sysex_data,sysex_block_size);
  524.                         if (sysex_data == NULL) {
  525.                             channel->errno = MIDI_ENOMEM;
  526.                             return (MidiMessageT *)(-1);
  527.                         }
  528.                         sysex_ptr = sysex_data+sysex_length;
  529.                     }
  530.                 }
  531.                 *sysex_ptr++ = data;
  532.                 ++sysex_length;
  533.                 sysex_data = realloc(sysex_data,sysex_length); // trim the block back to size
  534.                 msg->sysexData = sysex_data;
  535.                 msg->sysexLength = sysex_length;
  536.             }
  537.         }
  538.     }
  539.     msg->time = mpu->currentTime;
  540.     return msg;
  541. }
  542.  
  543. #ifdef PDOC
  544.  
  545. DESCRIPTION
  546.  
  547. This routine handles requests for track data from the interrupt service 
  548. routine.
  549.  
  550. The next MidiMessage is scheduled and sent here. Note that for precise
  551. scheduling, Midi Events must be posted at least 240 ticks prior to their
  552. actual occurrence.
  553.  
  554. Warning: this routine gets executed in the interrupts service routine!
  555.  
  556. REVISIONS
  557.  
  558. 5/6/92 Larry Troxler
  559. - If there are no messages queued, keep things alive by sending an MPU
  560.   timer overflow, and adjust the track time accordingly.
  561. -In the case where a message is taken from the queue and sent, adjust the
  562.  queue head pointer and the time.
  563. #endif
  564.  
  565. PRIVATE void HandleNextTrackRequest(void *handler_data,int track) {
  566.     MidiChannelT *channel = handler_data;
  567.     MpuPortT *mpu;
  568.     MidiMessageT *msg;
  569.     MidiTimeT time_to_next_msg;
  570.     int data_length;
  571.     int i;
  572.  
  573.     mpu = channel->mpu_port;
  574.  
  575.     msg = channel->sendQ[track];    // fetch first pending message
  576.     if (msg == NULL)
  577.     {
  578.         /* - lt */
  579.         SendData(mpu,0xf8);
  580.         channel->qTime[track] += 240;
  581.         return;
  582.     }
  583.     time_to_next_msg = msg->time - channel->qTime[track];
  584.     if (time_to_next_msg < 0)
  585.         time_to_next_msg = 0;
  586.     if (time_to_next_msg >= 240) {
  587.         SendData(mpu,0xF8); // MPU MARK. track timer <- 240
  588.         channel->qTime[track] += 240;
  589.     } else {
  590.         SendData(mpu,(UCHAR)time_to_next_msg); 
  591.         SendData(mpu,msg->midiCommand); // Note: MPU handles runnning status!
  592.         
  593.         data_length = MidiCommandLength[msg->midiCommand-0x80]-1;
  594.         for (i = 0; i < data_length; ++i)
  595.             SendData(mpu,msg->midiData[i]);
  596.  
  597.         channel->qTime[track] = msg->time; /* - lt */
  598.         channel->sendQ[track] = msg->next;    /* -lt */
  599.         _FreeMidiMessage(msg);  // Version of same which can be called from an interrupt
  600.         --channel->midiMessagesPending;
  601.     }
  602. }
  603.  
  604.  
  605. #ifdef DOC 
  606. PROTOTYPE
  607.  
  608. void ScheduleMidiMessage(
  609.     MidiChannelT *channel,
  610.     int track,                
  611.     MidiTimeT time,        // Elapsed time in ticks since playback started
  612.     MidiMessageT *msg
  613. );
  614.  
  615. DESCRIPTION 
  616.  
  617. Sends a scheduled midi VOICE message. The supplied time is in ticks after the
  618. start of playback.
  619.  
  620. Events may be scheduled BEFORE playback has actually started in order to 
  621. get ahead of the MPU.
  622.  
  623. Also note there is no throttling mechanism for the event queue. If you 
  624. call this routine too fast you will eventually run out of free memory 
  625. (used to allocate MidiMessageT blocks). You may want to hold off a bit
  626. by checking to how far ahead of the Track time counter you are.
  627.  
  628. Messages are freed once they are transmitted. Once the message has been
  629. scheduled, you may not access it again!
  630.  
  631. BUGS
  632.  
  633. Sysex messages and common messages may not be scheduled! Sysex messages 
  634. may only be sent directly.
  635. #endif
  636.  
  637.  
  638. void ScheduleMidiMessage(
  639.     MidiChannelT *channel,
  640.     int track,
  641.     MidiTimeT time,        // Elapsed time in ticks since playback started
  642.     MidiMessageT *msg
  643. ) {
  644.     MidiMessageT *head, *tail;
  645.     msg->time = time;
  646.     ++channel->midiMessagesPending;
  647.     disable();
  648.     if ((head = channel->sendQ[track]) == NULL) {
  649.         msg->next = NULL;
  650.         channel->sendQTail[track] = msg;
  651.         channel->sendQ[track] = msg;
  652.         enable();
  653.         return;
  654.     }
  655.     // First check to see if it goes at the end of the queue.
  656.     if ((tail = channel->sendQTail[track])->time <= time) {
  657.         tail->next = msg;
  658.         msg->next = NULL;
  659.         channel->sendQTail[track] = msg;
  660.         enable();
  661.         return;
  662.     }
  663.     // Check to see if we are first in the queue
  664.     if (head->time > time) {
  665.         msg->next = head;
  666.         channel->sendQ[track] = msg;
  667.         enable();
  668.         return;
  669.     } 
  670.     // Otherwise search down the queue until we find the p
  671.     while (head->next->time <= time)
  672.         head = head->next;    // Note: if we hit null then we would have caught it in 1st test!
  673.     msg->next = head->next;
  674.     head->next = msg;
  675.     enable();
  676.     return;    
  677. }
  678.  
  679.  
  680. #ifdef DOC
  681.  
  682. PROTOTYPE
  683.  
  684. BOOL SendMidiMessage(MidiChannelT *channel,MidiMessageT *msg);
  685.  
  686. DESCRIPTION 
  687.  
  688. Sends a message immediately. The message is freed before returning(!). Once
  689. the message has been sent, you may not access it again.
  690.  
  691. RETURNS
  692.     YES if success.
  693.     NO if failure.
  694.     Call GetMidiStatus to receive error code.
  695. #endif
  696.  
  697. BOOL SendMidiMessage(MidiChannelT *channel,MidiMessageT *msg) {
  698.     MpuPortT *mpu;
  699.     unsigned int i;
  700.     unsigned int length;
  701.     int cmd;
  702.     UCHAR *sysex_ptr;
  703.  
  704.     mpu = channel->mpu_port;
  705.  
  706.     if (msg->midiCommand >= COMMON_MSG) {
  707.         SendCommand(mpu,REQUEST_TO_SEND_SYSTEM_MSG_CMD);
  708.         length = msg->sysexLength;
  709.         sysex_ptr = msg->sysexData;
  710.         SendData(mpu,msg->midiCommand);
  711.         for (i = 0; i < length; ++i) {
  712.             SendData(mpu,*sysex_ptr++);
  713.         }
  714.         if (sysex_ptr[-1] != EOX_CMD)
  715.             SendData(mpu,EOX_CMD);
  716.         FreeMidiMessage(msg);
  717.     } else {
  718.         SendCommand(mpu,REQUEST_TO_SEND_DATA_CMD+7); // On track 8
  719.         cmd = msg->midiCommand;
  720.         SendData(mpu,cmd);
  721.         length = MidiCommandLength[cmd-0x80]-1;
  722.         for (i = 0; i < length; ++i) {
  723.             SendData(mpu,msg->midiData[i]);
  724.         }
  725.         FreeMidiMessage(msg);
  726.     }
  727.     return YES;
  728.  
  729. }
  730.  
  731. #ifdef DOC
  732.  
  733. PROTOTYPE
  734.  
  735. void SetMidiOperatingMode(
  736.     MidiChannelT *channel,
  737.     MpuOperatingModeT operating_mode
  738. );
  739.  
  740. DESCRIPTION
  741.  
  742. Set current midi channel operating mode.
  743.  
  744. One of:
  745.  
  746. RECORD_MODE,PLAY_MODE,RECORDPLAY_MODE, STOP_MODE
  747.  
  748. RECORD_MODE: Allows receiving of timestamped midi messages and 
  749.     sending of immediate midi messages.
  750.  
  751. PLAY_MODE: Allows sending of scheduled and immediate midi messages.
  752.  
  753. RECORDPLAY_MODE: Allows sending of scheduled and unscheduled midi messages,
  754.     and receiving of timestamped midi messages.
  755.  
  756. STOP_MODE: Stops sending and receiving of messages.
  757.  
  758. BUGS
  759.  
  760. CONTINUE should probably also be supported here, but isn't at the 
  761. present time.
  762. #endif
  763.  
  764. void SetMidiOperatingMode(
  765.     MidiChannelT *channel,
  766.     MpuOperatingModeT operating_mode
  767. ) {
  768.     SetMpuOperatingMode(channel->mpu_port,operating_mode);
  769. }
  770.  
  771. #ifdef DOC
  772.  
  773. PROTOTYPE
  774.  
  775. MidiChannelT *CreateMidiChannel(
  776.     int mpu_base_address, // Mpu base address (default 0x330)
  777.     int mpu_interrupt,      // Mpu interrupt number (default 2)
  778.     int rx_buffersize,      // Size of receive buffer (default 1024)
  779.     enum MpuOperatingModeT operating_mode,      // RECORD_MODE, PLAY_MODE,RECORDPLAY_MODE,STOP_MODE
  780.     enum MpuClockT mpu_clock_source, // MPU_INTERNAL_CLOCK, MPU_MIDI_CLOCK
  781.                                      // or MPU_FSK_CLOCK
  782.     int tempo,              // Beats per minute
  783.     enum MpuTimebaseT timebase,     // Ticks per beat (see MpuTimebaseT in MPU.H)
  784.     int metronome_measure_length, // beats per measure, 0 -> metronome off
  785.     int mode,              // See description
  786.     int midi_channel_mask, // bit n controls midi channel n+1
  787.                            // bit = 0 -> pass trough without host intervention
  788.                            // bit = 1 -> record/filter this midi channel    
  789.     int tracks,               // number of tracks (0 to 7) for scheduled midi messages
  790.     int *result               // retcode placed here
  791. );
  792.  
  793. DESCRIPTION
  794.  
  795. Create a MIDI channel.
  796.  
  797. mpu_base_address, mpu_interrupt, and rx_buffersize will default to 
  798. appropriate values if zero.
  799.  
  800. If you plan to send scheduled midi messages, it would probably be wise
  801. to initially set the operating mode to STOP_MODE, schedule some messages,
  802. and then set the MidiOperatingMode to PLAY_MODE once messages are ready to
  803. be scheduled.
  804.  
  805. The mode parameter allows selection of messages to be received and and
  806. passed through. Any of the following values (ORed together) may be specified:
  807.  
  808.     MPU_VOICES_THRU -- pass all non-common messages directly from 
  809.                              MIDI-IN to MIDI-OUT. Note that voice messages 
  810.                              on channels which are masked are passed through
  811.                              whether this mode option is specified or not.
  812.     MPU_EXCLUSIVE_THRU -- Pass through Sysex messages 
  813.     MPU_REALTIME_THRU  -- Pass through realtime (FA,FB,FC) messages
  814.     MPU_COMMON_THRU    -- Pass through other Common (F2,F3,F6) messages
  815.  
  816.     MPU_DEFAULT_THRU   -- (MPU_VOICES_THRU)
  817.  
  818.     MPU_RX_EXCLUSIVE     -- Receive Sysex messages
  819.     MPU_RX_REALTIME    -- Receive realtime (FA,FB,FC) messages
  820.     MPU_RX_BENDER      -- Receive bender messages
  821.     MPU_RX_MODE        -- receive mode messages
  822.     MPU_RX_COMMON      -- Receive common (F2,F3,F6) messages
  823.  
  824.     MPU_RX_DEFAULT         -- 0. (Voice messages only).
  825.     MPU_RX_ALL               -- Receive ALL messages.
  826.  
  827. midi_channel_mask selects midi channels for which messages should be 
  828. received. Note that if a channel is masked, messages are passed trough to 
  829. MIDI-OUT automatically. This is a problem with the MPU-401. There is no 
  830. way to prevent ALL messages from being passed through without actually
  831. receiving them.
  832.  
  833. The MPU-401 supports up to 8 tracks for playback. The 8th track is 
  834. reserved for sending immediate midi messages. Scheduled messages may
  835. be placed into any of these tracks. The maximum number of tracks which 
  836. will be used for playback should be set at create time. To be 
  837. perfectly honest, I can't think of a compelling reason to use more than
  838. one track.
  839.  
  840. If an error occurs, the error code is placed into *result. This error 
  841. message may originate from either the MIDI module or the MPU module.
  842. The MidiErrorString() routine will return an appropriate ASCII error message
  843. for either class of error messages.
  844.  
  845. NOTES
  846.  
  847. You must call DestroyMidiChannel before exiting your program, since 
  848. interrupt handlers for the MPU-401 are dropped at Create time.
  849.  
  850. RETURNS
  851.  
  852. NULL if error, *retval <= error code.
  853.  
  854. BUGS
  855.  
  856. There are currently no provisions for count-in measures during record or 
  857. playback. This will (may) be remedied in future versions of this module.
  858. #endif
  859.  
  860. MidiChannelT *CreateMidiChannel(
  861.     int mpu_base_address, // Mpu base address (default 0x330)
  862.     int mpu_interrupt,      // Mpu interrupt number (default 2)
  863.     int rx_buffersize,      // Size of receive buffer (default 1024)
  864.     enum MpuOperatingModeT operating_mode,      // RECORD_MODE, PLAY_MODE,RECORDPLAY_MODE,STOP_MODE
  865.     enum MpuClockT mpu_clock_source, // MPU_INTERNAL_CLOCK, MPU_MIDI_CLOCK
  866.                                      // or MPU_FSK_CLOCK
  867.     int tempo,              // Beats per minute
  868.     enum MpuTimebaseT timebase,     // Ticks per beat (see MpuTimebaseT in MPU.H)
  869.     int metronome_measure_length, // beats per measure, 0 -> metronome off
  870.     int mode,              // See description
  871.     int midi_channel_mask, // bit n controls midi channel n+1
  872.                            // bit = 0 -> pass trough without host intervention
  873.                            // bit = 1 -> record/filter this midi channel    
  874.     int tracks,               // number of tracks (0 to 7) for scheduled midi messages
  875.     int *result               // retcode placed here
  876. ) {
  877.     MpuPortT *mpu = NULL;
  878.     MidiChannelT *channel = NULL;
  879.     int i;
  880.  
  881.     channel = calloc(1,sizeof(MidiChannelT));
  882.     if (channel == NULL) {
  883.         *result = MIDI_ENOMEM;
  884.         return NULL;
  885.     }
  886.         
  887.     mpu = CreateMpuPort(
  888.         mpu_base_address,
  889.         mpu_interrupt,
  890.         rx_buffersize,
  891.         STOP_MODE,
  892.         mpu_clock_source,
  893.         tempo,
  894.         timebase,
  895.         metronome_measure_length,
  896.         mode,
  897.         midi_channel_mask,
  898.         result
  899.     );
  900.     if (*result) {
  901.         goto Cleanup;
  902.     }
  903.     channel->mpu_port = mpu;
  904.  
  905.     for (i = 0; i < MAX_MPU_TRACK; ++i) {
  906.         channel->sendQ[i] = NULL;
  907.         channel->sendQTail[i] = NULL;
  908.         channel->qTime[i] = 0;
  909.     }
  910.     mpu->trackRequestHandler = HandleNextTrackRequest;
  911.     mpu->requestHandlerData = channel;
  912.  
  913.     // Enable tracks
  914.     SendCommand(mpu,ACTIVE_TRACK_MASK_CMD);
  915.     SendData(mpu,(0x07F >> (7-tracks)));
  916.  
  917.     SendCommand(mpu,0xB8);
  918.  
  919.     SetMidiOperatingMode(channel,operating_mode);
  920.  
  921.     SendCommand(mpu,SEND_MEASURE_END_OFF_CMD);    /* - lt */
  922. #ifdef JUNK
  923.     SendCommand(mpu,0xFF);
  924.     SendCommand(mpu,0xEC);
  925.     SendData(mpu,0x01);
  926.     SendCommand(mpu,0xB8);
  927.     SendCommand(mpu,0x0A);
  928.  
  929. #endif
  930.     return channel;        
  931. Cleanup:    // Error exit w/ cleanup
  932.     if (mpu)
  933.         DestroyMpu(mpu);
  934.     if (channel) {
  935.         free(channel);
  936.     }
  937.     return NULL;
  938. }
  939.  
  940. void CancelScheduledMessages(MidiChannelT *channel) {
  941.     int i;
  942.     MidiMessageT *msg,*next;
  943.  
  944.     for (i = 0; i < MAX_MPU_TRACK; ++i) {
  945.         disable();
  946.         msg = channel->sendQ[i];
  947.         channel->sendQ[i] = 0;
  948.         enable();
  949.         while (msg != NULL) {
  950.             next  = msg->next;
  951.             FreeMidiMessage(msg);
  952.             msg = next;
  953.         }
  954.  
  955.     }
  956. }
  957.  
  958.  
  959. #ifdef DOC
  960.  
  961. PROTOTYPE
  962.  
  963. int DestroyMidiChannel(MidiChannelT *channel);
  964.  
  965. DESCRIPTION
  966.  
  967. Closes a midi channel, and deallocates all memory and resources used
  968. by that channel. Removes the MPU-401 interrupt handler.
  969.  
  970. RETURNS
  971.  
  972. 0 -> Success
  973. non-zero = MidiErrorT error code.
  974. #endif
  975.  
  976. int DestroyMidiChannel(MidiChannelT *channel) {
  977.     int status;
  978.     DestroyMpu(channel->mpu_port);
  979.     CancelScheduledMessages(channel);
  980.     free(channel);
  981.     return 0;
  982. }
  983.  
  984. #ifdef DOC
  985.  
  986. PROTOTYPE
  987.  
  988. BOOL SetMidiMessage(
  989.     MidiMessageT *msg,
  990.     UCHAR midi_command, 
  991.     UCHAR data1, 
  992.     UCHAR data2,
  993.     unsigned int sysex_length,    // Must be zero for non-SYSEX commands!
  994.     UCHAR *sysex_data            
  995. );
  996.  
  997.  
  998. DESCRIPTION
  999.  
  1000. Set the data of the supplied midi message.
  1001.  
  1002. Note that sysex data (if supplied) is copied into an in internal
  1003. memory block allocated from free store. The caller is responsible 
  1004. for deallocating the SUPPLIED sysex data (if applicable).
  1005.  
  1006. For sysex messages, data1 and data2 are unused. sysex_data must 
  1007. point to the first byte of sysex data following the sysex command.
  1008.  
  1009. The sequence of operations for sending a midi message:
  1010.  
  1011.     msg = AllocMidiMessage();
  1012.     SetMidiMessage(msg,cmd,data1,data2,0,NULL);
  1013.                 
  1014.     SendMidiMessage(midiChannel,msg);
  1015.         or
  1016.     SendScheduleMidiMessage(midiChannel,time,msg);
  1017.  
  1018. Note that a copy of the sysex data is made in heap memory, that will
  1019. be deallocated when the message is freed (or sent). This may pose a 
  1020. problem for large sysex messages.
  1021.  
  1022. RETURNS
  1023.  
  1024. No -> insufficient memory for copy of sysex data.
  1025.  
  1026. #endif
  1027.  
  1028. BOOL SetMidiMessage(
  1029.     MidiMessageT *msg,
  1030.     UCHAR midi_command, 
  1031.     UCHAR data1, 
  1032.     UCHAR data2,
  1033.     unsigned int sysex_length,    // Must be zero for non-SYSEX commands!
  1034.     UCHAR *sysex_data            
  1035. )
  1036. {
  1037.     msg->midiCommand = midi_command;
  1038.     msg->midiData[0] = data1;
  1039.     msg->midiData[1] = data2;
  1040.  
  1041.     msg->sysexLength = sysex_length;
  1042.     msg->sysexData = NULL;
  1043.     if (sysex_length != 0) {
  1044.         msg->sysexData = calloc(1,max(sysex_length,sizeof(void *)));
  1045.         if (msg->sysexData == NULL) {
  1046.             return NO;
  1047.         }
  1048.         memcpy(msg->sysexData,sysex_data,sysex_length);
  1049.     }
  1050.     return YES;
  1051. }
  1052. #ifdef DOC
  1053.  
  1054. PROTOTYPE
  1055.  
  1056. void GetMidiMessageData(
  1057.     MidiMessageT *msg,
  1058.     UCHAR *midi_cmd, // Receives midi command
  1059.     UCHAR *data1,     // Receives data1 (if not NULL)
  1060.     UCHAR *data2     // Receives data2 (if not NULL)
  1061. );
  1062.  
  1063. DESCRIPTION
  1064.  
  1065. Get data for current midi message.
  1066.  
  1067. The sequence for reading a midi message:
  1068.     
  1069.     msg = ReceiveMessage(channel);
  1070.     if (msg == (MidiMessageT *)(-1)) {
  1071.         handleMidiError(MidiStatus(channel));
  1072.         return;
  1073.     }
  1074.     if (msg != NULL) {
  1075.         GetMidiMessageData(msg,&cmd,&data1,&data2);
  1076.     }
  1077. #endif
  1078.  
  1079. void GetMidiMessageData(
  1080.     MidiMessageT *msg,
  1081.     UCHAR *midi_cmd, // Receives midi command
  1082.     UCHAR *data1,     // Receives data1 (if not NULL)
  1083.     UCHAR *data2     // Receives data2 (if not NULL)
  1084. ) {
  1085.     *midi_cmd = msg->midiCommand;
  1086.     if (data1 != NULL)
  1087.         *data1 = msg->midiData[0];
  1088.     if (data2 != NULL)
  1089.         *data2 = msg->midiData[1];
  1090. }
  1091.  
  1092. #ifdef DOC
  1093.  
  1094. PROTOTYPE
  1095.  
  1096. int MidiStatus(MidiChannelT *channel);
  1097.  
  1098. DESCRIPTION
  1099.  
  1100. Returns the error status code of the specified Midi channel.
  1101.  
  1102. The only way to clear the error status is to Destroy the channel 
  1103. and recreate it.
  1104.  
  1105. RETURNS
  1106.  
  1107. 0 -> No error
  1108. non-zero = enum MidiErrorT.
  1109. #endif
  1110.  
  1111. int MidiStatus(MidiChannelT *channel) {
  1112.     if (channel->errno != 0) {
  1113.         return channel->errno;
  1114.     }
  1115.     return GetMpuStatus(channel->mpu_port);
  1116. }
  1117.  
  1118. #ifdef DOC
  1119.  
  1120. PROTOTYPE
  1121.  
  1122. char *MidiErrorString(int error_code);
  1123.  
  1124. DESCRIPTION
  1125.  
  1126. Returns a string error message corresponding to the specified
  1127. (enum MidiErrorT or enum MpuErrorT) error code as returned by
  1128. either MidiStatus or CreateMidiChannel. Error codes originating
  1129. from either the MIDI module, or the MPU module are handled correctly.
  1130.  
  1131. #endif
  1132.  
  1133. char *MidiErrorString(int error_code) {
  1134.     if (error_code >= LOWEST_MIDI_ERROR) {
  1135.         return MidiErrorStrings[error_code-LOWEST_MIDI_ERROR];
  1136.     }
  1137.     return MpuErrorString(error_code);
  1138. }
  1139.  
  1140. #ifdef DOC
  1141. PROTOTYPE
  1142.  
  1143. int MidiMessagesPending(MidiChannelT *channel);
  1144.  
  1145. DESCRIPTION
  1146.  
  1147. Returns the number of scheduled messages which have not been sent
  1148. to the MPU-401.
  1149.  
  1150. This function provides a useful technique for controlling the amount
  1151. of memory used by the midi messages queue:
  1152.  
  1153.     if (MidiMessagesPending(channel) < 300) // or some arbitrary #
  1154.             sendNextMidiMessage();
  1155.  
  1156. #endif
  1157.  
  1158. int MidiMessagesPending(MidiChannelT *channel) {
  1159.     return channel->midiMessagesPending;
  1160. }
  1161.  
  1162.  
  1163. #ifdef DOC
  1164. PROTOTYPE
  1165.  
  1166. void GetMidiMessageSysexData(
  1167.     MidiMessageT *msg,
  1168.     int *sysexLength,
  1169.     UCHAR **sysexData    // Filled if not null
  1170. );
  1171.  
  1172. DESCRIPTION
  1173.  
  1174. This routine returns the length and a pointer to the extra data
  1175. bytes sent or received with a sysex message.
  1176.  
  1177. The pointer remains valid until the message is sent or freed. (Clients
  1178. should NOT free the pointer themselves).
  1179.  
  1180. The first byte of sysex data is the byte which immediately follows the 
  1181. Sysex (FF) midi command. The last byte of sysex data must be EOX_MSG (0xF7).
  1182. The contents of data1 and data2 as returned by GetMidiMessageData are 
  1183. unspecified when a sysex command is sent or recieved.
  1184.  
  1185. sysexLength includes the EOX_MSG byte at the end of the sysex message.
  1186.  
  1187. BUGS
  1188.  
  1189. In truth, midi sysex messages may be terminated by ANY Midi status byte.
  1190. The current code only handles MIDI sysex messages terminated by EOX_MSG.
  1191. This is probably a fairly major ommission, although I have been 
  1192. lucky enough not to have run into a machine which doesn't terminate 
  1193. sysex messages with EOX_MSG. You have been warned.
  1194.  
  1195. #endif
  1196. void GetMidiMessageSysexData(
  1197.     MidiMessageT *msg,
  1198.     int *sysexLength,
  1199.     UCHAR **sysexData    // Filled if not null
  1200. ) {
  1201.     *sysexLength = msg->sysexLength;
  1202.     *sysexData = msg->sysexData;
  1203. }
  1204.  
  1205. #ifdef DOC
  1206.  
  1207. PROTOTYPE
  1208.  
  1209. MidiTimeT GetMidiMessageReceiveTime(
  1210.     MidiMessageT *msg
  1211. );
  1212.  
  1213. DESCRIPTION
  1214.  
  1215. Returns the time that the message was received by the MPU-401 in ticks
  1216. since recording started. Results are unspecified for messages which 
  1217. haven't been obtained through ReceiveMidiMessage().
  1218.  
  1219. #endif
  1220.  
  1221. MidiTimeT GetMidiMessageReceiveTime(
  1222.     MidiMessageT *msg
  1223. ) {
  1224.     return msg->time;
  1225. }
  1226.