#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);