Initial checkin of miditool.
authorfredrik <fredrik@959494ce-11ee-0310-bf91-de5d638817bd>
Mon, 11 Feb 2008 00:22:34 +0000 (00:22 +0000)
committerfredrik <fredrik@959494ce-11ee-0310-bf91-de5d638817bd>
Mon, 11 Feb 2008 00:22:34 +0000 (00:22 +0000)
git-svn-id: svn+ssh://svn.dolda2000.com/srv/svn/repos/src/utils@1110 959494ce-11ee-0310-bf91-de5d638817bd

miditool.c [new file with mode: 0644]

diff --git a/miditool.c b/miditool.c
new file mode 100644 (file)
index 0000000..944aa1d
--- /dev/null
@@ -0,0 +1,418 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+int trackno = 0;
+int tickres;
+
+unsigned char fgetuc(FILE *in)
+{
+    return((unsigned char)fgetc(in));
+}
+
+int fgetl(FILE *in)
+{
+    return((fgetuc(in) << 24) |
+          (fgetuc(in) << 16) |
+          (fgetuc(in) << 8) |
+          fgetuc(in));
+}
+
+int fgetus(FILE *in)
+{
+    return((fgetuc(in) << 8) | fgetuc(in));
+}
+
+int fget3b(FILE *in)
+{
+    return((fgetuc(in) << 16) | (fgetuc(in) << 8) | fgetuc(in));
+}
+
+int fgetv(FILE *in, int *nb)
+{
+    int ret;
+    unsigned char c;
+    
+    for(ret = 0, c = 0x80, *nb = 0; (c & 0x80); ret = (ret << 7) | (c & 0x7f)) {
+       c = fgetuc(in);
+       (*nb)++;
+       if(*nb == 5) {
+           fprintf(stderr, "miditool: reading too many bytes from a variant number\n");
+           exit(1);
+       }
+    }
+    return(ret);
+}
+
+void fputl(uint32_t val, FILE *out)
+{
+    int i;
+    
+    for(i = 24; i >= 0; i -= 8)
+       fputc((val & (0xff << i)) >> i, out);
+}
+
+void fputus(uint16_t val, FILE *out)
+{
+    int i;
+    
+    for(i = 8; i >= 0; i -= 8)
+       fputc((val & (0xff << i)) >> i, out);
+}
+
+void fskip(FILE *in, int bytes)
+{
+    char *dummy;
+    
+    dummy = malloc(bytes);
+    fread(dummy, 1, bytes, in);
+    free(dummy);
+}
+
+void dumphead(FILE *in, FILE *out)
+{
+    int type, ntrk, res;
+    
+    type = fgetus(in);
+    ntrk = fgetus(in);
+    tickres = res = fgetus(in);
+    fprintf(out, "header %i %i %i\n", type, ntrk, res);
+    fprintf(out, "# Type: %i ", type);
+    if(type == 0)
+       fprintf(out, "(Single track)\n");
+    else if(type == 1)
+       fprintf(out, "(Simultaneous tracks)\n");
+    else if(type == 2)
+       fprintf(out, "(Sequential tracks)\n");
+    else
+       fprintf(out, "(Unknown)\n");
+    fprintf(out, "# Number of tracks: %i\n", ntrk);
+    fprintf(out, "# Resolution: %i ticks/qn\n", res);
+}
+
+int dumptrack(FILE *in, FILE *out, ssize_t len)
+{
+    int nb, dt, cmd, pcmd, mlen, llen;
+    int chan, note, val, id;
+    int tick;
+    
+    fprintf(out, "track\n");
+    fprintf(out, "# Number %i\n", trackno++);
+    fprintf(out, "# Length: %zi bytes\n", len);
+    pcmd = 0;
+    tick = 0;
+    while(len > 0) {
+       dt = fgetv(in, &nb);
+       tick += dt;
+       len -= nb;
+       cmd = fgetuc(in);
+       len--;
+       
+       if(!(cmd & 0x80))
+           cmd = pcmd;
+       pcmd = cmd;
+       if(!(cmd & 0x80)) {
+           fprintf(stderr, "miditool: illegal command (< 0x80)\n");
+           fskip(in, len);
+           return(1);
+       }
+       
+       chan = cmd & 0x0f;
+       cmd >>= 4;
+       if(cmd != 15)
+           fprintf(out, "ev %i %i ", tick, chan);
+       switch(cmd) {
+       case 8:
+       case 9:
+           note = fgetuc(in);
+           val = fgetuc(in);
+           len -= 2;
+           fprintf(out, "%s %i %i", (cmd == 9)?"on":"off", note, val);
+           break;
+       case 10:
+           val = fgetus(in);
+           len -= 2;
+           fprintf(out, "poly %i", val);
+           break;
+       case 11:
+           id = fgetuc(in);
+           val = fgetuc(in);
+           len -= 2;
+           fprintf(out, "ctrl %i %i", id, val);
+           break;
+       case 12:
+           val = fgetuc(in);
+           len--;
+           fprintf(out, "prgm %i", val);
+           break;
+       case 13:
+           val = fgetuc(in);
+           len--;
+           fprintf(out, "mono %i", val);
+           break;
+       case 14:
+           val = fgetus(in) - 8192;
+           len -= 2;
+           fprintf(out, "bend %i", val);
+           break;
+       case 15:
+           switch(chan) {
+           case 0:
+               fprintf(out, "msg0 ");
+               mlen = fgetv(in, &nb);
+               len -= nb;
+               for(; mlen > 0; mlen--) {
+                   fprintf(out, "%02x", fgetuc(in));
+                   len--;
+                   if(mlen > 0)
+                       fputc(' ', out);
+               }
+               fputc('\n', out);
+               break;
+           case 7:
+               fprintf(out, "msg7 ");
+               mlen = fgetv(in, &nb);
+               len -= nb;
+               for(; mlen > 0; mlen--) {
+                   fprintf(out, "%02x", fgetuc(in));
+                   len--;
+                   if(mlen > 0)
+                       fputc(' ', out);
+               }
+               fputc('\n', out);
+               break;
+           case 15:
+               id = fgetuc(in);
+               len--;
+               mlen = fgetv(in, &nb);
+               len -= nb;
+               llen = 0;
+               switch(id) {
+               case 1 ... 9:
+                   fprintf(out, "text %i ", id);
+                   for(llen = 0; llen < mlen; llen++) {
+                       val = fgetuc(in);
+                       if((val >= 20) && (val <= 126)) {
+                           fputc(val, out);
+                       } else {
+                           fprintf(out, "\\%03o", val);
+                       }
+                   }
+                   fputc('\n', out);
+                   break;
+               case 0x2f:
+                   fprintf(out, "end\n");
+                   break;
+               case 0x51:
+                   val = fget3b(in);
+                   llen = 3;
+                   fprintf(out, "tempo %i\n", val);
+                   fprintf(out, "# (%i us/qn) / (%i ticks/qn) = (%i us/tick)\n", val, tickres, val / tickres);
+                   break;
+               default:
+                   fprintf(out, "meta %i ", id);
+                   for(; mlen > 0; mlen--) {
+                       fprintf(out, "%02x", fgetuc(in));
+                       len--;
+                       if(mlen > 0)
+                           fputc(' ', out);
+                   }
+                   fputc('\n', out);
+                   fprintf(stderr, "miditool: warning: unknown midi meta event %i\n", id);
+                   fskip(in, mlen);
+                   len -= mlen;
+                   llen = -1;
+                   break;
+               }
+               if(llen != -1) {
+                   len -= llen;
+                   if(llen < mlen) {
+                       fskip(in, mlen - llen);
+                       len -= mlen - llen;
+                       fprintf(stderr, "miditool: warning: too little data read from meta event %i (%i < %i)\n", id, llen, mlen);
+                   } else if(llen > mlen) {
+                       fprintf(stderr, "miditool: too much data read from meta event %i (%i > %i)\n", id, llen, mlen);
+                       fskip(in, len);
+                       return(1);
+                   }
+               }
+               break;
+           default:
+               fprintf(stderr, "miditool: unknown midi special event %i\n", chan);
+               fskip(in, len);
+               return(1);
+           }
+           break;
+       }
+       if(cmd != 15)
+           fputc('\n', out);
+    }
+    if(len < 0) {
+       fprintf(stderr, "miditool: read %i bytes too much from track from track\n", -len);
+       return(1);
+    }
+    return(0);
+}
+
+int dumpchunk(FILE *in, FILE *out)
+{
+    char id[4];
+    size_t len;
+    
+    if((fread(id, 1, 4, in)) == 0)
+       return(0);
+    len = fgetl(in);
+    if(!memcmp(id, "MThd", 4)) {
+       if(len != 6) {
+           fprintf(stderr, "miditool: invalid header chunk of length %zi\n", len);
+           return(1);
+       }
+       dumphead(in, out);
+    } else if(!memcmp(id, "MTrk", 4)) {
+       if(dumptrack(in, out, len))
+           return(1);
+    } else {
+       fprintf(out, "# Unknown chunk type (%.4s) of length %zi\n", id, len);
+       fskip(in, len);
+    }
+    return(0);
+}
+
+char *constrack(FILE *in, int *len)
+{
+    unsigned char *bp;
+    char *buf;
+    char line[128], *p;
+    int sz;
+    int ltime, chan;
+    
+    void putv(int v, int c) {
+       if(v >= 128)
+           putv(v >> 7, 1);
+       *(bp++) = (c?0x80:0) | (v & 0x7f);
+    }
+    void putb(int v, int n) {
+       if(n > 1)
+           putb(v >> 8, n - 1);
+       *(bp++) = v & 0xff;
+    }
+    
+    buf = malloc(sz = 65536);
+    bp = (unsigned char *)buf;
+    ltime = 0;
+    while(fgets(line, sizeof(line), in) != NULL) {
+       if(((char *)bp - buf) < 32768)
+           buf = realloc(buf, sz += 65536);
+       p = strtok(line, " ");
+       if((p == NULL) || (p[0] == '#'))
+           continue;
+       if(!strcmp(p, "ev")) {
+           if((p = strtok(NULL, " ")) == NULL) {
+               fprintf(stderr, "miditool: truncated ev line\n");
+               goto err;
+           }
+           putv(atoi(p) - ltime, 0);
+           ltime = atoi(p);
+           if((p = strtok(NULL, " ")) == NULL) {
+               fprintf(stderr, "miditool: truncated ev line\n");
+               goto err;
+           }
+           chan = atoi(p);
+           if((p = strtok(NULL, " ")) == NULL) {
+               fprintf(stderr, "miditool: truncated ev line\n");
+               goto err;
+           }
+           if(!strcmp(p, "on")) {
+               *(bp++) = 0x90 | chan;
+               if((p = strtok(NULL, " ")) == NULL) {
+                   fprintf(stderr, "miditool: truncated ev on line\n");
+                   goto err;
+               }
+               *(bp++) = atoi(p);
+               if((p = strtok(NULL, " ")) == NULL) {
+                   fprintf(stderr, "miditool: truncated ev on line\n");
+                   goto err;
+               }
+               *(bp++) = atoi(p);
+           } else if(!strcmp(p, "off")) {
+               *(bp++) = 0x80 | chan;
+               if((p = strtok(NULL, " ")) == NULL) {
+                   fprintf(stderr, "miditool: truncated ev on line\n");
+                   goto err;
+               }
+               *(bp++) = atoi(p);
+               if((p = strtok(NULL, " ")) == NULL) {
+                   fprintf(stderr, "miditool: truncated ev on line\n");
+                   goto err;
+               }
+               *(bp++) = atoi(p);
+           } else {
+               fprintf(stderr, "miditool: ignoring unknown event %s\n", p);
+           }
+       } else if(!strcmp(p, "tempo")) {
+           *(bp++) = 0xff;
+           *(bp++) = 0x51;
+           putv(3, 0);
+           if((p = strtok(NULL, " ")) == NULL) {
+               fprintf(stderr, "miditool: tempo line without tempo\n");
+               goto err;
+           }
+           putb(atoi(p), 3);
+       } else if(!strcmp(p, "text")) {
+       } else if(!strcmp(p, "end")) {
+           *(bp++) = 0xff;
+           *(bp++) = 0x2f;
+           putv(0, 0);
+       } else {
+           fprintf(stderr, "miditool: ignoring unknown command %s\n", p);
+       }
+    }
+    return(buf);
+    
+err:
+    free(buf);
+    return(NULL);
+}
+
+int main(int argc, char **argv)
+{
+    FILE *in, *out;
+    char *cmd;
+    
+    if(argc < 2) {
+       fprintf(stderr, "usage: miditool command (see `miditool help' for a list of commands)\n");
+       exit(1);
+    }
+    cmd = argv[1];
+    argc--; argv++;
+    if(!strcmp(cmd, "help")) {
+       printf("commands:\n");
+       printf("\tdump\tDumps a MIDI file to text format\n");
+       printf("\tconsh\tConstructs a header chunk\n");
+    } else if(!strcmp(cmd, "consh")) {
+       if(argc < 4) {
+           fprintf(stderr, "usage: miditool consh TYPE NTRACKS RES\n");
+           exit(1);
+       }
+       out = stdout;
+       fputs("MThd", out);
+       fputl(6, out);
+       fputus(atoi(argv[1]), out);
+       fputus(atoi(argv[2]), out);
+       fputus(atoi(argv[3]), out);
+    } else if(!strcmp(cmd, "dump")) {
+       in = stdin;
+       if(argc > 1) {
+           if((in = fopen(argv[1], "r")) == NULL) {
+               perror(argv[1]);
+               exit(1);
+           }
+       }
+       while(!feof(in)) {
+           dumpchunk(in, stdout);
+       }
+    }
+    return(0);
+}