#include <math.h>
#include <string.h>
#if !defined(max) && !defined(__cplusplus)
#define max(a,b) (((a) > (b)) ? (a) : (b))
#endif
#if !defined(min) && !defined(__cplusplus)
#define min(a,b) (((a) < (b)) ? (a) : (b))
#endif
#define PI 3.141592653589793
#define MAXCELLS 18
#define WAVPREC 2048
static float AMPSCALE=(8192.0);
#define FRQSCALE (49716/512.0)
#define MODFACTOR 4.0
#define MFBFACTOR 1.0
#define ADJUSTSPEED 0.75
typedef struct
{
float val, t, tinc, vol, sustain, amp, mfb;
float a0, a1, a2, a3, decaymul, releasemul;
short *waveform;
long wavemask;
void (*cellfunc)(void *, float);
unsigned char flags, dum0, dum1, dum2;
} celltype;
static long numspeakers, bytespersample;
static float recipsamp;
static celltype cell[MAXCELLS];
static signed short wavtable[WAVPREC*3];
static float kslmul[4] = {0.0,0.5,0.25,1.0};
static float frqmul[16] = {.5,1,2,3,4,5,6,7,8,9,10,10,12,12,15,15}, nfrqmul[16];
static unsigned char adlibreg[256], ksl[8][16];
static unsigned char modulatorbase[9] = {0,1,2,8,9,10,16,17,18};
static unsigned char odrumstat = 0;
static unsigned char base2cell[22] = {0,1,2,0,1,2,0,0,3,4,5,3,4,5,0,0,6,7,8,6,7,8};
float lvol[9] = {1,1,1,1,1,1,1,1,1}; float rvol[9] = {1,1,1,1,1,1,1,1,1}; long lplc[9] = {0,0,0,0,0,0,0,0,0}; long rplc[9] = {0,0,0,0,0,0,0,0,0};
long nlvol[9], nrvol[9];
long nlplc[9], nrplc[9];
long rend = 0;
#define FIFOSIZ 256
static float *rptr[9], *nrptr[9];
static float rbuf[9][FIFOSIZ*2];
static float snd[FIFOSIZ*2];
#ifndef USING_ASM
#define _inline
#endif
#ifdef USING_ASM
static _inline void ftol (float f, long *a)
{
_asm
{
mov eax, a
fld f
fistp dword ptr [eax]
}
}
#else
static void ftol(float f, long *a) {
*a=f;
}
#endif
#define ctc ((celltype *)c)
void docell4 (void *c, float modulator) { (void)c; (void)modulator; }
void docell3 (void *c, float modulator)
{
long i;
ftol(ctc->t+modulator,&i);
ctc->t += ctc->tinc;
ctc->val += (ctc->amp*ctc->vol*((float)ctc->waveform[i&ctc->wavemask])-ctc->val)*ADJUSTSPEED;
}
void docell2 (void *c, float modulator)
{
long i;
ftol(ctc->t+modulator,&i);
if (*(long *)&ctc->amp <= 0x37800000)
{
ctc->amp = 0;
ctc->cellfunc = docell4;
}
ctc->amp *= ctc->releasemul;
ctc->t += ctc->tinc;
ctc->val += (ctc->amp*ctc->vol*((float)ctc->waveform[i&ctc->wavemask])-ctc->val)*ADJUSTSPEED;
}
void docell1 (void *c, float modulator)
{
long i;
ftol(ctc->t+modulator,&i);
if ((*(long *)&ctc->amp) <= (*(long *)&ctc->sustain))
{
if (ctc->flags&32)
{
ctc->amp = ctc->sustain;
ctc->cellfunc = docell3;
}
else
ctc->cellfunc = docell2;
}
else
ctc->amp *= ctc->decaymul;
ctc->t += ctc->tinc;
ctc->val += (ctc->amp*ctc->vol*((float)ctc->waveform[i&ctc->wavemask])-ctc->val)*ADJUSTSPEED;
}
void docell0 (void *c, float modulator)
{
long i;
ftol(ctc->t+modulator,&i);
ctc->amp = ((ctc->a3*ctc->amp + ctc->a2)*ctc->amp + ctc->a1)*ctc->amp + ctc->a0;
if ((*(long *)&ctc->amp) > 0x3f800000)
{
ctc->amp = 1;
ctc->cellfunc = docell1;
}
ctc->t += ctc->tinc;
ctc->val += (ctc->amp*ctc->vol*((float)ctc->waveform[i&ctc->wavemask])-ctc->val)*ADJUSTSPEED;
}
static long waveform[8] = {WAVPREC,WAVPREC>>1,WAVPREC,(WAVPREC*3)>>2,0,0,(WAVPREC*5)>>2,WAVPREC<<1};
static long wavemask[8] = {WAVPREC-1,WAVPREC-1,(WAVPREC>>1)-1,(WAVPREC>>1)-1,WAVPREC-1,((WAVPREC*3)>>2)-1,WAVPREC>>1,WAVPREC-1};
static long wavestart[8] = {0,WAVPREC>>1,0,WAVPREC>>2,0,0,0,WAVPREC>>3};
static float attackconst[4] = {1/2.82624,1/2.25280,1/1.88416,1/1.59744};
static float decrelconst[4] = {1/39.28064,1/31.41608,1/26.17344,1/22.44608};
void cellon (long i, long j, celltype *c, unsigned char iscarrier)
{
long frn, oct, toff;
float f;
frn = ((((long)adlibreg[i+0xb0])&3)<<8) + (long)adlibreg[i+0xa0];
oct = ((((long)adlibreg[i+0xb0])>>2)&7);
toff = (oct<<1) + ((frn>>9)&((frn>>8)|(((adlibreg[8]>>6)&1)^1)));
if (!(adlibreg[j+0x20]&16)) toff >>= 2;
f = pow(2.0,(adlibreg[j+0x60]>>4)+(toff>>2)-1)*attackconst[toff&3]*recipsamp;
c->a0 = .0377*f; c->a1 = 10.73*f+1; c->a2 = -17.57*f; c->a3 = 7.42*f;
f = -7.4493*decrelconst[toff&3]*recipsamp;
c->decaymul = pow(2.0,f*pow(2.0,(adlibreg[j+0x60]&15)+(toff>>2)));
c->releasemul = pow(2.0,f*pow(2.0,(adlibreg[j+0x80]&15)+(toff>>2)));
c->wavemask = wavemask[adlibreg[j+0xe0]&7];
c->waveform = &wavtable[waveform[adlibreg[j+0xe0]&7]];
if (!(adlibreg[1]&0x20)) c->waveform = &wavtable[WAVPREC];
c->t = wavestart[adlibreg[j+0xe0]&7];
c->flags = adlibreg[j+0x20];
c->cellfunc = docell0;
c->tinc = (float)(frn<<oct)*nfrqmul[adlibreg[j+0x20]&15];
c->vol = pow(2.0,((float)(adlibreg[j+0x40]&63) +
(float)kslmul[adlibreg[j+0x40]>>6]*ksl[oct][frn>>6]) * -.125 - 14);
c->sustain = pow(2.0,(float)(adlibreg[j+0x80]>>4) * -.5);
if (!iscarrier) c->amp = 0;
c->mfb = pow(2.0,((adlibreg[i+0xc0]>>1)&7)+5)*(WAVPREC/2048.0)*MFBFACTOR;
if (!(adlibreg[i+0xc0]&14)) c->mfb = 0;
c->val = 0;
}
void cellfreq (signed long i, signed long j, celltype *c)
{
long frn, oct;
frn = ((((long)adlibreg[i+0xb0])&3)<<8) + (long)adlibreg[i+0xa0];
oct = ((((long)adlibreg[i+0xb0])>>2)&7);
c->tinc = (float)(frn<<oct)*nfrqmul[adlibreg[j+0x20]&15];
c->vol = pow(2.0,((float)(adlibreg[j+0x40]&63) +
(float)kslmul[adlibreg[j+0x40]>>6]*ksl[oct][frn>>6]) * -.125 - 14);
}
static long initfirstime = 0;
void adlibinit (long dasamplerate, long danumspeakers, long dabytespersample)
{
long i, j, frn, oct;
memset((void *)adlibreg,0,sizeof(adlibreg));
memset((void *)cell,0,sizeof(celltype)*MAXCELLS);
memset((void *)rbuf,0,sizeof(rbuf));
rend = 0; odrumstat = 0;
for(i=0;i<MAXCELLS;i++)
{
cell[i].cellfunc = docell4;
cell[i].amp = 0;
cell[i].vol = 0;
cell[i].t = 0;
cell[i].tinc = 0;
cell[i].wavemask = 0;
cell[i].waveform = &wavtable[WAVPREC];
}
numspeakers = danumspeakers;
bytespersample = dabytespersample;
recipsamp = 1.0 / (float)dasamplerate;
for(i=15;i>=0;i--) nfrqmul[i] = frqmul[i]*recipsamp*FRQSCALE*(WAVPREC/2048.0);
if (!initfirstime)
{
initfirstime = 1;
for(i=0;i<(WAVPREC>>1);i++)
{
wavtable[i] =
wavtable[(i<<1) +WAVPREC] = (signed short)(16384*sin((float)((i<<1) )*PI*2/WAVPREC));
wavtable[(i<<1)+1+WAVPREC] = (signed short)(16384*sin((float)((i<<1)+1)*PI*2/WAVPREC));
}
for(i=0;i<(WAVPREC>>3);i++)
{
wavtable[i+(WAVPREC<<1)] = wavtable[i+(WAVPREC>>3)]-16384;
wavtable[i+((WAVPREC*17)>>3)] = wavtable[i+(WAVPREC>>2)]+16384;
}
ksl[7][0] = 0; ksl[7][1] = 24; ksl[7][2] = 32; ksl[7][3] = 37;
ksl[7][4] = 40; ksl[7][5] = 43; ksl[7][6] = 45; ksl[7][7] = 47;
ksl[7][8] = 48; for(i=9;i<16;i++) ksl[7][i] = i+41;
for(j=6;j>=0;j--)
for(i=0;i<16;i++)
{
oct = (long)ksl[j+1][i]-8; if (oct < 0) oct = 0;
ksl[j][i] = (unsigned char)oct;
}
}
else
{
for(i=0;i<9;i++)
{
frn = ((((long)adlibreg[i+0xb0])&3)<<8) + (long)adlibreg[i+0xa0];
oct = ((((long)adlibreg[i+0xb0])>>2)&7);
cell[i].tinc = (float)(frn<<oct)*nfrqmul[adlibreg[modulatorbase[i]+0x20]&15];
}
}
}
void adlib0 (long i, long v)
{
unsigned char tmp = adlibreg[i];
adlibreg[i] = v;
if (i == 0xbd)
{
if ((v&16) > (odrumstat&16)) {
cellon(6,16,&cell[6],0);
cellon(6,19,&cell[15],1);
cell[15].vol *= 2;
}
if ((v&8) > (odrumstat&8)) {
cellon(16,20,&cell[16],0);
cell[16].tinc *= 2*(nfrqmul[adlibreg[17+0x20]&15] / nfrqmul[adlibreg[20+0x20]&15]);
if (((adlibreg[20+0xe0]&7) >= 3) && ((adlibreg[20+0xe0]&7) <= 5)) cell[16].vol = 0;
cell[16].vol *= 2;
}
if ((v&4) > (odrumstat&4)) {
cellon(8,18,&cell[8],0);
cell[8].vol *= 2;
}
if ((v&2) > (odrumstat&2)) {
cellon(17,21,&cell[17],0);
cell[17].wavemask = wavemask[5];
cell[17].waveform = &wavtable[waveform[5]];
cell[17].tinc *= 16; cell[17].vol *= 2;
}
if ((v&1) > (odrumstat&1)) {
cellon(7,17,&cell[7],0);
if (((adlibreg[17+0xe0]&7) == 1) || ((adlibreg[17+0xe0]&7) == 4) ||
((adlibreg[17+0xe0]&7) == 5) || ((adlibreg[17+0xe0]&7) == 7)) cell[7].vol = 0;
if ((adlibreg[17+0xe0]&7) == 6) { cell[7].wavemask = 0; cell[7].waveform = &wavtable[(WAVPREC*7)>>2]; }
}
odrumstat = v;
}
else if (((unsigned)(i-0x40) < (unsigned)22) && ((i&7) < 6))
{
if ((i&7) < 3) cellfreq(base2cell[i-0x40],i-0x40,&cell[base2cell[i-0x40]]);
else cellfreq(base2cell[i-0x40],i-0x40,&cell[base2cell[i-0x40]+9]);
}
else if ((unsigned)(i-0xa0) < (unsigned)9)
{
cellfreq(i-0xa0,modulatorbase[i-0xa0],&cell[i-0xa0]);
cellfreq(i-0xa0,modulatorbase[i-0xa0]+3,&cell[i-0xa0+9]);
}
else if ((unsigned)(i-0xb0) < (unsigned)9)
{
if ((v&32) > (tmp&32))
{
cellon(i-0xb0,modulatorbase[i-0xb0],&cell[i-0xb0],0);
cellon(i-0xb0,modulatorbase[i-0xb0]+3,&cell[i-0xb0+9],1);
}
else if ((v&32) < (tmp&32))
cell[i-0xb0].cellfunc = cell[i-0xb0+9].cellfunc = docell2;
cellfreq(i-0xb0,modulatorbase[i-0xb0],&cell[i-0xb0]);
cellfreq(i-0xb0,modulatorbase[i-0xb0]+3,&cell[i-0xb0+9]);
}
}
#ifdef USING_ASM
static long fpuasm;
static float fakeadd = 8388608.0+128.0;
static _inline void clipit8 (float f, long a)
{
_asm
{
mov edi, a
fld dword ptr f
fadd dword ptr fakeadd
fstp dword ptr fpuasm
mov eax, fpuasm
test eax, 0x007fff00
jz short skipit
shr eax, 16
xor eax, -1
skipit: mov byte ptr [edi], al
}
}
static _inline void clipit16 (float f, long a)
{
_asm
{
mov eax, a
fld dword ptr f
fist word ptr [eax]
cmp word ptr [eax], 0x8000
jne short skipit2
fst dword ptr [fpuasm]
cmp fpuasm, 0x80000000
sbb word ptr [eax], 0
skipit2: fstp st
}
}
#else
static void clipit8(float f,unsigned char *a) {
f/=256.0;
f+=128.0;
if (f>254.5) *a=255;
else if (f<0.5) *a=0;
else *a=f;
}
static void clipit16(float f,short *a) {
if (f>32766.5) *a=32767;
else if (f<-32767.5) *a=-32768;
else *a=f;
}
#endif
void adlibsetvolume(int i) {
AMPSCALE=i;
}
void adlibgetsample (unsigned char *sndptr, long numbytes)
{
long i, j, k=0, ns, endsamples, rptrs, numsamples;
celltype *cptr;
float f;
short *sndptr2=(short *)sndptr;
numsamples = (numbytes>>(numspeakers+bytespersample-2));
if (bytespersample == 1) f = AMPSCALE/256.0; else f = AMPSCALE;
if (numspeakers == 1)
{
nlvol[0] = lvol[0]*f;
for(i=0;i<9;i++) rptr[i] = &rbuf[0][0];
rptrs = 1;
}
else
{
rptrs = 0;
for(i=0;i<9;i++)
{
if ((!i) || (lvol[i] != lvol[i-1]) || (rvol[i] != rvol[i-1]) ||
(lplc[i] != lplc[i-1]) || (rplc[i] != rplc[i-1]))
{
nlvol[rptrs] = lvol[i]*f;
nrvol[rptrs] = rvol[i]*f;
nlplc[rptrs] = rend-min(max(lplc[i],0),FIFOSIZ);
nrplc[rptrs] = rend-min(max(rplc[i],0),FIFOSIZ);
rptrs++;
}
rptr[i] = &rbuf[rptrs-1][0];
}
}
for(ns=0;ns<numsamples;ns+=endsamples)
{
endsamples = min(FIFOSIZ*2-rend,FIFOSIZ);
endsamples = min(endsamples,numsamples-ns);
for(i=0;i<9;i++)
nrptr[i] = &rptr[i][rend];
for(i=0;i<rptrs;i++)
memset((void *)&rbuf[i][rend],0,endsamples*sizeof(float));
if (adlibreg[0xbd]&0x20)
{
if (cell[15].cellfunc != docell4)
{
if (adlibreg[0xc6]&1)
{
for(i=0;i<endsamples;i++)
{
(cell[15].cellfunc)((void *)&cell[15],0.0);
nrptr[6][i] += cell[15].val;
}
}
else
{
for(i=0;i<endsamples;i++)
{
(cell[6].cellfunc)((void *)&cell[6],cell[6].val*cell[6].mfb);
(cell[15].cellfunc)((void *)&cell[15],cell[6].val*WAVPREC*MODFACTOR);
nrptr[6][i] += cell[15].val;
}
}
}
if ((cell[7].cellfunc != docell4) || (cell[8].cellfunc != docell4) || (cell[16].cellfunc != docell4) || (cell[17].cellfunc != docell4))
{
for(i=0;i<endsamples;i++)
{
k = k*1664525+1013904223;
(cell[16].cellfunc)((void *)&cell[16],k&((WAVPREC>>1)-1)); (cell[7].cellfunc)((void *)&cell[7],k&(WAVPREC-1)); (cell[17].cellfunc)((void *)&cell[17],k&((WAVPREC>>3)-1)); (cell[8].cellfunc)((void *)&cell[8],0.0); nrptr[7][i] += cell[7].val + cell[16].val;
nrptr[8][i] += cell[8].val + cell[17].val;
}
}
}
for(j=9-1;j>=0;j--)
{
if ((adlibreg[0xbd]&0x20) && (j >= 6) && (j < 9)) continue;
cptr = &cell[j]; k = j;
if (adlibreg[0xc0+k]&1)
{
if ((cptr[9].cellfunc == docell4) && (cptr->cellfunc == docell4)) continue;
for(i=0;i<endsamples;i++)
{
(cptr->cellfunc)((void *)cptr,cptr->val*cptr->mfb);
(cptr->cellfunc)((void *)&cptr[9],0);
nrptr[j][i] += cptr[9].val + cptr->val;
}
}
else
{
if (cptr[9].cellfunc == docell4) continue;
for(i=0;i<endsamples;i++)
{
(cptr->cellfunc)((void *)cptr,cptr->val*cptr->mfb);
(cptr[9].cellfunc)((void *)&cptr[9],cptr->val*WAVPREC*MODFACTOR);
nrptr[j][i] += cptr[9].val;
}
}
}
if (numspeakers == 1)
{
if (bytespersample == 1)
{
for(i=endsamples-1;i>=0;i--)
clipit8(nrptr[0][i]*nlvol[0],sndptr+1);
}
else
{
for(i=endsamples-1;i>=0;i--)
clipit16(nrptr[0][i]*nlvol[0],sndptr2+i);
}
}
else
{
memset((void *)snd,0,endsamples*sizeof(float)*2);
for(j=0;j<rptrs;j++)
{
for(i=0;i<endsamples;i++)
{
snd[(i<<1) ] += rbuf[j][(nlplc[j]+i)&(FIFOSIZ*2-1)]*nlvol[j];
snd[(i<<1)+1] += rbuf[j][(nrplc[j]+i)&(FIFOSIZ*2-1)]*nrvol[j];
}
nlplc[j] += endsamples;
nrplc[j] += endsamples;
}
if (bytespersample == 1)
{
for(i=(endsamples<<1)-1;i>=0;i--)
clipit8(snd[i],sndptr+i);
}
else
{
for(i=(endsamples<<1)-1;i>=0;i--)
clipit16(snd[i],sndptr2+i);
}
}
sndptr = sndptr+(numspeakers*endsamples);
sndptr2 = sndptr2+(numspeakers*endsamples);
rend = ((rend+endsamples)&(FIFOSIZ*2-1));
}
}