winamp/Src/Plugins/Input/in_midi/xmi.cpp
2024-09-24 14:54:57 +02:00

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