initial commit
This commit is contained in:
commit
36ca6059c1
49 changed files with 5290 additions and 0 deletions
574
midi.h
Normal file
574
midi.h
Normal file
|
@ -0,0 +1,574 @@
|
|||
enum mid_event {
|
||||
midev_noteoff = 0x80,
|
||||
midev_noteon = 0x90,
|
||||
midev_aftertouch = 0xa0,
|
||||
midev_control = 0xb0,
|
||||
midev_progchg = 0xc0,
|
||||
midev_chnpressure = 0xd0,
|
||||
midev_pitchbend = 0xe0,
|
||||
midev_sysex = 0xf0,
|
||||
midev_songpos = 0xf2,
|
||||
midev_songsel = 0xf3,
|
||||
midev_tunereq = 0xf6,
|
||||
midev_endsysex = 0xf7,
|
||||
midev_clock = 0xf8,
|
||||
midev_start = 0xfa,
|
||||
midev_continue = 0xfb,
|
||||
midev_stop = 0xfc,
|
||||
midev_actsense = 0xfe,
|
||||
midev_meta = 0xff
|
||||
};
|
||||
|
||||
enum mid_meta {
|
||||
midmt_seqnum = 0x00, // nn nn
|
||||
midmt_text = 0x01, // text...
|
||||
midmt_copyright = 0x02, // text...
|
||||
midmt_trackname = 0x03, // text...
|
||||
midmt_instrname = 0x04, // text...
|
||||
midmt_lyric = 0x05, // text...
|
||||
midmt_marker = 0x06, // text...
|
||||
midmt_cuepoint = 0x07, // text...
|
||||
midmt_chnprefix = 0x20, // cc
|
||||
midmt_endtrack = 0x2f, //
|
||||
midmt_tempo = 0x51, // tt tt tt
|
||||
midmt_smpte = 0x54, // hr mn se fr ff
|
||||
midmt_timesig = 0x58, // nn dd cc bb
|
||||
midmt_keysig = 0x59, // sf mi
|
||||
midmt_seqspec = 0x7f // data...
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t *buffer;
|
||||
uint32_t size;
|
||||
uint32_t pos;
|
||||
uint8_t status;
|
||||
uint8_t ending;
|
||||
uint32_t wait;
|
||||
uint16_t trknum;
|
||||
} mid_track;
|
||||
|
||||
typedef struct {
|
||||
mid_track *track;
|
||||
uint16_t tracks;
|
||||
uint32_t tpqn;
|
||||
uint32_t uspb;
|
||||
uint32_t ticktime;
|
||||
} mid_handle;
|
||||
|
||||
const char *mid_hdr = "MThd";
|
||||
const char *mid_trk = "MTrk";
|
||||
|
||||
#define MID_DEFTEMPO 500000
|
||||
|
||||
void mid_settempo(mid_handle *mid, uint32_t tempo) {
|
||||
mid->uspb = tempo;
|
||||
mid->ticktime = mid->uspb / mid->tpqn;
|
||||
}
|
||||
|
||||
uint32_t mid_read_uint32(uint8_t *data) {
|
||||
uint32_t value = 0;
|
||||
for(int h = 0; h < 4; h++) {
|
||||
value |= ((uint32_t)data[h]);
|
||||
value <<= h < 3 ? 8 : 0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
uint16_t mid_read_uint16(uint8_t *data) {
|
||||
uint16_t value = 0;
|
||||
for(int h = 0; h < 2; h++) {
|
||||
value |= ((uint16_t)data[h]);
|
||||
value <<= h < 1 ? 8 : 0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
uint32_t midt_read_uint32(mid_track *trk) {
|
||||
uint32_t value = 0;
|
||||
for(int h = 0; h < 4; h++) {
|
||||
if(trk->pos >= trk->size) {
|
||||
break;
|
||||
}
|
||||
value |= ((uint32_t)trk->buffer[trk->pos++]);
|
||||
value <<= h < 3 ? 8 : 0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
uint32_t midt_read_uint24(mid_track *trk) {
|
||||
uint32_t value = 0;
|
||||
for(int h = 0; h < 3; h++) {
|
||||
if(trk->pos >= trk->size) {
|
||||
break;
|
||||
}
|
||||
value |= ((uint32_t)trk->buffer[trk->pos++]);
|
||||
value <<= h < 2 ? 8 : 0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
uint16_t midt_read_uint16(mid_track *trk) {
|
||||
uint16_t value = 0;
|
||||
for(int h = 0; h < 2; h++) {
|
||||
if(trk->pos >= trk->size) {
|
||||
break;
|
||||
}
|
||||
value |= ((uint16_t)trk->buffer[trk->pos++]);
|
||||
value <<= h < 1 ? 8 : 0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
uint8_t midt_read_uint8(mid_track *trk) {
|
||||
uint8_t value = 0;
|
||||
if(trk->pos < trk->size) {
|
||||
value = trk->buffer[trk->pos++];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void midt_read_var(mid_track *trk, uint32_t len, uint8_t *chars) {
|
||||
memset(chars, 0, len);
|
||||
for(int h = 0; h < len; h++) {
|
||||
if(trk->pos >= trk->size) {
|
||||
break;
|
||||
}
|
||||
chars[h] = trk->buffer[trk->pos++];
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t mid_read_vlen(mid_track *trk)
|
||||
{
|
||||
uint32_t value;
|
||||
uint8_t bt;
|
||||
|
||||
if(trk->pos >= trk->size) {
|
||||
return 0;
|
||||
}
|
||||
if((value = trk->buffer[trk->pos++]) & 0x80)
|
||||
{
|
||||
value &= 0x7f;
|
||||
do {
|
||||
if(trk->pos >= trk->size) {
|
||||
break;
|
||||
}
|
||||
value = (value << 7) + ((bt = trk->buffer[trk->pos++]) & 0x7f);
|
||||
}
|
||||
while(bt & 0x80);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
bool mid_read(mid_handle *mid, const char *filename) {
|
||||
int err;
|
||||
FILE *fd = fopen(filename, "rb");
|
||||
if(fd == NULL) {
|
||||
err = errno;
|
||||
mid_log(STR_MID_OPNERR, filename, strerror(err), err);
|
||||
return false;
|
||||
}
|
||||
if(fseek(fd, 0L, SEEK_END)) {
|
||||
err = errno;
|
||||
mid_log(STR_MID_EGEN, filename, strerror(err), err);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
long size = ftell(fd);
|
||||
if(size < 0L) {
|
||||
err = errno;
|
||||
mid_log(STR_MID_EGEN, filename, strerror(err), err);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
if(fseek(fd, 0L, SEEK_SET)) {
|
||||
err = errno;
|
||||
mid_log(STR_MID_EGEN, filename, strerror(err), err);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(size < 14) {
|
||||
mid_log(STR_MID_ESHORT, filename);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
uint8_t header[14];
|
||||
if(fread(header, 1, 14, fd) != 14) {
|
||||
err = feof(fd);
|
||||
mid_log(err == 0 ? STR_MID_EIO : STR_MID_EEOF, filename);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
if(memcmp(header, mid_hdr, 4) != 0) {
|
||||
mid_log(STR_MID_EMFHDR, filename);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
if(mid_read_uint32(header+4) != 6) {
|
||||
mid_log(STR_MID_EHSIZE, filename);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
bool mtrack;
|
||||
uint16_t type = mid_read_uint16(header+8);
|
||||
if(type == 0) {
|
||||
mtrack = false;
|
||||
}
|
||||
else if((type == 1) || (type == 2)) {
|
||||
mtrack = true;
|
||||
}
|
||||
else {
|
||||
mid_log(STR_MID_ETKFMT, filename);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
mid->tracks = mid_read_uint16(header+10);
|
||||
if(mid->tracks == 0) {
|
||||
mid_log(STR_MID_ENOTRK, filename);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
else if((mtrack ^ 1) && (mid->tracks > 1)) {
|
||||
mid_log(STR_MID_ESMULT, filename);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
mid->tpqn = mid_read_uint16(header+12);
|
||||
if((mid->tpqn & 0x8000) > 0) {
|
||||
mid_log(STR_MID_ESMPTE, filename);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
if(mid->tpqn == 0) {
|
||||
mid_log(STR_MID_EDZERO, filename);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
uint32_t esize = 0;
|
||||
for(uint16_t trk = 0; trk < mid->tracks; trk++) {
|
||||
if(fread(header, 1, 8, fd) != 8) {
|
||||
err = feof(fd);
|
||||
mid_log(err == 0 ? STR_MID_EIO : STR_MID_EEOF, filename);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
if(memcmp(header, mid_trk, 4) != 0) {
|
||||
mid_log(STR_MID_ETKHDR, filename);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
uint32_t trks = mid_read_uint32(header+4);
|
||||
if((14 + esize + 8 + trks) > size) {
|
||||
mid_log(STR_MID_EOUTOF, filename, trk+1);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
if(fseek(fd, trks, SEEK_CUR)) {
|
||||
err = errno;
|
||||
mid_log(STR_MID_EGEN, filename, strerror(err), err);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
esize += trks + 8;
|
||||
}
|
||||
if(fseek(fd, 14L, SEEK_SET)) {
|
||||
err = errno;
|
||||
mid_log(STR_MID_EGEN, filename, strerror(err), err);
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
esize -= (mid->tracks * 8);
|
||||
uint8_t *buf = malloc((sizeof(mid_track) * mid->tracks) + esize);
|
||||
mid->track = (mid_track*)buf;
|
||||
buf += (sizeof(mid_track) * mid->tracks);
|
||||
for(uint16_t trk = 0; trk < mid->tracks; trk++) {
|
||||
memset((mid->track)+trk, 0, sizeof(mid_track));
|
||||
if(fread(header, 1, 8, fd) != 8) {
|
||||
err = feof(fd);
|
||||
mid_log(err == 0 ? STR_MID_EIO : STR_MID_EEOF, filename);
|
||||
fclose(fd);
|
||||
free(mid->track);
|
||||
return false;
|
||||
}
|
||||
mid->track[trk].size = mid_read_uint32(header+4);
|
||||
if(fread(buf, 1, mid->track[trk].size, fd) != mid->track[trk].size) {
|
||||
err = feof(fd);
|
||||
mid_log(err == 0 ? STR_MID_EIO : STR_MID_EEOF, filename);
|
||||
fclose(fd);
|
||||
free(mid->track);
|
||||
return false;
|
||||
}
|
||||
mid->track[trk].buffer = buf;
|
||||
mid->track[trk].pos = 0;
|
||||
mid->track[trk].wait = 0;
|
||||
mid->track[trk].trknum = trk;
|
||||
buf += mid->track[trk].size;
|
||||
}
|
||||
fclose(fd);
|
||||
mid_settempo(mid, MID_DEFTEMPO);
|
||||
return true;
|
||||
}
|
||||
|
||||
void mid_free(mid_handle *mid) {
|
||||
free(mid->track);
|
||||
}
|
||||
|
||||
void mid_process_meta(mid_handle *mid, mid_track *trk) {
|
||||
uint8_t meta = midt_read_uint8(trk);
|
||||
uint32_t size = mid_read_vlen(trk);
|
||||
switch(meta) {
|
||||
case midmt_seqnum:
|
||||
if(size == 0) {
|
||||
mid_dlog(STR_MID_SEQNUMO, trk->trknum, trk->trknum);
|
||||
return;
|
||||
}
|
||||
else if(size != 2) {
|
||||
trk->pos += size;
|
||||
mid_log(STR_MID_METALEN, trk->trknum, meta, 2, size);
|
||||
return;
|
||||
}
|
||||
mid_dlog(STR_MID_SEQNUM, trk->trknum, midt_read_uint16(trk));
|
||||
return;
|
||||
case midmt_text:
|
||||
case midmt_copyright:
|
||||
case midmt_trackname:
|
||||
case midmt_instrname:
|
||||
case midmt_lyric:
|
||||
case midmt_marker:
|
||||
case midmt_cuepoint: {
|
||||
uint8_t dt[size+1];
|
||||
dt[size] = 0;
|
||||
midt_read_var(trk, size, dt);
|
||||
mid_tlog(meta, dt);
|
||||
return;
|
||||
}
|
||||
case midmt_chnprefix:
|
||||
if(size != 1) {
|
||||
trk->pos += size;
|
||||
mid_log(STR_MID_METALEN, trk->trknum, meta, 1, size);
|
||||
return;
|
||||
}
|
||||
mid_dlog(STR_MID_CHNPFX, trk->trknum, midt_read_uint8(trk));
|
||||
return;
|
||||
case midmt_endtrack:
|
||||
trk->pos += size;
|
||||
trk->ending = 1;
|
||||
mid_dlog(STR_MID_END, trk->trknum);
|
||||
return;
|
||||
case midmt_tempo:
|
||||
if(size != 3) {
|
||||
trk->pos += size;
|
||||
mid_log(STR_MID_METALEN, trk->trknum, meta, 3, size);
|
||||
return;
|
||||
}
|
||||
mid_settempo(mid, midt_read_uint24(trk));
|
||||
mid_dlog(STR_MID_TEMPO, 60000000 / mid->uspb);
|
||||
return;
|
||||
case midmt_smpte:
|
||||
if(size != 5) {
|
||||
trk->pos += size;
|
||||
mid_log(STR_MID_METALEN, trk->trknum, meta, 5, size);
|
||||
return;
|
||||
}
|
||||
trk->pos += 5;
|
||||
mid_dlog(STR_MID_SMPTE);
|
||||
return;
|
||||
case midmt_timesig: {
|
||||
if(size != 4) {
|
||||
trk->pos += size;
|
||||
mid_log(STR_MID_METALEN, trk->trknum, meta, 4, size);
|
||||
return;
|
||||
}
|
||||
uint8_t n = midt_read_uint8(trk);
|
||||
uint32_t d = 1 << ((uint32_t)midt_read_uint8(trk));
|
||||
uint8_t c = midt_read_uint8(trk);
|
||||
uint8_t b = midt_read_uint8(trk);
|
||||
mid_dlog(STR_MID_TIMESIG, n, d, c, b);
|
||||
return;
|
||||
}
|
||||
case midmt_keysig: {
|
||||
if(size != 2) {
|
||||
trk->pos += size;
|
||||
mid_log(STR_MID_METALEN, trk->trknum, meta, 2, size);
|
||||
return;
|
||||
}
|
||||
int8_t s = midt_read_uint8(trk);
|
||||
uint8_t m = midt_read_uint8(trk);
|
||||
mid_dlog(STR_MID_KEYSIG, s, (m == 0) ? "MAJOR" : ((m == 1) ? "MINOR" : "- ? -"));
|
||||
return;
|
||||
}
|
||||
case midmt_seqspec: {
|
||||
trk->pos += size;
|
||||
mid_dlog(STR_MID_SEQDATA, trk->trknum, size);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
trk->pos += size;
|
||||
mid_dlog(STR_MID_UNKMETA, trk->trknum, meta, size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef MID_NOEVT
|
||||
void mid_process(mid_handle *mid, mid_track *trk, bank_handle *bank, opl3_chip *chip) {
|
||||
#else
|
||||
void mid_process(mid_handle *mid, mid_track *trk) {
|
||||
#endif
|
||||
if(trk->pos >= trk->size) {
|
||||
return;
|
||||
}
|
||||
uint8_t status = trk->buffer[trk->pos++];
|
||||
if((status & 0x80) == 0) {
|
||||
status = trk->status;
|
||||
trk->pos -= 1;
|
||||
}
|
||||
else {
|
||||
trk->status = status;
|
||||
}
|
||||
if((status & 0xf0) != 0xf0) {
|
||||
uint8_t channel = status & 0x0f;
|
||||
status &= 0xf0;
|
||||
switch(status) {
|
||||
case midev_noteoff: {
|
||||
uint8_t o_key = midt_read_uint8(trk);
|
||||
uint8_t o_velocity = midt_read_uint8(trk);
|
||||
#ifndef MID_NOEVT
|
||||
bank_noteoff(bank, chip, channel, o_key, o_velocity);
|
||||
#endif
|
||||
mid_dlog(STR_MID_NOTEOFF, channel+1, o_key, o_velocity);
|
||||
break;
|
||||
}
|
||||
case midev_noteon: {
|
||||
uint8_t key = midt_read_uint8(trk);
|
||||
uint8_t velocity = midt_read_uint8(trk);
|
||||
#ifndef MID_NOEVT
|
||||
if(velocity == 0) {
|
||||
bank_noteoff(bank, chip, channel, key, velocity);
|
||||
}
|
||||
else {
|
||||
bank_noteon(bank, chip, channel, key, velocity);
|
||||
}
|
||||
#endif
|
||||
mid_dlog(STR_MID_NOTEON, channel+1, key, velocity);
|
||||
break;
|
||||
}
|
||||
case midev_aftertouch: {
|
||||
uint8_t pressure = midt_read_uint8(trk);
|
||||
mid_dlog(STR_MID_PRESS, channel+1, pressure);
|
||||
break;
|
||||
}
|
||||
case midev_control: {
|
||||
uint8_t control = midt_read_uint8(trk);
|
||||
uint8_t value = midt_read_uint8(trk);
|
||||
#ifndef MID_NOEVT
|
||||
bank_control(bank, chip, channel, control, value);
|
||||
#endif
|
||||
mid_dlog(STR_MID_CC, channel+1, control, value);
|
||||
break;
|
||||
}
|
||||
case midev_progchg: {
|
||||
uint8_t program = midt_read_uint8(trk);
|
||||
#ifndef MID_NOEVT
|
||||
bank_progchange(bank, chip, channel, program);
|
||||
#endif
|
||||
mid_dlog(STR_MID_PROG, channel+1, program);
|
||||
break;
|
||||
}
|
||||
case midev_chnpressure: {
|
||||
uint8_t cpressure = midt_read_uint8(trk);
|
||||
mid_dlog(STR_MID_CPRESS, channel+1, cpressure);
|
||||
break;
|
||||
}
|
||||
case midev_pitchbend: {
|
||||
uint16_t pb = ((uint16_t)midt_read_uint8(trk)) | (((uint16_t)midt_read_uint8(trk)) << 7);
|
||||
int16_t pitch = ((int16_t)pb) - 0x2000;
|
||||
#ifndef MID_NOEVT
|
||||
bank_pitchbend(bank, chip, channel, pitch);
|
||||
#endif
|
||||
mid_dlog(STR_MID_PITCH, channel+1, pitch);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch(status) {
|
||||
case midev_sysex: {
|
||||
uint32_t slen = mid_read_vlen(trk);
|
||||
trk->pos += slen;
|
||||
mid_dlog(STR_MID_SYSEX, slen);
|
||||
break;
|
||||
}
|
||||
case midev_songpos:
|
||||
mid_dlog(STR_MID_SONGPOS, ((uint16_t)midt_read_uint8(trk)) | (((uint16_t)midt_read_uint8(trk)) << 7));
|
||||
break;
|
||||
case midev_songsel:
|
||||
mid_dlog(STR_MID_SONGSEL, midt_read_uint8(trk));
|
||||
break;
|
||||
case midev_tunereq:
|
||||
mid_dlog(STR_MID_TUNE);
|
||||
break;
|
||||
case midev_endsysex: {
|
||||
uint32_t elen = mid_read_vlen(trk);
|
||||
trk->pos += elen;
|
||||
mid_dlog(STR_MID_ESYSEX, elen);
|
||||
break;
|
||||
}
|
||||
case midev_clock:
|
||||
case midev_start:
|
||||
case midev_continue:
|
||||
case midev_stop:
|
||||
case midev_actsense:
|
||||
mid_dlog(STR_MID_GENSTAT, status);
|
||||
break;
|
||||
case midev_meta:
|
||||
mid_process_meta(mid, trk);
|
||||
break;
|
||||
default:
|
||||
mid_log(STR_MID_UNKSTAT, status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef MID_NOEVT
|
||||
uint8_t mid_tick(mid_handle *mid, bank_handle *bank, opl3_chip *chip) {
|
||||
#else
|
||||
uint8_t mid_tick(mid_handle *mid) {
|
||||
#endif
|
||||
uint8_t end = 1;
|
||||
for(int trk = 0; trk < mid->tracks; trk++) {
|
||||
mid_track *track = (mid->track)+trk;
|
||||
if(track->ending) {
|
||||
continue;
|
||||
}
|
||||
if(track->wait > 0) {
|
||||
track->wait -= 1;
|
||||
if(track->wait > 0) {
|
||||
end = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
while(true) {
|
||||
if(track->pos > 0) {
|
||||
#ifndef MID_NOEVT
|
||||
mid_process(mid, track, bank, chip);
|
||||
#else
|
||||
mid_process(mid, track);
|
||||
#endif
|
||||
if((track->ending ^ 1) && (track->pos >= track->size)) {
|
||||
mid_log(STR_MID_EEND, track->trknum);
|
||||
track->ending = 1;
|
||||
}
|
||||
if(track->ending) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
track->wait = mid_read_vlen(track);
|
||||
if(track->wait > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
end &= track->ending;
|
||||
}
|
||||
return end ^ 1;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue