home *** CD-ROM | disk | FTP | other *** search
/ C++ Games Programming / CPPGAMES.ISO / mt / mf_to_mt.c < prev    next >
C/C++ Source or Header  |  1989-01-14  |  17KB  |  521 lines

  1. /* mf_to_mt.c  converts Standard MIDI Files 1.0 format to MT song files */
  2. /* `MIDI Sequencing In C', Jim Conger, M&T Books, 1989 */
  3.  
  4. /* #define TURBOC 1   Define if using TURBOC, leave out for Microsoft */
  5.  
  6. #include "standard.h"
  7. #include <stdio.h>
  8. #include <string.h>
  9.  
  10. #ifdef TURBOC
  11.     #include <alloc.h>
  12. #else
  13.     #include <malloc.h>
  14. #endif
  15.  
  16. #define NTRACK              8
  17. #define TITLE_WIDE          51
  18. #define TRACK_NAME_WIDE     9
  19. #define MT_TICKS            120
  20. #define NBYTES              60000   /* default track data buffer */
  21. #define TIME_OUT            0xF8
  22. #define MEAS_END            0xF9
  23. #define ALL_END             0xFC
  24.  
  25. #define META                0xFF    /* meta event codes */
  26. #define TEXTEVENT           0x01
  27. #define SEQNAME             0x03
  28. #define INSNAME             0x04
  29. #define CHANPREF            0x20
  30. #define ENDTRACK            0x2F
  31. #define SETTEMPO            0x51
  32. #define TIMESIG             0X58
  33.  
  34. /* function prototypes */
  35.  
  36. char *copy_event(unsigned char *buf, unsigned char nbyte, unsigned char b1, 
  37.     unsigned char b2, unsigned char b3, unsigned char b4);
  38. void skip_byte(int n, FILE *infile);
  39. long get_var_len(FILE *infile);
  40. long get_long(FILE *infile);
  41. int get_int(FILE *infile);
  42. int get_tempo(FILE *infile);
  43. void get_string(char *c, FILE *infile, int size);
  44. void write_buf(char *sp, char *ep, FILE *outfile);
  45. int find_str(FILE *infile, char *str);
  46. long ratio_time(long input_time, int division);
  47. FILE *open_file(char *filename, char *status);
  48. void exit_program(void);
  49.  
  50.  
  51. void
  52. main(argc, argv)
  53. int argc;
  54. char *argv[];
  55. {
  56.     unsigned char *buf, *cp, nbyte, b0, b1, b2, b3, runstatus, chunk[5], 
  57.         fbyte, sbyte;
  58.     unsigned char *title, *metrate, *meter, *pitchbend, *exclusive, *endp;
  59.     unsigned char *trackname[NTRACK], *channel[NTRACK], *events[NTRACK],
  60.         *status[NTRACK], *volume[NTRACK];
  61.     int i, n, len, length, format, trk, tracks, end_track, meas_time,
  62.         meas_ticks, add_data, division;
  63.     long ln, time, eventcount;
  64.     FILE *infile, *outfile;
  65.  
  66.     
  67.     if (argc < 3){  /* did not specify infile and outfile on command line */
  68.         exit_program();
  69.     }   
  70.     infile = open_file(argv[1], "rb");
  71.     outfile = open_file(argv[2], "wb");
  72.     
  73.     /* buf is a buffer that holds entire track until written to file */
  74.  
  75.     buf = (char *)malloc(NBYTES);
  76.     if (buf == NULL){
  77.         fputs("\nCould not allocate memory for track data.", stdout);
  78.         exit(0);
  79.     }
  80.  
  81. /* intialization portion of program */
  82.     
  83.     title = buf;            /* find pointers to all header items */
  84.     metrate = title + TITLE_WIDE;
  85.     meter = metrate + sizeof(int);
  86.     pitchbend = meter + sizeof(int);
  87.     exclusive = pitchbend + sizeof(int);
  88.     endp = exclusive + sizeof(int);
  89.     for (i = 0; i < NTRACK; i++){
  90.         trackname[i] = endp;
  91.         channel[i] = trackname[i] + TRACK_NAME_WIDE;
  92.         events[i] = channel[i] + sizeof(int);
  93.         status[i] = events[i] + sizeof(long);
  94.         volume[i] = status[i] + sizeof(int);
  95.         endp = volume[i] + sizeof(int);     /* endp points to start of */
  96.     }                                       /* track data area */
  97.     
  98.     cp = buf;                       /* clear buffer area in header zone */
  99.     while (cp != endp){
  100.         *cp++ = '\0';
  101.     }
  102.     
  103.     strcpy(title, "Not titled.");   /* put in default values */
  104.     n = 100;
  105.     memcpy(metrate, &n, sizeof(int));
  106.     n = 4;
  107.     memcpy(meter, &n, sizeof(int));
  108.     meas_ticks = n * MT_TICKS;
  109.     n = 0;
  110.     memcpy(pitchbend, &n, sizeof(int));
  111.     memcpy(exclusive, &n, sizeof(int));
  112.     for (i = 0; i < NTRACK; i++){
  113.         strcpy(trackname[i], "<      >");
  114.         memcpy(channel[i], &i, sizeof(int));
  115.         ln = 1;
  116.         memcpy(events[i], &ln, sizeof(long));
  117.         n = 0;
  118.         memcpy(status[i], &n, sizeof(int));
  119.         n = 100;
  120.         memcpy(volume[i], &n, sizeof(int));
  121.     }
  122.     
  123. /* end initialization */
  124.  
  125.     /* read by any junk in front of MThd chunk, such as 128 byte */
  126.     /* header on files from a Mac. */ 
  127.     
  128.     if (find_str(infile, "MThd") == 0){
  129.         fputs("\nInput file lacks header chunk, can't read it", stdout);
  130.         exit(0);
  131.     }
  132.     length = get_long(infile);
  133.     format = get_int(infile);
  134.     if (format != 1){
  135.         fputs("\nInput file does not use MIDI files format 1, can't read it.",
  136.             stdout);
  137.         exit(0);
  138.     }
  139.     tracks = get_int(infile);
  140.     division = get_int(infile);
  141.     
  142.     fputs("\nConverting to MT .SNG file format (ESC to exit)...\n", stdout);
  143.  
  144. /* read each track's data in sequence and write to the buffer */
  145.     
  146.     for (trk = 0; trk < tracks && trk < NTRACK + 1; trk++){
  147.         eventcount = runstatus = add_data = 0;
  148.         get_string(chunk, infile, 4);
  149.         length = get_long(infile);
  150.         if (strcmp(chunk, "MTrk")){
  151.             fputs("\nIgnored unrecognized chunk type ", stdout);
  152.             fputs(chunk, stdout);
  153.             fputc('\n', stdout);
  154.             skip_byte(length, infile);
  155.         }
  156.                 /* A track chunk, so reading data until end found */
  157.         else{
  158.             meas_time = end_track = 0;
  159.             while (!end_track){
  160.                 if (kbhit()){       /* allow keypress to stop program */
  161.                     if (ESC == getch()){
  162.                         fclose(infile);
  163.                         fclose(outfile);
  164.                         fputs("\nInterupted before conversion completed.\n",
  165.                             stdout);
  166.                         exit(0);
  167.                     }
  168.                 }
  169.                 time = ratio_time(get_var_len(infile), division);
  170.                 fbyte = fgetc(infile);
  171.                 if (fbyte == META){     /* meta event ? */
  172.                     sbyte = fgetc(infile);
  173.                     len = (int)get_var_len(infile);
  174.                     if (len == -1){
  175.                         end_track = 1;
  176.                         fputs("\nRan off end of input file.\n", stdout);
  177.                     }
  178.                     switch (sbyte){
  179.                     case (1):           /* text meta event */
  180.                     case (3):           /* sequence/track name */
  181.                     case (4):           /* instrument name */
  182.                         if (trk == 0){
  183.                             if (len < TITLE_WIDE - 1)
  184.                                 get_string(title, infile, len);
  185.                             else{
  186.                                 get_string(title, infile, TITLE_WIDE - 1);
  187.                                 skip_byte(len - (TITLE_WIDE - 1), infile);
  188.                             }
  189.                         }
  190.                         else {
  191.                             if (len < TRACK_NAME_WIDE - 1)
  192.                                 get_string(trackname[trk - 1], infile, len);
  193.                             else{
  194.                                 get_string(trackname[trk - 1], infile, 
  195.                                     TRACK_NAME_WIDE - 1);
  196.                                 skip_byte(len - (TRACK_NAME_WIDE - 1), 
  197.                                     infile);
  198.                             }
  199.                         }
  200.                         break;
  201.                     case (0x20):        /* MIDI channel prefix */
  202.                         *channel[trk - 1] = (unsigned char)fgetc(infile);
  203.                         break;
  204.                     case (0x2F):        /* end of track */
  205.                         end_track = 1;
  206.                         break;
  207.                     case (0x51):        /* set tempo */
  208.                         n = get_tempo(infile);
  209.                         memcpy(metrate, &n, sizeof(int));
  210.                         break;
  211.                     case (0x58):        /* time signature */
  212.                         n = fgetc(infile);
  213.                         memcpy(meter, &n, sizeof(int));
  214.                         meas_ticks = *meter * MT_TICKS;
  215.                         skip_byte(3, infile);
  216.                         break;
  217.                     default:            /* ignore all other meta events */
  218.                         skip_byte(len, infile);
  219.                         break;
  220.                     }                   
  221.                 }
  222.                 else {                  /* if not a META event */
  223.                     if (!add_data){     /* if first event on track */
  224.                         add_data = 1;
  225.                         endp = copy_event(endp, 2, 0, MEAS_END, 0, 0);
  226.                         eventcount++;
  227.                     }
  228.                     /* if a four byte MIDI message */
  229.                     if (fbyte >= 0x80 && fbyte <= 0xBF || 
  230.                         fbyte >= 0xE0 && fbyte <= 0xEF){
  231.                         nbyte = 4;
  232.                         b1 = runstatus = fbyte;
  233.                         b2 = (char)fgetc(infile);
  234.                         b3 = (char)fgetc(infile);
  235.                         n = b1 & 0x0F;
  236.                         memcpy(channel[trk - 1], &n, sizeof(int));
  237.                     }
  238.                     /* if a three byte MIDI message */
  239.                     else if (fbyte >= 0xC0 && fbyte <= 0xDF){
  240.                         nbyte = 3;
  241.                         b1 = runstatus = fbyte;
  242.                         b2 = (char)fgetc(infile);
  243.                         b3 = 0;
  244.                     }                       
  245.                     else{   /* running status, so only two bytes fetched */
  246.                         b1 = runstatus;
  247.                         b2 = fbyte;
  248.                         if (nbyte > 3)
  249.                             b3 = (char)fgetc(infile);
  250.                         else
  251.                             b3 = 0;
  252.                     }
  253.  
  254.                     while (time >= 0){   /* write event */
  255.                         if (meas_time + time < meas_ticks){
  256.                             if (time < 240){
  257.                                 b0 = (unsigned char)time;
  258.                                 endp = copy_event(endp, nbyte, b0, b1, 
  259.                                     b2, b3);
  260.                                 eventcount++;
  261.                                 meas_time += time;
  262.                                 time = -1;  /* wrote event, so quit loop */
  263.                             }
  264.                             else{       /* need to add a time out marker */
  265.                                 endp = copy_event(endp, 1, TIME_OUT, 0, 0, 0);
  266.                                 eventcount++;
  267.                                 time -= 240;
  268.                                 meas_time += 240;
  269.                             }
  270.                         }
  271.                         else{       /* need measure end before event */
  272.                             if (meas_ticks - meas_time < 240){
  273.                                 sbyte = meas_ticks - meas_time; 
  274.                                 endp = copy_event(endp, 2, sbyte, MEAS_END, 
  275.                                     0, 0);
  276.                                 eventcount++;
  277.                                 time -= sbyte;
  278.                                 meas_time = 0;
  279.                             }
  280.                             else{       /* need to add a time out marker */
  281.                                 endp = copy_event(endp, 1, TIME_OUT, 0, 0, 0);
  282.                                 eventcount++;
  283.                                 time -= 240;
  284.                                 meas_time += 240;
  285.                             }
  286.                         }
  287.                     }
  288.                 } 
  289.             } /* while (!end_track) */
  290.             if (add_data){
  291.                 endp = copy_event(endp, 2, 0, ALL_END, 0, 0);
  292.                 eventcount++;
  293.             }
  294.             if (trk > 0){
  295.                 memcpy(events[trk - 1], &eventcount, sizeof(long));
  296.             }
  297.         } /* if track chunk */
  298.     } /* for each track */
  299.     
  300.     /* add starting MEAS_END for any empty tracks */
  301.     
  302.     for (trk = tracks; trk < NTRACK + 1; trk++){
  303.         endp = copy_event(endp, 2, 0, MEAS_END, 0, 0);
  304.     }
  305.     
  306.     write_buf(buf, endp, outfile);      /* send all data to outfile */
  307.     fclose(infile);                     /* close all files */
  308.     fclose(outfile);
  309.     fputs("\nCompleted data conversion.", stdout);
  310.     exit(0);
  311. }
  312.  
  313.  
  314. /* copies five char values to one memory area in sequence */
  315. char
  316. *copy_event(buf, nbyte, b1, b2, b3, b4)
  317. unsigned char *buf, nbyte, b1, b2, b3, b4;
  318. {
  319.     *buf++ = nbyte;
  320.     *buf++ = b1;
  321.     *buf++ = b2;
  322.     *buf++ = b3;
  323.     *buf++ = b4;
  324.     return (buf);
  325. }
  326.  
  327.  
  328. /* skip over n bytes in infile */
  329. void
  330. skip_byte(n, infile)
  331. int n;
  332. FILE *infile;
  333. {
  334.     int i;
  335.     
  336.     for (i = 0; i < n; i++)
  337.         fgetc(infile);
  338. }
  339.  
  340.  
  341. /* Get a variable length number from infile.  Variable length numbers are */
  342. /* a part of the MIDI files specification.  Bit 7 in each byte is used to */
  343. /* designate continuance of the number into the next byte.  The lower 7 */
  344. /* bits hold the numeric information.  This allows numbers of different */
  345. /* magnitudes to take up the minimum space on disk. */
  346. long
  347. get_var_len(infile)
  348. FILE *infile;
  349. {
  350.     register long ln;
  351.     unsigned char c;
  352.     int i;
  353.     
  354.     i = ln = 0;
  355.     do {
  356.         c = fgetc(infile);
  357.         ln = (ln << 7) + (c & 0x7F);
  358.         i++;
  359.     } while (c & 0x80 && i < 5);
  360.     return (ln);
  361. }
  362.     
  363.     
  364. /* read and return long int value from file infile */
  365. long
  366. get_long(infile)
  367. FILE *infile;
  368. {
  369.     int i;
  370.     register long ln;
  371.     
  372.     ln = 0;
  373.     for (i = 0; i < sizeof(long); i++){
  374.         ln = (ln << 8) + fgetc(infile);
  375.     }
  376.     return (ln);
  377. }
  378.  
  379.  
  380. /* read and return int value from file infile */
  381. int
  382. get_int(infile)
  383. FILE *infile;
  384. {
  385.     int i, n;
  386.     
  387.     n = 0;
  388.     for (i = 0; i < sizeof(int); i++){
  389.         n = (n << 8) + fgetc(infile);
  390.     }
  391.     return (n);
  392. }
  393.  
  394.  
  395. /* read string of size into memory pointed to by c */
  396. void
  397. get_string(c, infile, size)
  398. char *c;
  399. FILE *infile;
  400. int size;
  401. {
  402.     int i;
  403.     
  404.     for (i = 0; i < size; i++){
  405.         *c++ = (char)fgetc(infile);
  406.     }
  407.     *c = '\0';
  408. }
  409.     
  410.  
  411. /* read MIDI files tempo data in milliseconds/beat and return as MT tempo */
  412. /* in beats per minute */
  413. int
  414. get_tempo(infile)
  415. FILE *infile;
  416. {
  417.     long msec_beat;
  418.     int i;
  419.     
  420.     msec_beat = 0;
  421.     for (i = 0; i < 3; i++){
  422.         msec_beat = (msec_beat << 8) + fgetc(infile);
  423.     }
  424.     i = (int)(60000000/msec_beat);
  425.     return(i);
  426. }
  427.  
  428.  
  429. /* send bytes pointed to from s to e to outfile */ 
  430. void
  431. write_buf(s, e, outfile)
  432. char *s, *e;
  433. FILE *outfile;
  434. {
  435.     while (s != e)
  436.         fputc(*s++, outfile);
  437. }
  438.  
  439.  
  440. /* find string in input stream, return 1 if found, 0 if not, leaves infile */
  441. /* pointing to end of str, ready for next read */
  442. int
  443. find_str(infile, str)
  444. FILE *infile;
  445. char *str;
  446. {
  447.     char *s;
  448.     int c;
  449.  
  450.     s = str;
  451.     while (*s != '\0'){
  452.         c = fgetc(infile);
  453.         if (c == EOF)
  454.             return(0);
  455.         else if (c == *s)
  456.             s++;
  457.         else
  458.             s = str;
  459.     }
  460.     return(1);
  461. }
  462.  
  463.  
  464. /* Round time to nearest MT clock tick.  Roundoff errors can accumulate if */
  465. /* time values are truncated to match MT's clock. */
  466. long
  467. ratio_time(input_time, division)
  468. long input_time;
  469. int division;
  470. {
  471.     int time;
  472.     float f, divf, intime;
  473.     static int odd_even = 0;
  474.     
  475.     divf = division;
  476.     intime = input_time;
  477.     time = f = (intime * MT_TICKS) / divf;
  478.     if (f - time == 0.5){           /* alternate distribution of even time */
  479.         if (odd_even){              /* splits to avoid one track gaining on */
  480.             odd_even = 0;           /* another. */
  481.             return(time);
  482.         }
  483.         else{
  484.             odd_even = 1;
  485.             return(time + 1);
  486.         }
  487.     }
  488.     else if (f - time > 0.5){       /* otherwise, just round times */
  489.         return(time + 1);
  490.     }
  491.     return(time);
  492. }
  493.  
  494.  
  495. /* Open a file.  status is "rb" for read binary, "wb" for write binary, etc */
  496. FILE
  497. *open_file(filename, status)
  498. char *filename, *status;
  499. {
  500.     FILE *file;
  501.     
  502.     file = fopen(filename, status);
  503.     if (file == NULL){
  504.         fputs("\nCould not open file ", stdout);
  505.         fputs(filename, stdout);
  506.         fputc('\n', stdout);
  507.         exit(0);
  508.     }
  509.     return(file);
  510. }
  511.  
  512.  
  513. void
  514. exit_program()
  515. {
  516.     fputs("\nUsage:  mf_to_mt  infile  outfile", stdout);
  517.     fputs("\nWhere infile is the Standard MIDI file file name;", stdout);
  518.     fputs("\n outfile is the MT .SNG file name for output.\n", stdout);
  519.     exit(0);
  520. }
  521.