1065 lines
19 KiB
C++
1065 lines
19 KiB
C++
#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);
|