ref: f2d78c632d9384ef45c7da914321f9e30f79ff67
dir: /midifile.c/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include "midifile.h"
Trk *tr;
int mfmt, ntrk, div = 1, tempo = 500000;
int trace, stream;
vlong tic;
Biobuf *ib;
void *
emalloc(ulong n)
{
void *p;
p = mallocz(n, 1);
if(p == nil)
sysfatal("mallocz: %r");
setmalloctag(p, getcallerpc(&n));
return p;
}
void
dprint(char *fmt, ...)
{
char s[256];
va_list arg;
if(!trace)
return;
va_start(arg, fmt);
vseprint(s, s+sizeof s, fmt, arg);
va_end(arg);
fprint(2, "%s", s);
}
Biobuf *
bfdopen(int fd, int mode)
{
Biobuf *bf;
bf = Bfdopen(fd, mode);
if(bf == nil)
sysfatal("bfdopen: %r");
Blethal(bf, nil);
return bf;
}
Biobuf *
bopen(char *file, int mode)
{
int fd;
fd = open(file, mode);
if(fd < 0)
sysfatal("bopen: %r");
return bfdopen(fd, mode);
}
void
bread(void *u, int n)
{
if(Bread(ib, u, n) != n)
sysfatal("bread: short read");
}
u8int
get8(Trk *x)
{
u8int v;
if(x == nil || x->p == nil || x->e == nil)
Bread(ib, &v, 1);
else{
if(x->p >= x->e || x->ended)
sysfatal("track overflow");
v = *x->p++;
}
return v;
}
u16int
get16(Trk *x)
{
u16int v;
v = get8(x) << 8;
return v | get8(x);
}
u32int
get32(Trk *x)
{
u32int v;
v = get16(x) << 16;
return v | get16(x);
}
u8int
peekbyte(Trk *x)
{
return *x->p;
}
u32int
peekvar(Trk *x)
{
uchar *p;
uint v;
p = x->p;
v = getvar(x);
x->p = p;
return v;
}
void
skip(Trk *x, int n)
{
while(n-- > 0)
get8(x);
}
int
getvar(Trk *x)
{
int v, w;
w = get8(x);
v = w & 0x7f;
while(w & 0x80){
if(v & 0xff000000)
sysfatal("invalid variable-length number");
v <<= 7;
w = get8(x);
v |= w & 0x7f;
}
return v;
}
double
tc(double n)
{
return (n * tempo * Rate / div) / 1e6;
}
int
nextevent(Trk *x)
{
int e;
x->prev = x->p;
e = get8(x);
if((e & 0x80) == 0){
if(x->p != nil){
x->p--;
x->prev--;
}
e = x->ev;
if((e & 0x80) == 0)
sysfatal("invalid event %#ux", e);
}else
x->ev = e;
return e;
}
/* FIXME: use midump's implementation to make this not suck */
static void
setmsg(Msg *m, int chan, int type, int arg1, int arg2)
{
m->chan = chan;
m->type = type;
m->arg1 = arg1;
m->arg2 = arg2;
}
void
translate(Trk *x, int e, Msg *msg)
{
int c, n, m, type;
uchar *p;
c = e & 0xf;
dprint("ch%02d ", c);
n = get8(x);
m = -1;
type = Cunknown;
switch(e >> 4){
case 0x8:
m = get8(x);
dprint("note off\t%02ux\taftertouch\t%02ux", n, m);
type = Cnoteoff;
break;
case 0x9:
m = get8(x);
dprint("note on\t%02ux\tvelocity\t%02ux", n, m);
type = Cnoteon;
break;
case 0xb:
m = get8(x);
dprint("control change: ");
switch(n){
case 0x00:
dprint("bank select msb\t%02ux", m);
type = Cbankmsb;
break;
case 0x07:
dprint("channel volume\t%02ux", m);
type = Cchanvol;
break;
case 0x0a:
dprint("pan\t%02ux", m);
type = Cpan;
break;
default:
dprint("unknown controller %.4ux", n);
break;
}
break;
case 0xc:
dprint("program change\t%02ux", n);
type = Cprogram;
break;
case 0xe:
n = (get8(x) << 7 | n) - 0x4000 / 2;
dprint("pitch bend\t%02x", n);
type = Cpitchbend;
break;
case 0xf:
dprint("sysex:\t");
if((e & 0xf) == 0){
m = 0;
while(get8(x) != 0xf7)
m++;
fprint(2, "sysex n %d m %d\n", n, m);
type = Csysex;
break;
}
m = get8(x);
switch(n){
case 0x2f:
dprint("... so long!");
x->ended = 1;
type = Ceot;
break;
case 0x51:
tempo = get16(x) << 8;
tempo |= get8(x);
dprint("tempo change\t%d", tempo);
type = Ctempo;
break;
default:
dprint("skipping unhandled event %02ux", n);
skip(x, m);
break;
}
break;
case 0xa:
m = get8(x);
dprint("polyphonic key pressure/aftertouch\t%02ux\t%02ux", n, m);
type = Ckeyafter;
break;
case 0xd:
m = get8(x);
dprint("channel pressure/aftertouch\t%02ux\t%02ux", n, m);
type = Cchanafter;
break;
default: sysfatal("invalid event %#ux", e >> 4);
}
setmsg(msg, c, type, n, m);
dprint("\t[");
for(p=x->prev; p<x->p; p++)
dprint("%02ux", *p);
dprint("]\n");
}
int
readmid(char *file)
{
u32int n, z;
uchar *s;
Trk *x;
ib = file != nil ? bopen(file, OREAD) : bfdopen(0, OREAD);
if(get32(nil) != 0x4d546864 || get32(nil) != 6){
werrstr("invalid header");
return -1;
}
mfmt = get16(nil);
ntrk = get16(nil);
if(ntrk == 1)
mfmt = 0;
if(mfmt < 0 || mfmt > 1){
werrstr("unsupported format %d", mfmt);
return -1;
}
div = get16(nil);
tr = emalloc(ntrk * sizeof *tr);
for(x=tr, z=-1UL; x<tr+ntrk; x++){
if(get32(nil) != 0x4d54726b){
werrstr("invalid track");
return -1;
}
n = get32(nil);
x->s = emalloc(n+1);
bread(x->s, n);
x->p = x->s;
x->prev = x->s;
x->e = x->s + n;
x->Δ = getvar(x); /* prearm */
if(x->Δ < z)
z = x->Δ;
}
for(x=tr; x<tr+ntrk; x++)
x->Δ -= z;
Bterm(ib);
ib = nil;
return 0;
}