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; }