diff options
Diffstat (limited to 'Src/Plugins/Input/in_midi/cleaner.cpp')
-rw-r--r-- | Src/Plugins/Input/in_midi/cleaner.cpp | 587 |
1 files changed, 587 insertions, 0 deletions
diff --git a/Src/Plugins/Input/in_midi/cleaner.cpp b/Src/Plugins/Input/in_midi/cleaner.cpp new file mode 100644 index 00000000..1c72c668 --- /dev/null +++ b/Src/Plugins/Input/in_midi/cleaner.cpp @@ -0,0 +1,587 @@ +#include "main.h" + +#pragma warning(disable:4200) + +extern BYTE ff7loopstart[12]; +extern BYTE ff7loopend[10]; + +extern cfg_int cfg_hack_xg_drums, cfg_hack_dls_instruments, cfg_hack_dls_drums, cfg_ff7loopz; + +typedef union +{ + BYTE b[4]; + DWORD dw; +} b_dw; + +typedef struct +{ + DWORD pos, tm, sz; + BYTE le; + BYTE data[]; +} +TRACK; + +DWORD _fastcall rev32(DWORD); +//WORD _fastcall rev16(WORD); + + + +int test_drum_kit(DWORD no, IDirectMusicCollection* dls); +void do_dls_check(DWORD * i, IDirectMusicCollection * dls); + + +class CCleaner +{ +public: + INSTRUMENT_DESC* instr, **instr_ptr; + BYTE ctab[16][128]; + // UINT dm_vol; + grow_buf outbuf; + UINT ntrax, ntrax1; + UINT maxvol; + TRACK** in_trax; + TRACK* out_trax[16]; + DWORD ct; + UINT tf; + MIDI_file* mf; + DWORD vol_set; + + bool drumfix, insfix; + b_dw ins[16], ins_set[16]; + + bool f2, tr1, dm, only_ins, ins_no_lsb; + bool hasnotes[16]; + void check_ins(UINT msb, UINT lsb, UINT patch, UINT note, BOOL drum, UINT ch) //called on note + { + if (ins_no_lsb) lsb = 0; + INSTRUMENT_DESC * d = instr; + while (d) + { + if (d->bank_hi == msb && d->bank_lo == lsb && d->patch == patch && d->drum == drum) break; + d = d->next; + } + if (d) + { + d->count++; + if (d->note_max < note) d->note_max = note; + if (d->note_min > note) d->note_min = note; + d->channels |= 1 << ch; + } + else + { + d = new INSTRUMENT_DESC; + *instr_ptr = d; + instr_ptr = &d->next; + d->next = 0; + d->note_min = d->note_max = note; + d->bank_hi = msb; + d->bank_lo = lsb; + d->patch = patch; + d->count = 1; + d->drum = drum; + d->user = 0; + d->channels = 1 << ch; + } + } + void AdvanceTime(TRACK* t); + void AddEvent(BYTE ev, BYTE* data); + void WriteTrack(TRACK* t); + int Run(MIDI_file* mf, DWORD, void ** out_data, int * out_size); + + void do_shit(UINT n); + + UINT get_next_time() + { + UINT t = -1; + UINT n; + for (n = 0;n < ntrax;n++) + { + UINT t1 = in_trax[n]->tm; + if (t1 < t) t = t1; + } + return t; + } + + BOOL event_ok(BYTE e, BYTE* p) + { + BYTE _c = e & 0xF0; + BYTE ch = e & 0xF; + if (_c == 0xB0) + { + if (cfg_hack_xg_drums && ch == 9 && p[1] == 0 && (p[0] == 0 || p[0] == 0x20)) + return 0; + + if (p[0] > 127) + return 0; + + ctab[ch][p[0]] = p[1]; + + + if (p[0] == 0) + { + ins[ch].b[2] = p[1]; + if (insfix) return 0; + } + if (p[0] == 0x20) + { + ins[ch].b[1] = p[1]; + if (insfix) return 0; + } + + if (dm) //keep dm drivers happy... + { + if (p[0] >= 0x20 && p[0] < 0x40) //lsb values + { + return 0; + } + else if (p[0] < 0x20) + { + BYTE data[2] = {(BYTE)(p[0] + 0x20),ctab[ch][p[0] + 0x20]}; + AddEvent(e, data); + } + } + + return 1; + + } + + else if (_c == 0xC0) + { + if (ch == 9) + { + if (drumfix && !test_drum_kit(p[0], mf->pDLS)) return 0; + ins[ch].b[0] = p[0]; + } + else + { + ins[ch].b[0] = p[0]; + if (insfix) return 0; + } + } + else if (_c == 0x90 && p[1]) + { + if (only_ins) + check_ins(ins[ch].b[2], ins[ch].b[1], ins[ch].b[0], p[0], ch == 9, ch); + if (ch != 9 && insfix) + { + if (ins_set[ch].dw != ins[ch].dw) + { + do_dls_check(&ins[ch].dw, mf->pDLS); + + + if (ins_set[ch].b[1] != ins[ch].b[1]) + { + BYTE t[2] = {0x20, ins[ch].b[1]}; + AddEvent(0xB0 | ch, t); + } + + if (ins_set[ch].b[2] != ins[ch].b[2]) + { + BYTE t[2] = {0, ins[ch].b[2]}; + AddEvent(0xB0 | ch, t); + } + AddEvent(0xC0 | ch, ins[ch].b); + + ins_set[ch].dw = ins[ch].dw; + } + } + } + + return 1; + } + + CCleaner() + { + memset(ins, 0, sizeof(ins)); + memset(ins_set, -1, sizeof(ins_set)); + memset(hasnotes, 0, sizeof(hasnotes)); + memset(out_trax, 0, sizeof(out_trax)); + in_trax = 0; + } + ~CCleaner() + { + UINT n; + if (in_trax) + { + for (n = 0;n < ntrax;n++) + if (in_trax[n]) {free(in_trax[n]);in_trax[n] = 0;} + free(in_trax); + } + for (n = 0;n < 16;n++) + { + if (out_trax[n]) + { + free(out_trax[n]); + out_trax[n] = 0; + } + } + } +}; + +void CCleaner::do_shit(UINT n) +{ + BYTE ce = 0; + TRACK* t = in_trax[n]; + if (!t) return ; + while (t->tm == ct) + { + + if (t->pos >= t->sz) + { + t->pos = -1; + t->tm = -1; + tf++; + break; + } + BYTE c0 = t->data[t->pos]; + if (c0 == 0xFF) //Meta-events + { + + if (cfg_ff7loopz + && (t->sz - t->pos) >= sizeof(ff7loopend) // bounds check + && !memcmp(t->data + t->pos, ff7loopend, sizeof(ff7loopend))) + { + // MessageBox(GetActiveWindow(),"blah",0,0); + // AdvanceTime(t); + tf = ntrax; + // return; + } + BYTE c1 = t->data[t->pos + 1]; + if (c1 == 0x2F) + { + t->pos += 3; + t->tm = -1; + tf++; + } + { + t->pos += 2; + if (t->pos < t->sz) + { + + unsigned int _d; + t->pos += DecodeDelta(t->data + t->pos, &_d, t->sz - t->pos); + t->pos += _d; + } + } + } else if (c0 == 0xF0) + { + t->pos += ReadSysex(&t->data[t->pos], t->sz - t->pos); + } + else if (c0 == 0xF7) t->pos++; + else if ((c0&0xF0) == 0xF0) //WTF? + { + t->pos = -1; + t->tm = -1; + tf++; + break; + } + else + { + if (c0&0x80) + { + ce = c0; + t->pos++; + } + else ce = t->le; + + if (event_ok(ce, &t->data[t->pos])) AddEvent(ce, &t->data[t->pos]); + + if ((ce&0xF0) == 0xC0 || (ce&0xF0) == 0xD0) t->pos++; + else t->pos += 2; + t->le = ce; + } + + if (t->tm != -1 && t->pos >= t->sz) + { + t->pos = -1; + t->tm = -1; + tf++; + break; + } + AdvanceTime(t); + } +} + +#define WriteBuf(A,B) outbuf.write(A,B) + +#pragma pack(push) +#pragma pack(1) +typedef struct +{ + WORD t, n, d; +} +MHD; +typedef struct +{ + DWORD c, s; +} +CHD; +#pragma pack(pop) + + +void CCleaner::AdvanceTime(TRACK* t) +{ + if (t->tm != -1) + { + unsigned int d; + UINT _n = DecodeDelta(t->data + t->pos, &d, t->sz - t->pos); + if (_n < 4) t->tm += d; + t->pos += _n; + } +} + +void CCleaner::AddEvent(BYTE ev, BYTE* data) +{ + if (only_ins) return ; + BYTE nt = ev & 0xF; + BYTE ec = ev & 0xF0; + if (tr1) nt = 0; + TRACK *t = out_trax[nt]; + + ZeroMemory(ctab, sizeof(ctab)); + + + if (!t) + { + t = out_trax[nt] = (TRACK*)malloc(sizeof(TRACK) + 0x1000); + if (!t) return ; + ZeroMemory(t, 16); + t->sz = 0x1000; + t->tm = 0; + + } + else if (t->pos > t->sz - 0x10) + { + t->sz *= 2; + out_trax[nt] = (TRACK*)realloc(t, sizeof(TRACK) + t->sz); + if (!out_trax[nt]) + { + free(t); + return ; + } + t = out_trax[nt]; + } + + if (t->tm < ct) + { + t->pos += EncodeDelta(&t->data[t->pos], ct - t->tm); + t->tm = ct; + } + else + { + t->data[t->pos++] = 0; + } + if (ec == 0x90) + { + hasnotes[nt] = 1; + data[0] &= 0x7F; /* don't allow 8bit note numbers */ + } + else if (ec == 0x80) + { + data[0] &= 0x7F; /* don't allow 8bit note numbers */ + } + /*if (ev!=t->le) */{t->data[t->pos++] = ev;t->le = ev;} + t->data[t->pos++] = data[0]; + if (ec != 0xC0 && ec != 0xD0) t->data[t->pos++] = data[1]; +} + +void CCleaner::WriteTrack(TRACK* t) +{ + CHD chd; + chd.c = 'krTM'; + chd.s = rev32(t->pos); + WriteBuf(&chd, 8); + WriteBuf(&t->data, t->pos); + ntrax1++; +} + +int DoCleanUp(MIDI_file* mf, DWORD mode, void** out_data, int * out_size) +{ + CCleaner c; + c.only_ins = 0; + return c.Run(mf, mode, out_data, out_size); +} + +int CCleaner::Run(MIDI_file* _mf, DWORD _md, void ** out_data, int * out_size) +{ + f2 = *(WORD*)(_mf->data + 8) == 0x0200; + maxvol = 90; + vol_set = 0; + dm = (_md & CLEAN_DM) ? 1 : 0; + tr1 = (_md & CLEAN_1TRACK) ? 1 : 0; + + if (_md&CLEAN_DLS) + { + drumfix = dm && cfg_hack_dls_drums; + insfix = dm && cfg_hack_dls_instruments; + } + else + { + drumfix = insfix = 0; + } + + + + mf = _mf; + + instr_ptr = &instr; + instr = 0; + + UINT n; + + ct = 0; + tf = 0; + ntrax = ntrax1 = 0; + CHD chd; + MHD mhd; + DWORD ptr = 8; + + + + mhd = *(MHD*)(mf->data + 8); + + ptr += 6; + + mhd.t = rev16(mhd.t); + mhd.n = rev16(mhd.n); + + if (mhd.t > 2) + goto fail; + ntrax = mhd.n; + n = 0; + in_trax = (TRACK**)malloc(sizeof(void*) * ntrax); + for (;n < ntrax && ptr < (UINT)mf->size;n++) + { + chd = *(CHD*)(mf->data + ptr); + ptr += 8; + if (chd.c != 'krTM' || ptr > (UINT)mf->size) + { + ntrax = n; + break; + } + chd.s = rev32(chd.s); + //if (ptr+chd.s>(UINT)mf->size) + if (chd.s > ((UINT)mf->size - ptr)) + { + chd.s = mf->size - ptr; + } + //goto fail; + in_trax[n] = (TRACK*)malloc(16 + chd.s); + in_trax[n]->sz = chd.s; + in_trax[n]->tm = 0; + in_trax[n]->le = 0; + in_trax[n]->pos = 0; + memcpy(in_trax[n]->data, mf->data + ptr, chd.s); + ptr += chd.s; + AdvanceTime(in_trax[n]); + } + if (f2) + { + for (n = 0;n < ntrax;n++) + { + in_trax[n]->tm = ct; + while (tf <= n) + { + do_shit(n); + if (in_trax[n]->tm != -1) ct = in_trax[n]->tm; + } + } + } + else + { + while (tf < ntrax) + { + UINT nt = get_next_time(); //ct++; + if (nt == -1) break; + ct = nt; + for (n = 0;n < ntrax && tf < ntrax;n++) + { + do_shit(n); + } + } + } + + if (!only_ins) + { + + + mhd.t = 0x0100; + mhd.n = 0; //rev16(ntrax1); + chd.c = 'dhTM'; + chd.s = 0x06000000; + WriteBuf(&chd, 8); + WriteBuf(&mhd, 6); + if (!(_md&CLEAN_NOTEMPO) && mf->tmap) + { + /* BYTE *tt=mf->tmap->BuildTrack(); + if (tt) + { + WriteBuf(tt,rev32(*(DWORD*)(tt+4))+8); + ntrax1++; + free(tt); + }*/ + if (mf->tmap->BuildTrack(outbuf)) + { + ntrax1++; + } + } + if (!(_md&CLEAN_NOSYSEX) && mf->smap) + { + /* BYTE *st=mf->smap->BuildTrack(); + if (st) + { + WriteBuf(st,rev32(*(DWORD*)(st+4))+8); + ntrax1++; + free(st); + }*/ + if (mf->smap->BuildTrack(outbuf)) + { + ntrax1++; + } + } + + + + for (n = 0;n < 16;n++) if (out_trax[n] && hasnotes[n] && out_trax[n]->pos) + { + TRACK *t = out_trax[n]; + t->pos += EncodeDelta(t->data + t->pos, ct - t->tm); + t->data[t->pos++] = 0xFF; + t->data[t->pos++] = 0x2F; + t->data[t->pos++] = 0; + WriteTrack(t); + } + { + WORD t = rev16(ntrax1); + outbuf.write_ptr(&t, 2, 10); + } + if (out_size) *out_size = outbuf.get_size(); + if (out_data) *out_data = outbuf.finish(); +#if 0 + { + HANDLE f = CreateFile("c:\\dump.mid", GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); + DWORD bw = 0; + WriteFile(f, rv, bs, &bw, 0); + CloseHandle(f); + } +#endif + + } + return 1; +fail: + // ErrorBox("WARNING: cleaner messed up"); + + return 0; + + //TO DESTRUCTOR + +} + +INSTRUMENT_DESC* GetInstruments(MIDI_file* mf, BOOL do_lsb) +{ + CCleaner c; + c.only_ins = 1; + c.ins_no_lsb = !do_lsb; + c.Run(mf, 0, 0, 0); + return c.instr; +} |