home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / midifil2.zip / mfread / mfread.c next >
Text File  |  1994-07-28  |  22KB  |  733 lines

  1. /* ===========================================================================
  2.  * mfread.c
  3.  *
  4.  * Reads in and displays information about a MIDI file, including all events in an Mtrk chunk.
  5.  * =========================================================================
  6.  */
  7.  
  8. #include <os2.h>
  9. #include <stdio.h>
  10. #include "midifile.h"
  11.  
  12. /* Uncomment this define if you want standard C buffered file I/O */
  13. /* #define BUFFERED_IO 1 */
  14.  
  15. /* function definitions */
  16. VOID initfuncs(CHAR * fn);
  17.  
  18. /* Need a CALLBACK structure for the DLL */
  19. CALLBACK cb;
  20.  
  21. /* Need a MIDIFILE structure for the DLL */
  22. MIDIFILE mfs;
  23.  
  24. #ifdef BUFFERED_IO
  25. CHAR * fname;
  26. #endif
  27.  
  28. /* A text description of the various text-based Meta-Events (types 0x01 to 0x0F) */
  29. CHAR types[8][14] = {
  30.     "Unknown Text",
  31.     "Text event  ",
  32.     "Copyright   ",
  33.     "Track Name  ",
  34.     "Instrument  ",
  35.     "Lyric       ",
  36.     "Marker      ",
  37.     "Cue Point   ",
  38. };
  39.  
  40. UCHAR keys[15][3] = { "Cb", "Gb", "Db", "Ab", "Eb", "Bb", " F", " C", " G", " D", " A", " E", " B", "F#", "C#" };
  41.  
  42. ULONG packet;
  43.  
  44. UCHAR compress=0;
  45.  
  46. ULONG counts[23];
  47.  
  48. UCHAR * strptrs[] = { "Note Off", "Note On", "Aftertouch", "Controller", "Program", "Channel Pressure",
  49.             "Pitch Wheel", "System Exclusive", "Escaped", "Unknown Text",
  50.             "Text", "Copyright", "Track Name", "Instrument Name", "Lyric",
  51.             "Marker", "Cue Point", "Proprietary", "Unknown Meta", "Key Signature",
  52.             "Tempo", "Time Signature", "SMPTE" };
  53.  
  54.  
  55.  
  56.  
  57. /*********************************** main() ***********************************
  58.  * Program entry point. Calls the MIDIFILE.DLL function to read in a MIDI file. The callbacks do
  59.  * all of the real work of displaying info about the file.
  60.  ****************************************************************************/
  61.  
  62. main(int argc, char *argv[], char *envp[])
  63. {
  64.     LONG result;
  65.     UCHAR buf[60];
  66.  
  67.     /* If no filename arg supplied by user, exit with usage info */
  68.     if ( argc < 2 )
  69.     {
  70.      printf("This program displays the contents of a MIDI (sequencer)\n");
  71.      printf("file. It requires MIDIFILE.DLL to run.\n\n");
  72.      printf("Syntax: MFREAD.EXE [filename] /I\n");
  73.      printf("    where /I means list info about the MTrk but not each event\r\n");
  74.      exit(1);
  75.     }
  76.  
  77.     /* See if he wants compressed listing */
  78.     if (argc > 2)
  79.     {
  80.      if (strncmp(argv[2],"/I",2) ||strncmp(argv[2],"/i",2) )
  81.      {
  82.           compress=1;
  83.      }
  84.     }
  85.  
  86.     /* Initialize the pointers to our callback functions (ie, for the MIDIFILE.DLL to call) */
  87.     initfuncs(argv[1]);
  88.  
  89.     /* Set the Flags */
  90.     mfs.Flags = MIDIDENOM;
  91.  
  92.     /* Tell MIDIFILE.DLL to read in the file, calling my callback functions */
  93.     result = MidiReadFile(&mfs);
  94.  
  95.     /* Print out error message. NOTE: For 0, DLL returns a "Successful MIDI file load" message.
  96.     Also note that DLL returns the length even though we ignore it. */
  97.     MidiGetErr(&mfs, result, &buf[0]);
  98.     printf(&buf[0]);
  99.  
  100.     exit(0);
  101. }
  102.  
  103.  
  104.  
  105.  
  106. /********************************** prtime() *********************************
  107.  * Prints out the time that the event occurs upon. This time is referenced from 0 (ie, as opposed
  108.  * to the previous event in the track as is done with delta-times in the MIDI file). The MIDIFILE
  109.  * DLL automatically maintains the MIDIFILE's Time field, updating it for the current event.
  110.  **************************************************************************/
  111.  
  112. VOID prtime(MIDIFILE * mf)
  113. {
  114.     if (!compress)
  115.     printf("%8ld |",mf->Time);
  116. }
  117.  
  118.  
  119.  
  120.  
  121. /******************************** startMThd() ********************************
  122.  * This is called by MIDIFILE.DLL when it encounters the start of an MThd chunk (ie, at the head
  123.  * of the MIDI file). We simply print out loaded info from that header. At this point, the valid
  124.  * MIDIFILE fields set by the DLL include Handle, FileSize, Format, NumTracks, Division,
  125.  * ChunkSize (minus the 6 loaded bytes of a standard MThd), and TrackNum=-1 (ie, we haven't
  126.  * encountered an MTrk yet).
  127.  **************************************************************************/
  128.  
  129. LONG EXPENTRY startMThd(MIDIFILE * mf)
  130. {
  131.     /* Print the MThd info. */
  132.     printf("MThd Format=%d, # of Tracks=%d, Division=%d\r\n",mf->Format,mf->NumTracks,mf->Division);
  133.  
  134.     /* Return 0 to indicate no error */
  135.     return(0);
  136. }
  137.  
  138.  
  139.  
  140.  
  141. /********************************* startMTrk() ********************************
  142.  * This is called by MIDIFILE.DLL when it encounters the start of an Mtrk chunk. We simply print
  143.  * out a message that we encountered an Mtrk start, and the track number. We return 0 to let the
  144.  * DLL load/process the MTrk 1 event at a time.
  145.  **************************************************************************/
  146.  
  147. LONG EXPENTRY startMTrk(MIDIFILE * mf)
  148. {
  149.     USHORT i;
  150.  
  151.     /* Print heading */
  152.     printf("\r\n==================== Track #%d ====================\r\n   %s      Event\r\n", mf->TrackNum, (compress) ? "Total" : "Time" );
  153.  
  154.     /* If compressing info on display, init array for this track */
  155.     if (compress)
  156.     {
  157.      for (i=0; i<23; i++)
  158.      {
  159.           counts[i]=0;
  160.      }
  161.     }
  162.  
  163.     return(0);
  164. }
  165.  
  166.  
  167.  
  168.  
  169. /******************************* standardEvt() *********************************
  170.  * This is called by MIDIFILE.DLL when it encounters a MIDI message of Status < 0xF0 within
  171.  * an Mtrk chunk. We simply print out info about that event.
  172.  ***************************************************************************/
  173.  
  174. LONG EXPENTRY standardEvt(MIDIFILE * mf)
  175. {
  176.     /* Get MIDI channel for this event */
  177.     UCHAR chan = mf->Status & 0x0F;
  178.  
  179.     if (!compress)
  180.     {
  181.      /* Print out the event's time */
  182.      prtime((MIDIFILE *)mf);
  183.  
  184.      switch ( mf->Status & 0xF0 )
  185.      {
  186.           case 0x80:
  187.            printf("Note off    | chan=%2d   | pitch= %-3d | vol=%d\r\n", chan, mf->Data[0], mf->Data[1]);
  188.            break;
  189.  
  190.           case 0x90:
  191.            printf("Note on     | chan=%2d   | pitch= %-3d | vol=%d\r\n", chan, mf->Data[0], mf->Data[1]);
  192.            break;
  193.  
  194.           case 0xA0:
  195.            printf("Aftertouch  | chan=%2d   | pitch= %-3d | press=%d\r\n", chan, mf->Data[0], mf->Data[1]);
  196.            break;
  197.  
  198.           case 0xB0:
  199.            printf("Controller  | chan=%2d   | contr #%-3d | value=%d\r\n", chan, mf->Data[0], mf->Data[1]);
  200.            break;
  201.  
  202.           case 0xC0:
  203.            printf("Program     | chan=%2d   | pgm #=%d\r\n", chan, mf->Data[0]);
  204.            break;
  205.  
  206.           case 0xD0:
  207.            printf("Poly Press  | chan=%2d   | press= %-3d\r\n", chan, mf->Data[0]);
  208.            break;
  209.  
  210.           case 0xE0:
  211.            printf("Pitch Wheel | chan=%2d   | LSB=%d MSB=%d\r\n", chan, mf->Data[0], mf->Data[1]);
  212.      }
  213.     }
  214.     else
  215.     {
  216.      switch ( mf->Status & 0xF0 )
  217.      {
  218.           case 0x80:
  219.            counts[0]+=1;
  220.            break;
  221.  
  222.           case 0x90:
  223.            counts[1]+=1;
  224.            break;
  225.  
  226.           case 0xA0:
  227.            counts[2]+=1;
  228.            break;
  229.  
  230.           case 0xB0:
  231.            counts[3]+=1;
  232.            break;
  233.  
  234.           case 0xC0:
  235.            counts[4]+=1;
  236.            break;
  237.  
  238.           case 0xD0:
  239.            counts[5]+=1;
  240.            break;
  241.  
  242.           case 0xE0:
  243.            counts[6]+=1;
  244.      }
  245.     }
  246.     return(0);
  247. }
  248.  
  249.  
  250.  
  251.  
  252. /********************************* sysexEvt() ********************************
  253.  * This is called by MIDIFILE.DLL when it encounters a MIDI SYSEX or ESCAPE (ie, usually
  254.  * REALTIME or COMMON) event within an Mtrk chunk. We simply print out info about that event.
  255.  * Note that 0xF7 is used both as SYSEX CONTINUE and ESCAPE. Which one it is depends upon
  256.  * what preceded it. When 0xF7 is used as a SYSEX CONTINUE, it must follow an 0xF0 event
  257.  * that doesn't end with 0xF7, and there can't be intervening Meta-Events or MIDI messages with
  258.  * Status < 0xF0 inbetween. Of course, it IS legal to have an ESCAPE event, (ie, REALTIME)
  259.  * inbetween the 0xF0 and SYSEX CONTINUE events. The DLL helps differentiate between SYSEX
  260.  * CONTINUE and ESCAPE by setting the MIDISYSEX flag when a 0xF0 is encountered. If this
  261.  * flag is not set when you receive a 0xF7 event, then it is definitely an ESCAPE. If MIDISYSEX
  262.  * flag is set, then a 0xF7 could be either SYSEX CONTINUE or an ESCAPE. You can check the
  263.  * first loaded byte; if it is < 0x80 or it's 0xF7, then you've got a SYSEX CONTINUE. Whenever
  264.  * you have an event that ends in 0xF7 while the MIDISYSEX is set, then you should clear the
  265.  * MIDISYSEX flag this is the end of a stream of SYSEX packets. NOTE: The DLL will skip any
  266.  * bytes that we don't read in of this event.
  267.  ***************************************************************************/
  268.  
  269. LONG EXPENTRY sysexEvt(MIDIFILE * mf)
  270. {
  271.     UCHAR chr;
  272.     LONG result;
  273.  
  274.     prtime((MIDIFILE *)mf);
  275.  
  276.     /* Load the first byte. */
  277.     if ( (result =MidiReadBytes(mf, &chr, 1)) ) return(result);
  278.  
  279.     /* Did we encounter a SYSEX (0xF0)? If not, then this can't possibly be a SYSEX event.
  280.     It must be an ESCAPE. */
  281.     if ( mf->Flags & MIDISYSEX )
  282.     {
  283.      /* If the first byte > 0x7F but not 0xF7, then we've really got an ESCAPE */
  284.      if (chr > 0x7F && chr != 0xF7) goto escd;
  285.  
  286.      /* NOTE: Normally, we would allocate some memory to contain the SYSEX message, and
  287.          read it in via MidiReadBytes. We'd check the last loaded byte of this event, and if
  288.          a 0xF7, then clear MIDISYSEX. Note that, by simply returning, the DLL will skip any
  289.          bytes that we haven't read of this event. */
  290.  
  291.      /* Seek to and read the last byte */
  292.      chr=0;
  293.      if (mf->EventSize)
  294.      {
  295.           MidiSeek(mf, mf->EventSize-1);
  296.           if ( (result =MidiReadBytes(mf, &chr, 1)) ) return(result);
  297.      }
  298.  
  299.      /* 0xF7 (SYSEX CONTINUE) */
  300.      if (mf->Status == 0xF7)
  301.      {
  302.           /* If last char = 0xF7, then this is the end of the SYSEX stream (ie, last packet) */
  303.           if (chr == 0xF7)
  304.           {
  305.            if (!compress)
  306.             printf("Last Packet | len=%-6ld|\r\n",(mf->EventSize)+2);
  307.           }
  308.           else
  309.           {
  310.            if (!compress)
  311.             printf("Packet %-5ld| len=%-6ld|\r\n", packet, (mf->EventSize)+2);
  312.           }
  313.      }
  314.  
  315.      /* 0xF0 */
  316.      else
  317.      {
  318.           if (!compress)
  319.           {
  320.            /* If last char = 0xF7, then this is a full SYSEX message, rather than the first of
  321.                a series of packets (ie, more SYSEX CONTINUE events to follow) */
  322.            if (chr == 0xF7)
  323.            {
  324.             printf("Sysex 0xF0  | len=%-6ld|\r\n",(mf->EventSize)+3); /* +3 to also include the 0xF0 status which
  325.                                               is normally considered part of the SYSEX */
  326.            }
  327.            else
  328.            {
  329.             packet=1;
  330.             printf("First Packet| len=%-6ld|\r\n",(mf->EventSize)+3);
  331.            }
  332.           }
  333.           else
  334.            counts[7]+=1;
  335.      }
  336.     }
  337.  
  338.     /* Must be an escaped event such as MIDI REALTIME or COMMON. */
  339.     else
  340.     {
  341. escd:
  342.      if (!compress)
  343.           printf("Escape 0x%02x | len=%-6ld|\r\n", chr, mf->EventSize+1);
  344.      else
  345.           counts[8]+=1;
  346.      /* NOTE: The DLL will skip any bytes of this event that we don't read in, so let's just
  347.          ignore the rest of this event even if there are more bytes to it. */
  348.     }
  349.  
  350.     return(0);
  351. }
  352.  
  353.  
  354.  
  355.  
  356. /********************************* metatext() ********************************
  357.  * This is called by MIDIFILE.DLL when it encounters a variable length Meta-Event within an Mtrk
  358.  * chunk. We figure out which of the several "type of events" it is, and simply print out info
  359.  * about that event. NOTE: The DLL will skip any bytes that we don't read in of this event.
  360.  **************************************************************************/
  361.  
  362. LONG EXPENTRY metatext(MIDIFILE * mf)
  363. {
  364.     USHORT i, p;
  365.     UCHAR chr[60];
  366.     LONG result;
  367.  
  368.     prtime((MIDIFILE *)mf);
  369.  
  370.     /* If a text-based event, print what kind of event */
  371.     if ( mf->Status < 0x10 )
  372.     {
  373.      if (mf->Status > 7) mf->Status=0;    /* I only know about 7 of the possible 15 */
  374.      if (!compress)
  375.           printf("%s| len=%-6ld|", &types[mf->Status][0], mf->EventSize);
  376.      else
  377.           counts[9+mf->Status]+=1;
  378.     }
  379.  
  380.     /* A Proprietary event, or some Meta event that we don't know about */
  381.     else
  382.     {
  383.      switch (mf->Status)
  384.      {
  385.           case 0x7F:
  386.            if (!compress)
  387.             printf("Proprietary | len=%-6ld|", mf->EventSize);
  388.            else
  389.             counts[17]+=1;
  390.            break;
  391.  
  392.           default:
  393.            if (!compress)
  394.             printf("Unknown Meta| Type = 0x%02x, len=%ld", mf->Status, mf->EventSize);
  395.            else
  396.             counts[18]+=1;
  397.      }
  398.     }
  399.  
  400.     /* Print out the actual text, one byte at a time, 15 chars to a line */
  401.     if (!compress)
  402.     {
  403.      i=0;
  404.      while (mf->EventSize)
  405.      {
  406.           if (!i) printf("\r\n         <");
  407.  
  408.           /* NOTE: MidiReadBytes() decrements EventSize by the number of bytes read */
  409.           if ( (result =MidiReadBytes(mf, &chr[i], 1)) ) return(result);
  410.  
  411.           printf( (isprint(chr[i])||isspace(chr[i])) ? "%c" : "." , chr[i]);
  412.  
  413.           if (i++ == 15)
  414.           {
  415.            printf(">    ");
  416.            for (i=0; i<15; i++)
  417.            {
  418.              printf("%02x ", chr[i]);
  419.            }
  420.            i=0;
  421.           }
  422.      }
  423.  
  424.      if (i)
  425.      {
  426.           printf(">");
  427.           for (p=20-i; p; p--)
  428.           {
  429.            printf(" ");
  430.           }
  431.           for (p=0; p<i; p++)
  432.           {
  433.            printf("%02x ", chr[p]);
  434.           }
  435.           printf("\r\n");
  436.      }
  437.     }
  438.  
  439.     return(0);
  440. }
  441.  
  442.  
  443.  
  444.  
  445. /******************************** metaseq() *********************************
  446.  * This is called by MIDIFILE.DLL when it encounters a Sequence Number Meta-Event within an
  447.  * Mtrk chunk. We simply print out info about that event.
  448.  **************************************************************************/
  449.  
  450. LONG EXPENTRY metaseq(METASEQ * mf)
  451. {
  452.     prtime((MIDIFILE *)mf);
  453.     printf("Seq # = %-4d|\r\n", mf->SeqNum);
  454.     return(0);
  455. }
  456.  
  457.  
  458.  
  459.  
  460. /********************************* metaend() ********************************
  461.  * This is called by MIDIFILE.DLL when it encounters an End of Track Meta-Event within an Mtrk
  462.  * chunk. We simply print out info about that event.
  463.  **************************************************************************/
  464.  
  465. LONG EXPENTRY metaend(METAEND * mf)
  466. {
  467.     USHORT i;
  468.  
  469.     if (!compress)
  470.     {
  471.      prtime((MIDIFILE *)mf);
  472.      printf("End of track\r\n");
  473.     }
  474.  
  475.     /* If compressing info on display, print out the counts now that we're at the end of track */
  476.     else
  477.     {
  478.      for (i=0; i<23; i++)
  479.      {
  480.           printf("%-10ld %s events.\r\n", counts[i], strptrs[i]);
  481.      }
  482.     }
  483.  
  484.     return(0);
  485. }
  486.  
  487.  
  488.  
  489.  
  490. /********************************** metakey() ********************************
  491.  * This is called by MIDIFILE.DLL when it encounters a Key Signature Meta-Event within an Mtrk
  492.  * chunk. We simply print out info about that event.
  493.  ***************************************************************************/
  494.  
  495. LONG EXPENTRY metakey(METAKEY * mf)
  496. {
  497.     UCHAR * ptr;
  498.  
  499.     prtime((MIDIFILE *)mf);
  500.  
  501.     if (!compress)
  502.      printf("Key sig     | %s %-7s|\r\n", &keys[mf->Key+7][0], mf->Minor ? "Minor" : "Major");
  503.     else
  504.      counts[19]+=1;
  505.  
  506.     return(0);
  507. }
  508.  
  509.  
  510.  
  511.  
  512. /******************************** metatempo() ********************************
  513.  * This is called by MIDIFILE.DLL when it encounters a Tempo Meta-Event within an Mtrk
  514.  * chunk. We simply print out info about that event.
  515.  ***************************************************************************/
  516.  
  517. LONG EXPENTRY metatempo(METATEMPO * mf)
  518. {
  519.     prtime((MIDIFILE *)mf);
  520.  
  521.     if (!compress)
  522.      printf("Tempo       | BPM=%-6d| micros/quarter=%ld \r\n", mf->TempoBPM, mf->Tempo);
  523.     else
  524.      counts[20]+=1;
  525.  
  526.     return(0);
  527. }
  528.  
  529.  
  530.  
  531.  
  532. /******************************** metatime() *********************************
  533.  * This is called by MIDIFILE.DLL when it encounters a Time Signature Meta-Event within an
  534.  * Mtrk chunk. We simply print out info about that event.
  535.  **************************************************************************/
  536.  
  537. LONG EXPENTRY metatime(METATIME * mf)
  538. {
  539.     prtime((MIDIFILE *)mf);
  540.  
  541.     if (!compress)
  542.      printf("Time sig    | %2d/%-7d| MIDI-clocks/click=%d | 32nds/quarter=%d\r\n",
  543.         mf->Nom, mf->Denom, mf->Clocks, mf->_32nds);
  544.     else
  545.      counts[21]+=1;
  546.  
  547.     return(0);
  548. }
  549.  
  550.  
  551.  
  552.  
  553. /********************************* metasmpte() ********************************
  554.  * This is called by MIDIFILE.DLL when it encounters a SMPTE Meta-Event within an Mtrk
  555.  * chunk. We simply print out info about that event.
  556.  ****************************************************************************/
  557.  
  558. LONG EXPENTRY metasmpte(METASMPTE * mf)
  559. {
  560.     prtime((MIDIFILE *)mf);
  561.  
  562.     if (!compress)
  563.      printf("SMPTE       | hour=%d | min=%d | sec=%d | frame=%d | subs=%d\r\n",
  564.         mf->Hours, mf->Minutes, mf->Seconds, mf->Frames, mf->SubFrames);
  565.     else
  566.      counts[22]+=1;
  567.  
  568.     return(0);
  569. }
  570.  
  571.  
  572.  
  573.  
  574. /********************************* unknown() *********************************
  575.  * This is called by MIDIFILE.DLL when it encounters a chunk that isn't an Mtrk or MThd.
  576.  * We simply print out info about this. NOTES: The ID and ChunkSize are in the MIDIFILE. If
  577.  * TrackNum is -1, then this was encountered after the MThd, but before any MTrk chunks.
  578.  * Otherwise, TrackNum is the MTrk number that this chunk follows.
  579.  ***************************************************************************/
  580.  
  581. LONG EXPENTRY unknown(MIDIFILE * mf)
  582. {
  583.     register UCHAR * ptr = (UCHAR *)&mf->ID;
  584.     register USHORT i;
  585.     UCHAR str[6];
  586.  
  587.     /* Normally, we would inspect the ID to see if it's something that we recognize. If so, we
  588.     would read in the chunk with 1 or more calls to MidiReadBytes until ChunkSize is 0.
  589.     (ie, We could read in the entire chunk with 1 call, or many calls). Note that MidiReadBytes
  590.     automatically decrements ChunkSize by the number of bytes that we ask it to read. We
  591.     should never try to read more bytes than ChunkSize. On the other hand, we could read less
  592.     bytes than ChunkSize. Upon return, the DLL would skip any bytes that we didn't read from
  593.     that chunk. */
  594.  
  595.     /* Copy ID to str[] */
  596.     for (i=0; i<4; i++)
  597.     {
  598.     str[i] = *(ptr)++;
  599.     }
  600.     str[4] = 0;
  601.  
  602.     printf("\r\n******************** Unknown ********************\r\n");
  603.     printf("     ID = %s    ChunkSize=%ld\r\n", &str[0], mf->ChunkSize);
  604.  
  605.     return(0);
  606. }
  607.  
  608.  
  609.  
  610.  
  611. #ifdef BUFFERED_IO
  612.  
  613. /******************************* midi_open() *********************************
  614.  * Here's an example of providing a MidiOpen callback, so that I can use standard C buffered I/O,
  615.  * instead of letting the DLL use unbuffered I/O.
  616.  **************************************************************************/
  617.  
  618. LONG EXPENTRY midi_open(MIDIFILE * mf)
  619. {
  620.     /* Open the file. Store the handle in the MIDIFILE's Handle field, although this isn't required,
  621.     but it's a handy place to keep it. */
  622.     if ( !(mf->Handle=(ULONG)fopen(fname, "rb")) )
  623.     {
  624.        printf("Can't open %s\r\n", fname);
  625.        return(MIDIERRFILE);
  626.     }
  627.  
  628.     /* Set the # of bytes to parse */
  629.     mf->FileSize = _filelength(_fileno((struct __file *)mf->Handle));
  630.     return(0);
  631. }
  632.  
  633.  
  634.  
  635.  
  636. /******************************* midi_close() *********************************
  637.  * Here's an example of providing a MidiClose callback, so that I can use standard C buffered I/O,
  638.  * instead of letting the DLL use unbuffered I/O.
  639.  ***************************************************************************/
  640.  
  641. LONG EXPENTRY midi_close(MIDIFILE * mf)
  642. {
  643.     /* Close the file. Note that this never gets called if my MidiOpen callback returned an error.
  644.     But, if any other subsequent errors occur, this gets called before the read operation
  645.     aborts. So, I can always be assured that the file handle gets closed, even if an error
  646.     occurs. */
  647.     fclose((struct __file *)mf->Handle);
  648.  
  649.     return(0);
  650. }
  651.  
  652.  
  653.  
  654.  
  655. /******************************* midi_seek() *********************************
  656.  * Here's an example of providing a MidiSeek callback, so that I can use standard C buffered I/O,
  657.  * instead of letting the DLL use unbuffered I/O.
  658.  **************************************************************************/
  659.  
  660. LONG EXPENTRY midi_seek(MIDIFILE * mf, LONG amt, ULONG type)
  661. {
  662.     /* NOTE: The DLL only ever passes a seek type of DosSeek's FILE_CURRENT, which
  663.     translates to SEEK_CUR for C/Set2 fseek(). */
  664.     if( fseek((struct __file *)mf->Handle, amt, SEEK_CUR) )
  665.     {
  666.        printf("Seek error");
  667.     }
  668.  
  669.     /* Don't bother with error return. The DLL ignores it, assuming that you'll encounter an
  670.        error with a subsequent read */
  671. }
  672.  
  673.  
  674.  
  675.  
  676. /******************************* midi_read() *********************************
  677.  * Here's an example of providing a MidiReadWrite callback, so that I can use standard C buffered I/O,
  678.  * instead of letting the DLL use unbuffered I/O.
  679.  **************************************************************************/
  680.  
  681. LONG EXPENTRY midi_read(MIDIFILE * mf, UCHAR * buffer, ULONG count)
  682. {
  683.      if( fread(buffer, 1L, count, (struct __file *)mf->Handle) ) return(0);
  684.      return(MIDIERRREAD);
  685. }
  686.  
  687. #endif
  688.  
  689.  
  690.  
  691.  
  692. /********************************* initfuncs() ********************************
  693.  * This initializes the CALLBACK structure containing pointers to functions. These are used by
  694.  * MIDIFILE.DLL to call our callback routines while reading the MIDI file. It also initializes a few
  695.  * fields of MIDIFILE structure.
  696.  **************************************************************************/
  697.  
  698. VOID initfuncs(CHAR * fn)
  699. {
  700.     /* Place CALLBACK into MIDIFILE */
  701.     mfs.Callbacks = &cb;
  702.  
  703. #ifdef BUFFERED_IO
  704.     /* I'll do my own buffered Open, Read, Seek, and Close */
  705.     cb.OpenMidi = midi_open;
  706.     cb.ReadWriteMidi = midi_read;
  707.     cb.SeekMidi = midi_seek;
  708.     cb.CloseMidi = midi_close;
  709.     mfs.Handle = 0;
  710.     fname = fn;
  711. #else
  712.     /* Let the DLL Open, Read, Seek, and Close the MIDI file */
  713.     cb.OpenMidi = 0;
  714.     cb.ReadWriteMidi = 0;
  715.     cb.SeekMidi = 0;
  716.     cb.CloseMidi = 0;
  717.     mfs.Handle = (ULONG)fn;
  718. #endif
  719.  
  720.     cb.UnknownChunk = unknown;
  721.     cb.StartMThd = startMThd;
  722.     cb.StartMTrk = startMTrk;
  723.     cb.StandardEvt = standardEvt;
  724.     cb.SysexEvt = sysexEvt;
  725.     cb.MetaSMPTE = metasmpte;
  726.     cb.MetaTimeSig = metatime;
  727.     cb.MetaTempo = metatempo;
  728.     cb.MetaKeySig = metakey;
  729.     cb.MetaSeqNum = metaseq;
  730.     cb.MetaText = metatext;
  731.     cb.MetaEOT = metaend;
  732. }
  733.