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