home *** CD-ROM | disk | FTP | other *** search
/ HomeWare 14 / HOMEWARE14.bin / music / vaisdk.arj / PMIDI.C < prev    next >
C/C++ Source or Header  |  1994-04-10  |  51KB  |  1,903 lines

  1.  
  2. ;   /*\
  3. ;---|*|----====< Play MIDI >====----
  4. ;---|*|
  5. ;---|*| play/record blocks of MIDI
  6. ;---|*|
  7. ;---|*| Copyright (c) 1993,1994  V.E.S.A, Inc. All Rights Reserved.
  8. ;---|*|
  9. ;---|*| VBE/AI 1.0 Specification
  10. ;---|*|    February 2, 1994. 1.00 release
  11. ;---|*|
  12. ;---|*| Additional Changes:
  13. ;---|*|   02/16 - Added a switch to allow sending Active Sensing as an option.
  14. ;---|*|   02/16 - Added a routine to send All Notes Off before exiting.
  15. ;---|*|   02/27 - Removed a bunch of unused code to clean up the pgm
  16. ;---|*|   03/10 - Added cheap quantization of the tempo to reduce the
  17. ;---|*|           interrupt rate to drive the tempo.
  18. ;---|*|   04/05 - Changed the handling of the delta and track processing
  19. ;---|*|           because the cheap quantization threw things out of order.
  20. ;---|*|
  21. ;   \*/
  22.  
  23. #include <stdio.h>
  24. #include <signal.h>
  25.  
  26. #include "vbeai.h"
  27. #include "vesa.h"
  28.  
  29. #define TRUE    -1
  30. #define FALSE    0
  31. #define ON      TRUE
  32. #define OFF     FALSE
  33.  
  34. #if DEBUG
  35. #define BREAKPOINT _asm{int 3};
  36. #else
  37. #define BREAKPOINT _asm{nop};
  38. #endif
  39.  
  40.  
  41. ;   /*\
  42. ;---|*| External Variables
  43. ;   \*/
  44.  
  45.         extern char *melodicnames[];
  46.         extern char *percussivenames[];
  47.  
  48.  
  49. ;   /*\
  50. ;---|*| Global Variables
  51. ;   \*/
  52.  
  53.         typedef struct {
  54.             char fileid[4];     // file ID - "MThd"
  55.             long flen;          // header length
  56.             int  ftype;         // midi file type
  57.             int  ftracks;       // number of tracks
  58.             int  fdiv;          // time division
  59.         } MIDIheader;
  60.  
  61.         typedef struct {
  62.             char fileid[4];     // file ID - "MTrk"
  63.             long flen;          // midi track len
  64.         } MDtrack;
  65.  
  66.         typedef struct {
  67.             MDtrack mt;         // Track Header
  68.             int  tnum;          // track number
  69.             long delta;         // delta time counting
  70.             char far *p;        // pointer to the data
  71.         } MIDItrack;
  72.  
  73.         int  deltastep = 1;     // incrementing step count
  74.         long deltacount= 0;     // full delta count
  75.  
  76. #define MAXTRACKS   32
  77.         MIDIheader mhdr     = { 0 };    // midi file header
  78.         MIDItrack  mtrk[32] = { 0 };    // track structures
  79.  
  80.         int  CallBackOccured;
  81.         int  clocktick = 0;
  82.         int  ScatteredMessages = TRUE;
  83.  
  84.         VESAHANDLE hMIDI  = 0;
  85.  
  86.         FILE *rfile;
  87.         char far *memptr  = 0;
  88.         char far *midptr  = 0;
  89.         char *MIDFileName;
  90.  
  91.         GeneralDeviceClass gdc; // receives a copy of the VESA driver info block
  92.         fpMIDServ msv;          // pointer to MIDI functions
  93.  
  94. #define FILE_UNK    0
  95. #define FILE_MIDI   1
  96.         int  filetype = FILE_UNK;
  97.  
  98.         char DoTracks[16] = {
  99.             TRUE, TRUE, TRUE, TRUE,
  100.             TRUE, TRUE, TRUE, TRUE,
  101.             TRUE, TRUE, TRUE, TRUE,
  102.             TRUE, TRUE, TRUE, TRUE
  103.         };
  104.  
  105.         unsigned char PatchXlate[256];  // patch translation table
  106.         unsigned char ChannelXlate[16]; // channel translation table
  107.  
  108.         int  keyoffset      = 0;        // key (note) offset by one octave
  109.         int  patchoffset    = 0;        // no patch offset
  110.         int  tempooffset    = 0;        // no tempo offset
  111.         int  MicrosoftGMfmt = FALSE;    // microsoft GM file format
  112.         long micros         = 500000;   // 500ms per quarter note
  113.         int  sigdenom       = 2;        // qnote gets the beat
  114.         int  SystemMsgTick  = 0;        // system msg (0xFE) needed
  115.         int  msgsent        = FALSE;    // any msg has been sent
  116.         int  ActiveSensing  = FALSE;    // defaults to OFF
  117.  
  118.  
  119. ;   /*\
  120. ;---|*| prototypes
  121. ;   \*/
  122.  
  123.         int  LoadMIDITrack          ( MIDItrack * );
  124.         int  LoadMIDIFile           ( );
  125.         int  ProcessMidiMsg         ( MIDItrack * );
  126.         int  SystemMessage          ( unsigned int, MIDItrack * );
  127.  
  128.         long GetValue               ( char *, long );
  129.         int  readblock              ( int, char huge *, int );
  130.  
  131.         int  swapdw                 ( int );
  132.         long swapdd                 ( long );
  133.         long ReadVariable           ( MIDItrack * );
  134.  
  135.         void far pascal OurSystemMSGCallBack  ( );
  136.         void far pascal OurTimerCallBack  ( );
  137.         void far pascal OurMIDIReceiver   ( int, int, char, long );
  138.  
  139.         void SetSongTempo           ( long, int );
  140.         VESAHANDLE OpenTheDriver    ( int );
  141.         void OutputMidiMsg          ( char far *, int );
  142.              PrintMsg               ( int, char far *, int );
  143.  
  144.         int  DriverError;
  145.         int  VerboseMode  = FALSE;
  146.         int  StatusMode   = FALSE;
  147.         int  UserPref     = 0;      // highest level to be used
  148.         int  maxtones;              // maximum # of tones supported by the h/w
  149.         int  InputOnly    = FALSE;  // MIDI receiver tests
  150.  
  151.         char *msgnames[8] = {
  152.             "Note Off", // 0x80
  153.             "Note On ", // 0x90
  154.             "Poly Key", // 0xA0
  155.             "Ctlr Chg", // 0xB0
  156.             "Pgm  Chg", // 0xC0
  157.             "Chan Pre", // 0xD0
  158.             "Pitch wh", // 0xE0
  159.             "System  "  // 0xF0
  160.         };
  161.  
  162.     // only allows us to run on this version of the VBE interface. This
  163.     // will be removed after the interface is ratified. Changes may be made
  164.     // that would cause this code to crash if run on other version. Again,
  165.     // this will be removed in the final software.
  166.  
  167.         int  VersionControl = 0x0100;
  168.  
  169.  
  170. ;   /*\
  171. ;---|*|------------------==============================-------------------
  172. ;---|*|------------------====< Start of execution >====-------------------
  173. ;---|*|------------------==============================-------------------
  174. ;   \*/
  175.  
  176. main(argc,argv)
  177.     int argc;
  178.     char *argv[];
  179. {
  180. int loop = TRUE;
  181. int n,c;
  182. int maxticks,ticks;
  183. int NotHalted;
  184. int exitcode = 0;
  185. long l;
  186. int trk,deadcnt;
  187. int laps = 10;
  188. int all,nxt;
  189.  
  190.     // process the command line
  191.  
  192.         CommandLine (argc,argv);
  193.  
  194.     // disable the ^C so the devices will close properly.
  195.  
  196.         signal (SIGINT,SIG_IGN);
  197.  
  198.     // Find one MIDI device
  199.  
  200.         if (!OpenTheDriver(UserPref)) { // get the driver the user prefer
  201.             printf ("Cannot find any installed VBE/AI devices!\n");
  202.             DoExit(-1);
  203.         }
  204.  
  205.     // This is playback, so load the MIDI file header, then each track
  206.  
  207.         if (!LoadMIDIFile())
  208.             DoExit(-1);
  209.  
  210.         for (n=0;n<mhdr.ftracks;n++) {
  211.             mtrk[n].p=midptr;
  212.             LoadMIDITrack(&mtrk[n]);
  213.             midptr += mtrk[n].mt.flen;              // get the next ptr
  214.             mtrk[n].delta = ReadVariable(&mtrk[n]); // read the 1st delta from the file
  215.         }
  216.         deltacount = 0;
  217.  
  218.     // setup a callback for active system messages
  219.  
  220.         VESARegisterTimer( 254, &OurSystemMSGCallBack, VESARateToDivisor(5)   );
  221.         VESARegisterTimer( 255, &OurTimerCallBack,     VESARateToDivisor(120) );
  222.  
  223.     // process the tracks
  224.  
  225.         while (loop) {
  226.  
  227.         // see if we need an active sensing message to keep the device active
  228.  
  229.             KeepActive();
  230.  
  231.         // Callbacks occur to give us a tempo for MIDI timing
  232.  
  233.             if (CallBackOccured) {
  234.  
  235.                 ReportCallback();
  236.                 CallBackOccured--;
  237.  
  238.                 // if we have run out of data, exit the program...
  239.  
  240.                 deltacount += deltastep;
  241.  
  242.                 while(1) {
  243.  
  244.                     // find the track with the lowest delta time
  245.  
  246.                     deadcnt = 0;
  247.                     for (nxt=n=0;n<mhdr.ftracks;n++) {
  248.  
  249.                         if (mtrk[n].mt.flen > 0) {
  250.                             if (mtrk[n].delta < mtrk[nxt].delta)
  251.                                 nxt = n;
  252.                         }
  253.                         else {
  254.                             deadcnt++;
  255.                             if (nxt == n) nxt++;
  256.                         }
  257.                     }
  258.  
  259.                     // if all tracks are dead, exit
  260.  
  261.                     if (deadcnt >= mhdr.ftracks)
  262.                         break;
  263.  
  264.                     // if higher that the current delta count, exit
  265.  
  266.                     if (mtrk[nxt].delta > deltacount)
  267.                         break;
  268.  
  269.                     ProcessMidiMsg(&mtrk[nxt]);
  270.  
  271.                 }
  272.             }
  273.  
  274.         // see if the user wishes to exit
  275.  
  276.             if (kbhit()) {
  277.  
  278.                 switch (c = getch()) {
  279.  
  280.                     // quit the program
  281.  
  282.                     case 's':   // do a fast forward in the song
  283.                     case 'S':
  284.                         for (n=0;n<mhdr.ftracks;n++)
  285.                             mtrk[n].delta = deltacount;
  286.                         break;
  287.  
  288.                     case 0x20:
  289.  
  290.                         // flush our callback control
  291.  
  292.                         VESARegisterTimer( 255, 0, 0 );
  293.  
  294.                         // wait on the user...
  295.  
  296.                         printf ("Song Now Paused!\n");
  297.                         while (!kbhit())
  298.                             KeepActive();
  299.                         getch();
  300.  
  301.                         // set the tempo & let'er rip!
  302.  
  303.                         SetSongTempo ( micros, sigdenom );
  304.                         break;
  305.  
  306.                     case 0x1b:
  307.                         AllNotesOff();
  308.                         loop = 0;
  309.                         exitcode = TRUE;
  310.  
  311.                     default:
  312.                         break;
  313.  
  314.                 }
  315.             }
  316.  
  317.         // if the driver posts an error, go report it
  318.  
  319.             if ( (DriverError = (msv->msGetLastError)()) )
  320.                 printf ("Driver is reporting an internal error! (code=%d)\n",DriverError);
  321.  
  322.         // if in status mode, dump status every x times through the loop
  323.  
  324.             if (StatusMode) {
  325.                 if (!--laps) {
  326.                     laps = 10;
  327.                     DumpSomeInfo();
  328.                 }
  329.             }
  330.         }
  331.  
  332.     // exit now...
  333.  
  334.         DoExit(exitcode);
  335. }
  336.  
  337. ;   /*\
  338. ;---|*|----------------------=======================---------------------------
  339. ;---|*|----------------------====< Subroutines >====---------------------------
  340. ;---|*|----------------------=======================---------------------------
  341. ;   \*/
  342.  
  343. ;   /*\
  344. ;---|*|----====< AllNotesOff >====----
  345. ;---|*|
  346. ;---|*| Turn each track off
  347. ;---|*|
  348. ;   \*/
  349. AllNotesOff()
  350. {
  351. int n;
  352. char msg[3] = { 0,0x7b,0x00 };
  353.  
  354.     for(n=0;n<16;n++) {
  355.         msg[0] = 0xb0 + n;
  356.         OutputMidiMsg (msg,3);
  357.     }
  358. }
  359.  
  360.  
  361. ;   /*\
  362. ;---|*|----====< CommandLine >====----
  363. ;---|*|
  364. ;---|*| Process the command line switches
  365. ;---|*|
  366. ;   \*/
  367. CommandLine(argc,argv)
  368.     int argc;
  369.     char *argv[];
  370. {
  371. int n,x;
  372. char *s,c;
  373. FILE *mapnam;
  374. int vh,vl;
  375.  
  376.     // print the hello...
  377.  
  378.         vh = VersionControl >> 8;
  379.         vl = VersionControl &  0xFF;
  380.  
  381.         printf ("\nVESA VBE/AI MIDI Output Test Program, %02x.%02x\n",vh,vl);
  382.         printf ("Copyright (c) 1993,1994  VESA, Inc. All Rights Reserved.\n\n");
  383.  
  384.     // setup the patch translation table defaults
  385.  
  386.         for (n=0;n<256;n++)
  387.             PatchXlate[n] = n & 0x7F;   // only 7 bits worth
  388.  
  389.     // exit if no other parameters
  390.  
  391.         if (argc < 2) {
  392.             GiveHelps();
  393.             DoExit(0);
  394.         }
  395.  
  396.         MIDFileName = argv[1];
  397.         printf ("Attempting to use \"%s\" input file",MIDFileName);
  398.         filetype = FILE_MIDI;
  399.         printf (" as a MIDI file\n");
  400.  
  401.         n = 2;
  402.         while (n<argc) {
  403.  
  404.             s = argv[n++];
  405.  
  406.             if (*s == '-') s++;
  407.             if (*s == '/') s++;
  408.  
  409.             switch (*s & 0xDF) {
  410.  
  411.                 case 'A':
  412.                     if (*++s == '-')
  413.                         ActiveSensing = FALSE;
  414.                     else
  415.                         ActiveSensing = TRUE;
  416.  
  417.                     printf ("Active sensing is turned ");
  418.                     (ActiveSensing) ? printf ("ON\n") : printf ("OFF\n");
  419.                     break;
  420.  
  421.                 case 'C':
  422.  
  423.                     printf ("Using Channel map file \"%s\"\n",++s);
  424.  
  425.                     if (!(mapnam = fopen (s,"rb")) ) {
  426.                         printf ("Unable to open the channel map file!\n");
  427.                         DoExit(-1);
  428.                     }
  429.                     if (fread (&ChannelXlate,sizeof(char),16,mapnam) != 16) {
  430.                         printf ("Unable to load the whole channel map file!\n");
  431.                         DoExit(-1);
  432.                     }
  433.  
  434.                     // if the channel is 0xFF, kill it...
  435.  
  436.                     BREAKPOINT
  437.  
  438.                     for (n=0;n<16;n++)
  439.                         if (ChannelXlate[n] == 0xFF)
  440.                             DoTracks[n] = FALSE;
  441.  
  442.                     fclose (mapnam);
  443.  
  444.                     break;
  445.  
  446.                 case 'F':
  447.                     ScatteredMessages = FALSE;
  448.                     printf ("Will process messages in full length\n");
  449.                     break;
  450.  
  451.                 case 'H':
  452.                     for (x=0;x<16;x++) {
  453.                         if (x < 12)
  454.                             DoTracks[x] = TRUE;
  455.                         else
  456.                             DoTracks[x] = FALSE;
  457.                     }
  458.                     printf ("High end synth for tracks 1 - 12\n");
  459.                     break;
  460.  
  461.                 case 'I':
  462.                     InputOnly = TRUE;
  463.                     printf ("In INPUT mode only - no output performed\n");
  464.                     break;
  465.  
  466.                 case 'K':
  467.                     s++;
  468.  
  469.                     keyoffset = 1;      // there will be an offset
  470.  
  471.                     if (*s == '-')      // get the PLUS or MINUS direction
  472.                         c = *s++;
  473.  
  474.                     if (*s == '+')
  475.                         c = *s++;
  476.  
  477.                     keyoffset = (int)GetValue(s,(unsigned long)keyoffset);
  478.  
  479.                     if (c == '-')
  480.                         keyoffset = -keyoffset;
  481.  
  482.                     printf ("Key offset by %d keys\n",keyoffset);
  483.                     break;
  484.  
  485.                 case 'L':
  486.                     MicrosoftGMfmt = TRUE;
  487.                     for (x=0;x<16;x++) {
  488.                         if (x < 12)
  489.                             DoTracks[x] = FALSE;
  490.                         else
  491.                             DoTracks[x] = TRUE;
  492.                     }
  493.  
  494.                     printf ("Low end synth for tracks 13 - 16\n");
  495.                     break;
  496.  
  497.                 case 'M':
  498.  
  499.                     printf ("Using patch Map file \"%s\"\n",++s);
  500.  
  501.                     if (!(mapnam = fopen (s,"rb")) ) {
  502.                         printf ("Unable to open the patch map file!\n");
  503.                         DoExit(-1);
  504.                     }
  505.  
  506.                     if (fread (&PatchXlate,sizeof(char),256,mapnam) != 256) {
  507.                         printf ("Unable to load the whole patch map file!\n");
  508.                         DoExit(-1);
  509.                     }
  510.  
  511.                     fclose (mapnam);
  512.  
  513.                     break;
  514.  
  515.                 case 'P':
  516.                     UserPref     = (int)GetValue (++s,0);
  517.                     printf ("User Prefers (%d) level drivers\n",UserPref);
  518.                     break;
  519.  
  520.                 case 'R':               // program change offset
  521.                     s++;
  522.  
  523.                     patchoffset = 1;    // there will be an offset
  524.  
  525.                     c = 0;
  526.                     if (*s == '-')      // get the PLUS or MINUS direction
  527.                         c = *s++;
  528.  
  529.                     if (*s == '+')
  530.                         c = *s++;
  531.  
  532.                     patchoffset = (int)GetValue(s,(unsigned long)patchoffset);
  533.  
  534.                     if (c == '-')
  535.                         patchoffset = -patchoffset;
  536.  
  537.                     printf ("Patch offset by %d program steps\n",patchoffset);
  538.                     break;
  539.  
  540.                 case 'S':               // status dumps
  541.                     StatusMode = GetValue(++s,(StatusMode = 10));
  542.                     printf ("Status mode = %d\n",StatusMode);
  543.                     break;
  544.  
  545.                 case 'T':               // tempo offset percentage
  546.                     s++;
  547.  
  548.                     c = 0;
  549.                     if (*s == '-')      // get the PLUS or MINUS direction
  550.                         c = *s++;
  551.                     if (*s == '+')
  552.                         c = *s++;
  553.  
  554.                     tempooffset = (int)GetValue(s,10L);
  555.  
  556.                     if (c == '-')
  557.                         tempooffset = -tempooffset;
  558.  
  559.                     printf ("Tempo offset by %d%%\n",tempooffset);
  560.                     break;
  561.  
  562.                 case 'V':   // print everything at a given level
  563.                     VerboseMode = GetValue(++s,(VerboseMode = 10));
  564.                     printf ("Verbose level = %d\n",VerboseMode);
  565.                     break;
  566.  
  567.                 default:
  568.                     printf ("Unknown option - %s\n",s);
  569.                     DoExit(-1);
  570.             }
  571.         }
  572.  
  573.     // separate the parameter text from the rest of the report
  574.  
  575.         printf ("\n");
  576. }
  577.  
  578.  
  579. ;   /*\
  580. ;---|*|----====< DoExit >====----
  581. ;---|*|
  582. ;---|*| Shut everything down, then exit to DOS
  583. ;---|*|
  584. ;   \*/
  585. DoExit(cc)
  586.     int cc;
  587. {
  588.  
  589.     // close the device if already opened
  590.  
  591.         if (msv)
  592.             VESACloseDevice(hMIDI);     // close the device
  593.  
  594.     // flush our callback control
  595.  
  596.         VESARegisterTimer( 255, 0, 0 );
  597.         VESARegisterTimer( 254, 0, 0 );
  598.  
  599.     // return to DOS, don't return to caller
  600.  
  601.         exit(cc);
  602. }
  603.  
  604.  
  605. ;   /*\
  606. ;---|*|----====< ProcessMidiMsg >====----
  607. ;---|*|
  608. ;---|*| Process the MIDI notes
  609. ;---|*|
  610. ;   \*/
  611.  
  612. int ProcessMidiMsg(m)
  613.     MIDItrack *m;
  614. {
  615. unsigned int status;
  616. char far *msg;
  617. char far *patch;
  618. int  psize,ch,doit,idx;
  619. unsigned int ptch;
  620.  
  621. static  unsigned int running = 0;
  622.  
  623.     // if no more data, bail...
  624.  
  625.         if (!m->mt.flen)
  626.             return(0);
  627.  
  628.     // get the status, make it running
  629.  
  630.         status = running;
  631.  
  632.     // maintain running status
  633.  
  634.         if (*m->p & 0x80) {
  635.             if (((status = *m->p++ & 0xFF) & 0xF0) != 0xF0)
  636.                 running = status;
  637.             m->mt.flen--;
  638.         }
  639.  
  640.     // always send the message. Running status will be done later.
  641.  
  642.         *(msg=m->p-1) = status;       // load the message into memory
  643.  
  644.     // make sound if this is a track we want to hear
  645.  
  646.         if (DoTracks[ch=status & 0x0F]) {
  647.  
  648.             switch (status & 0xF0) {
  649.  
  650.                 case 0x80:  // Note Off
  651.  
  652.                     if(((ch==9)&&DoTracks[9]) || ((MicrosoftGMfmt)&&(ch==15))) {
  653.                         msg[0] = 0x89;          // make it channel #9
  654.                         msg[1] = PatchXlate[(msg[1]+0x80) & 0xFF] & 0x7F;
  655.                     }
  656.                     else {
  657.                         msg[1] += keyoffset;    // offset the note
  658.                         msg[1] &= 0x7F;         // keep it in 7 bits
  659.                     }
  660.  
  661.                     PrintMsg (9,msg,3);         // display the msg
  662.                     OutputMidiMsg (msg,3);      // send it to the h/w
  663.                     m->mt.flen -= 2;            // adjust the length
  664.                     m->p += 2;
  665.                     break;
  666.  
  667.                 case 0x90:  // Note On
  668.  
  669.                     // make sure all GM MIDI percussive channel patches
  670.                     // are loaded before starting
  671.  
  672.                     if  ( ((ch==9)&&DoTracks[9]) || ((MicrosoftGMfmt)&&(ch == 15)) ) {
  673.  
  674.                         // for Microsofts MIDI channel 15 to channel 9
  675.  
  676.                         if ((ch == 15) && MicrosoftGMfmt)
  677.                             msg[0] = 0x99;  // make it channel #9
  678.  
  679.                         // translate the patch from the 2nd half of the tbl
  680.  
  681.                         ptch = PatchXlate[(msg[1] + 128) & 0xFF];
  682.  
  683.                         // if there are external patches, load them now
  684.  
  685.                         if (gdc.u.gdmi.milibrary[0]) {
  686.  
  687.                             // skip if preloaded
  688.  
  689.                             if (!PatchIsLoaded(ptch+0x80)) {
  690.  
  691.                                 // load the percussive patch now
  692.  
  693.                                 VESAPreLoadPatch(hMIDI, ptch, (msg[0]&0x0F));
  694.  
  695.                                 if (VerboseMode >= 7)
  696.                                     printf ("%s  ",percussivenames[ptch]);
  697.                                 PrintMsg (7,msg,3);
  698.  
  699.                             }
  700.                         }
  701.  
  702.                         // save the translated patch #
  703.  
  704.                         msg[1] = ptch;
  705.  
  706.                     }
  707.                     else {
  708.  
  709.                         msg[1] += keyoffset;    // offset the note
  710.                         msg[1] &= 0x7F;         // keep it in 7 bits
  711.  
  712.                     }
  713.  
  714.                     PrintMsg (9,msg,3);         // display the msg
  715.                     OutputMidiMsg (msg,3);      // send it to the h/w
  716.                     m->mt.flen -= 2;            // adjust the length
  717.                     m->p += 2;
  718.                     break;
  719.  
  720.                 case 0xA0:  // Poly Key Pressure
  721.  
  722.                     if(((ch==9)&&DoTracks[9]) || ((MicrosoftGMfmt)&&(ch == 15)))
  723.                         msg[1] = PatchXlate[(msg[1] + 0x80) & 0xFF] & 0x7F;
  724.                     else
  725.                         msg[1] += keyoffset;    // offset the note
  726.  
  727.                     msg[1] &= 0x7F;             // keep it in 7 bits
  728.  
  729.                     // falls through...
  730.  
  731.                 case 0xB0:  // Control Change
  732.                 case 0xE0:  // Pitch Wheel change
  733.  
  734.                     PrintMsg (6,msg,3);         // display the msg
  735.                     OutputMidiMsg (msg,3);      // send it to the h/w
  736.                     m->mt.flen -= 2;            // adjust the length
  737.                     m->p += 2;
  738.                     break;
  739.  
  740.                 case 0xC0:  // Program Change
  741.  
  742.                     // handle the percussive channel separate from melodics
  743.  
  744.                     if (((ch==9)&&DoTracks[9]) || ((MicrosoftGMfmt)&&(ch==15))) {
  745.  
  746.                         // patch xlate on percussive patches
  747.  
  748.                         ptch = (msg[1] = PatchXlate[(msg[1] | 0x80) & 0xFF]);
  749.  
  750.                         // if MSGMFMT & channel 15, force to channel #9
  751.  
  752.                         if  ((MicrosoftGMfmt)&&(ch == 15)) {
  753.                             msg[0] = 0xC9;  // make it channel #9
  754.                         }
  755.  
  756.                         if (VerboseMode >= 7)
  757.                             printf ("%s  ",percussivenames[ptch]);
  758.                         PrintMsg (7,msg,2);
  759.                     }
  760.                     else {
  761.  
  762.                         // patch xlate on melodic patches
  763.  
  764.                         ptch =  PatchXlate[msg[1] & 0x7F];  // translate to a new patch
  765.                         ptch =  (ptch + patchoffset) & 0x7F;// offset the patch
  766.                         msg[1] = ptch;                      // save the patch
  767.  
  768.                         if (VerboseMode >= 7)
  769.                             printf ("%s  ",melodicnames[ptch]);
  770.                         PrintMsg (7,msg,2);
  771.                     }
  772.  
  773.                     // if there is an external library, load a patch now
  774.  
  775.                     if (gdc.u.gdmi.milibrary[0]) {
  776.  
  777.                         // if preloaded, skip the process
  778.  
  779.                         doit=FALSE;
  780.                         if (((ch==9)&&DoTracks[9]) || ((MicrosoftGMfmt)&&(ch==15))) {
  781.                             if (!PatchIsLoaded(ptch+0x80))
  782.                                 doit=TRUE;
  783.                         }
  784.                         else {
  785.                             if (!PatchIsLoaded(ptch))
  786.                                 doit=TRUE;
  787.                         }
  788.  
  789.                         // if percussive or melodic needs one, do it
  790.  
  791.                         if (doit) {
  792.  
  793.                             // get the patch & get the size
  794.  
  795.                             VESAPreLoadPatch( hMIDI, ptch, msg[0]&0x0F );
  796.  
  797.                         }
  798.                     }
  799.                     OutputMidiMsg (msg,2);
  800.                     m->mt.flen--;               // adjust the length
  801.                     m->p++;
  802.                     break;
  803.  
  804.                 case 0xD0:  // Channel Pressure
  805.                     PrintMsg (6,msg,2);
  806.                     OutputMidiMsg (msg,2);
  807.                     m->mt.flen--;               // adjust the length
  808.                     m->p++;
  809.                     break;
  810.  
  811.                 case 0xF0:
  812.                     PrintMsg (6,msg,5);
  813.                     SystemMessage (status,m);
  814.                     break;
  815.  
  816.                 default:
  817.                     PrintMsg (1,msg,5);
  818.                     printf ("Broken MIDI stream! (no status/running status!)\n");
  819.                     DoExit(1);
  820.             }
  821.         }
  822.  
  823.     // toss out these tracks
  824.  
  825.         else {
  826.  
  827.             switch (status & 0xF0) {
  828.  
  829.                 case 0x80:  // Note Off
  830.                 case 0x90:  // Note On
  831.                 case 0xA0:  // Poly Key Pressure
  832.                 case 0xB0:  // Control Change
  833.                 case 0xE0:  // Pitch Wheel change
  834.  
  835.                     if (VerboseMode >= 9)
  836.                         printf ("   ");
  837.  
  838.                     PrintMsg (9,msg,3);
  839.                     m->mt.flen -= 2;            // adjust the length
  840.                     m->p += 2;
  841.                     break;
  842.  
  843.                 case 0xC0:  // Program Change
  844.                 case 0xD0:  // Channel Pressure
  845.  
  846.                     if (VerboseMode >= 9)
  847.                         printf ("   ");
  848.  
  849.                     PrintMsg (9,msg,2);
  850.                     m->mt.flen--;               // adjust the length
  851.                     m->p++;
  852.                     break;
  853.  
  854.                 case 0xF0:  // always do these...
  855.  
  856.                     PrintMsg (9,msg,5);
  857.                     SystemMessage (status,m);
  858.                     break;
  859.  
  860.                 default:
  861.  
  862.                     if (VerboseMode >= 9)
  863.                         printf ("   ");
  864.  
  865.                     PrintMsg (9,msg,5);
  866.                     printf ("Broken MIDI stream! (no status/running status!)\n");
  867.                     DoExit(1);
  868.             }
  869.         }
  870.  
  871.     // get the next midi msg, or bail out
  872.  
  873.         if (m->mt.flen)
  874.             m->delta += ReadVariable(m); // read the 1st delta from the file
  875.  
  876.     // return the adjusted track pointer
  877.  
  878.         return(-1);
  879. }
  880.  
  881. ;
  882. ;   /*\
  883. ;---|*|----====< OpenTheDriver >====----
  884. ;---|*|
  885. ;---|*| Find the driver with the highest user preference, and return it to
  886. ;---|*| the caller
  887. ;---|*|
  888. ;   \*/
  889. int OpenTheDriver(pref)
  890.     int pref;
  891. {
  892. int driverpref = 256;   // real low preference
  893. long l;
  894.  
  895.     // get the device information
  896.  
  897.         do {
  898.  
  899.             // Find one MIDI device, else ]ail if non found
  900.  
  901.                 if ((hMIDI = VESAFindADevice(MIDDEVICE)) == 0)
  902.                     return(0);
  903.  
  904.             // get the device information
  905.  
  906.                 if (VESAQueryDevice(hMIDI, VESAQUERY2 ,&gdc) == 0) {
  907.                     printf ("Cannot query the installed VBE/AI devices!\n");
  908.                     DoExit(-1);
  909.                 }
  910.             // make sure it's midi
  911.  
  912.                 if (gdc.gdclassid != MIDDEVICE) {
  913.                     printf ("The VESA find device query returned a NON MIDI device!\n");
  914.                     DoExit(-1);
  915.                 }
  916.  
  917.             // make sure it's matches the beta version #
  918.  
  919.                 if (gdc.gdvbever != VersionControl) {
  920.                     printf ("The VESA device version # does not match, cannot continue!\n");
  921.                     DoExit(-1);
  922.                 }
  923.  
  924.             // get the drivers user preference level
  925.  
  926.                 driverpref = gdc.u.gdmi.midevpref;
  927.  
  928.             // if the caller is not expressing a preference, then use this one
  929.  
  930.                 if (pref == -1)
  931.                     break;
  932.  
  933.         } while (driverpref != pref);
  934.  
  935.     // get the memory needed by the device
  936.  
  937.         if (!(memptr = AllocateBuffer(gdc.u.gdmi.mimemreq))) {
  938.             printf ("We don't have memory for the device!\n");
  939.             DoExit(-1);
  940.         }
  941.  
  942.         if (!(midptr = AllocateBuffer(0xffff))) {
  943.             printf ("We don't have memory for the loading the MIDI file!\n");
  944.             DoExit(-1);
  945.         }
  946.  
  947.     // if the MIDI device doesn't open, bomb out...
  948.  
  949.         if ((msv = (fpMIDServ)VESAOpenADevice(hMIDI,0,memptr)) == 0) {
  950.             printf ("Cannot Open the installed devices!\n");
  951.             DoExit(-1);
  952.         }
  953.  
  954.     // if there is a patch library, load it now...
  955.  
  956.         if (gdc.u.gdmi.milibrary[0])
  957.             if (VESALoadPatchBank(hMIDI, msv, &gdc.u.gdmi.milibrary[0]) == 0)
  958.                 DoExit(-1);
  959.  
  960.     // callbacks, reset & other things...
  961.  
  962.         msv->msApplMIDIIn = &OurMIDIReceiver;
  963.         (msv->msGlobalReset)();
  964.         maxtones = gdc.u.gdmi.miactivetones;
  965.  
  966.         return(hMIDI);
  967. }
  968.  
  969.  
  970. ;   /*\
  971. ;---|*|----====< PatchIsLoaded >====----
  972. ;---|*|
  973. ;---|*| Test the driver's bit table to determine if the patch is preloaded
  974. ;---|*|
  975. ;   \*/
  976. int PatchIsLoaded(int ptch)
  977. {
  978. int far *p = &msv->mspatches[0];
  979.  
  980.     _asm {
  981.         push    es
  982.         les     bx,[p]
  983.         mov     ax,[ptch]
  984.         and     ax,0FFh
  985.         mov     cx,ax
  986.         shr     ax,3
  987.         add     bx,ax
  988.  
  989.         and     cx,007h
  990.         mov     ax,0080h
  991.         shr     ax,cl
  992.  
  993.         and     ax,es:[bx]
  994.         pop     es
  995.     }
  996. }
  997.  
  998.  
  999. ;   /*\
  1000. ;---|*|----====< PrintMsg >====----
  1001. ;---|*|
  1002. ;---|*| Print the MIDI messages
  1003. ;---|*|
  1004. ;   \*/
  1005.  
  1006. PrintMsg (level,msg,len)
  1007.     int level;
  1008.     char far *msg;
  1009.     int len;
  1010. {
  1011. int n;
  1012. int cmd;
  1013. char far *m;
  1014.  
  1015.     // verbose mode must be active to print
  1016.  
  1017.         if (!VerboseMode)
  1018.             return(0);
  1019.  
  1020.     // the priority level must be higher (0=highest, 5=lowest)
  1021.  
  1022.         if (level > VerboseMode)
  1023.             return(0);
  1024.  
  1025.     // get the command. change zero velocity NOTE ON to NOTE OFF
  1026.  
  1027.         cmd = (*msg & 0xF0) >> 4;
  1028.         if (cmd == 9)
  1029.             if (msg[2] == 0)
  1030.                 cmd = 8;
  1031.  
  1032.     // print each byte in the string as a hex pair
  1033.  
  1034.         m = msg;
  1035.         for (n=len;n;n--)
  1036.             printf ("%02x ",*m++ & 0xFF);
  1037.  
  1038.     // print the command name and a CR/LF
  1039.  
  1040.         printf("%s\n",msgnames[cmd-8]);
  1041. }
  1042.  
  1043.  
  1044. ;   /*\
  1045. ;---|*|----====< ReadVariable >====----
  1046. ;---|*|
  1047. ;---|*| Read a variable length 32 bit number
  1048. ;---|*|
  1049. ;   \*/
  1050. long ReadVariable(m)
  1051.     MIDItrack *m;
  1052. {
  1053. long result = 0;
  1054. register char c;
  1055.  
  1056.         while (m->mt.flen) {
  1057.             result = (result << 7) + ((c=*m->p++) & 0x7F);
  1058.             m->mt.flen--;
  1059.             if (!(c & 0x80)) break;
  1060.         }
  1061.         return(result);
  1062. }
  1063.  
  1064.  
  1065. ;   /*\
  1066. ;---|*|----====< SystemMessage >====----
  1067. ;---|*|
  1068. ;---|*| Process a system message
  1069. ;---|*|
  1070. ;   \*/
  1071. SystemMessage (status,mtk)
  1072.     unsigned int status;
  1073.     MIDItrack *mtk;
  1074. {
  1075. long melen;
  1076. long us;
  1077. char metype;
  1078. int  n;
  1079.  
  1080.     // parse the SYSTEM mesage
  1081.  
  1082.         switch (status) {
  1083.  
  1084.             case 0xF7:  // sysex scan
  1085.             case 0xF0:  // sysex scan for the eot
  1086.                 melen = ReadVariable(mtk);
  1087.                 mtk->p += melen;            // skip over the sysex
  1088.                 mtk->mt.flen -= melen;
  1089.                 break;
  1090.  
  1091.             case 0xF2:  // state
  1092.  
  1093.                 mtk->mt.flen--;             // eat two parameters by
  1094.                 mtk->p++;                   // falling through
  1095.  
  1096.             case 0xF1:  // MTC Quarter-Frame
  1097.             case 0xF3:  // continue
  1098.  
  1099.                 mtk->mt.flen--;             // eat one parameter...
  1100.                 mtk->p++;
  1101.  
  1102.             case 0xF4:  // undefined - no parameters
  1103.             case 0xF5:  // undefined - no parameters
  1104.             case 0xF6:  // active sensing
  1105.             case 0xF8:  // timing clock
  1106.             case 0xF9:  // undefined
  1107.             case 0xFA:  // state
  1108.             case 0xFB:  // continue
  1109.             case 0xFC:  // stop
  1110.             case 0xFD:  // undefined
  1111.             case 0xFE:  // active sensing
  1112.                 break;
  1113.  
  1114.             case 0xFF:  // meta event.
  1115.  
  1116.                 metype = *mtk->p++;         // get the type
  1117.                 mtk->mt.flen--;
  1118.  
  1119.                 melen  = ReadVariable(mtk); // get the length
  1120.  
  1121.                 switch (metype) {
  1122.  
  1123.                     case 0x01:
  1124.                     case 0x02:
  1125.                     case 0x03:
  1126.                     case 0x04:
  1127.                     case 0x05:
  1128.                     case 0x06:
  1129.                     case 0x07:
  1130.  
  1131.                         // text messages
  1132.  
  1133.                         mtk->mt.flen -= melen;
  1134.  
  1135.                         // print all characters under 128
  1136.  
  1137.                         for (n=melen;n;n--) {
  1138.                             if ((*mtk->p & 0x80) == 0)
  1139.                                 putchar(*mtk->p);
  1140.                             mtk->p++;
  1141.                         }
  1142.                         printf ("\n");
  1143.                         break;
  1144.  
  1145.                     case 0x51:
  1146.  
  1147.                         // microseconds per division
  1148.  
  1149.                         us  = (long)(*mtk->p++ & 0xff) << 16;
  1150.                         us += (long)(*mtk->p++ & 0xff) <<  8;
  1151.                         us += (long)(*mtk->p++ & 0xff);
  1152.  
  1153.                         mtk->mt.flen -= 3;
  1154.  
  1155.                         SetSongTempo ( (micros=us), sigdenom );
  1156.  
  1157.                         break;
  1158.  
  1159.                     case 0x58:
  1160.  
  1161.                         sigdenom = mtk->p[1];       // get the sign. denom
  1162.  
  1163.                         mtk->mt.flen -= melen;
  1164.                         mtk->p       += melen;
  1165.  
  1166.                     ////SetSongTempo ( micros, sigdenom );
  1167.                         break;
  1168.  
  1169.                     default:
  1170.                         mtk->p += melen;            // just blow it off
  1171.                         mtk->mt.flen -= melen;
  1172.                         if (metype == 0x2F) {
  1173.                             if (VerboseMode)
  1174.                                 printf ("End of Track #%d\n",mtk->tnum);
  1175.                             mtk->mt.flen = 0;
  1176.                             mtk->delta = deltacount;
  1177.                         }
  1178.                     break;
  1179.                 }
  1180.                 break;
  1181.  
  1182.             default:
  1183.                 break;
  1184.         }
  1185. }
  1186.  
  1187.  
  1188. ;   /*\
  1189. ;---|*|----====< ProcessNote >====----
  1190. ;---|*|
  1191. ;---|*| Perfrom a ramp of notes up and down
  1192. ;---|*|
  1193. ;   \*/
  1194. ProcessNote()
  1195. {
  1196. int n;
  1197. int p;
  1198.  
  1199. #define BASENOTE    30
  1200. static int direction = ON;
  1201. static int note  = BASENOTE;
  1202.  
  1203. char midimsg[3]  = { 0x90, 0x30, 0x7f };
  1204.  
  1205.     // turn the voice on, or off
  1206.  
  1207.         if (direction == ON) {
  1208.  
  1209.             // if out of voices, start turning them off
  1210.  
  1211.             if ((msv->msDeviceCheck) (MIDITONES, 0) == 0) {
  1212.                 direction = OFF;
  1213.                 note = BASENOTE;
  1214.             }
  1215.             else {
  1216.  
  1217.                 // build a message for NOTEON
  1218.  
  1219.                 midimsg[1] += note++;   // make each note different
  1220.                 midimsg[2]  = 0x7f;     // full velocity
  1221.  
  1222.                 if (note > 90)
  1223.                     note = 90;
  1224.  
  1225.                 // go play the note
  1226.  
  1227.                 OutputMidiMsg (&midimsg[0],3);
  1228.  
  1229.             }
  1230.         }
  1231.  
  1232.         if (direction == OFF) {
  1233.  
  1234.             if ((msv->msDeviceCheck) (MIDITONES, 0) == maxtones) {
  1235.                 direction = ON;
  1236.                 note = BASENOTE;
  1237.             }
  1238.             else {
  1239.  
  1240.                 // build a message for NOTEON
  1241.  
  1242.                 midimsg[1] += note++;   // make each note different
  1243.                 midimsg[2]  = 0x00;     // zero velocity
  1244.  
  1245.                 if (note > 90)
  1246.                     note = 90;
  1247.  
  1248.                 // go stop the note
  1249.  
  1250.                 OutputMidiMsg (&midimsg[0],3);
  1251.             }
  1252.         }
  1253. }
  1254.  
  1255.  
  1256. ;   /*\
  1257. ;---|*|----------------------=======================----------------------
  1258. ;---|*|----------------------====< Subroutines >====----------------------
  1259. ;---|*|----------------------=======================----------------------
  1260. ;   \*/
  1261.  
  1262. ;   /*\
  1263. ;---|*|----====< DumpSomeInfo >====----
  1264. ;---|*|
  1265. ;---|*| Do direct screen writes to display our status information. Dumps
  1266. ;---|*| 256 characters indicating the preloaded bits
  1267. ;---|*|
  1268. ;   \*/
  1269. DumpSomeInfo()
  1270. {
  1271. int far *vp = (int far *)0xB8000320;    // starting on video row 5
  1272. int x,y,v,m;
  1273.  
  1274.     // display the preloaded bits in the services table
  1275.  
  1276.         for (y=0;y<16;y++) {
  1277.  
  1278.             v = msv->mspatches[y];
  1279.             m = 0x0001;
  1280.  
  1281.             for (x=0;x<16;x++) {
  1282.  
  1283.                 *vp++ = 0x3f30 + ((v & m) ? 1 : 0);
  1284.                 m <<= 1;
  1285.             }
  1286.         }
  1287. }
  1288.  
  1289. ;
  1290. ;   /*\
  1291. ;---|*|----====< GetKey >====----
  1292. ;---|*|
  1293. ;---|*| Get a key from the keyboard
  1294. ;---|*|
  1295. ;   \*/
  1296. int GetKey(flag)
  1297.     int flag;
  1298. {
  1299. int c;
  1300.  
  1301.     // flush the keys coming in
  1302.  
  1303.         if (flag)
  1304.             while (kbhit())
  1305.                 getch();
  1306.  
  1307.     // get the real key
  1308.  
  1309.         if ((c = getch()) == 0)
  1310.             c = getch();
  1311.  
  1312.     // return to the caller
  1313.  
  1314.         return(c);
  1315. }
  1316.  
  1317.  
  1318. ;   /*\
  1319. ;---|*|----====< GetValue >====----
  1320. ;---|*|
  1321. ;---|*| Return a value from the string, or the last value
  1322. ;---|*|
  1323. ;   \*/
  1324. long GetValue (s,orig)
  1325.     char * s;
  1326.     long orig;
  1327. {
  1328. long w;
  1329. int NegateFlag = FALSE;
  1330.  
  1331.     // if the first character is negative, then set out neg flag
  1332.  
  1333.         if (*s == '-') {
  1334.             s++;
  1335.             NegateFlag = TRUE;
  1336.         }
  1337.  
  1338.     // check the first character, if zero, then it's octal or hex
  1339.  
  1340.         if (*s == '0') {
  1341.  
  1342.             w = 0;
  1343.  
  1344.             if ((*++s & 0x5F) == 'X') {
  1345.  
  1346.                 if (sscanf (++s,"%lx",&w) != 1)
  1347.                     w = orig;
  1348.  
  1349.             }
  1350.             else {
  1351.                 if (isdigit(*s)) {
  1352.  
  1353.                     if (sscanf (s,"%lo",&w) != 1)
  1354.                         w = orig;
  1355.                 }
  1356.                 else {
  1357.                     w = 0;
  1358.                 }
  1359.             }
  1360.         }
  1361.  
  1362.     // return a decimal value
  1363.  
  1364.         else {
  1365.  
  1366.             if (sscanf (s,"%ld",&w) != 1)
  1367.                 w = orig;
  1368.  
  1369.         }
  1370.  
  1371.     // we have something...
  1372.  
  1373.         if (NegateFlag)
  1374.             w = 0 - w;
  1375.  
  1376.         return(w);
  1377.  
  1378. }
  1379.  
  1380.  
  1381. ;   /*\
  1382. ;---|*|----====< GiveHelps >====----
  1383. ;---|*|
  1384. ;---|*| Give the user the commandline option list
  1385. ;---|*|
  1386. ;   \*/
  1387.  
  1388. GiveHelps()
  1389. {
  1390.  
  1391.         printf ("\nTo Use:  DOS>pmidi [song] [A] [H] [L] [M] [K{+|-xx}] [R{+|-xx}] [V{xx}]\n\n");
  1392.  
  1393.         printf ("Where: [song]     is the .MID file to be played\n");
  1394.         printf ("       [A{-|+}]   Active Sense Enable(+) or Disable(-)\n");
  1395.         printf ("       [H]        High end synth playing on channels 1-10.\n");
  1396.         printf ("       [I]        to receive only.\n");
  1397.         printf ("       [K{+|-}xx] shifts all key #s up|down by xx count (cheap transpose).\n");
  1398.         printf ("       [L]        Low end synth playing on channels 11-16.\n");
  1399.         printf ("       [Mxxxx]    patch Map file name (256 bytes for 2 tables).\n");
  1400.         printf ("       [Pxx]      selects a VBE device at this (xx) user preference level.\n");
  1401.         printf ("       [R{+|-}xx] shifts all patch #s up|down by xx count.\n");
  1402.         printf ("       [T+|-xx]   Tempo shift faster (+) or (-) slower, in percent.\n");
  1403.         printf ("       [V{xx}]    verbose mode to dump events as they play. xx can be 1-5.\n");
  1404.  
  1405.         printf ("\n");
  1406.  
  1407. }
  1408.  
  1409.  
  1410. ;   /*\
  1411. ;---|*|----====< LoadMIDIFile >====----
  1412. ;---|*|
  1413. ;---|*| Load the MIDI file
  1414. ;---|*|
  1415. ;   \*/
  1416. LoadMIDIFile()
  1417. {
  1418. int n;
  1419. int fhan;
  1420. long len;
  1421. char str[80];
  1422.  
  1423.         if ((rfile = fopen (MIDFileName,"rb")) == 0) {
  1424.  
  1425.             strcpy (str,MIDFileName);
  1426.             strcat (str,".MID");
  1427.  
  1428.             if ((rfile = fopen (str,"rb")) == 0) {
  1429.                 printf ("cannot open the MIDI file!\n");
  1430.                 DoExit(1);
  1431.             }
  1432.         }
  1433.  
  1434.         fhan = fileno(rfile);
  1435.  
  1436.         readblock (fhan,(char huge *)&mhdr,sizeof(MIDIheader));
  1437.  
  1438.     // intel fixup
  1439.  
  1440.         mhdr.flen    = swapdd (mhdr.flen   );
  1441.         mhdr.ftype   = swapdw (mhdr.ftype  );
  1442.         mhdr.ftracks = swapdw (mhdr.ftracks);
  1443.         mhdr.fdiv    = swapdw (mhdr.fdiv   );
  1444.  
  1445.         if (VerboseMode) {
  1446.             printf ("mhdr.flen    = %ld\n",mhdr.flen    );
  1447.             printf ("mhdr.ftype   = %d\n", mhdr.ftype   );
  1448.             printf ("mhdr.ftracks = %d\n", mhdr.ftracks );
  1449.             printf ("mhdr.fdiv    = %d\n", mhdr.fdiv    );
  1450.         }
  1451.  
  1452.         if (mhdr.fdiv < 0) {
  1453.             printf ("This MIDI file is not based upon PPQN time!\n");
  1454.             return(0);
  1455.         }
  1456.  
  1457.         if (mhdr.ftracks > MAXTRACKS) {
  1458.             printf ("WARNING: This MIDI file has too many tracks!\n");
  1459.             mhdr.ftracks = MAXTRACKS;   // limit it to MAXTRACKS tracks
  1460.         }
  1461.  
  1462.         return(1);
  1463.  
  1464. }
  1465.  
  1466.  
  1467. ;   /*\
  1468. ;---|*|----====< LoadMIDITrack >====----
  1469. ;---|*|
  1470. ;---|*| Load just one MIDI Track into memory
  1471. ;---|*|
  1472. ;   \*/
  1473. LoadMIDITrack(m)
  1474.     MIDItrack *m;
  1475. {
  1476. int n;
  1477. int fhan;
  1478.  
  1479. static int thistrk = 0;
  1480.  
  1481.     // go direct to DOS using handles
  1482.  
  1483.         fhan = fileno(rfile);
  1484.  
  1485.     // get the header
  1486.  
  1487.         readblock (fhan,(char huge *)&m->mt,sizeof(MDtrack));
  1488.  
  1489.         if (strcmp (&m->mt.fileid,"MTrk")) {
  1490.             printf ("Not a MIDI Track!\n");
  1491.             DoExit(1);
  1492.         }
  1493.  
  1494.     // intel fixup
  1495.  
  1496.         m->mt.flen = swapdd (m->mt.flen);
  1497.  
  1498.     // if there is data, read it into the buffer
  1499.  
  1500.         if (m->mt.flen)
  1501.             readblock (fhan,(char huge *)m->p,(int)m->mt.flen);
  1502.  
  1503.     // flush the delta time to start everything immediately, give a trk #
  1504.  
  1505.         printf ("MIDI Track %d length = %ld\n",thistrk,m->mt.flen);
  1506.         m->tnum  = thistrk++;
  1507. }
  1508.  
  1509.  
  1510. ;   /*\
  1511. ;---|*|----====< OurMIDIReceiver >====----
  1512. ;---|*|
  1513. ;---|*| Print the received data byte.
  1514. ;---|*|
  1515. ;   \*/
  1516.  
  1517. void far pascal OurMIDIReceiver ( han, delta, mbyte, filler )
  1518.     int  han;       // the caller's handle
  1519.     int  delta;     // the delta time
  1520.     char mbyte;     // the midi byte
  1521.     long filler;    // reserved
  1522. {
  1523. }
  1524.  
  1525.  
  1526. ;   /*\
  1527. ;---|*|----====< OurSystemMSGCallBack >====----
  1528. ;---|*|
  1529. ;---|*| Timer tick callback for processing 0xFE messages. NOTE:
  1530. ;---|*| No assumptions can be made about
  1531. ;---|*| the segment registers! (DS,ES,GS,FS)
  1532. ;---|*|
  1533. ;   \*/
  1534.  
  1535. void far pascal OurSystemMSGCallBack()
  1536. {
  1537.  
  1538.     // setup our data segment
  1539.  
  1540.         _asm {
  1541.  
  1542.             push    ds
  1543.             mov     ax,seg SystemMsgTick
  1544.             mov     ds,ax
  1545.  
  1546.         }
  1547.  
  1548.     // save for later reporting...
  1549.  
  1550.         SystemMsgTick++;
  1551.  
  1552.     // we're done here...
  1553.  
  1554.         _asm {
  1555.             pop     ds
  1556.         }
  1557. }
  1558.  
  1559.  
  1560. ;   /*\
  1561. ;---|*|----====< OurTimerCallBack >====----
  1562. ;---|*|
  1563. ;---|*| Timer tick callback. NOTE: No assumptions can be made about
  1564. ;---|*| the segment registers! (DS,ES,GS,FS)
  1565. ;---|*|
  1566. ;   \*/
  1567.  
  1568. void far pascal OurTimerCallBack()
  1569. {
  1570.  
  1571.     // setup our data segment
  1572.  
  1573.         _asm {
  1574.  
  1575.             push    ds
  1576.             mov     ax,seg CallBackOccured
  1577.             mov     ds,ax
  1578.  
  1579.         }
  1580.  
  1581.     // save for later reporting...
  1582.  
  1583.         CallBackOccured++;
  1584.  
  1585.     // we're done here...
  1586.  
  1587.         _asm {
  1588.             pop     ds
  1589.         }
  1590. }
  1591.  
  1592.  
  1593. ;
  1594. ;   /*\
  1595. ;---|*|----====< KeepActive >====----
  1596. ;---|*|
  1597. ;---|*| Check to see if we need to send active messages
  1598. ;---|*|
  1599. ;   \*/
  1600. KeepActive()
  1601. {
  1602. static char activemsg = 0xFE;
  1603.  
  1604.     // bail if not doing active sensing
  1605.  
  1606.         if (!ActiveSensing)
  1607.             return(0);
  1608.  
  1609.     // if any message has been sent since the last system msg tick,
  1610.     // we don't have to send the active msg
  1611.  
  1612.         if (msgsent)
  1613.             return(msgsent = SystemMsgTick = 0);
  1614.  
  1615.     // send the message to keep the system from going idle
  1616.  
  1617.         if (SystemMsgTick) {
  1618.             PrintMsg (9,&activemsg,1);
  1619.             OutputMidiMsg (&activemsg,1);
  1620.             SystemMsgTick = 0;
  1621.         }
  1622. }
  1623.  
  1624.  
  1625. ;
  1626. ;   /*\
  1627. ;---|*|----====< outputMIDIMsg >====----
  1628. ;---|*|
  1629. ;---|*| This routine is not necessary for normal midi, but is added here
  1630. ;---|*| to thrash the drivers harder by making the data stream closer
  1631. ;---|*| to "real world" occurances of running status, and incomplete
  1632. ;---|*| message, etc. The incomplete messages are valid, but are sent to the
  1633. ;---|*| driver, one byte at a time, to test it's handling of broken up
  1634. ;---|*| messages. This routine will alternate between sending a whole block
  1635. ;---|*| of msgs, and sending a block, one byte at a time. Remember, the
  1636. ;---|*| MIDI driver is supposed to handle messages that span calls.
  1637. ;---|*|
  1638. ;   \*/
  1639.  
  1640. void OutputMidiMsg (m,l)
  1641.     char far *m;
  1642.     int l;
  1643. {
  1644. int n;
  1645. char far *s;
  1646.  
  1647. static int toggle = 0;
  1648. static char lastmsg = 0;
  1649. static int col = 0;
  1650.  
  1651.     // if in a verbose mode, then print the messages
  1652.  
  1653.         if (VerboseMode == 1) {
  1654.             s = m;
  1655.             for (n=l;n;n--) {
  1656.                 if ((col++ & 0x0F) == 0)
  1657.                     printf ("\n");
  1658.                 printf ("%02x ",*s++ & 0xFF);
  1659.             }
  1660.         }
  1661.  
  1662.     // test the drivers running status capaiblities
  1663.  
  1664.         if (lastmsg == *m) {    // if the same msg, drop the msg byte
  1665.  
  1666.             if (l > 1) {        // if the whole msg length is over one
  1667.                 m++;            // one byte we will do this, else
  1668.                 l--;            // let the whole thing pass to the driver
  1669.             }
  1670.         }
  1671.         else
  1672.             lastmsg = *m;       // not the same, so make it our next victim
  1673.  
  1674.     // test the drivers ability to receive incomplete messages by
  1675.     // alternately sending a full message, incremental messages, full, etc...
  1676.  
  1677.         if (ScatteredMessages) {
  1678.  
  1679.             toggle ^= 1;    // toggle the full/incremental flag
  1680.  
  1681.             if (toggle) {   // send it off a single byte at a time
  1682.  
  1683.                 for (;l;l--)
  1684.                     (msv->msMIDImsg) (m++,1);
  1685.  
  1686.             }
  1687.             else            // send off the data as a block
  1688.  
  1689.                 (msv->msMIDImsg) (m,l);
  1690.         }
  1691.  
  1692.     // scattered messages are disabled in favor of full msgs so low
  1693.     // level debugging can be done easier.
  1694.  
  1695.         else
  1696.             (msv->msMIDImsg) (m,l);
  1697.  
  1698.         msgsent = TRUE;     // we sent something...
  1699. }
  1700.  
  1701. ;
  1702. ;   /*\
  1703. ;---|*|----====< readblock >====----
  1704. ;---|*|
  1705. ;---|*| read a chunk of the PCM file into the huge buffer
  1706. ;---|*|
  1707. ;   \*/
  1708. int readblock (han,tptr,len)
  1709.     int han;
  1710.     char huge *tptr;
  1711.     int len;
  1712. {
  1713. int siz = 0;
  1714.  
  1715.     // go get it...
  1716.  
  1717.         _asm {
  1718.             push    ds
  1719.  
  1720.             mov     cx,[len]
  1721.             mov     ax,cx
  1722.             add     cx,word ptr [tptr]  // wrap?
  1723.             jnc     rdbl05              // no, go get the size
  1724.  
  1725.             sub     ax,cx               // ax holds the # of bytes to read
  1726.             mov     cx,ax
  1727.             sub     [len],ax
  1728.  
  1729.             mov     ah,03fh             // cx holds the length
  1730.             mov     bx,[han]
  1731.             lds     dx,[tptr]
  1732.             int     21h
  1733.  
  1734.             mov     [siz],ax            // we moved this much
  1735.             add     word ptr [tptr+2],0x1000
  1736.  
  1737.             cmp     ax,cx               // same size?
  1738.             jnz     rdbldone            // no, exit...
  1739.         ;
  1740.         rdbl05:
  1741.  
  1742.             mov     ah,03fh
  1743.             mov     bx,[han]
  1744.             mov     cx,[len]
  1745.  
  1746.             jcxz    rdbldone
  1747.  
  1748.             lds     dx,[tptr]
  1749.             int     21h
  1750.  
  1751.             add     [siz],ax            // we moved this much
  1752.         ;
  1753.         rdbldone:
  1754.             pop     ds
  1755.  
  1756.         }
  1757.  
  1758.     // return the amount read
  1759.  
  1760.         return(siz);
  1761. }
  1762.  
  1763.  
  1764. ;   /*\
  1765. ;---|*|----====< ReportCallback >====----
  1766. ;---|*|
  1767. ;---|*| Print some useful information to the user
  1768. ;---|*|
  1769. ;   \*/
  1770. ReportCallBack()
  1771. {
  1772. register char far *vidptr = (char far *)0xb80000A0;
  1773. char sstr[80],*s;
  1774.  
  1775. static int cbcnt = 0;
  1776.  
  1777.     // create the string
  1778.  
  1779.         sprintf (sstr,"%5d ",cbcnt++);
  1780.  
  1781.     // blast it out...
  1782.  
  1783.         s = sstr;
  1784.         while (*s) {
  1785.             *vidptr++ = *s++;
  1786.             *vidptr++ = 0x3F;
  1787.         }
  1788.  
  1789. }
  1790.  
  1791.  
  1792. ;   /*\
  1793. ;---|*|----====< SetSongTempo >====----
  1794. ;---|*|
  1795. ;---|*| Set the song tempo from the MIDI file tempo meta event
  1796. ;---|*|
  1797. ;   \*/
  1798. void SetSongTempo(us,denom)
  1799.     long us;    // microseconds per quarter note beat
  1800.     int denom;  // denominator - which partial qnote gets the beat
  1801. {
  1802. long cbrate;
  1803. int sign;
  1804. long cboff;
  1805.  
  1806.     // calculate the callback rate
  1807.  
  1808.         cbrate   = (micros=us) / mhdr.fdiv; // # of us between timer callbacks
  1809.         cbrate   = 1000000/cbrate;          // calc the # of ints at this rate
  1810.  
  1811.         if (tempooffset) {
  1812.  
  1813.             sign = 0;
  1814.  
  1815.             if (tempooffset < 0) {
  1816.                 sign = -1;
  1817.                 tempooffset = -tempooffset;
  1818.             }
  1819.  
  1820.             cboff = cbrate * tempooffset / 100;
  1821.  
  1822.             if (sign)
  1823.                 cboff = -cboff;
  1824.  
  1825.             cbrate += cboff;
  1826.  
  1827.         }
  1828.  
  1829.     // report the true rate
  1830.  
  1831.         printf ("Callback rate is %ld times per second\n",cbrate);
  1832.         printf ("Tempo = %ldus\n",micros);
  1833.  
  1834.     // reduce to a reasonable rate
  1835.  
  1836.         if (ScatteredMessages) {
  1837.             deltastep = 1;
  1838.             if (cbrate > 300) {
  1839.  
  1840.                 while (cbrate > 300) {
  1841.                     deltastep <<= 1;
  1842.                     cbrate >>= 1;
  1843.                 }
  1844.                 printf("The callback rate is being adjusted to %ld callbacks per second\n",cbrate);
  1845.             }
  1846.         }
  1847.  
  1848.     ////if (denom != 2)
  1849.     ////////printf ("DENOMINATOR IS NOT A QUARTER NOTE!(%d)\n",denom);
  1850.  
  1851.     // make sure we get callbacks too!
  1852.  
  1853.         VESARegisterTimer( 255, &OurTimerCallBack, VESARateToDivisor((int)cbrate) );
  1854.  
  1855. }
  1856.  
  1857.  
  1858. ;   /*\
  1859. ;---|*|----====< swapdw >====----
  1860. ;---|*|
  1861. ;---|*| swap the bytes in the words
  1862. ;---|*|
  1863. ;   \*/
  1864.  
  1865. int  swapdw ( val )
  1866.     int val;
  1867. {
  1868.     _asm {
  1869.         mov     ax,[val]
  1870.         xchg    ah,al
  1871.     }
  1872. }
  1873.  
  1874.  
  1875. ;   /*\
  1876. ;---|*|----====< swapdd >====----
  1877. ;---|*|
  1878. ;---|*| swap the bytes in the long
  1879. ;---|*|
  1880. ;   \*/
  1881.  
  1882. long swapdd ( val )
  1883.     long val;
  1884. {
  1885.     _asm {
  1886.         mov     ax,word ptr [val+0]
  1887.         mov     dx,word ptr [val+2]
  1888.         xchg    ah,al
  1889.         xchg    dh,dl
  1890.         xchg    ax,dx
  1891.     }
  1892. }
  1893.  
  1894.  
  1895. ;   /*\
  1896. ;---|*| end of PMIDI.C
  1897. ;   \*/
  1898.  
  1899.  
  1900.  
  1901.  
  1902.  
  1903.