aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Input/in_midi/cleaner.cpp
diff options
context:
space:
mode:
authorJean-Francois Mauguit <jfmauguit@mac.com>2024-09-24 09:03:25 -0400
committerGitHub <noreply@github.com>2024-09-24 09:03:25 -0400
commitbab614c421ed7ae329d26bf028c4a3b1d2450f5a (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/Plugins/Input/in_midi/cleaner.cpp
parent4bde6044fddf053f31795b9eaccdd2a5a527d21f (diff)
parent20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (diff)
downloadwinamp-bab614c421ed7ae329d26bf028c4a3b1d2450f5a.tar.gz
Merge pull request #5 from WinampDesktop/community
Merge to main
Diffstat (limited to 'Src/Plugins/Input/in_midi/cleaner.cpp')
-rw-r--r--Src/Plugins/Input/in_midi/cleaner.cpp587
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;
+}