310 lines
5.2 KiB
C++
310 lines
5.2 KiB
C++
|
#include "main.h"
|
||
|
#include "cvt.h"
|
||
|
|
||
|
#pragma pack(push)
|
||
|
#pragma pack(1)
|
||
|
typedef struct
|
||
|
{
|
||
|
DWORD mthd,hdsize;
|
||
|
MIDIHEADER mhd;
|
||
|
} FILEHEADER;
|
||
|
|
||
|
typedef struct
|
||
|
{
|
||
|
DWORD mtrk,size;
|
||
|
} TRACKHEADER;
|
||
|
|
||
|
#pragma pack(pop)
|
||
|
|
||
|
struct XMI_cvt
|
||
|
{
|
||
|
public:
|
||
|
grow_buf out;
|
||
|
bool _end;
|
||
|
DWORD tr_sz;
|
||
|
DWORD cur_time,wr_time;
|
||
|
// DWORD loopstart;
|
||
|
bool hasevents;
|
||
|
void q_add(BYTE ch,BYTE note,DWORD tm);
|
||
|
void WriteDelta(DWORD _d);
|
||
|
void DoQueue();
|
||
|
DWORD ProcessNote(const BYTE* e);
|
||
|
DWORD ProcessDelta(const BYTE* d);
|
||
|
DWORD WriteEvent(const BYTE* e);
|
||
|
bool run(MIDI_file* mf,const BYTE*,DWORD);
|
||
|
#pragma pack(push)
|
||
|
#pragma pack(1)
|
||
|
#define Q_MAX 512
|
||
|
|
||
|
struct
|
||
|
{
|
||
|
DWORD time;
|
||
|
BYTE note;
|
||
|
BYTE channel;
|
||
|
} ch_q[Q_MAX];
|
||
|
|
||
|
#pragma pack(pop)
|
||
|
|
||
|
};
|
||
|
|
||
|
|
||
|
#define WriteBuf(A,B) out.write(A,B)
|
||
|
#define WriteBufB(A) out.write_byte(A)
|
||
|
#define WriteBufD(A) out.write_dword(A)
|
||
|
|
||
|
//WORD _fastcall rev16(WORD);
|
||
|
DWORD _fastcall rev32(DWORD);
|
||
|
|
||
|
|
||
|
#define FixHeader(H) {(H).fmt=rev16((H).fmt);(H).trax=rev16((H).trax);(H).dtx=rev16((H).dtx);}
|
||
|
#define MThd 'dhTM'
|
||
|
#define MTrk 'krTM'
|
||
|
#define EVNT 'TNVE'
|
||
|
|
||
|
void XMI_cvt::q_add(BYTE ch,BYTE note,DWORD tm)
|
||
|
{
|
||
|
UINT n,_n=-1;
|
||
|
for(n=0;n<Q_MAX;n++)
|
||
|
{
|
||
|
if (ch_q[n].note==note && ch_q[n].channel==ch && ch_q[n].time!=-1)
|
||
|
{
|
||
|
/*if (ch_q[n].time>tm) */ch_q[n].time=tm;
|
||
|
// q_notes++;
|
||
|
return;
|
||
|
}
|
||
|
else if (ch_q[n].time==-1) _n=n;
|
||
|
}
|
||
|
if (_n!=-1)
|
||
|
{
|
||
|
ch_q[_n].channel=ch;
|
||
|
ch_q[_n].time=tm;
|
||
|
ch_q[_n].note=note;
|
||
|
// q_notes++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void XMI_cvt::WriteDelta(DWORD _d)
|
||
|
{
|
||
|
DWORD d=_d-wr_time;
|
||
|
wr_time=_d;
|
||
|
int st=out.get_size();
|
||
|
gb_write_delta(out,d);
|
||
|
tr_sz+=out.get_size()-st;
|
||
|
}
|
||
|
|
||
|
DWORD _inline ReadDelta1(const BYTE* d,DWORD* _l)
|
||
|
{
|
||
|
DWORD r=d[0],l=0;
|
||
|
while(!(d[l+1]&0x80))
|
||
|
{
|
||
|
r+=d[++l];
|
||
|
}
|
||
|
*_l=l+1;
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
void XMI_cvt::DoQueue()
|
||
|
{
|
||
|
while(1)
|
||
|
{
|
||
|
DWORD _i=-1;
|
||
|
DWORD _mt=-1;
|
||
|
UINT i;
|
||
|
for(i=0;i<Q_MAX;i++)
|
||
|
{
|
||
|
if (ch_q[i].time<_mt) {_i=i;_mt=ch_q[i].time;}
|
||
|
}
|
||
|
if (_mt<=cur_time)
|
||
|
{
|
||
|
WriteDelta(_mt);
|
||
|
BYTE t[3]={(BYTE)(0x80|ch_q[_i].channel),ch_q[_i].note,0};
|
||
|
WriteBuf(t,3);
|
||
|
ch_q[_i].time=-1;
|
||
|
tr_sz+=3;
|
||
|
// q_notes--;
|
||
|
}
|
||
|
else break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DWORD XMI_cvt::ProcessNote(const BYTE* e)
|
||
|
{
|
||
|
DoQueue();
|
||
|
WriteDelta(cur_time);
|
||
|
|
||
|
WriteBuf(e,3);
|
||
|
tr_sz+=3;
|
||
|
DWORD l=3;
|
||
|
unsigned int _d;
|
||
|
l+=DecodeDelta(e+l,&_d);
|
||
|
|
||
|
|
||
|
if (e[2]) q_add(e[0]&0xF,e[1],cur_time+_d);
|
||
|
|
||
|
return l;
|
||
|
}
|
||
|
|
||
|
DWORD XMI_cvt::ProcessDelta(const BYTE* d)
|
||
|
{
|
||
|
DWORD l;
|
||
|
cur_time+=ReadDelta1(d,&l);
|
||
|
return l;
|
||
|
}
|
||
|
|
||
|
DWORD XMI_cvt::WriteEvent(const BYTE* e)
|
||
|
{
|
||
|
if ((e[0]&0xF0)==0xF0 && e[0]!=0xFF && e[0]!=0xF0)//hack
|
||
|
{
|
||
|
UINT l=1;
|
||
|
while(!(e[l]&0x80)) l++;
|
||
|
return l;
|
||
|
}
|
||
|
else if (e[0]==0xFF)
|
||
|
{
|
||
|
if (e[1]==0x2F)
|
||
|
{
|
||
|
DWORD _ct=cur_time;
|
||
|
cur_time=-2;
|
||
|
DoQueue();
|
||
|
|
||
|
// loopstart=_ct-wr_time;
|
||
|
cur_time=_ct;
|
||
|
DWORD _ev=0x002FFF00;
|
||
|
WriteBuf(&_ev,4);
|
||
|
tr_sz+=4;
|
||
|
_end=1;
|
||
|
return 3;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
UINT l=e[2];
|
||
|
if (l&0x80)
|
||
|
{
|
||
|
l=((l<<7)|e[3])+1;
|
||
|
}
|
||
|
return 3+l;
|
||
|
}
|
||
|
}
|
||
|
DoQueue();
|
||
|
WriteDelta(cur_time);
|
||
|
if (e[0]==0xF0)
|
||
|
{
|
||
|
unsigned int d;
|
||
|
UINT l = 1 + DecodeDelta(e+1,&d);
|
||
|
l+=d;
|
||
|
WriteBuf(e,l);
|
||
|
tr_sz+=l;
|
||
|
return l;
|
||
|
}
|
||
|
DWORD l;
|
||
|
//hasevents=1;
|
||
|
if ((e[0]&0xF0)==0xC0 || (e[0]&0xF0)==0xD0) l=2;
|
||
|
else l=3;
|
||
|
WriteBuf(e,l);
|
||
|
tr_sz+=l;
|
||
|
return l;
|
||
|
}
|
||
|
|
||
|
BYTE xmi_track0[19]={'M','T','r','k',0,0,0,11,0,0xFF,0x51,0x03,0x20,0x8d,0xb7,0,0xFF,0x2F,0};
|
||
|
|
||
|
void ReleaseObject(IUnknown* o);
|
||
|
|
||
|
bool XMI_cvt::run(MIDI_file * mf,const BYTE* buf,DWORD sz)
|
||
|
{
|
||
|
// q_notes=0;
|
||
|
FILEHEADER fhd=
|
||
|
{
|
||
|
MThd,
|
||
|
0x06000000,
|
||
|
0x0100,0x0000,0x0001
|
||
|
};
|
||
|
WriteBuf(&fhd,sizeof(fhd));
|
||
|
UINT ptr=0x22;
|
||
|
DWORD _sz;
|
||
|
DWORD sp;
|
||
|
UINT ntrax=1;
|
||
|
WriteBuf(xmi_track0,sizeof(xmi_track0));
|
||
|
while(ptr<sz-4 && *(DWORD*)(buf+ptr)!=EVNT) ptr++;
|
||
|
if (*(DWORD*)(buf+ptr)!=EVNT) goto fail;
|
||
|
// loopstart=0;
|
||
|
_track:
|
||
|
cur_time=wr_time=0;
|
||
|
WriteBufD(_rv('MTrk'));
|
||
|
sp=out.get_size();
|
||
|
WriteBufD(0);
|
||
|
ntrax++;
|
||
|
tr_sz=0;
|
||
|
ptr+=4;
|
||
|
_sz=ptr+4+rev32(*(DWORD*)(buf+ptr));
|
||
|
if (_sz>sz+1) goto fail;
|
||
|
ptr+=4;
|
||
|
_end=0;
|
||
|
{
|
||
|
UINT n;
|
||
|
for(n=0;n<Q_MAX;n++) ch_q[n].time=-1;
|
||
|
}
|
||
|
hasevents=0;
|
||
|
while(ptr<sz-1 && !_end)
|
||
|
{
|
||
|
if ((buf[ptr]&0x80)==0)
|
||
|
{
|
||
|
ptr+=ProcessDelta(buf+ptr);
|
||
|
}
|
||
|
if ((buf[ptr]&0xF0)==0x90)
|
||
|
{
|
||
|
hasevents=1;
|
||
|
ptr+=ProcessNote(buf+ptr);
|
||
|
}
|
||
|
else ptr+=WriteEvent(buf+ptr);
|
||
|
}
|
||
|
if (!hasevents) {out.truncate(sp-4);ntrax--;}
|
||
|
else out.write_dword_ptr(rev32(tr_sz),sp);
|
||
|
if (ptr&1) ptr++;
|
||
|
if (ptr>=sz) goto _e;
|
||
|
if (*(DWORD*)(buf+ptr)==_rv('FORM') && *(DWORD*)(buf+ptr+8)==_rv('XMID'))
|
||
|
{
|
||
|
ptr+=12;
|
||
|
_te: if (ptr&1) ptr++;
|
||
|
if (*(DWORD*)(buf+ptr)==_rv('EVNT')) goto _track;
|
||
|
else if (*(DWORD*)(buf+ptr)==_rv('TIMB'))
|
||
|
{
|
||
|
ptr+=8+rev32(*(DWORD*)(buf+ptr+4));
|
||
|
if (ptr<sz) goto _te;
|
||
|
}
|
||
|
}
|
||
|
_e:
|
||
|
{
|
||
|
WORD w=rev16(ntrax);
|
||
|
out.write_ptr(&w,2,10);
|
||
|
if (ntrax>1)
|
||
|
w=0x200;
|
||
|
out.write_ptr(&w,2,8);
|
||
|
}
|
||
|
|
||
|
mf->size = out.get_size();
|
||
|
mf->data = (BYTE*)out.finish();
|
||
|
if (!mf->data) return 0;
|
||
|
|
||
|
// if (loopstart>0x10) mf->loopstart=loopstart;
|
||
|
#ifdef MF_USE_DMCRAP
|
||
|
if (sz>ptr+0x20 && *(DWORD*)(buf+ptr)==_rv('FORM') && *(DWORD*)(buf+ptr+8)==_rv('XDLS'))
|
||
|
{
|
||
|
DWORD rs=rev32(*(DWORD*)(buf+_sz+4));
|
||
|
if (rs+12+_sz>sz) goto _ret;
|
||
|
if (*(DWORD*)(buf+ptr+0x14)!=_rv('DLS ')) goto _ret;
|
||
|
mf->DLSsize=rs;
|
||
|
mf->pDLSdata=(BYTE*)malloc(rs);
|
||
|
memcpy(mf->pDLSdata,buf+_sz+12,rs);
|
||
|
}
|
||
|
#endif
|
||
|
_ret:
|
||
|
return 1;
|
||
|
fail:
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
bool load_xmi(MIDI_file * mf,const BYTE* buf,size_t sz)
|
||
|
{
|
||
|
XMI_cvt c;
|
||
|
return c.run(mf,buf,sz);
|
||
|
}
|