aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Input/in_midi/utils.cpp
diff options
context:
space:
mode:
authorJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
committerJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
commit20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/Plugins/Input/in_midi/utils.cpp
parent537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff)
downloadwinamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz
Initial community commit
Diffstat (limited to 'Src/Plugins/Input/in_midi/utils.cpp')
-rw-r--r--Src/Plugins/Input/in_midi/utils.cpp1064
1 files changed, 1064 insertions, 0 deletions
diff --git a/Src/Plugins/Input/in_midi/utils.cpp b/Src/Plugins/Input/in_midi/utils.cpp
new file mode 100644
index 00000000..c76d49f0
--- /dev/null
+++ b/Src/Plugins/Input/in_midi/utils.cpp
@@ -0,0 +1,1064 @@
+#include "main.h"
+#include "../Agave/language/api_language.h"
+#include <commdlg.h>
+#include "resource.h"
+
+DWORD _fastcall rev32(DWORD d) {return _rv(d);}
+
+void CPipe::WriteData(void* b,UINT s)
+{
+ if (closed) return;
+ sec.enter();
+ if (buf_n+s>buf_s)
+ {
+#ifdef USE_LOG
+ log_write("buffer overflow");
+#endif
+ s=buf_s-buf_n;
+ s-=s%align;
+ }
+ if (s)
+ {
+ if (buf_wp+s<buf_s)
+ {
+ memcpy(buf+buf_wp,b,s);
+ buf_wp+=s;
+ }
+ else
+ {
+ UINT d=buf_s-buf_wp;
+ memcpy(buf+buf_wp,b,d);
+ memcpy(buf,(BYTE*)b+d,s-d);
+ buf_wp=s-d;
+ }
+ buf_n+=s;
+ }
+ sec.leave();
+}
+
+UINT CPipe::ReadData(void* _b,UINT s,bool* ks)
+{
+ UINT rv=0;
+ BYTE * b=(BYTE*)_b;
+ sec.enter();
+ while(1)
+ {
+ UINT d=s;
+ if (d>buf_n) d=buf_n;
+ if (d)
+ {
+ if (buf_rp+d<buf_s)
+ {
+ memcpy(b,buf+buf_rp,d);
+ buf_rp+=d;
+ }
+ else
+ {
+ UINT d1=buf_s-buf_rp;
+ memcpy(b,buf+buf_rp,d1);
+ memcpy(b+d1,buf,d-d1);
+ buf_rp=d-d1;
+ }
+ buf_n-=d;
+ s-=d;
+ rv+=d;
+ b+=d;
+ }
+ if (closed || !s || *ks) break;
+ sec.leave();
+ MIDI_callback::Idle();
+ sec.enter();
+ }
+ sec.leave();
+ return rv;
+}
+
+#ifdef USE_LOG
+static HANDLE hLog;
+void log_start()
+{
+ hLog=CreateFile("c:\\in_midi.log",GENERIC_WRITE,FILE_SHARE_READ,0,OPEN_ALWAYS,0,0);
+ SetFilePointer(hLog,0,0,FILE_END);
+ log_write("opening log");
+}
+
+void log_quit() {log_write("closing log");log_write("");log_write("");CloseHandle(hLog);}
+
+void log_write(char* t)
+{
+ DWORD bw;
+ WriteFile(hLog,t,strlen(t),&bw,0);
+ char _t[2]={13,10};
+ WriteFile(hLog,_t,2,&bw,0);
+ FlushFileBuffers(hLog);
+}
+#endif
+
+
+
+
+
+
+
+
+
+
+//tempo map object
+
+CTempoMap* tmap_create()
+{
+ CTempoMap* m=new CTempoMap;
+ if (m)
+ {
+ m->pos=0;
+ m->size=0x100;
+ m->data=(TMAP_ENTRY*)malloc(m->size*sizeof(TMAP_ENTRY));
+ }
+ return m;
+}
+
+void CTempoMap::AddEntry(int _p,int tm)
+{
+ if (!data) {pos=size=0;return;}
+ if (pos && _p<=data[pos-1].pos) {data[pos-1].tm=tm;return;}
+ if (pos==size)
+ {
+ size*=2;
+ data=(TMAP_ENTRY*)realloc(data,size*sizeof(TMAP_ENTRY));
+ if (!data) {pos=0;return;}
+ }
+ data[pos].pos=_p;
+ data[pos].tm=tm;
+ pos++;
+}
+
+int ReadSysex(const BYTE* src,int ml)
+{
+ int r=1;
+ while(r<ml)
+ {
+ r++;
+ if (src[r]==0xF7) return r+1;
+ }
+ unsigned int d;
+ r=1+DecodeDelta(src+1,&d);
+ r+=d;
+ return r;
+}
+
+unsigned int DecodeDelta(const BYTE* src,unsigned int* _d, unsigned int limit)
+{
+ unsigned int l=0;
+ unsigned int d=0;
+ BYTE b;
+ do
+ {
+ if (l >= limit)
+ {
+ *_d=0;
+ return l;
+ }
+ b=src[l++];
+ d=(d<<7)|(b&0x7F);
+ } while(b&0x80);
+ *_d=d;
+ return l;
+}
+
+int EncodeDelta(BYTE* dst,int d)
+{
+ if (d==0)
+ {
+ dst[0]=0;
+ return 1;
+ }
+ else
+ {
+ int r=0;
+ int n=1;
+ unsigned int temp=d;
+ while (temp >>= 7)
+ {
+ n++;
+ }
+
+ do {
+ n--;
+ BYTE b=(BYTE)((d>>(7*n))&0x7F);
+ if (n) b|=0x80;
+ dst[r++]=b;
+ } while(n);
+ return r;
+ }
+}
+
+int CTempoMap::BuildTrack(grow_buf & out)
+{
+ if (!pos) return 0;
+ int start=out.get_size();
+ //BYTE* trk=(BYTE*)malloc(8+4+pos*10);
+ //if (!trk) return 0;
+ out.write_dword(_rv('MTrk'));
+ out.write_dword(0);//track size
+ DWORD ct=0;
+ int n;
+ BYTE t_event[6]={0xFF,0x51,0x03,0,0,0};
+ for(n=0;n<pos;n++)
+ {
+ DWORD t=data[n].pos;
+ gb_write_delta(out,t-ct);
+ ct=t;
+ t=data[n].tm;
+ t_event[3]=(BYTE)(t>>16);
+ t_event[4]=(BYTE)(t>>8);
+ t_event[5]=(BYTE)(t);
+ out.write(t_event,6);
+ }
+ out.write_dword(0x002FFF00);
+ out.write_dword_ptr(rev32(out.get_size()-(start+8)),start+4);
+ return 1;
+}
+
+//sysex map management
+
+void CSysexMap::AddEvent(const BYTE* e,DWORD s,DWORD t)
+{
+ if (!data || !events) return;
+ DWORD np=pos+1;
+ if (np>=e_size)
+ {
+ do {
+ e_size<<=1;
+ } while(np>=e_size);
+ events=(SYSEX_ENTRY*)realloc(events,e_size*sizeof(SYSEX_ENTRY));
+ if (!events) return;
+ }
+ DWORD nd=d_pos+s;
+ if (nd>=d_size)
+ {
+ do {
+ d_size<<=1;
+ } while(nd>=d_size);
+ data=(BYTE*)realloc(data,d_size);
+ if (!data) return;
+ }
+ data[d_pos]=0xF0;
+ unsigned int x;
+ unsigned int sp=DecodeDelta(e+1,&x);
+ if (sp >= s)
+ return;
+ memcpy(data+d_pos+1,e+1+sp,s-1-sp);
+ events[pos].pos=t;
+ events[pos].ofs=d_pos;
+ events[pos].len=s-sp;
+ d_pos=nd-sp;
+ pos++;
+}
+
+CSysexMap* smap_create()
+{
+ CSysexMap* s=new CSysexMap;
+ if (s)
+ {
+ s->e_size=0x10;
+ s->d_size=0x40;
+ s->events=(SYSEX_ENTRY*)malloc(sizeof(SYSEX_ENTRY)*s->e_size);
+ s->data=(BYTE*)malloc(s->d_size);
+ s->d_pos=s->pos=0;
+ }
+ return s;
+}
+
+
+CSysexMap::~CSysexMap()
+{
+ if (data) free(data);
+ if (events) free(events);
+}
+
+BYTE d_GMReset[6]={0xF0,0x7E,0x7F,0x09,0x01,0xF7};
+BYTE d_XGReset[9]={0xf0,0x43,0x10,0x4c,0x00,0x00,0x7e,0x00,0xf7};
+BYTE d_GSReset[11]={0xF0,0x41,0x10,0x42,0x12,0x40,0x00,0x7F,0x00,0x41,0xF7};
+
+CSysexMap* CSysexMap::Translate(MIDI_file * mf)
+{
+ CTempoMap* tmap=mf->tmap;
+ if (!events || !data || !tmap) return 0;
+ CSysexMap* nm=smap_create();
+ if (!nm) return 0;
+ nm->d_size=d_size;
+ nm->d_pos=d_pos;
+ nm->data=(BYTE*)realloc(nm->data,nm->d_size);
+ if (!nm->data) {delete nm;return 0;}
+ memcpy(nm->data,data,d_pos);
+ nm->e_size=e_size;
+ nm->pos=pos;
+ nm->events=(SYSEX_ENTRY*)realloc(nm->events,sizeof(SYSEX_ENTRY)*nm->e_size);
+ if (!nm->events) {delete nm;return 0;}
+
+ int pos_ms=0;
+ int n=0;
+ int cur_temp=0;
+ int ntm=tmap->pos,t_pos=0;
+ int p_t=0;
+ int dtx = rev16(*(WORD*)(mf->data+12))*1000;
+ int pos_tx=0;
+
+ while(n<pos)
+ {
+ pos_tx=events[n].pos;
+ int d=pos_tx-p_t;
+ p_t=pos_tx;
+ while(t_pos<ntm && pos_tx+d>=tmap->data[t_pos].pos)
+ {
+ DWORD d1=tmap->data[t_pos].pos-pos_tx;
+ pos_ms+=MulDiv(cur_temp,d1<<8,dtx);
+ cur_temp=tmap->data[t_pos].tm;
+ t_pos++;
+ pos_tx+=d1;
+ d-=d1;
+ }
+ pos_ms+=MulDiv(cur_temp,d<<8,dtx);
+ pos_tx+=d;
+
+ nm->events[n].pos=pos_ms>>8;
+ nm->events[n].ofs=events[n].ofs;
+ nm->events[n].len=events[n].len;
+ n++;
+ }
+ return nm;
+}
+
+int CSysexMap::BuildTrack(grow_buf & out)
+{
+ if (!pos) return 0;
+
+ int start=out.get_size();
+ out.write_dword(_rv('MTrk'));
+ out.write_dword(0);
+
+ int ct=0;
+ int n;
+ for(n=0;n<pos;n++)
+ {
+ DWORD t=events[n].pos;
+ gb_write_delta(out,t-ct);
+ ct=t;
+ out.write_byte(0xF0);
+ gb_write_delta(out,events[n].len-1);
+ out.write(data+events[n].ofs+1,events[n].len-1);
+ }
+ out.write_dword(0x002FFF00);
+ out.write_dword_ptr(rev32(out.get_size()-(start+8)),start+4);
+ return 1;
+}
+
+const char* CSysexMap::GetType()
+{
+ int ret=0;
+ int n;
+ for(n=0;n<pos;n++)
+ {
+ ret=data[events[n].ofs+1];
+ if (ret!=0x7E) break;
+ }
+
+ switch(ret)
+ {
+ case 0x7E:
+ return "GM";
+ case 0x43:
+ return "XG";
+ case 0x42:
+ return "X5";
+ case 0x41:
+ return "GS";
+ }
+ return 0;
+}
+
+void CSysexMap::CleanUp()
+{
+ if (!pos) return;
+ int n,m;
+ for(n=0;n<pos-1;n++)
+ {
+ for(m=n+1;m<pos;m++)
+ {
+ if (events[n].pos>events[m].pos)
+ {
+ SYSEX_ENTRY t=events[n];
+ events[n]=events[m];
+ events[m]=t;
+ }
+ }
+ }
+}
+
+char* BuildFilterString(UINT res_id, char* ext, int* len)
+{
+ static char filterStr[256];
+ char *f = filterStr;
+ ZeroMemory(filterStr,256);
+ *len = 0;
+ WASABI_API_LNGSTRING_BUF(res_id,filterStr,256);
+ f += (*len = lstrlenA(filterStr) + 1);
+ lstrcatA(f,"*.");
+ f += 2;
+ lstrcatA(f,ext);
+ *(f + lstrlenA(ext)+1) = 0;
+ *len += lstrlenA(ext)+3;
+ return filterStr;
+}
+
+BOOL DoOpenFile(HWND w,char* fn,UINT res_id, char* ext,BOOL save)
+{
+ int len = 0;
+ OPENFILENAMEA ofn = {sizeof(ofn),0};
+ ofn.hwndOwner=w;
+ ofn.lpstrFilter=BuildFilterString(res_id,ext,&len);
+ ofn.lpstrFile=fn;
+ ofn.nMaxFile=MAX_PATH;
+ ofn.lpstrDefExt=ext;
+ if (save)
+ {
+ ofn.Flags=OFN_OVERWRITEPROMPT|OFN_PATHMUSTEXIST|OFN_HIDEREADONLY;
+ return GetSaveFileNameA(&ofn);
+ }
+ else
+ {
+ ofn.Flags=OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST|OFN_HIDEREADONLY;
+ return GetOpenFileNameA(&ofn);
+ }
+}
+
+BOOL DoSaveFile(HWND w, char* fn, char* filt, char* ext)
+{
+ OPENFILENAMEA ofn;
+ ZeroMemory(&ofn,sizeof(ofn));
+ ofn.lStructSize=sizeof(ofn);
+ ofn.hwndOwner=w;
+ ofn.lpstrFilter=filt;
+ ofn.lpstrFile=fn;
+ ofn.nMaxFile=MAX_PATH;
+ ofn.lpstrDefExt=ext;
+ ofn.Flags=OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST|OFN_HIDEREADONLY;
+ return GetOpenFileNameA(&ofn);
+}
+
+typedef void (*SYSEXFUNC)(void*,BYTE*,UINT);
+
+#define rsysex(X) f(i,X,sizeof(X))
+#define _sysex(X,Y) f(i,X,Y)
+
+bool need_sysex_start()
+{
+ return cfg_hardware_reset>0
+ || cfg_sysex_table.num_entries()>0
+ ;
+}
+
+void sysex_startup(SYSEXFUNC f,void* i)
+{
+ if (cfg_hardware_reset>0)
+ {
+ switch(cfg_hardware_reset)
+ {
+ case 1:rsysex(d_GMReset);break;
+ case 2:rsysex(d_GSReset);break;
+ case 3:rsysex(d_XGReset);break;
+ }
+ MIDI_callback::Idle(200);
+ }
+ if (cfg_sysex_table.num_entries()>0)
+ {
+ int idx=0;
+ BYTE * data;
+ int size,time;
+ while(cfg_sysex_table.get_entry(idx++,&data,&size,&time))
+ {
+ _sysex(data,size);
+ MIDI_callback::Idle(time);
+ }
+ }
+}
+
+
+MIDI_EVENT* do_table(MIDI_file * mf,UINT prec,UINT * size,UINT* _lstart,DWORD cflags)
+{
+ BYTE * data_ptr = 0;
+ int data_size = 0;
+ if (!DoCleanUp(mf,CLEAN_1TRACK|CLEAN_NOSYSEX|CLEAN_NOTEMPO|cflags,(void**)&data_ptr,&data_size)) return 0;
+ if (data_size<=0x0e) {free(data_ptr);return 0;}
+
+ UINT ts;
+ BYTE* track;
+ UINT ntm;
+ track=data_ptr+8+6+8;
+ ts=rev32(*(DWORD*)(track-4));
+ CTempoMap* tmap=mf->tmap;
+ UINT n=0;
+ UINT pt=0;
+ ntm=tmap->pos;
+ CSysexMap* smap;
+
+ if (!cfg_nosysex && mf->smap && mf->smap->pos)
+ {
+ smap=mf->smap;
+ }
+ else smap=0;
+
+ n=0;
+ DWORD pos=0;
+ DWORD pos_ms=0;
+ DWORD t_pos=0;
+ DWORD cur_temp=0;
+ UINT dtx=(UINT)rev16(*(WORD*)(data_ptr+8+4))*1000/prec;
+ grow_buf boo;
+
+ int ns=0;
+ UINT track_pos=0,smap_pos=0;
+ UINT loop_start=-1;
+
+ {
+ unsigned int _d;
+ n+=DecodeDelta(track+n,&_d);
+ track_pos+=_d;
+ }
+
+ if (smap)
+ {
+ smap_pos=smap->events[0].pos;
+ }
+ else smap_pos=-1;
+
+ while(1)
+ {
+ DWORD ev=0;
+ DWORD d=0;
+ {
+ if (n >= (data_size-26))
+ break;
+ if (track_pos<smap_pos)
+ {
+ d=track_pos-pos;
+ ev=(*(DWORD*)(track+n))&0xFFFFFF;
+ if ((ev&0xF0)==0xF0)
+ {
+ track_pos=-1;
+ continue;
+ }
+ if ((ev&0xF0)==0xC0 || (ev&0xF0)==0xD0)
+ {
+ ev&=0xFFFF;n+=2;
+ }
+ else
+ {
+ n+=3;
+ }
+ if ((ev&0xFF00F0)==0x90)
+ {
+ ev=(ev&0xFF0F)|0x7F0080;
+ }
+ unsigned int _d;
+ n+=DecodeDelta(track+n,&_d);
+ track_pos+=_d;
+ if (n >= (data_size-26))
+ break;
+ }
+ else if (smap_pos!=-1)
+ {
+ d=smap_pos-pos;
+ ev=0x80000000|ns;
+ ns++;
+ if (ns==smap->pos)
+ smap_pos=-1;
+ else
+ smap_pos=smap->events[ns].pos;
+ }
+ }
+ if (!ev) break;
+ while(t_pos<ntm && pos+d>=(UINT)tmap->data[t_pos].pos)
+ {
+ DWORD d1=tmap->data[t_pos].pos-pos;
+ if (loop_start==-1 && (UINT)mf->loopstart_t<=pos+d1) loop_start=pos_ms+MulDiv(cur_temp,pos+d1-mf->loopstart_t,dtx);
+ pos_ms+=MulDiv(cur_temp,d1,dtx);
+ cur_temp=tmap->data[t_pos].tm;
+ t_pos++;
+ pos+=d1;
+ d-=d1;
+ }
+ if (loop_start==-1 && (UINT)mf->loopstart_t<=pos+d) loop_start=pos_ms+MulDiv(cur_temp,d,dtx);
+ pos_ms+=MulDiv(cur_temp,d,dtx);
+ pos+=d;
+ {
+ MIDI_EVENT me={pos_ms,ev};
+ boo.write(&me,sizeof(me));
+ }
+ }
+
+ free(data_ptr);
+
+ UINT sz=boo.get_size();
+ MIDI_EVENT* ret=(MIDI_EVENT*)boo.finish();
+ if (ret)
+ {
+ *size=sz>>3;//sz/sizeof(MIDI_EVENT);
+ if (cfg_loop_type==2 && loop_start==-1) loop_start=0;
+ else if (cfg_loop_type==0) loop_start=-1;
+ if (_lstart) *_lstart=loop_start;
+ }
+ return ret;
+}
+
+
+void gb_write_delta(grow_buf & gb,DWORD d)
+{
+ BYTE tmp[8] = {0};
+ gb.write(tmp,EncodeDelta(tmp,d));
+}
+
+void do_messages(HWND w,bool* b)
+{
+ MSG msg;
+ while(b && *b)
+ {
+ BOOL b=GetMessage(&msg,w,0,0);
+ if (b==-1 || !b) break;
+ DispatchMessage(&msg);
+ }
+}
+
+static wchar_t cb_class[]=TEXT("CallbackWndClass0");
+
+ATOM do_callback_class(WNDPROC p)
+{
+ cb_class[sizeof(cb_class)-2]++;
+ WNDCLASS wc=
+ {
+ 0,p,0,4,MIDI_callback::GetInstance(),0,0,0,0,cb_class
+ };
+ return RegisterClassW(&wc);
+}
+
+HWND create_callback_wnd(ATOM cl,void* p)
+{
+ HWND w=CreateWindowA((char*)cl,0,0,0,0,0,0,MIDI_callback::GetMainWindow(),0,MIDI_callback::GetInstance(),0);
+ if (w) SetWindowLong(w,0,(long)p);
+ return w;
+}
+
+CTempoMap* tmap_merge(CTempoMap* m1,CTempoMap* m2)
+{
+ int p1=0,p2=0;
+ CTempoMap * ret=0;
+ if (m1 && m2 && m1->data && m2->data)
+ {
+ ret=tmap_create();
+ if (ret)
+ {
+ while(p1<m1->pos && p2<m2->pos)
+ {
+ if (m1->data[p1].pos<=m2->data[p2].pos)
+ {
+ ret->AddEntry(m1->data[p1].pos,m1->data[p1].tm);
+ p1++;
+ }
+ else
+ {
+ ret->AddEntry(m2->data[p2].pos,m2->data[p2].tm);
+ p2++;
+ }
+ }
+ while(p1<m1->pos)
+ {
+ ret->AddEntry(m1->data[p1].pos,m1->data[p1].tm);
+ p1++;
+ }
+ while(p2<m2->pos)
+ {
+ ret->AddEntry(m2->data[p2].pos,m2->data[p2].tm);
+ p2++;
+ }
+ }
+ }
+ if (m1) delete m1;
+ if (m2) delete m2;
+ return ret;
+
+}
+
+KAR_ENTRY * kmap_create(MIDI_file* mf,UINT prec,UINT * num,char** text)
+{
+ if (!mf->kar_track) return 0;
+ grow_buf b_data,b_map;
+ KAR_ENTRY te;
+ BYTE *track=(BYTE*)mf->data+mf->kar_track+8;
+ BYTE *track_end = track+rev32(*(DWORD*)(mf->data+mf->kar_track+4));
+ int time=0;
+ int ptr=0;
+ BYTE lc=0;
+ while(track<track_end)
+ {
+ unsigned int d;
+ track+=DecodeDelta(track,&d);
+ time+=d;
+ if (*track==0xFF) //meta
+ {
+ BYTE type=track[1];
+ track+=2;
+ track+=DecodeDelta(track,&d);
+ char * ptr=(char*)track;
+ track+=d;
+ if ((type==0x5 || type==0x1) && d && *ptr!='@') //lyrics
+ {
+ te.time=time;
+ te.foo=1;
+ unsigned int n;
+ te.start=b_data.get_size();
+ for(n=0;n<d;n++)
+ {
+ switch(ptr[n])
+ {
+// case '@':
+ case '\\':
+ case '/':
+ case 0x0D:
+ b_data.write("\x0d\x0a",2);
+ break;
+ case 0x0A:
+ break;
+ default:
+ te.foo=0;
+ b_data.write_byte(ptr[n]);
+ break;
+ }
+ }
+ te.end=b_data.get_size();
+ if (te.start<te.end) b_map.write(&te,sizeof(te));
+ }
+ }
+ else if (*track==0xF0)
+ {
+ track++;
+ track+=DecodeDelta(track,&d);
+ track+=d;
+ }
+ else if ((*track&0xF0)==0xF0)
+ {
+ track++;//hack
+ }
+ else
+ {
+ if (*track&0x80) lc=*(track++)&0xF0;
+ if (lc==0 || lc==0xC0 || lc==0xD0) track++;
+ else track+=2;
+ }
+ }
+ int map_siz = b_map.get_size();
+ KAR_ENTRY * map=(KAR_ENTRY*)b_map.finish();
+ map_siz/=sizeof(KAR_ENTRY);
+
+ if (num) *num=map_siz;
+
+ if (text)
+ {
+ b_data.write_byte(0);
+ *text=(char*)b_data.finish();
+ }
+ else b_data.reset();
+
+ if (map)
+ {
+ int n;
+
+ time=0;
+
+ CTempoMap* tmap=mf->tmap;
+
+ int pos_ms=0;
+ int t_pos=0;
+ int cur_temp=0;
+ int dtx=(UINT)rev16(*(WORD*)(mf->data+8+4))*1000/prec;
+
+ for(n=0;n<map_siz;n++)
+ {
+ int d=0;
+ d=map[n].time-time;
+
+ while(t_pos<tmap->pos && time+d>=tmap->data[t_pos].pos)
+ {
+ DWORD d1=tmap->data[t_pos].pos-time;
+ pos_ms+=MulDiv(cur_temp,d1,dtx);
+ cur_temp=tmap->data[t_pos].tm;
+ t_pos++;
+ time+=d1;
+ d-=d1;
+ }
+ pos_ms+=MulDiv(cur_temp,d,dtx);
+ time+=d;
+ map[n].time=pos_ms;
+ }
+ }
+
+ return map;
+}
+
+int sysex_table::num_entries() const
+{
+ int num=0;
+ entry * ptr=entries;
+ while(ptr) {ptr=ptr->next;num++;}
+ return num;
+}
+
+int sysex_table::get_entry(int idx,BYTE ** p_data,int * p_size,int * p_time) const
+{
+ entry * ptr=entries;
+ while(ptr && idx>0) {ptr=ptr->next;idx--;}
+ if (!ptr) return 0;
+ if (p_data) *p_data = ptr->data;
+ if (p_size) *p_size = ptr->size;
+ if (p_time) *p_time = ptr->time;
+ return 1;
+}
+
+void sysex_table::insert_entry(int idx,BYTE * data,int size,int time)
+{
+ entry ** ptr = &entries;
+ while(idx>0 && *ptr)
+ {
+ ptr = &(*ptr)->next;
+ idx--;
+ }
+ entry * insert = new entry;
+ insert->data = (BYTE*)malloc(size);
+ memcpy(insert->data,data,size);
+ insert->size = size;
+ insert->time = time;
+ insert->next = *ptr;
+ *ptr = insert;
+}
+
+int sysex_table::remove_entry(int idx)
+{
+ entry ** ptr = &entries;
+ while(idx>0 && *ptr)
+ {
+ ptr = &(*ptr)->next;
+ idx--;
+ }
+ if (!*ptr) return 0;
+ entry * remove = *ptr;
+ *ptr=remove->next;
+ free(remove->data);
+ delete remove;
+ return 1;
+}
+
+
+int sysex_table::file_write(const char* file) const
+{
+ HANDLE f=CreateFileA(file,GENERIC_WRITE,0,0,CREATE_ALWAYS,0,0);
+ if (f==INVALID_HANDLE_VALUE) return 0;
+
+ int size;
+ void * ptr = memblock_write(&size);
+ DWORD bw = 0;
+ WriteFile(f,ptr,size,&bw,0);
+ free(ptr);
+ CloseHandle(f);
+ return 1;
+}
+
+void * sysex_table::memblock_write(int * size) const
+{
+ grow_buf wb;
+
+ entry * ptr;
+ //MAGIC:DWORD , NUM: DWORD,DATA_SIZE:DWORD, offsets, sleep,data
+ DWORD temp;
+ temp=MHP_MAGIC;
+ wb.write(&temp,4);
+ temp=num_entries();
+ wb.write(&temp,4);
+
+ temp=0;
+ for(ptr=entries;ptr;ptr=ptr->next) temp+=ptr->size;
+ wb.write(&temp,4);
+ temp=0;
+ for(ptr=entries;ptr;ptr=ptr->next)
+ {
+ wb.write(&temp,4);
+ temp+=ptr->size;
+ }
+ for(ptr=entries;ptr;ptr=ptr->next)
+ {
+ temp = ptr->time;
+ wb.write(&temp,4);
+ }
+
+ for(ptr=entries;ptr;ptr=ptr->next)
+ {
+ wb.write(ptr->data,ptr->size);
+ }
+
+ if (size) *size = wb.get_size();
+
+ return wb.finish();
+}
+
+int sysex_table::memblock_read(const void * block,int size)
+{
+ entry * ptr;
+ const BYTE * src = (const BYTE*)block;
+ DWORD temp,total_size,total_num;
+
+
+ if (*(DWORD*)src!=MHP_MAGIC) return 0;
+ src+=4;
+
+ temp=total_num=*(DWORD*)src;
+ src+=4;
+ if (total_num>0xFFFF) return 0;
+
+ reset();
+ while(temp>0)
+ {
+ ptr=new entry;
+ ptr->next=entries;
+ entries = ptr;
+ temp--;
+ }
+
+ total_size=*(DWORD*)src;
+
+ UINT n;
+
+ for(n=0,ptr=entries;ptr;ptr=ptr->next,n++)
+ {
+//offset : 12 + 4 * n;
+//time : 12 + 4 * total_num + 4 * n;
+//data : 12 + 8 * total_num + offset
+ DWORD offset,time,offset2;
+ src = (const BYTE*)block + 12 + 4*n;
+ offset=*(DWORD*)src;
+
+ if (n!=total_num-1) offset2=*(DWORD*)(src+4);
+ else offset2=total_size;
+ ptr->size = offset2-offset;
+ src = (const BYTE*)block + 12 + 4*total_num + 4*n;
+ time = *(DWORD*)src;
+
+ ptr->data = (BYTE*)malloc(offset2);
+ src = (const BYTE*)block + 12 + 8*total_num + offset;
+ memcpy(ptr->data,src,ptr->size);
+
+ ptr->time = time;
+ }
+
+ return 1;
+}
+
+int sysex_table::file_read(const char* file)
+{
+
+ HANDLE f=CreateFileA(file,GENERIC_READ,FILE_SHARE_READ,0,OPEN_EXISTING,0,0);
+ if (f==INVALID_HANDLE_VALUE) return 0;
+ int size = GetFileSize(f,0);
+ void * temp = malloc(size);
+ DWORD br = 0;
+ ReadFile(f,temp,size,&br,0);
+ CloseHandle(f);
+ int rv = memblock_read(temp,size);
+ free(temp);
+ return rv;
+}
+
+int sysex_table::print_preview(int idx,char * out) const
+{
+ BYTE* data;
+ int size,time;
+ if (!get_entry(idx,&data,&size,&time)) return 0;
+ int size2=size;
+ if (size2>10) size2=10;
+ wsprintfA(out,WASABI_API_LNGSTRING(STRING_MS_FMT),time);
+ while(out && *out) out++;
+ int n;
+ for(n=0;n<size2;n++)
+ {
+ wsprintfA(out," %02X",data[n]);
+ out+=3;
+ }
+
+ if (size!=size2)
+ {
+ strcpy(out,"...");
+ }
+ return 1;
+}
+
+void sysex_table::print_edit(int idx,HWND wnd) const
+{
+ BYTE* data;
+ int size,time;
+ if (!get_entry(idx,&data,&size,&time)) {SetWindowTextA(wnd,"");return;}
+ if (size<=2) {SetWindowTextA(wnd,"");return;}
+ char *temp = (char*)malloc(3*size);
+ char *ptr = temp;
+ int n;
+ for(n=1;n<size-1;n++)
+ {
+ wsprintfA(ptr,"%02X ",data[n]);
+ ptr+=3;
+ }
+ ptr[-1]=0;
+ SetWindowTextA(wnd,temp);
+ free(temp);
+}
+
+void sysex_table::copy(const sysex_table & src)
+{
+ reset();
+ int idx=0;
+ BYTE * data;
+ int size,time;
+ while(src.get_entry(idx++,&data,&size,&time))//ASS SLOW
+ insert_entry(idx,data,size,time);
+}
+
+//special sysex table cfg_var hack
+class cfg_var_sysex : private cfg_var
+{
+private:
+ sysex_table * tab;
+
+ virtual void read(HKEY hk)
+ {
+ int size=reg_get_struct_size(hk);
+ if (size>0)
+ {
+ void * temp = malloc(size);
+ if (temp)
+ {
+ reg_read_struct(hk,temp,size);
+ tab->memblock_read(temp,size);
+ free(temp);
+ }
+ }
+ }
+ virtual void write(HKEY hk)
+ {
+ void * data;
+ int size;
+ data = tab->memblock_write(&size);
+ if (data) reg_write_struct(hk,data,size);
+
+ }
+ virtual void reset() {tab->reset();}
+
+public:
+ cfg_var_sysex(const char * name,sysex_table * p_tab) : cfg_var(name) {tab=p_tab;}
+};
+
+static cfg_var_sysex thevar("sysex_table",&cfg_sysex_table);