518 lines
12 KiB
C
518 lines
12 KiB
C
|
|
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;
|
|
}
|