From: fredrik Date: Mon, 11 Feb 2008 00:22:34 +0000 (+0000) Subject: Initial checkin of miditool. X-Git-Url: http://dolda2000.com/gitweb/?p=utils.git;a=commitdiff_plain;h=9f167cc92bf7c118bba3a6a0e3880bdba6c75da6 Initial checkin of miditool. git-svn-id: svn+ssh://svn.dolda2000.com/srv/svn/repos/src/utils@1110 959494ce-11ee-0310-bf91-de5d638817bd --- diff --git a/miditool.c b/miditool.c new file mode 100644 index 0000000..944aa1d --- /dev/null +++ b/miditool.c @@ -0,0 +1,418 @@ +#include +#include +#include +#include +#include + +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); +}