first and last commit
This commit is contained in:
commit
c9e78fd810
381 changed files with 37141 additions and 0 deletions
518
midi.h
Normal file
518
midi.h
Normal file
|
@ -0,0 +1,518 @@
|
|||
|
||||
void snd_logd(const char *fmt, ...);
|
||||
void snd_loge(const char *fmt, ...);
|
||||
void snd_logx(const char *fmt, ...);
|
||||
|
||||
void mid_dlog(const char *format, ...) {
|
||||
if(sgt.log_debug)
|
||||
snd_logd(format);
|
||||
}
|
||||
|
||||
void mid_settempo(mid_handle *mid, uint tempo) {
|
||||
mid->uspb = tempo;
|
||||
mid->ticktime = mid->uspb / mid->tpqn;
|
||||
}
|
||||
|
||||
uint mid_read_uint32(byte *data) {
|
||||
uint value = 0;
|
||||
for(int h = 0; h < 4; h++) {
|
||||
value |= ((uint)data[h]);
|
||||
value <<= h < 3 ? 8 : 0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
ushort mid_read_uint16(byte *data) {
|
||||
ushort value = 0;
|
||||
for(int h = 0; h < 2; h++) {
|
||||
value |= ((ushort)data[h]);
|
||||
value <<= h < 1 ? 8 : 0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
uint midt_read_uint32(mid_track *trk) {
|
||||
uint value = 0;
|
||||
for(int h = 0; h < 4; h++) {
|
||||
if(trk->pos >= trk->size) {
|
||||
break;
|
||||
}
|
||||
value |= ((uint)trk->buffer[trk->pos++]);
|
||||
value <<= h < 3 ? 8 : 0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
uint midt_read_uint24(mid_track *trk) {
|
||||
uint value = 0;
|
||||
for(int h = 0; h < 3; h++) {
|
||||
if(trk->pos >= trk->size) {
|
||||
break;
|
||||
}
|
||||
value |= ((uint)trk->buffer[trk->pos++]);
|
||||
value <<= h < 2 ? 8 : 0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
ushort midt_read_uint16(mid_track *trk) {
|
||||
ushort value = 0;
|
||||
for(int h = 0; h < 2; h++) {
|
||||
if(trk->pos >= trk->size) {
|
||||
break;
|
||||
}
|
||||
value |= ((ushort)trk->buffer[trk->pos++]);
|
||||
value <<= h < 1 ? 8 : 0;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
byte midt_read_uint8(mid_track *trk) {
|
||||
byte value = 0;
|
||||
if(trk->pos < trk->size) {
|
||||
value = trk->buffer[trk->pos++];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void midt_read_var(mid_track *trk, uint len, byte *chars) {
|
||||
memset(chars, 0, len);
|
||||
for(int h = 0; h < len; h++) {
|
||||
if(trk->pos >= trk->size) {
|
||||
break;
|
||||
}
|
||||
chars[h] = trk->buffer[trk->pos++];
|
||||
}
|
||||
}
|
||||
|
||||
uint mid_read_vlen(mid_track *trk)
|
||||
{
|
||||
uint value;
|
||||
byte 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;
|
||||
}
|
||||
|
||||
uint mid_read(mid_handle *mid, const char *filename) {
|
||||
int err;
|
||||
FILE *fd = fopen(filename, "rb");
|
||||
if(fd == NULL) {
|
||||
err = errno;
|
||||
loge(LOG_IO, STR_MID_OPNERR, filename, strerror(err), err);
|
||||
return 0;
|
||||
}
|
||||
if(fseek(fd, 0L, SEEK_END)) {
|
||||
err = errno;
|
||||
loge(LOG_IO, STR_MID_EGEN, filename, strerror(err), err);
|
||||
fclose(fd);
|
||||
return 0;
|
||||
}
|
||||
long size = ftell(fd);
|
||||
if(size < 0L) {
|
||||
err = errno;
|
||||
loge(LOG_IO, STR_MID_EGEN, filename, strerror(err), err);
|
||||
fclose(fd);
|
||||
return 0;
|
||||
}
|
||||
if(fseek(fd, 0L, SEEK_SET)) {
|
||||
err = errno;
|
||||
loge(LOG_IO, STR_MID_EGEN, filename, strerror(err), err);
|
||||
fclose(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(size < 14) {
|
||||
loge(LOG_IO, STR_MID_ESHORT, filename);
|
||||
fclose(fd);
|
||||
return 0;
|
||||
}
|
||||
byte header[14];
|
||||
if(fread(header, 1, 14, fd) != 14) {
|
||||
err = feof(fd);
|
||||
loge(LOG_IO, err == 0 ? STR_MID_EIO : STR_MID_EEOF, filename);
|
||||
fclose(fd);
|
||||
return 0;
|
||||
}
|
||||
if(memcmp(header, mid_hdr, 4) != 0) {
|
||||
loge(LOG_IO, STR_MID_EMFHDR, filename);
|
||||
fclose(fd);
|
||||
return 0;
|
||||
}
|
||||
if(mid_read_uint32(header+4) != 6) {
|
||||
loge(LOG_IO, STR_MID_EHSIZE, filename);
|
||||
fclose(fd);
|
||||
return 0;
|
||||
}
|
||||
byte mtrack;
|
||||
ushort type = mid_read_uint16(header+8);
|
||||
if(type == 0) {
|
||||
mtrack = 0;
|
||||
}
|
||||
else if((type == 1) || (type == 2)) {
|
||||
mtrack = 1;
|
||||
}
|
||||
else {
|
||||
loge(LOG_IO, STR_MID_ETKFMT, filename);
|
||||
fclose(fd);
|
||||
return 0;
|
||||
}
|
||||
mid->tracks = mid_read_uint16(header+10);
|
||||
if(mid->tracks == 0) {
|
||||
loge(LOG_IO, STR_MID_ENOTRK, filename);
|
||||
fclose(fd);
|
||||
return 0;
|
||||
}
|
||||
else if((mtrack ^ 1) && (mid->tracks > 1)) {
|
||||
loge(LOG_IO, STR_MID_ESMULT, filename);
|
||||
fclose(fd);
|
||||
return 0;
|
||||
}
|
||||
mid->tpqn = mid_read_uint16(header+12);
|
||||
if((mid->tpqn & 0x8000) > 0) {
|
||||
loge(LOG_IO, STR_MID_ESMPTE, filename);
|
||||
fclose(fd);
|
||||
return 0;
|
||||
}
|
||||
if(mid->tpqn == 0) {
|
||||
loge(LOG_IO, STR_MID_EDZERO, filename);
|
||||
fclose(fd);
|
||||
return 0;
|
||||
}
|
||||
uint esize = 0;
|
||||
for(ushort trk = 0; trk < mid->tracks; trk++) {
|
||||
if(fread(header, 1, 8, fd) != 8) {
|
||||
err = feof(fd);
|
||||
loge(LOG_IO, err == 0 ? STR_MID_EIO : STR_MID_EEOF, filename);
|
||||
fclose(fd);
|
||||
return 0;
|
||||
}
|
||||
if(memcmp(header, mid_trk, 4) != 0) {
|
||||
loge(LOG_IO, STR_MID_ETKHDR, filename);
|
||||
fclose(fd);
|
||||
return 0;
|
||||
}
|
||||
uint trks = mid_read_uint32(header+4);
|
||||
if((14 + esize + 8 + trks) > size) {
|
||||
loge(LOG_IO, STR_MID_EOUTOF, filename, trk+1);
|
||||
fclose(fd);
|
||||
return 0;
|
||||
}
|
||||
if(fseek(fd, trks, SEEK_CUR)) {
|
||||
err = errno;
|
||||
loge(LOG_IO, STR_MID_EGEN, filename, strerror(err), err);
|
||||
fclose(fd);
|
||||
return 0;
|
||||
}
|
||||
esize += trks + 8;
|
||||
}
|
||||
if(fseek(fd, 14L, SEEK_SET)) {
|
||||
err = errno;
|
||||
loge(LOG_IO, STR_MID_EGEN, filename, strerror(err), err);
|
||||
fclose(fd);
|
||||
return 0;
|
||||
}
|
||||
esize -= (mid->tracks * 8);
|
||||
byte *buf = mem_alloc((sizeof(mid_track) * mid->tracks) + esize, MEM_FILE);
|
||||
mid->track = (mid_track*)buf;
|
||||
buf += (sizeof(mid_track) * mid->tracks);
|
||||
for(ushort trk = 0; trk < mid->tracks; trk++) {
|
||||
memset((mid->track)+trk, 0, sizeof(mid_track));
|
||||
if(fread(header, 1, 8, fd) != 8) {
|
||||
err = feof(fd);
|
||||
loge(LOG_IO, err == 0 ? STR_MID_EIO : STR_MID_EEOF, filename);
|
||||
fclose(fd);
|
||||
mem_free(mid->track);
|
||||
return 0;
|
||||
}
|
||||
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);
|
||||
loge(LOG_IO, err == 0 ? STR_MID_EIO : STR_MID_EEOF, filename);
|
||||
fclose(fd);
|
||||
mem_free(mid->track);
|
||||
return 0;
|
||||
}
|
||||
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 (sizeof(mid_track) * mid->tracks) + esize;
|
||||
}
|
||||
|
||||
void mid_process_meta(mid_handle *mid, mid_track *trk) {
|
||||
byte meta = midt_read_uint8(trk);
|
||||
uint 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;
|
||||
snd_loge(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: {
|
||||
byte dt[size+1];
|
||||
dt[size] = 0;
|
||||
midt_read_var(trk, size, dt);
|
||||
snd_logx("%d%s", meta, dt);
|
||||
return;
|
||||
}
|
||||
case midmt_chnprefix:
|
||||
if(size != 1) {
|
||||
trk->pos += size;
|
||||
snd_loge(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;
|
||||
snd_loge(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;
|
||||
snd_loge(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;
|
||||
snd_loge(STR_MID_METALEN, trk->trknum, meta, 4, size);
|
||||
return;
|
||||
}
|
||||
byte n = midt_read_uint8(trk);
|
||||
uint d = 1 << ((uint)midt_read_uint8(trk));
|
||||
byte c = midt_read_uint8(trk);
|
||||
byte b = midt_read_uint8(trk);
|
||||
mid_dlog(STR_MID_TIMESIG, n, d, c, b);
|
||||
return;
|
||||
}
|
||||
case midmt_keysig: {
|
||||
if(size != 2) {
|
||||
trk->pos += size;
|
||||
snd_loge(STR_MID_METALEN, trk->trknum, meta, 2, size);
|
||||
return;
|
||||
}
|
||||
int8_t s = midt_read_uint8(trk);
|
||||
byte 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;
|
||||
}
|
||||
byte status = trk->buffer[trk->pos++];
|
||||
if((status & 0x80) == 0) {
|
||||
status = trk->status;
|
||||
trk->pos -= 1;
|
||||
}
|
||||
else {
|
||||
trk->status = status;
|
||||
}
|
||||
if((status & 0xf0) != 0xf0) {
|
||||
byte channel = status & 0x0f;
|
||||
status &= 0xf0;
|
||||
switch(status) {
|
||||
case midev_noteoff: {
|
||||
byte o_key = midt_read_uint8(trk);
|
||||
byte 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: {
|
||||
byte key = midt_read_uint8(trk);
|
||||
byte 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: {
|
||||
byte pressure = midt_read_uint8(trk);
|
||||
mid_dlog(STR_MID_PRESS, channel+1, pressure);
|
||||
break;
|
||||
}
|
||||
case midev_control: {
|
||||
byte control = midt_read_uint8(trk);
|
||||
byte 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: {
|
||||
byte 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: {
|
||||
byte cpressure = midt_read_uint8(trk);
|
||||
mid_dlog(STR_MID_CPRESS, channel+1, cpressure);
|
||||
break;
|
||||
}
|
||||
case midev_pitchbend: {
|
||||
ushort pb = ((ushort)midt_read_uint8(trk)) | (((ushort)midt_read_uint8(trk)) << 7);
|
||||
short pitch = ((short)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: {
|
||||
uint slen = mid_read_vlen(trk);
|
||||
trk->pos += slen;
|
||||
mid_dlog(STR_MID_SYSEX, slen);
|
||||
break;
|
||||
}
|
||||
case midev_songpos:
|
||||
mid_dlog(STR_MID_SONGPOS, ((ushort)midt_read_uint8(trk)) | (((ushort)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: {
|
||||
uint 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:
|
||||
snd_loge(STR_MID_UNKSTAT, status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef MID_NOEVT
|
||||
byte mid_tick(mid_handle *mid, bank_handle *bank, opl3_chip *chip) {
|
||||
#else
|
||||
byte mid_tick(mid_handle *mid) {
|
||||
#endif
|
||||
byte 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(1) {
|
||||
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)) {
|
||||
snd_loge(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;
|
||||
}
|
Reference in a new issue