Browse Source

JPN Support & better RIX playback

louyihua 8 years ago
parent
commit
4a3eb9621f
20 changed files with 5520 additions and 2460 deletions
  1. 600 0
      adplug/adlibemu.c
  2. 26 0
      adplug/adlibemu.h
  3. 90 0
      adplug/demuopl.cpp
  4. 14 38
      adplug/demuopl.h
  5. 1089 1097
      adplug/dosbox_opl.cpp
  6. 23 21
      adplug/dosbox_opl.h
  7. 2261 1136
      adplug/fmopl.c
  8. 199 136
      adplug/fmopl.h
  9. 61 0
      adplug/kemuopl.h
  10. 10 4
      global.c
  11. 2 1
      global.h
  12. 6 4
      itemmenu.c
  13. 2 2
      magicmenu.c
  14. 943 0
      resampler.c
  15. 63 0
      resampler.h
  16. 100 17
      rixplay.cpp
  17. 1 1
      script.c
  18. 11 2
      sdlpal.vcxproj
  19. 18 0
      sdlpal.vcxproj.filters
  20. 1 1
      sound.h

+ 600 - 0
adplug/adlibemu.c

@@ -0,0 +1,600 @@
+/*
+ * ADLIBEMU.C
+ * Copyright (C) 1998-2001 Ken Silverman
+ * Ken Silverman's official web site: "http://www.advsys.net/ken"
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*
+This file is a digital Adlib emulator for OPL2 and possibly OPL3
+
+Features that could be added in a future version:
+- Amplitude and Frequency Vibrato Bits (not hard, but a big speed hit)
+- Global Keyboard Split Number Bit (need to research this one some more)
+- 2nd Adlib chip for OPL3 (simply need to make my cell array bigger)
+- Advanced connection modes of OPL3 (Just need to add more "docell" cases)
+- L/R Stereo bits of OPL3 (Need adlibgetsample to return stereo)
+
+Features that aren't worth supporting:
+- Anything related to adlib timers&interrupts (Sorry - I always used IRQ0)
+- Composite sine wave mode (CSM) (Supported only on ancient cards)
+
+I'm not sure about a few things in my code:
+- Attack curve.  What function is this anyway?  I chose to use an order-3
+  polynomial to approximate but this doesn't seem right.
+- Attack/Decay/Release constants - my constants may not be exact
+- What should ADJUSTSPEED be?
+- Haven't verified that Global Keyboard Split Number Bit works yet
+- Some of the drums don't always sound right.  It's pretty hard to guess
+  the exact waveform of drums when you look at random data which is
+  slightly randomized due to digital ADC recording.
+- Adlib seems to have a lot more treble than my emulator does.  I'm not
+  sure if this is simply unfixable due to the sound blaster's different
+  filtering on FM and digital playback or if it's a serious bug in my
+  code.
+*/
+
+#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)
+
+//Constants for Ken's Awe32, on a PII-266 (Ken says: Use these for KSM's!)
+#define MODFACTOR 4.0      //How much of modulator cell goes into carrier
+#define MFBFACTOR 1.0      //How much feedback goes back into modulator
+#define ADJUSTSPEED 0.75   //0<=x<=1  Simulate finite rate of change of state
+
+//Constants for Ken's Awe64G, on a P-133
+//#define MODFACTOR 4.25   //How much of modulator cell goes into carrier
+//#define MFBFACTOR 0.5    //How much feedback goes back into modulator
+//#define ADJUSTSPEED 0.85 //0<=x<=1  Simulate finite rate of change of state
+
+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};  //Volume multiplier on left speaker
+float rvol[9] = {1,1,1,1,1,1,1,1,1};  //Volume multiplier on right speaker
+long lplc[9] = {0,0,0,0,0,0,0,0,0};   //Samples to delay on left speaker
+long rplc[9] = {0,0,0,0,0,0,0,0,0};   //Samples to delay on right speaker
+
+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)      //A rare attempt to make code easier to read!
+void docell4 (void *c, float 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;
+}
+
+//This function (and bug fix) written by Chris Moeller
+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;
+	}
+
+	//[table in book]*8/3
+	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)) //BassDrum
+	{
+	    cellon(6,16,&cell[6],0);
+	    cellon(6,19,&cell[15],1);
+	    cell[15].vol *= 2;
+	}
+	if ((v&8) > (odrumstat&8)) //Snare
+	{
+	    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)) //TomTom
+	{
+	    cellon(8,18,&cell[8],0);
+	    cell[8].vol *= 2;
+	}
+	if ((v&2) > (odrumstat&2)) //Cymbal
+	{
+	    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;
+
+	    //cell[17].waveform = &wavtable[WAVPREC]; cell[17].wavemask = 0;
+	    //if (((adlibreg[21+0xe0]&7) == 0) || ((adlibreg[21+0xe0]&7) == 6))
+	    //   cell[17].waveform = &wavtable[(WAVPREC*7)>>2];
+	    //if (((adlibreg[21+0xe0]&7) == 2) || ((adlibreg[21+0xe0]&7) == 3))
+	    //   cell[17].waveform = &wavtable[(WAVPREC*5)>>2];
+	}
+	if ((v&1) > (odrumstat&1)) //Hihat
+	{
+	    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) // Modulator
+	    cellfreq(base2cell[i-0x40],i-0x40,&cell[base2cell[i-0x40]]);
+	else          // Carrier
+	    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]);
+    }
+
+    //outdata(i,v);
+}
+
+#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];
+	}
+    }
+
+
+    //CPU time used to be somewhat less when emulator was only mono!
+    //   Because of no delay fifos!
+
+    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)
+	{
+				//BassDrum (j=6)
+	    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;
+		    }
+		}
+	    }
+
+				//Snare/Hihat (j=7), Cymbal/TomTom (j=8)
+	    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)); //Snare
+		    (cell[7].cellfunc)((void *)&cell[7],k&(WAVPREC-1));       //Hihat
+		    (cell[17].cellfunc)((void *)&cell[17],k&((WAVPREC>>3)-1)); //Cymbal
+		    (cell[8].cellfunc)((void *)&cell[8],0.0);                 //TomTom
+		    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));
+    }
+}

+ 26 - 0
adplug/adlibemu.h

@@ -0,0 +1,26 @@
+/*
+ * ADLIBEMU.H
+ * Copyright (C) 1998-2001 Ken Silverman
+ * Ken Silverman's official web site: "http://www.advsys.net/ken"
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+void adlibinit(long dasamplerate,long danumspeakers,long dabytespersample);
+void adlib0(long i,long v);
+void adlibgetsample(void *sndptr,long numbytes);
+void adlibsetvolume(int i);
+void randoinsts();
+extern float lvol[9],rvol[9],lplc[9],rplc[9];

+ 90 - 0
adplug/demuopl.cpp

@@ -0,0 +1,90 @@
+//
+//  demuopl.cpp
+//  SDLPal
+//
+//  Created by palxex on 14-7-20.
+//  Copyright (c) 2014年 Wei Mingzhi. All rights reserved.
+//
+
+#include "demuopl.h"
+#include <math.h>
+#include <stdlib.h> // rand()
+#include <string.h>
+
+int CDemuopl::channels = 0;
+
+struct OPLHandler {
+    virtual void init(int rate) = 0;
+    virtual void getsample(short *buf,int samples) = 0;
+    virtual void write(int reg,int val) = 0;
+};
+
+namespace OPLCore {
+#include "dosbox_opl.cpp"
+	struct DOSBoxOPLHandler : OPLHandler {
+		void init(int rate) {
+			adlib_init(rate);
+		}
+		void getsample(short *buf, int samples) {
+			adlib_getsample(buf, samples);
+		}
+		void write(int reg, int val) {
+			adlib_write(reg, val);
+		}
+	};
+}
+
+namespace OPLCore2 {
+#include "dosbox_opl.cpp"
+	struct DOSBoxOPLHandler : OPLHandler {
+		void init(int rate) {
+			adlib_init(rate);
+		}
+		void getsample(short *buf, int samples) {
+			adlib_getsample(buf, samples);
+		}
+		void write(int reg, int val) {
+			adlib_write(reg, val);
+		}
+	};
+}
+
+CDemuopl::CDemuopl(int rate, bool bit16, bool usestereo)
+	:use16bit(bit16), stereo(usestereo)
+{
+	if (channels++ == 0)
+		pHandler = new OPLCore::DOSBoxOPLHandler;
+	else
+		pHandler = new OPLCore2::DOSBoxOPLHandler;
+	pHandler->init(rate);
+	currType = TYPE_OPL2;
+}
+
+void CDemuopl::update(short *buf, int samples)
+{
+	short *mixbuf1 = NULL;
+	short *outbuf;
+	if (use16bit) outbuf = buf;
+	else{
+		mixbuf1 = new short[samples * 2];
+		outbuf = mixbuf1;
+	}
+	pHandler->getsample(outbuf, samples);
+	if (stereo)
+		for (int i = samples - 1; i >= 0; i--) {
+			outbuf[i * 2] = outbuf[i];
+			outbuf[i * 2 + 1] = outbuf[i];
+		}
+	//now reduce to 8bit if we need to
+	if (!use16bit) {
+		for (int i = 0; i < (stereo ? samples * 2 : samples); i++)
+			((char *)buf)[i] = (outbuf[i] >> 8) ^ 0x80;
+		delete[] mixbuf1;
+	}
+}
+
+// template methods
+void CDemuopl::write(int reg, int val)
+{
+    pHandler->write(reg, val);
+};

+ 14 - 38
adplug/demuopl.h

@@ -28,48 +28,24 @@
 
 #include <assert.h>
 
+struct OPLHandler;
+
 class CDemuopl: public Copl
 {
 public:
-  CDemuopl(int rate, bool bit16, bool usestereo)
-	  :use16bit(bit16), stereo(usestereo)
-    {
-      adlib_init(rate);
-      currType = TYPE_OPL2;
-    };
-
-  void update(short *buf, int samples)
-    {
-      short *mixbuf0 = new short[samples*2],*mixbuf1 = new short[samples*2];
-      short *outbuf;
-      if(use16bit) outbuf = buf;
-      else outbuf = mixbuf1;
-      //if(use16bit) samples *= 2;
-      //if(stereo) samples *= 2;
-      adlib_getsample(outbuf, samples);
-      if(stereo)
-	for(int i=samples-1;i>=0;i--) {
-	  outbuf[i*2] = outbuf[i];
-	  outbuf[i*2+1] = outbuf[i];
-	}
-      //now reduce to 8bit if we need to
-      if(!use16bit)
-	for(int i=0;i<(stereo ? samples*2 : samples);i++)
-	  ((char *)buf)[i] = (outbuf[i] >> 8) ^ 0x80;
-      delete[] mixbuf0; delete[] mixbuf1;
-    }
-
-  // template methods
-  void write(int reg, int val)
-    {
-      if(currChip == 0)
-	adlib_write(reg, val);
-    };
-
-  void init() {};
-
+    CDemuopl(int rate, bool bit16, bool usestereo);
+    
+    void update(short *buf, int samples);
+    
+    // template methods
+    void write(int reg, int val);
+    
+    void init() {};
+    
 protected:
-  bool use16bit,stereo;
+    bool use16bit,stereo;
+    OPLHandler *pHandler;
+    static int channels;
 };
 
 #endif

File diff suppressed because it is too large
+ 1089 - 1097
adplug/dosbox_opl.cpp


+ 23 - 21
adplug/dosbox_opl.h

@@ -1,17 +1,17 @@
 /*
- *  Copyright (C) 2002-2011  The DOSBox Team
+ *  Copyright (C) 2002-2013  The DOSBox Team
  *  OPL2/OPL3 emulation library
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Lesser General Public
  *  License as published by the Free Software Foundation; either
  *  version 2.1 of the License, or (at your option) any later version.
- * 
+ *
  *  This library is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  *  Lesser General Public License for more details.
- * 
+ *
  *  You should have received a copy of the GNU Lesser General Public
  *  License along with this library; if not, write to the Free Software
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
@@ -28,10 +28,10 @@
 #define fltype double
 
 /*
-	define Bits, Bitu, Bit32s, Bit32u, Bit16s, Bit16u, Bit8s, Bit8u here
-*/
+ define Bits, Bitu, Bit32s, Bit32u, Bit16s, Bit16u, Bit8s, Bit8u here
+ */
 
-#ifdef _MSC_VER
+#if _MSC_VER <= 1600
 #include <windows.h>
 #define uintptr_t ULONG
 #define intptr_t LONG
@@ -55,11 +55,10 @@ typedef uint8_t		Bit8u;
 typedef int8_t		Bit8s;
 
 
-
 /*
-	define attribution that inlines/forces inlining of a function (optional)
-*/
-#define OPL_INLINE INLINE
+ define attribution that inlines/forces inlining of a function (optional)
+ */
+#define OPL_INLINE inline
 
 
 #undef NUM_CHANNELS
@@ -123,16 +122,16 @@ typedef int8_t		Bit8s;
 
 
 /* operator struct definition
-     For OPL2 all 9 channels consist of two operators each, carrier and modulator.
-     Channel x has operators x as modulator and operators (9+x) as carrier.
-     For OPL3 all 18 channels consist either of two operators (2op mode) or four
-     operators (4op mode) which is determined through register4 of the second
-     adlib register set.
-     Only the channels 0,1,2 (first set) and 9,10,11 (second set) can act as
-     4op channels. The two additional operators for a channel y come from the
-     2op channel y+3 so the operatorss y, (9+y), y+3, (9+y)+3 make up a 4op
-     channel.
-*/
+ For OPL2 all 9 channels consist of two operators each, carrier and modulator.
+ Channel x has operators x as modulator and operators (9+x) as carrier.
+ For OPL3 all 18 channels consist either of two operators (2op mode) or four
+ operators (4op mode) which is determined through register4 of the second
+ adlib register set.
+ Only the channels 0,1,2 (first set) and 9,10,11 (second set) can act as
+ 4op channels. The two additional operators for a channel y come from the
+ 2op channel y+3 so the operatorss y, (9+y), y+3, (9+y)+3 make up a 4op
+ channel.
+ */
 typedef struct operator_struct {
 	Bit32s cval, lastcval;			// current output/last output (used for feedback)
 	Bit32u tcount, wfpos, tinc;		// time (position in waveform) and time increment
@@ -157,13 +156,14 @@ typedef struct operator_struct {
 	Bits env_step_a,env_step_d,env_step_r;	// number of std samples of one step (for attack/decay/release mode)
 	Bit8u step_skip_pos_a;			// position of 8-cyclic step skipping (always 2^x to check against mask)
 	Bits env_step_skip_a;			// bitmask that determines if a step is skipped (respective bit is zero then)
-
+    
 #if defined(OPLTYPE_IS_OPL3)
 	bool is_4op,is_4op_attached;	// base of a 4op channel/part of a 4op channel
 	Bit32s left_pan,right_pan;		// opl3 stereo panning amount
 #endif
 } op_type;
 
+
 // enable an operator
 void enable_operator(Bitu regbase, op_type* op_pt);
 
@@ -186,3 +186,5 @@ void adlib_getsample(Bit16s* sndptr, Bits numsamples);
 
 Bitu adlib_reg_read(Bitu port);
 void adlib_write_index(Bitu port, Bit8u val);
+
+static Bit32u generator_add;	// should be a chip parameter

File diff suppressed because it is too large
+ 2261 - 1136
adplug/fmopl.c


+ 199 - 136
adplug/fmopl.h

@@ -1,20 +1,15 @@
 #ifndef __FMOPL_H_
 #define __FMOPL_H_
 
-#ifdef _MSC_VER
-#pragma warning (disable:4018)
-#pragma warning (disable:4244)
-#pragma warning (disable:4996)
-#endif
+#define HAS_YM3812	1
 
 /* --- select emulation chips --- */
 #define BUILD_YM3812 (HAS_YM3812)
-//#define BUILD_YM3526 (HAS_YM3526)
-//#define BUILD_Y8950  (HAS_Y8950)
+#define BUILD_YM3526 (HAS_YM3526)
+#define BUILD_Y8950  (HAS_Y8950)
 
-/* --- system optimize --- */
-/* select bit size of output : 8 or 16 */
-#define OPL_OUTPUT_BIT 16
+/* select output bits size of output : 8 or 16 */
+#define OPL_SAMPLE_BITS 16
 
 /* compiler dependence */
 #ifndef OSD_CPU_H
@@ -27,154 +22,222 @@ typedef signed short	INT16;   /* signed 16bit   */
 typedef signed int		INT32;   /* signed 32bit   */
 #endif
 
-#if (OPL_OUTPUT_BIT==16)
+#if (OPL_SAMPLE_BITS==16)
 typedef INT16 OPLSAMPLE;
 #endif
-#if (OPL_OUTPUT_BIT==8)
-typedef unsigned char  OPLSAMPLE;
+#if (OPL_SAMPLE_BITS==8)
+typedef INT8 OPLSAMPLE;
 #endif
 
 
-#if BUILD_Y8950
-#include "ymdeltat.h"
-#endif
-
 typedef void (*OPL_TIMERHANDLER)(int channel,double interval_Sec);
 typedef void (*OPL_IRQHANDLER)(int param,int irq);
 typedef void (*OPL_UPDATEHANDLER)(int param,int min_interval_us);
 typedef void (*OPL_PORTHANDLER_W)(int param,unsigned char data);
 typedef unsigned char (*OPL_PORTHANDLER_R)(int param);
 
-/* !!!!! here is private section , do not access there member direct !!!!! */
-
-#define OPL_TYPE_WAVESEL   0x01  /* waveform select    */
-#define OPL_TYPE_ADPCM     0x02  /* DELTA-T ADPCM unit */
-#define OPL_TYPE_KEYBOARD  0x04  /* keyboard interface */
-#define OPL_TYPE_IO        0x08  /* I/O port */
-
-/* Saving is necessary for member of the 'R' mark for suspend/resume */
-/* ---------- OPL one of slot  ---------- */
-typedef struct fm_opl_slot {
-   INT32 TL;		/* total level     :TL << 8            */
-   INT32 TLL;		/* adjusted now TL                     */
-   UINT8  KSR;		/* key scale rate  :(shift down bit)   */
-   INT32 *AR;		/* attack rate     :&AR_TABLE[AR<<2]   */
-   INT32 *DR;		/* decay rate      :&DR_TALBE[DR<<2]   */
-   INT32 SL;		/* sustin level    :SL_TALBE[SL]       */
-   INT32 *RR;		/* release rate    :&DR_TABLE[RR<<2]   */
-   UINT8 ksl;		/* keyscale level  :(shift down bits)  */
-   UINT8 ksr;		/* key scale rate  :kcode>>KSR         */
-   UINT32 mul;		/* multiple        :ML_TABLE[ML]       */
-   UINT32 Cnt;		/* frequency count :                   */
-   UINT32 Incr;	/* frequency step  :                   */
-   /* envelope generator state */
-   UINT8 eg_typ;	/* envelope type flag                  */
-   UINT8 evm;		/* envelope phase                      */
-   INT32 evc;		/* envelope counter                    */
-   INT32 eve;		/* envelope counter end point          */
-   INT32 evs;		/* envelope counter step               */
-   INT32 evsa;	/* envelope step for AR :AR[ksr]           */
-   INT32 evsd;	/* envelope step for DR :DR[ksr]           */
-   INT32 evsr;	/* envelope step for RR :RR[ksr]           */
-   /* LFO */
-   UINT8 ams;		/* ams flag                            */
-   UINT8 vib;		/* vibrate flag                        */
-   /* wave selector */
-   INT32 **wavetable;
-}OPL_SLOT;
-
-/* ---------- OPL one of channel  ---------- */
-typedef struct fm_opl_channel {
-   OPL_SLOT SLOT[2];
-   UINT8 CON;			/* connection type                     */
-   UINT8 FB;			/* feed back       :(shift down bit)   */
-   INT32 *connect1;	/* slot1 output pointer                */
-   INT32 *connect2;	/* slot2 output pointer                */
-   INT32 op1_out[2];	/* slot1 output for selfeedback        */
-   /* phase generator state */
-   UINT32  block_fnum;	/* block+fnum      :                   */
-   UINT8 kcode;		/* key code        : KeyScaleCode      */
-   UINT32  fc;			/* Freq. Increment base                */
-   UINT32  ksl_base;	/* KeyScaleLevel Base step             */
-   UINT8 keyon;		/* key on/off flag                     */
+
+
+#define OPL_TYPE_WAVESEL   0x01  /* waveform select		*/
+#define OPL_TYPE_ADPCM     0x02  /* DELTA-T ADPCM unit	*/
+#define OPL_TYPE_KEYBOARD  0x04  /* keyboard interface	*/
+#define OPL_TYPE_IO        0x08  /* I/O port			*/
+
+/* ---------- Generic interface section ---------- */
+#define OPL_TYPE_YM3526 (0)
+#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL)
+#define OPL_TYPE_Y8950  (OPL_TYPE_ADPCM|OPL_TYPE_KEYBOARD|OPL_TYPE_IO)
+
+
+typedef struct{
+	UINT32	ar;			/* attack rate: AR<<2			*/
+	UINT32	dr;			/* decay rate:  DR<<2			*/
+	UINT32	rr;			/* release rate:RR<<2			*/
+	UINT8	KSR;		/* key scale rate				*/
+	UINT8	ksl;		/* keyscale level				*/
+	UINT8	ksr;		/* key scale rate: kcode>>KSR	*/
+	UINT8	mul;		/* multiple: mul_tab[ML]		*/
+
+	/* Phase Generator */
+	UINT32	Cnt;		/* frequency counter			*/
+	UINT32	Incr;		/* frequency counter step		*/
+	UINT8   FB;			/* feedback shift value			*/
+	INT32   *connect1;	/* slot1 output pointer			*/
+	INT32   op1_out[2];	/* slot1 output for feedback	*/
+	UINT8   CON;		/* connection (algorithm) type	*/
+
+	/* Envelope Generator */
+	UINT8	eg_type;	/* percussive/non-percussive mode */
+	UINT8	state;		/* phase type					*/
+	UINT32	TL;			/* total level: TL << 2			*/
+	INT32	TLL;		/* adjusted now TL				*/
+	INT32	volume;		/* envelope counter				*/
+	UINT32	sl;			/* sustain level: sl_tab[SL]	*/
+	UINT8	eg_sh_ar;	/* (attack state)				*/
+	UINT8	eg_sel_ar;	/* (attack state)				*/
+	UINT8	eg_sh_dr;	/* (decay state)				*/
+	UINT8	eg_sel_dr;	/* (decay state)				*/
+	UINT8	eg_sh_rr;	/* (release state)				*/
+	UINT8	eg_sel_rr;	/* (release state)				*/
+	UINT32	key;		/* 0 = KEY OFF, >0 = KEY ON		*/
+
+	/* LFO */
+	UINT32	AMmask;		/* LFO Amplitude Modulation enable mask */
+	UINT8	vib;		/* LFO Phase Modulation enable flag (active high)*/
+
+	/* waveform select */
+	unsigned int wavetable;
+} OPL_SLOT;
+
+typedef struct{
+	OPL_SLOT SLOT[2];
+	/* phase generator state */
+	UINT32  block_fnum;	/* block+fnum					*/
+	UINT32  fc;			/* Freq. Increment base			*/
+	UINT32  ksl_base;	/* KeyScaleLevel Base step		*/
+	UINT8   kcode;		/* key code (for key scaling)	*/
 } OPL_CH;
 
 /* OPL state */
 typedef struct fm_opl_f {
-   UINT8 type;			/* chip type                         */
-   int clock;			/* master clock  (Hz)                */
-   int rate;			/* sampling rate (Hz)                */
-   double freqbase;	/* frequency base                    */
-   double TimerBase;	/* Timer base time (==sampling time) */
-   UINT8 address;		/* address register                  */
-   UINT8 status;		/* status flag                       */
-   UINT8 statusmask;	/* status mask                       */
-   UINT32 mode;		/* Reg.08 : CSM , notesel,etc.       */
-   /* Timer */
-   int T[2];			/* timer counter                     */
-   UINT8 st[2];		/* timer enable                      */
-   /* FM channel slots */
-   OPL_CH *P_CH;		/* pointer of CH                     */
-   int	max_ch;			/* maximum channel                   */
-   /* Rythm sention */
-   UINT8 rythm;		/* Rythm mode , key flag */
+	/* FM channel slots */
+	OPL_CH	P_CH[9];				/* OPL/OPL2 chips have 9 channels*/
+
+	UINT32	eg_cnt;					/* global envelope generator counter	*/
+	UINT32	eg_timer;				/* global envelope generator counter works at frequency = chipclock/72 */
+	UINT32	eg_timer_add;			/* step of eg_timer						*/
+	UINT32	eg_timer_overflow;		/* envelope generator timer overlfows every 1 sample (on real chip) */
+
+	UINT8	rhythm;					/* Rhythm mode					*/
+
+	UINT32	fn_tab[1024];			/* fnumber->increment counter	*/
+
+	/* LFO */
+	UINT8	lfo_am_depth;
+	UINT8	lfo_pm_depth_range;
+	UINT32	lfo_am_cnt;
+	UINT32	lfo_am_inc;
+	UINT32	lfo_pm_cnt;
+	UINT32	lfo_pm_inc;
+
+	UINT32	noise_rng;				/* 23 bit noise shift register	*/
+	UINT32	noise_p;				/* current noise 'phase'		*/
+	UINT32	noise_f;				/* current noise period			*/
+
+	UINT8	wavesel;				/* waveform select enable flag	*/
+
+	int		T[2];					/* timer counters				*/
+	UINT8	st[2];					/* timer enable					*/
+
 #if BUILD_Y8950
-   /* Delta-T ADPCM unit (Y8950) */
-   YM_DELTAT *deltat;			/* DELTA-T ADPCM       */
+	/* Delta-T ADPCM unit (Y8950) */
+
+	YM_DELTAT *deltat;
+
+	/* Keyboard and I/O ports interface */
+	UINT8	portDirection;
+	UINT8	portLatch;
+	OPL_PORTHANDLER_R porthandler_r;
+	OPL_PORTHANDLER_W porthandler_w;
+	int		port_param;
+	OPL_PORTHANDLER_R keyboardhandler_r;
+	OPL_PORTHANDLER_W keyboardhandler_w;
+	int		keyboard_param;
 #endif
-   /* Keyboard / I/O interface unit (Y8950) */
-   UINT8 portDirection;
-   UINT8 portLatch;
-   OPL_PORTHANDLER_R porthandler_r;
-   OPL_PORTHANDLER_W porthandler_w;
-   int port_param;
-   OPL_PORTHANDLER_R keyboardhandler_r;
-   OPL_PORTHANDLER_W keyboardhandler_w;
-   int keyboard_param;
-   /* time tables */
-   INT32 AR_TABLE[75];	/* atttack rate tables */
-   INT32 DR_TABLE[75];	/* decay rate tables   */
-   UINT32 FN_TABLE[1024];  /* fnumber -> increment counter */
-   /* LFO */
-   INT32 *ams_table;
-   INT32 *vib_table;
-   INT32 amsCnt;
-   INT32 amsIncr;
-   INT32 vibCnt;
-   INT32 vibIncr;
-   /* wave selector enable flag */
-   UINT8 wavesel;
-   /* external event callback handler */
-   OPL_TIMERHANDLER  TimerHandler;		/* TIMER handler   */
-   int TimerParam;						/* TIMER parameter */
-   OPL_IRQHANDLER    IRQHandler;		/* IRQ handler    */
-   int IRQParam;						/* IRQ parameter  */
-   OPL_UPDATEHANDLER UpdateHandler;	/* stream update handler   */
-   int UpdateParam;					/* stream update parameter */
+
+	/* external event callback handlers */
+	OPL_TIMERHANDLER  TimerHandler;	/* TIMER handler				*/
+	int TimerParam;					/* TIMER parameter				*/
+	OPL_IRQHANDLER    IRQHandler;	/* IRQ handler					*/
+	int IRQParam;					/* IRQ parameter				*/
+	OPL_UPDATEHANDLER UpdateHandler;/* stream update handler		*/
+	int UpdateParam;				/* stream update parameter		*/
+
+	UINT8 type;						/* chip type					*/
+	UINT8 address;					/* address register				*/
+	UINT8 status;					/* status flag					*/
+	UINT8 statusmask;				/* status mask					*/
+	UINT8 mode;						/* Reg.08 : CSM,notesel,etc.	*/
+
+	int clock;						/* master clock  (Hz)			*/
+	int rate;						/* sampling rate (Hz)			*/
+	double freqbase;				/* frequency base				*/
+	double TimerBase;				/* Timer base time (==sampling time)*/
 } FM_OPL;
+extern FM_OPL *OPLCreate(int type, int clock, int rate);
+extern void OPLDestroy(FM_OPL *OPL);
+extern int OPLWrite(FM_OPL *OPL,int a,int v);
+extern void OPLResetChip(FM_OPL *OPL);
 
-/* ---------- Generic interface section ---------- */
-#define OPL_TYPE_YM3526 (0)
-#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL)
-#define OPL_TYPE_Y8950  (OPL_TYPE_ADPCM|OPL_TYPE_KEYBOARD|OPL_TYPE_IO)
+#if BUILD_YM3812
 
-FM_OPL *OPLCreate(int type, int clock, int rate);
-void OPLDestroy(FM_OPL *OPL);
-void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset);
-void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param);
-void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param);
-/* Y8950 port handlers */
-void OPLSetPortHandler(FM_OPL *OPL,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param);
-void OPLSetKeyboardHandler(FM_OPL *OPL,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param);
+int  YM3812Init(int num, int clock, int rate);
+void YM3812Shutdown(void);
+void YM3812ResetChip(int which);
+int  YM3812Write(int which, int a, int v);
+unsigned char YM3812Read(int which, int a);
+int  YM3812TimerOver(int which, int c);
+void YM3812UpdateOne(FM_OPL		*OPL, INT16 *buffer, int length);
 
-void OPLResetChip(FM_OPL *OPL);
-int OPLWrite(FM_OPL *OPL,int a,int v);
-unsigned char OPLRead(FM_OPL *OPL,int a);
-int OPLTimerOver(FM_OPL *OPL,int c);
+void YM3812SetTimerHandler(int which, OPL_TIMERHANDLER TimerHandler, int channelOffset);
+void YM3812SetIRQHandler(int which, OPL_IRQHANDLER IRQHandler, int param);
+void YM3812SetUpdateHandler(int which, OPL_UPDATEHANDLER UpdateHandler, int param);
+
+#endif
 
-/* YM3626/YM3812 local section */
-void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length);
 
-void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length);
+#if BUILD_YM3526
+
+/*
+** Initialize YM3526 emulator(s).
+**
+** 'num' is the number of virtual YM3526's to allocate
+** 'clock' is the chip clock in Hz
+** 'rate' is sampling rate
+*/
+int  YM3526Init(int num, int clock, int rate);
+/* shutdown the YM3526 emulators*/
+void YM3526Shutdown(void);
+void YM3526ResetChip(int which);
+int  YM3526Write(int which, int a, int v);
+unsigned char YM3526Read(int which, int a);
+int  YM3526TimerOver(int which, int c);
+/*
+** Generate samples for one of the YM3526's
+**
+** 'which' is the virtual YM3526 number
+** '*buffer' is the output buffer pointer
+** 'length' is the number of samples that should be generated
+*/
+void YM3526UpdateOne(int which, INT16 *buffer, int length);
+
+void YM3526SetTimerHandler(int which, OPL_TIMERHANDLER TimerHandler, int channelOffset);
+void YM3526SetIRQHandler(int which, OPL_IRQHANDLER IRQHandler, int param);
+void YM3526SetUpdateHandler(int which, OPL_UPDATEHANDLER UpdateHandler, int param);
 
 #endif
+
+
+#if BUILD_Y8950
+
+/* Y8950 port handlers */
+void Y8950SetPortHandler(int which, OPL_PORTHANDLER_W PortHandler_w, OPL_PORTHANDLER_R PortHandler_r, int param);
+void Y8950SetKeyboardHandler(int which, OPL_PORTHANDLER_W KeyboardHandler_w, OPL_PORTHANDLER_R KeyboardHandler_r, int param);
+void Y8950SetDeltaTMemory(int which, void * deltat_mem_ptr, int deltat_mem_size );
+
+int  Y8950Init (int num, int clock, int rate);
+void Y8950Shutdown (void);
+void Y8950ResetChip (int which);
+int  Y8950Write (int which, int a, int v);
+unsigned char Y8950Read (int which, int a);
+int  Y8950TimerOver (int which, int c);
+void Y8950UpdateOne (int which, INT16 *buffer, int length);
+
+void Y8950SetTimerHandler (int which, OPL_TIMERHANDLER TimerHandler, int channelOffset);
+void Y8950SetIRQHandler (int which, OPL_IRQHANDLER IRQHandler, int param);
+void Y8950SetUpdateHandler (int which, OPL_UPDATEHANDLER UpdateHandler, int param);
+
+#endif
+
+
+#endif /* __FMOPL_H_ */

+ 61 - 0
adplug/kemuopl.h

@@ -0,0 +1,61 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2005 Simon Peter, <dn.tlp@gmx.net>, et al.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * kemuopl.h - Emulated OPL using Ken Silverman's emulator, by Simon Peter
+ *             <dn.tlp@gmx.net>
+ */
+
+#ifndef H_ADPLUG_KEMUOPL
+#define H_ADPLUG_KEMUOPL
+
+#include "opl.h"
+extern "C" {
+#include "adlibemu.h"
+}
+
+class CKemuopl: public Copl
+{
+public:
+  CKemuopl(int rate, bool bit16, bool usestereo)
+    : use16bit(bit16), stereo(usestereo)
+    {
+      adlibinit(rate, usestereo ? 2 : 1, bit16 ? 2 : 1);
+      currType = TYPE_OPL2;
+    };
+
+  void update(short *buf, int samples)
+    {
+      if(use16bit) samples *= 2;
+      if(stereo) samples *= 2;
+      adlibgetsample(buf, samples);
+    }
+
+  // template methods
+  void write(int reg, int val)
+    {
+      if(currChip == 0)
+	adlib0(reg, val);
+    };
+
+  void init() {};
+
+private:
+  bool	use16bit,stereo;
+};
+
+#endif

+ 10 - 4
global.c

@@ -61,7 +61,8 @@ PAL_InitGlobals(
    FILE     *fp;
    CODEPAGE  iCodePage = CP_BIG5;	// Default for PAL DOS/WIN95
    DWORD     dwWordLength = 10;		// Default for PAL DOS/WIN95
-   DWORD     dwExtraDescLines = 0;	// Default for PAL DOS/WIN95
+   DWORD     dwExtraMagicDescLines = 0;	// Default for PAL DOS/WIN95
+   DWORD     dwExtraItemDescLines = 0;	// Default for PAL DOS/WIN95
 #endif
 
    if (gpGlobals == NULL)
@@ -114,9 +115,13 @@ PAL_InitGlobals(
 				   {
 					   sscanf(ptr, "%u", &dwWordLength);
 				   }
-				   else if (SDL_strcasecmp(p, "EXTRADESCLINES") == 0)
+				   else if (SDL_strcasecmp(p, "EXTRAMAGICDESCLINES") == 0)
 				   {
-					   sscanf(ptr, "%u", &dwExtraDescLines);
+					   sscanf(ptr, "%u", &dwExtraMagicDescLines);
+				   }
+				   else if (SDL_strcasecmp(p, "EXTRAITEMDESCLINES") == 0)
+				   {
+					   sscanf(ptr, "%u", &dwExtraItemDescLines);
 				   }
 			   }
 		   }
@@ -144,7 +149,8 @@ PAL_InitGlobals(
 #ifdef PAL_UNICODE
    gpGlobals->iCodePage = iCodePage;
    gpGlobals->dwWordLength = dwWordLength;
-   gpGlobals->dwExtraDescLines = dwExtraDescLines;
+   gpGlobals->dwExtraMagicDescLines = dwExtraMagicDescLines;
+   gpGlobals->dwExtraItemDescLines = dwExtraItemDescLines;
 #endif
 
    return 0;

+ 2 - 1
global.h

@@ -568,7 +568,8 @@ typedef struct tagGLOBALVARS
 #ifdef PAL_UNICODE
    CODEPAGE         iCodePage;
    DWORD            dwWordLength;
-   DWORD            dwExtraDescLines;
+   DWORD            dwExtraMagicDescLines;
+   DWORD            dwExtraItemDescLines;
 #endif
 } GLOBALVARS, *LPGLOBALVARS;
 

+ 6 - 4
itemmenu.c

@@ -59,11 +59,12 @@ PAL_ItemSelectMenuUpdate(
 #ifdef PAL_UNICODE
    const int          iItemsPerLine = 34 / gpGlobals->dwWordLength;
    const int          iItemTextWidth = 8 * gpGlobals->dwWordLength + 20;
-   const int          iLinesPerPage = 7 - gpGlobals->dwExtraDescLines;
-   const int          iBoxHeightOffset = gpGlobals->dwExtraDescLines * 16;
+   const int          iLinesPerPage = 7 - gpGlobals->dwExtraItemDescLines;
+   const int          iBoxHeightOffset = gpGlobals->dwExtraItemDescLines * 16;
    const int          iCursorXOffset = gpGlobals->dwWordLength * 5 / 2;
    const int          iAmountXOffset = gpGlobals->dwWordLength * 8 + 1;
    const int          iPageLineOffset = (iLinesPerPage + 1) / 2;
+   const int          iPictureYOffset = (gpGlobals->dwExtraItemDescLines > 1) ? (gpGlobals->dwExtraItemDescLines - 1) * 16 : 0;
 #else
    const int          iItemsPerLine = 3;
    const int          iItemTextWidth = 100;
@@ -72,6 +73,7 @@ PAL_ItemSelectMenuUpdate(
    const int          iCursorXOffset = 25;
    const int          iAmountXOffset = 81;
    const int          iPageLineOffset = 4;
+   const int          iPictureYOffset = 0;
 #endif
 
    //
@@ -218,7 +220,7 @@ PAL_ItemSelectMenuUpdate(
    // Draw the picture of current selected item
    //
    PAL_RLEBlitToSurface(PAL_SpriteGetFrame(gpSpriteUI, SPRITENUM_ITEMBOX), gpScreen,
-      PAL_XY(5, 140));
+      PAL_XY(5, 140 - iPictureYOffset));
 
    wObject = gpGlobals->rgInventory[gpGlobals->iCurInvMenuItem].wItem;
 
@@ -237,7 +239,7 @@ PAL_ItemSelectMenuUpdate(
 
    if (wPrevImageIndex != 0xFFFF)
    {
-      PAL_RLEBlitToSurface(bufImage, gpScreen, PAL_XY(12, 148));
+      PAL_RLEBlitToSurface(bufImage, gpScreen, PAL_XY(12, 148 - iPictureYOffset));
    }
 
    //

+ 2 - 2
magicmenu.c

@@ -64,8 +64,8 @@ PAL_MagicSelectionMenuUpdate(
 #ifdef PAL_UNICODE
    const int   iItemsPerLine = 32 / gpGlobals->dwWordLength;
    const int   iItemTextWidth = 8 * gpGlobals->dwWordLength + 7;
-   const int   iLinesPerPage = 5 - gpGlobals->dwExtraDescLines;
-   const int   iBoxYOffset = gpGlobals->dwExtraDescLines * 16;
+   const int   iLinesPerPage = 5 - gpGlobals->dwExtraMagicDescLines;
+   const int   iBoxYOffset = gpGlobals->dwExtraMagicDescLines * 16;
    const int   iCursorXOffset = gpGlobals->dwWordLength * 5 / 2;
    const int   iPageLineOffset = iLinesPerPage / 2;
 #else

+ 943 - 0
resampler.c

@@ -0,0 +1,943 @@
+#include <stdlib.h>
+#include <string.h>
+#define _USE_MATH_DEFINES
+#include <math.h>
+#if (defined(_M_IX86) || defined(__i386__) || defined(_M_X64) || defined(__amd64__))
+#include <xmmintrin.h>
+#define RESAMPLER_SSE
+#endif
+
+#ifdef _MSC_VER
+#define ALIGNED     _declspec(align(16))
+#else
+#define ALIGNED     __attribute__((aligned(16)))
+#endif
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+#include "resampler.h"
+
+enum { RESAMPLER_SHIFT = 10 };
+enum { RESAMPLER_RESOLUTION = 1 << RESAMPLER_SHIFT };
+enum { SINC_WIDTH = 16 };
+enum { SINC_SAMPLES = RESAMPLER_RESOLUTION * SINC_WIDTH };
+enum { CUBIC_SAMPLES = RESAMPLER_RESOLUTION * 4 };
+
+ALIGNED static float cubic_lut[CUBIC_SAMPLES];
+
+static float sinc_lut[SINC_SAMPLES + 1];
+static float window_lut[SINC_SAMPLES + 1];
+
+enum { resampler_buffer_size = SINC_WIDTH * 4 };
+
+static int fEqual(const float b, const float a)
+{
+    return fabs(a - b) < 1.0e-6;
+}
+
+static float sinc(float x)
+{
+    return fEqual(x, 0.0f) ? 1.0f : (float)(sin(x * M_PI) / (x * M_PI));
+}
+
+#ifdef RESAMPLER_SSE
+#ifdef _MSC_VER
+#include <intrin.h>
+#elif defined(__clang__) || defined(__GNUC__)
+static inline void
+__cpuid(int *data, int selector)
+{
+#if defined(__PIC__) && defined(__i386__)
+    asm("xchgl %%ebx, %%esi; cpuid; xchgl %%ebx, %%esi"
+        : "=a" (data[0]),
+        "=S" (data[1]),
+        "=c" (data[2]),
+        "=d" (data[3])
+        : "0" (selector));
+#elif defined(__PIC__) && defined(__amd64__)
+    asm("xchg{q} {%%}rbx, %q1; cpuid; xchg{q} {%%}rbx, %q1"
+        : "=a" (data[0]),
+        "=&r" (data[1]),
+        "=c" (data[2]),
+        "=d" (data[3])
+        : "0" (selector));
+#else
+    asm("cpuid"
+        : "=a" (data[0]),
+        "=b" (data[1]),
+        "=c" (data[2]),
+        "=d" (data[3])
+        : "0" (selector));
+#endif
+}
+#else
+#define __cpuid(a,b) memset((a), 0, sizeof(int) * 4)
+#endif
+
+static int query_cpu_feature_sse() {
+    int buffer[4];
+    __cpuid(buffer,1);
+    if ((buffer[3]&(1<<25)) == 0) return 0;
+    return 1;
+}
+
+static int resampler_has_sse = 0;
+#endif
+
+void resampler_init(void)
+{
+    unsigned i;
+    double dx = (float)(SINC_WIDTH) / SINC_SAMPLES, x = 0.0;
+    for (i = 0; i < SINC_SAMPLES + 1; ++i, x += dx)
+    {
+        float y = x / SINC_WIDTH;
+#if 0
+        // Blackman
+        float window = 0.42659 - 0.49656 * cos(M_PI + M_PI * y) + 0.076849 * cos(2.0 * M_PI * y);
+#elif 1
+        // Nuttal 3 term
+        float window = 0.40897 + 0.5 * cos(M_PI * y) + 0.09103 * cos(2.0 * M_PI * y);
+#elif 0
+        // C.R.Helmrich's 2 term window
+        float window = 0.79445 * cos(0.5 * M_PI * y) + 0.20555 * cos(1.5 * M_PI * y);
+#elif 0
+        // Lanczos
+        float window = sinc(y);
+#endif
+        sinc_lut[i] = fabs(x) < SINC_WIDTH ? sinc(x) : 0.0;
+        window_lut[i] = window;
+    }
+    dx = 1.0 / (float)(RESAMPLER_RESOLUTION);
+    x = 0.0;
+    for (i = 0; i < RESAMPLER_RESOLUTION; ++i, x += dx)
+    {
+        cubic_lut[i*4]   = (float)(-0.5 * x * x * x +       x * x - 0.5 * x);
+        cubic_lut[i*4+1] = (float)( 1.5 * x * x * x - 2.5 * x * x           + 1.0);
+        cubic_lut[i*4+2] = (float)(-1.5 * x * x * x + 2.0 * x * x + 0.5 * x);
+        cubic_lut[i*4+3] = (float)( 0.5 * x * x * x - 0.5 * x * x);
+    }
+#ifdef RESAMPLER_SSE
+    resampler_has_sse = query_cpu_feature_sse();
+#endif
+}
+
+typedef struct resampler
+{
+    int write_pos, write_filled;
+    int read_pos, read_filled;
+    unsigned int phase;
+    unsigned int phase_inc;
+    unsigned int inv_phase;
+    unsigned int inv_phase_inc;
+    unsigned char quality;
+    signed char delay_added;
+    signed char delay_removed;
+    float last_amp;
+    float accumulator;
+    float buffer_in[resampler_buffer_size * 2];
+    float buffer_out[resampler_buffer_size + SINC_WIDTH * 2 - 1];
+} resampler;
+
+void * resampler_create(void)
+{
+    resampler * r = ( resampler * ) malloc( sizeof(resampler) );
+    if ( !r ) return 0;
+
+    r->write_pos = SINC_WIDTH - 1;
+    r->write_filled = 0;
+    r->read_pos = 0;
+    r->read_filled = 0;
+    r->phase = 0;
+    r->phase_inc = 0;
+    r->inv_phase = 0;
+    r->inv_phase_inc = 0;
+    r->quality = RESAMPLER_QUALITY_MAX;
+    r->delay_added = -1;
+    r->delay_removed = -1;
+    r->last_amp = 0;
+    r->accumulator = 0;
+    memset( r->buffer_in, 0, sizeof(r->buffer_in) );
+    memset( r->buffer_out, 0, sizeof(r->buffer_out) );
+
+    return r;
+}
+
+void resampler_delete(void * _r)
+{
+    free( _r );
+}
+
+void * resampler_dup(const void * _r)
+{
+    const resampler * r_in = ( const resampler * ) _r;
+    resampler * r_out = ( resampler * ) malloc( sizeof(resampler) );
+    if ( !r_out ) return 0;
+
+    r_out->write_pos = r_in->write_pos;
+    r_out->write_filled = r_in->write_filled;
+    r_out->read_pos = r_in->read_pos;
+    r_out->read_filled = r_in->read_filled;
+    r_out->phase = r_in->phase;
+    r_out->phase_inc = r_in->phase_inc;
+    r_out->inv_phase = r_in->inv_phase;
+    r_out->inv_phase_inc = r_in->inv_phase_inc;
+    r_out->quality = r_in->quality;
+    r_out->delay_added = r_in->delay_added;
+    r_out->delay_removed = r_in->delay_removed;
+    r_out->last_amp = r_in->last_amp;
+    r_out->accumulator = r_in->accumulator;
+    memcpy( r_out->buffer_in, r_in->buffer_in, sizeof(r_in->buffer_in) );
+    memcpy( r_out->buffer_out, r_in->buffer_out, sizeof(r_in->buffer_out) );
+
+    return r_out;
+}
+
+void resampler_dup_inplace(void *_d, const void *_s)
+{
+    const resampler * r_in = ( const resampler * ) _s;
+    resampler * r_out = ( resampler * ) _d;
+
+    r_out->write_pos = r_in->write_pos;
+    r_out->write_filled = r_in->write_filled;
+    r_out->read_pos = r_in->read_pos;
+    r_out->read_filled = r_in->read_filled;
+    r_out->phase = r_in->phase;
+    r_out->phase_inc = r_in->phase_inc;
+    r_out->inv_phase = r_in->inv_phase;
+    r_out->inv_phase_inc = r_in->inv_phase_inc;
+    r_out->quality = r_in->quality;
+    r_out->delay_added = r_in->delay_added;
+    r_out->delay_removed = r_in->delay_removed;
+    r_out->last_amp = r_in->last_amp;
+    r_out->accumulator = r_in->accumulator;
+    memcpy( r_out->buffer_in, r_in->buffer_in, sizeof(r_in->buffer_in) );
+    memcpy( r_out->buffer_out, r_in->buffer_out, sizeof(r_in->buffer_out) );
+}
+
+void resampler_set_quality(void *_r, int quality)
+{
+    resampler * r = ( resampler * ) _r;
+    if (quality < RESAMPLER_QUALITY_MIN)
+        quality = RESAMPLER_QUALITY_MIN;
+    else if (quality > RESAMPLER_QUALITY_MAX)
+        quality = RESAMPLER_QUALITY_MAX;
+    if ( r->quality != quality )
+    {
+        if ( quality == RESAMPLER_QUALITY_BLEP || r->quality == RESAMPLER_QUALITY_BLEP )
+        {
+            r->read_pos = 0;
+            r->read_filled = 0;
+            r->last_amp = 0;
+            r->accumulator = 0;
+            memset( r->buffer_out, 0, sizeof(r->buffer_out) );
+        }
+        r->delay_added = -1;
+        r->delay_removed = -1;
+    }
+    r->quality = (unsigned char)quality;
+}
+
+int resampler_get_free_count(void *_r)
+{
+    resampler * r = ( resampler * ) _r;
+    return resampler_buffer_size - r->write_filled;
+}
+
+static int resampler_min_filled(resampler *r)
+{
+    switch (r->quality)
+    {
+    default:
+    case RESAMPLER_QUALITY_ZOH:
+    case RESAMPLER_QUALITY_BLEP:
+        return 1;
+            
+    case RESAMPLER_QUALITY_LINEAR:
+        return 2;
+            
+    case RESAMPLER_QUALITY_CUBIC:
+        return 4;
+            
+    case RESAMPLER_QUALITY_SINC:
+        return SINC_WIDTH * 2;
+    }
+}
+
+static int resampler_input_delay(resampler *r)
+{
+    switch (r->quality)
+    {
+    default:
+    case RESAMPLER_QUALITY_ZOH:
+    case RESAMPLER_QUALITY_BLEP:
+    case RESAMPLER_QUALITY_LINEAR:
+        return 0;
+            
+    case RESAMPLER_QUALITY_CUBIC:
+        return 1;
+            
+    case RESAMPLER_QUALITY_SINC:
+        return SINC_WIDTH - 1;
+    }
+}
+
+static int resampler_output_delay(resampler *r)
+{
+    switch (r->quality)
+    {
+    default:
+    case RESAMPLER_QUALITY_ZOH:
+    case RESAMPLER_QUALITY_LINEAR:
+    case RESAMPLER_QUALITY_CUBIC:
+    case RESAMPLER_QUALITY_SINC:
+        return 0;
+            
+    case RESAMPLER_QUALITY_BLEP:
+        return SINC_WIDTH - 1;
+    }
+}
+
+int resampler_ready(void *_r)
+{
+    resampler * r = ( resampler * ) _r;
+    return r->write_filled > resampler_min_filled(r);
+}
+
+void resampler_clear(void *_r)
+{
+    resampler * r = ( resampler * ) _r;
+    r->write_pos = SINC_WIDTH - 1;
+    r->write_filled = 0;
+    r->read_pos = 0;
+    r->read_filled = 0;
+    r->phase = 0;
+    r->delay_added = -1;
+    r->delay_removed = -1;
+    memset(r->buffer_in, 0, (SINC_WIDTH - 1) * sizeof(r->buffer_in[0]));
+    memset(r->buffer_in + resampler_buffer_size, 0, (SINC_WIDTH - 1) * sizeof(r->buffer_in[0]));
+    if (r->quality == RESAMPLER_QUALITY_BLEP)
+        memset(r->buffer_out, 0, sizeof(r->buffer_out));
+}
+
+void resampler_set_rate(void *_r, double new_factor)
+{
+    resampler * r = ( resampler * ) _r;
+    r->phase_inc = (int)( new_factor * RESAMPLER_RESOLUTION );
+    new_factor = 1.0 / new_factor;
+    r->inv_phase_inc = (int)( new_factor * RESAMPLER_RESOLUTION );
+}
+
+void resampler_write_sample(void *_r, short s)
+{
+    resampler * r = ( resampler * ) _r;
+
+    if ( r->delay_added < 0 )
+    {
+        r->delay_added = 0;
+        r->write_filled = resampler_input_delay( r );
+    }
+    
+    if ( r->write_filled < resampler_buffer_size )
+    {
+        float s32 = s;
+        s32 *= 256.0;
+
+        r->buffer_in[ r->write_pos ] = s32;
+        r->buffer_in[ r->write_pos + resampler_buffer_size ] = s32;
+
+        ++r->write_filled;
+
+        r->write_pos = ( r->write_pos + 1 ) % resampler_buffer_size;
+    }
+}
+
+void resampler_write_sample_fixed(void *_r, int s, unsigned char depth)
+{
+    resampler * r = ( resampler * ) _r;
+    
+    if ( r->delay_added < 0 )
+    {
+        r->delay_added = 0;
+        r->write_filled = resampler_input_delay( r );
+    }
+    
+    if ( r->write_filled < resampler_buffer_size )
+    {
+        float s32 = s;
+        s32 /= (double)(1 << (depth - 1));
+        
+        r->buffer_in[ r->write_pos ] = s32;
+        r->buffer_in[ r->write_pos + resampler_buffer_size ] = s32;
+        
+        ++r->write_filled;
+        
+        r->write_pos = ( r->write_pos + 1 ) % resampler_buffer_size;
+    }
+}
+
+static int resampler_run_zoh(resampler * r, float ** out_, float * out_end)
+{
+    int in_size = r->write_filled;
+    float const* in_ = r->buffer_in + resampler_buffer_size + r->write_pos - r->write_filled;
+    int used = 0;
+    in_size -= 1;
+    if ( in_size > 0 )
+    {
+        float* out = *out_;
+        float const* in = in_;
+        float const* const in_end = in + in_size;
+        int phase = r->phase;
+        int phase_inc = r->phase_inc;
+        
+        do
+        {
+            float sample;
+            
+            if ( out >= out_end )
+                break;
+
+            sample = *in;
+            *out++ = sample;
+            
+            phase += phase_inc;
+            
+            in += phase >> RESAMPLER_SHIFT;
+            
+            phase &= RESAMPLER_RESOLUTION-1;
+        }
+        while ( in < in_end );
+        
+        r->phase = (unsigned short) phase;
+        *out_ = out;
+        
+        used = (int)(in - in_);
+        
+        r->write_filled -= used;
+    }
+    
+    return used;
+}
+
+static int resampler_run_blep(resampler * r, float ** out_, float * out_end)
+{
+    int in_size = r->write_filled;
+    float const* in_ = r->buffer_in + resampler_buffer_size + r->write_pos - r->write_filled;
+    int used = 0;
+    in_size -= 1;
+    if ( in_size > 0 )
+    {
+        float* out = *out_;
+        float const* in = in_;
+        float const* const in_end = in + in_size;
+        float last_amp = r->last_amp;
+        int inv_phase = r->inv_phase;
+        int inv_phase_inc = r->inv_phase_inc;
+        
+        const int step = RESAMPLER_RESOLUTION;
+        
+        do
+        {
+            float kernel[SINC_WIDTH * 2], kernel_sum = 0.0;
+            int i = SINC_WIDTH;
+            float sample;
+            
+            if ( out + SINC_WIDTH * 2 > out_end )
+                break;
+            
+            for (; i >= -SINC_WIDTH + 1; --i)
+            {
+                int pos = i * step;
+                int abs_pos = abs(inv_phase - pos);
+                kernel_sum += kernel[i + SINC_WIDTH - 1] = sinc_lut[abs_pos] * window_lut[abs_pos];
+            }
+            sample = *in++ - last_amp;
+            last_amp += sample;
+            sample /= kernel_sum;
+            for (sample = 0, i = 0; i < SINC_WIDTH * 2; ++i)
+                out[i] += sample * kernel[i];
+            
+            inv_phase += inv_phase_inc;
+            
+            out += inv_phase >> RESAMPLER_SHIFT;
+            
+            inv_phase &= RESAMPLER_RESOLUTION-1;
+        }
+        while ( in < in_end );
+        
+        r->inv_phase = inv_phase;
+        r->last_amp = last_amp;
+        *out_ = out;
+        
+        used = (int)(in - in_);
+        
+        r->write_filled -= used;
+    }
+    
+    return used;
+}
+
+#ifdef RESAMPLER_SSE
+static int resampler_run_blep_sse(resampler * r, float ** out_, float * out_end)
+{
+    int in_size = r->write_filled;
+    float const* in_ = r->buffer_in + resampler_buffer_size + r->write_pos - r->write_filled;
+    int used = 0;
+    in_size -= 1;
+    if ( in_size > 0 )
+    {
+        float* out = *out_;
+        float const* in = in_;
+        float const* const in_end = in + in_size;
+        float last_amp = r->last_amp;
+        int inv_phase = r->inv_phase;
+        int inv_phase_inc = r->inv_phase_inc;
+        
+        const int step = RESAMPLER_RESOLUTION;
+        
+        do
+        {
+            // accumulate in extended precision
+            float kernel_sum = 0.0;
+            __m128 kernel[SINC_WIDTH / 2];
+            __m128 temp1, temp2;
+            __m128 samplex;
+            float sample;
+            float *kernelf = (float*)(&kernel);
+            int i = SINC_WIDTH;
+            
+            if ( out + SINC_WIDTH * 2 > out_end )
+                break;
+            
+            for (; i >= -SINC_WIDTH + 1; --i)
+            {
+                int pos = i * step;
+                int abs_pos = abs(inv_phase - pos);
+                kernel_sum += kernelf[i + SINC_WIDTH - 1] = sinc_lut[abs_pos] * window_lut[abs_pos];
+            }
+            sample = *in++ - last_amp;
+            last_amp += sample;
+            sample /= kernel_sum;
+            samplex = _mm_set1_ps( sample );
+            for (i = 0; i < SINC_WIDTH / 2; ++i)
+            {
+                temp1 = _mm_load_ps( (const float *)( kernel + i ) );
+                temp1 = _mm_mul_ps( temp1, samplex );
+                temp2 = _mm_loadu_ps( (const float *) out + i * 4 );
+                temp1 = _mm_add_ps( temp1, temp2 );
+                _mm_storeu_ps( (float *) out + i * 4, temp1 );
+            }
+            
+            inv_phase += inv_phase_inc;
+            
+            out += inv_phase >> RESAMPLER_SHIFT;
+            
+            inv_phase &= RESAMPLER_RESOLUTION - 1;
+        }
+        while ( in < in_end );
+        
+        r->inv_phase = inv_phase;
+        r->last_amp = last_amp;
+        *out_ = out;
+        
+        used = (int)(in - in_);
+        
+        r->write_filled -= used;
+    }
+    
+    return used;
+}
+#endif
+
+static int resampler_run_linear(resampler * r, float ** out_, float * out_end)
+{
+    int in_size = r->write_filled;
+    float const* in_ = r->buffer_in + resampler_buffer_size + r->write_pos - r->write_filled;
+    int used = 0;
+    in_size -= 2;
+    if ( in_size > 0 )
+    {
+        float* out = *out_;
+        float const* in = in_;
+        float const* const in_end = in + in_size;
+        int phase = r->phase;
+        int phase_inc = r->phase_inc;
+        
+        do
+        {
+            float sample;
+            
+            if ( out >= out_end )
+                break;
+            
+            sample = in[0] + (in[1] - in[0]) * ((float)phase / RESAMPLER_RESOLUTION);
+            *out++ = sample;
+            
+            phase += phase_inc;
+            
+            in += phase >> RESAMPLER_SHIFT;
+            
+            phase &= RESAMPLER_RESOLUTION-1;
+        }
+        while ( in < in_end );
+        
+        r->phase = phase;
+        *out_ = out;
+        
+        used = (int)(in - in_);
+        
+        r->write_filled -= used;
+    }
+    
+    return used;
+}
+
+static int resampler_run_cubic(resampler * r, float ** out_, float * out_end)
+{
+    int in_size = r->write_filled;
+    float const* in_ = r->buffer_in + resampler_buffer_size + r->write_pos - r->write_filled;
+    int used = 0;
+    in_size -= 4;
+    if ( in_size > 0 )
+    {
+        float* out = *out_;
+        float const* in = in_;
+        float const* const in_end = in + in_size;
+        int phase = r->phase;
+        int phase_inc = r->phase_inc;
+        
+        do
+        {
+            float * kernel;
+            int i;
+            float sample;
+            
+            if ( out >= out_end )
+                break;
+            
+            kernel = cubic_lut + phase * 4;
+            
+            for (sample = 0, i = 0; i < 4; ++i)
+                sample += in[i] * kernel[i];
+            *out++ = sample;
+            
+            phase += phase_inc;
+            
+            in += phase >> RESAMPLER_SHIFT;
+            
+            phase &= RESAMPLER_RESOLUTION-1;
+        }
+        while ( in < in_end );
+        
+        r->phase = phase;
+        *out_ = out;
+        
+        used = (int)(in - in_);
+        
+        r->write_filled -= used;
+    }
+    
+    return used;
+}
+
+#ifdef RESAMPLER_SSE
+static int resampler_run_cubic_sse(resampler * r, float ** out_, float * out_end)
+{
+    int in_size = r->write_filled;
+    float const* in_ = r->buffer_in + resampler_buffer_size + r->write_pos - r->write_filled;
+    int used = 0;
+    in_size -= 4;
+    if ( in_size > 0 )
+    {
+        float* out = *out_;
+        float const* in = in_;
+        float const* const in_end = in + in_size;
+        int phase = r->phase;
+        int phase_inc = r->phase_inc;
+        
+        do
+        {
+            __m128 temp1, temp2;
+            __m128 samplex = _mm_setzero_ps();
+            
+            if ( out >= out_end )
+                break;
+            
+            temp1 = _mm_loadu_ps( (const float *)( in ) );
+            temp2 = _mm_load_ps( (const float *)( cubic_lut + phase * 4 ) );
+            temp1 = _mm_mul_ps( temp1, temp2 );
+            samplex = _mm_add_ps( samplex, temp1 );
+            temp1 = _mm_movehl_ps( temp1, samplex );
+            samplex = _mm_add_ps( samplex, temp1 );
+            temp1 = samplex;
+            temp1 = _mm_shuffle_ps( temp1, samplex, _MM_SHUFFLE(0, 0, 0, 1) );
+            samplex = _mm_add_ps( samplex, temp1 );
+            _mm_store_ss( out, samplex );
+            ++out;
+            
+            phase += phase_inc;
+            
+            in += phase >> RESAMPLER_SHIFT;
+            
+            phase &= RESAMPLER_RESOLUTION - 1;
+        }
+        while ( in < in_end );
+        
+        r->phase = phase;
+        *out_ = out;
+        
+        used = (int)(in - in_);
+        
+        r->write_filled -= used;
+    }
+    
+    return used;
+}
+#endif
+
+static int resampler_run_sinc(resampler * r, float ** out_, float * out_end)
+{
+    int in_size = r->write_filled;
+    float const* in_ = r->buffer_in + resampler_buffer_size + r->write_pos - r->write_filled;
+    int used = 0;
+    in_size -= SINC_WIDTH * 2;
+    if ( in_size > 0 )
+    {
+        float* out = *out_;
+        float const* in = in_;
+        float const* const in_end = in + in_size;
+        int phase = r->phase;
+        int phase_inc = r->phase_inc;
+
+        int step = phase_inc > RESAMPLER_RESOLUTION ? RESAMPLER_RESOLUTION * RESAMPLER_RESOLUTION / phase_inc : RESAMPLER_RESOLUTION;
+        int window_step = RESAMPLER_RESOLUTION;
+
+        do
+        {
+            float kernel[SINC_WIDTH * 2], kernel_sum = 0.0;
+            int i = SINC_WIDTH;
+            int phase_adj = phase * step / RESAMPLER_RESOLUTION;
+            float sample;
+
+            if ( out >= out_end )
+                break;
+
+            for (; i >= -SINC_WIDTH + 1; --i)
+            {
+                int pos = i * step;
+                int window_pos = i * window_step;
+                kernel_sum += kernel[i + SINC_WIDTH - 1] = sinc_lut[abs(phase_adj - pos)] * window_lut[abs(phase - window_pos)];
+            }
+            for (sample = 0, i = 0; i < SINC_WIDTH * 2; ++i)
+                sample += in[i] * kernel[i];
+            *out++ = (float)(sample / kernel_sum);
+
+            phase += phase_inc;
+
+            in += phase >> RESAMPLER_SHIFT;
+
+            phase &= RESAMPLER_RESOLUTION-1;
+        }
+        while ( in < in_end );
+
+        r->phase = phase;
+        *out_ = out;
+
+        used = (int)(in - in_);
+
+        r->write_filled -= used;
+    }
+
+    return used;
+}
+
+#ifdef RESAMPLER_SSE
+static int resampler_run_sinc_sse(resampler * r, float ** out_, float * out_end)
+{
+    int in_size = r->write_filled;
+    float const* in_ = r->buffer_in + resampler_buffer_size + r->write_pos - r->write_filled;
+    int used = 0;
+    in_size -= SINC_WIDTH * 2;
+    if ( in_size > 0 )
+    {
+        float* out = *out_;
+        float const* in = in_;
+        float const* const in_end = in + in_size;
+        int phase = r->phase;
+        int phase_inc = r->phase_inc;
+        
+        int step = phase_inc > RESAMPLER_RESOLUTION ? RESAMPLER_RESOLUTION * RESAMPLER_RESOLUTION / phase_inc : RESAMPLER_RESOLUTION;
+        int window_step = RESAMPLER_RESOLUTION;
+        
+        do
+        {
+            // accumulate in extended precision
+            float kernel_sum = 0.0;
+            __m128 kernel[SINC_WIDTH / 2];
+            __m128 temp1, temp2;
+            __m128 samplex = _mm_setzero_ps();
+            float *kernelf = (float*)(&kernel);
+            int i = SINC_WIDTH;
+            int phase_adj = phase * step / RESAMPLER_RESOLUTION;
+            
+            if ( out >= out_end )
+                break;
+            
+            for (; i >= -SINC_WIDTH + 1; --i)
+            {
+                int pos = i * step;
+                int window_pos = i * window_step;
+                kernel_sum += kernelf[i + SINC_WIDTH - 1] = sinc_lut[abs(phase_adj - pos)] * window_lut[abs(phase - window_pos)];
+            }
+            for (i = 0; i < SINC_WIDTH / 2; ++i)
+            {
+                temp1 = _mm_loadu_ps( (const float *)( in + i * 4 ) );
+                temp2 = _mm_load_ps( (const float *)( kernel + i ) );
+                temp1 = _mm_mul_ps( temp1, temp2 );
+                samplex = _mm_add_ps( samplex, temp1 );
+            }
+            kernel_sum = 1.0f / kernel_sum;
+            temp1 = _mm_movehl_ps( temp1, samplex );
+            samplex = _mm_add_ps( samplex, temp1 );
+            temp1 = samplex;
+            temp1 = _mm_shuffle_ps( temp1, samplex, _MM_SHUFFLE(0, 0, 0, 1) );
+            samplex = _mm_add_ps( samplex, temp1 );
+            temp1 = _mm_set_ss( kernel_sum );
+            samplex = _mm_mul_ps( samplex, temp1 );
+            _mm_store_ss( out, samplex );
+            ++out;
+            
+            phase += phase_inc;
+            
+            in += phase >> RESAMPLER_SHIFT;
+            
+            phase &= RESAMPLER_RESOLUTION - 1;
+        }
+        while ( in < in_end );
+        
+        r->phase = phase;
+        *out_ = out;
+        
+        used = (int)(in - in_);
+        
+        r->write_filled -= used;
+    }
+    
+    return used;
+}
+#endif
+
+static void resampler_fill(resampler * r)
+{
+    int min_filled = resampler_min_filled(r);
+    int quality = r->quality;
+    while ( r->write_filled > min_filled &&
+            r->read_filled < resampler_buffer_size )
+    {
+        int write_pos = ( r->read_pos + r->read_filled ) % resampler_buffer_size;
+        int write_size = resampler_buffer_size - write_pos;
+        float * out = r->buffer_out + write_pos;
+        if ( write_size > ( resampler_buffer_size - r->read_filled ) )
+            write_size = resampler_buffer_size - r->read_filled;
+        switch (quality)
+        {
+        case RESAMPLER_QUALITY_ZOH:
+            resampler_run_zoh( r, &out, out + write_size );
+            break;
+                
+        case RESAMPLER_QUALITY_BLEP:
+        {
+            int used;
+            int write_extra = 0;
+            if ( write_pos >= r->read_pos )
+                write_extra = r->read_pos;
+            if ( write_extra > SINC_WIDTH * 2 - 1 )
+                write_extra = SINC_WIDTH * 2 - 1;
+            memcpy( r->buffer_out + resampler_buffer_size, r->buffer_out, write_extra * sizeof(r->buffer_out[0]) );
+            if ( resampler_has_sse )
+                used = resampler_run_blep_sse( r, &out, out + write_size + write_extra );
+            else
+                used = resampler_run_blep( r, &out, out + write_size + write_extra );
+            memcpy( r->buffer_out, r->buffer_out + resampler_buffer_size, write_extra * sizeof(r->buffer_out[0]) );
+            if (!used)
+                return;
+            break;
+        }
+                
+        case RESAMPLER_QUALITY_LINEAR:
+            resampler_run_linear( r, &out, out + write_size );
+            break;
+                
+        case RESAMPLER_QUALITY_CUBIC:
+#ifdef RESAMPLER_SSE
+            if ( resampler_has_sse )
+                resampler_run_cubic_sse( r, &out, out + write_size );
+            else
+#endif
+                resampler_run_cubic( r, &out, out + write_size );
+            break;
+                
+        case RESAMPLER_QUALITY_SINC:
+#ifdef RESAMPLER_SSE
+            if ( resampler_has_sse )
+                resampler_run_sinc_sse( r, &out, out + write_size );
+            else
+#endif
+                resampler_run_sinc( r, &out, out + write_size );
+            break;
+        }
+        r->read_filled += out - r->buffer_out - write_pos;
+    }
+}
+
+static void resampler_fill_and_remove_delay(resampler * r)
+{
+    resampler_fill( r );
+    if ( r->delay_removed < 0 )
+    {
+        int delay = resampler_output_delay( r );
+        r->delay_removed = 0;
+        while ( delay-- )
+            resampler_remove_sample( r );
+    }
+}
+
+int resampler_get_sample_count(void *_r)
+{
+    resampler * r = ( resampler * ) _r;
+    if ( r->read_filled < 1 && (r->quality != RESAMPLER_QUALITY_BLEP || r->inv_phase_inc))
+        resampler_fill_and_remove_delay( r );
+    return r->read_filled;
+}
+
+int resampler_get_sample(void *_r)
+{
+    resampler * r = ( resampler * ) _r;
+    if ( r->read_filled < 1 && r->phase_inc)
+        resampler_fill_and_remove_delay( r );
+    if ( r->read_filled < 1 )
+        return 0;
+    if ( r->quality == RESAMPLER_QUALITY_BLEP )
+        return (int)(r->buffer_out[ r->read_pos ] + r->accumulator);
+    else
+        return (int)r->buffer_out[ r->read_pos ];
+}
+
+void resampler_remove_sample(void *_r)
+{
+    resampler * r = ( resampler * ) _r;
+    if ( r->read_filled > 0 )
+    {
+        if ( r->quality == RESAMPLER_QUALITY_BLEP )
+        {
+            r->accumulator += r->buffer_out[ r->read_pos ];
+            r->buffer_out[ r->read_pos ] = 0;
+            r->accumulator -= r->accumulator * (1.0 / 8192.0);
+            if (fabs(r->accumulator) < 1e-20)
+                r->accumulator = 0;
+        }
+        --r->read_filled;
+        r->read_pos = ( r->read_pos + 1 ) % resampler_buffer_size;
+    }
+}

+ 63 - 0
resampler.h

@@ -0,0 +1,63 @@
+#ifndef _RESAMPLER_H_
+#define _RESAMPLER_H_
+
+// Ugglay
+#ifdef RESAMPLER_DECORATE
+#define PASTE(a,b) a ## b
+#define EVALUATE(a,b) PASTE(a,b)
+#define resampler_init EVALUATE(RESAMPLER_DECORATE,_resampler_init)
+#define resampler_create EVALUATE(RESAMPLER_DECORATE,_resampler_create)
+#define resampler_delete EVALUATE(RESAMPLER_DECORATE,_resampler_delete)
+#define resampler_dup EVALUATE(RESAMPLER_DECORATE,_resampler_dup)
+#define resampler_dup_inplace EVALUATE(RESAMPLER_DECORATE,_resampler_dup_inplace)
+#define resampler_set_quality EVALUATE(RESAMPLER_DECORATE,_resampler_set_quality)
+#define resampler_get_free_count EVALUATE(RESAMPLER_DECORATE,_resampler_get_free_count)
+#define resampler_write_sample EVALUATE(RESAMPLER_DECORATE,_resampler_write_sample)
+#define resampler_write_sample_fixed EVALUATE(RESAMPLER_DECORATE,_resampler_write_sample_fixed)
+#define resampler_set_rate EVALUATE(RESAMPLER_DECORATE,_resampler_set_rate)
+#define resampler_ready EVALUATE(RESAMPLER_DECORATE,_resampler_ready)
+#define resampler_clear EVALUATE(RESAMPLER_DECORATE,_resampler_clear)
+#define resampler_get_sample_count EVALUATE(RESAMPLER_DECORATE,_resampler_get_sample_count)
+#define resampler_get_sample EVALUATE(RESAMPLER_DECORATE,_resampler_get_sample)
+#define resampler_remove_sample EVALUATE(RESAMPLER_DECORATE,_resampler_remove_sample)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void resampler_init(void);
+
+void * resampler_create(void);
+void resampler_delete(void *);
+void * resampler_dup(const void *);
+void resampler_dup_inplace(void *, const void *);
+
+enum
+{
+    RESAMPLER_QUALITY_MIN = 0,
+    RESAMPLER_QUALITY_ZOH = 0,
+    RESAMPLER_QUALITY_BLEP = 1,
+    RESAMPLER_QUALITY_LINEAR = 2,
+    RESAMPLER_QUALITY_CUBIC = 3,
+    RESAMPLER_QUALITY_SINC = 4,
+    RESAMPLER_QUALITY_MAX = 4
+};
+
+void resampler_set_quality(void *, int quality);
+
+int resampler_get_free_count(void *);
+void resampler_write_sample(void *, short sample);
+void resampler_write_sample_fixed(void *, int sample, unsigned char depth);
+void resampler_set_rate( void *, double new_factor );
+int resampler_ready(void *);
+void resampler_clear(void *);
+int resampler_get_sample_count(void *);
+int resampler_get_sample(void *);
+void resampler_remove_sample(void *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 100 - 17
rixplay.cpp

@@ -19,12 +19,22 @@
 
 #include "rixplay.h"
 
-#define USE_SURROUNDOPL      0
-#define USE_DEMUOPL          0
+#define USE_SURROUNDOPL      1
+#define USE_DEMUOPL          1
+#define USE_KEMUOPL          0
+#define USE_SSRC             1
+
+#define OPL_SAMPLERATE       49716
+#include "resampler.h"
 
 #include "adplug/opl.h"
 #include "adplug/emuopl.h"
+#if USE_KEMUOPL
+#include "adplug/kemuopl.h"
+#endif
+#if USE_DEMUOPL
 #include "adplug/demuopl.h"
+#endif
 #include "adplug/surroundopl.h"
 #include "adplug/rix.h"
 
@@ -38,6 +48,7 @@ typedef struct tagRIXPLAYER
    tagRIXPLAYER() : iCurrentMusic(-1) {}
    Copl                      *opl;
    CrixPlayer                *rix;
+   void                      *resampler[2];
    INT                        iCurrentMusic; // current playing music number
    INT                        iNextMusic; // the next music number to switch to
    DWORD                      dwStartFadeTime;
@@ -45,7 +56,7 @@ typedef struct tagRIXPLAYER
    enum { FADE_IN, FADE_OUT } FadeType; // fade in or fade out ?
    BOOL                       fLoop;
    BOOL                       fNextLoop;
-   BYTE                       buf[PAL_SAMPLE_RATE / 70 * 2 * PAL_CHANNELS];
+   BYTE                       buf[OPL_SAMPLERATE / 70 * sizeof(short) * PAL_CHANNELS];
    LPBYTE                     pos;
 } RIXPLAYER, *LPRIXPLAYER;
 
@@ -190,7 +201,46 @@ RIX_FillBuffer(
                return;
             }
          }
-         gpRixPlayer->opl->update((short *)(gpRixPlayer->buf), PAL_SAMPLE_RATE / 70);
+          int sample_count = OPL_SAMPLERATE / 70;
+          if( gpRixPlayer->resampler[0] ) {
+              
+              unsigned int samples_written = 0;
+			  short *tempBuf = (short*)_alloca(64 * 2 * sizeof(short));
+              short *finalBuf = (short*)gpRixPlayer->buf;
+              
+              while ( sample_count )
+              {
+                  int to_write = resampler_get_free_count( gpRixPlayer->resampler[0] );
+                  if ( to_write )
+                  {
+                      gpRixPlayer->opl->update( tempBuf, to_write );
+                      for ( int i = 0; i < to_write; i++ )
+                      {
+                          resampler_write_sample( gpRixPlayer->resampler[0], tempBuf[i * 2] );
+                          resampler_write_sample( gpRixPlayer->resampler[1], tempBuf[i * 2 + 1] );
+                      }
+                  }
+                  
+                  /* if ( !lanczos_resampler_ready( m_resampler ) ) break; */ /* We assume that by filling the input buffer completely every pass, there will always be samples ready. */
+                  
+                  int ls = resampler_get_sample( gpRixPlayer->resampler[0] );
+                  int rs = resampler_get_sample( gpRixPlayer->resampler[1] );
+                  resampler_remove_sample( gpRixPlayer->resampler[0] );
+                  resampler_remove_sample( gpRixPlayer->resampler[1] );
+                  
+                  finalBuf[ samples_written++ ] = ls/256.0;
+                  finalBuf[ samples_written++ ] = rs/256.0;
+                  --sample_count;
+              }
+              
+//              p_chunk.set_data_size( samples_written );
+//              p_chunk.set_sample_rate( srate );
+//              p_chunk.set_channels( 2, audio_chunk::channel_config_stereo );
+//              p_chunk.set_sample_count( samples_written / 2 );
+//              
+//              audio_math::convert_from_int32( sample_buffer.get_ptr(), samples_written, p_chunk.get_data(), 1 << 8 );
+          }else
+              gpRixPlayer->opl->update((short *)(gpRixPlayer->buf), sample_count);
       }
 
       l = sizeof(gpRixPlayer->buf) - (gpRixPlayer->pos - gpRixPlayer->buf);
@@ -207,9 +257,6 @@ RIX_FillBuffer(
       {
          SHORT s = SWAP16((int)(*(SHORT *)(gpRixPlayer->pos)) * volume / SDL_MIX_MAXVOLUME);
 
-#if !USE_SURROUNDOPL
-         for (int j = 0; j < PAL_CHANNELS; j++)
-#endif
          {
             *(SHORT *)(stream) = s;
             stream += sizeof(SHORT);
@@ -248,19 +295,48 @@ RIX_Init(
    {
       return -1;
    }
-#if USE_SURROUNDOPL && (PAL_CHANNELS == 2)
-#	if USE_DEMUOPL
-   gpRixPlayer->opl = new CSurroundopl(new CDemuopl(PAL_SAMPLE_RATE, true, false),
-      new CDemuopl(PAL_SAMPLE_RATE, true, false), true);
-#	else
-   gpRixPlayer->opl = new CSurroundopl(new CEmuopl(PAL_SAMPLE_RATE, true, false),
-      new CEmuopl(PAL_SAMPLE_RATE, true, false), true);
-#	endif
+#if PAL_CHANNELS == 2
+#   if USE_SURROUNDOPL
+#       if USE_DEMUOPL
+   gpRixPlayer->opl = new CSurroundopl(new CDemuopl(OPL_SAMPLERATE, true, false),
+                                       new CDemuopl(OPL_SAMPLERATE, true, false), true);
+#       elif USE_KEMUOPL
+   gpRixPlayer->opl = new CSurroundopl(new CKemuopl(OPL_SAMPLERATE, true, false),
+                                       new CKemuopl(OPL_SAMPLERATE, true, false), true);
+#       else
+   gpRixPlayer->opl = new CSurroundopl(new CEmuopl(OPL_SAMPLERATE, true, false),
+                                       new CEmuopl(OPL_SAMPLERATE, true, false), true);
+#       endif
+#   if OPL_SAMPLERATE != PAL_SAMPLE_RATE && USE_SSRC
+    resampler_init();
+    gpRixPlayer->resampler[0] = resampler_create();
+    gpRixPlayer->resampler[1] = resampler_create();
+    
+    resampler_set_quality( gpRixPlayer->resampler[0], RESAMPLER_QUALITY_MAX );
+    resampler_set_quality( gpRixPlayer->resampler[1], RESAMPLER_QUALITY_MAX );
+    
+    resampler_set_rate( gpRixPlayer->resampler[0], OPL_SAMPLERATE / (double)PAL_SAMPLE_RATE );
+    resampler_set_rate( gpRixPlayer->resampler[1], OPL_SAMPLERATE / (double)PAL_SAMPLE_RATE );
+#   else
+    gpRixPlayer->resampler[0] = 0;
+    gpRixPlayer->resampler[1] = 0;
+#   endif
+#   else
+#       if USE_DEMUOPL
+    gpRixPlayer->opl = new CDemuopl(OPL_SAMPLERATE, true, true);
+#       elif USE_KEMUOPL
+    gpRixPlayer->opl = new CKemuopl(OPL_SAMPLERATE, true, true);
+#       else
+    gpRixPlayer->opl = new CEmuopl(OPL_SAMPLERATE, true, true);
+#       endif
+#   endif
 #else
 #	if USE_DEMUOPL
-   gpRixPlayer->opl = new CDemuopl(PAL_SAMPLE_RATE, true, false);
+    gpRixPlayer->opl = new CDemuopl(OPL_SAMPLERATE, true, false);
+#	elif USE_KEMUOPL
+    gpRixPlayer->opl = new CKemuopl(OPL_SAMPLERATE, true, false);
 #	else
-   gpRixPlayer->opl = new CEmuopl(PAL_SAMPLE_RATE, true, false);
+   gpRixPlayer->opl = new CEmuopl(OPL_SAMPLERATE, true, false);
 #	endif
 #endif
 
@@ -368,6 +444,13 @@ RIX_Play(
    // Stop the current CD music.
    //
    SOUND_PlayCDA(-1);
+    
+    gpRixPlayer->opl->init();
+    if ( gpRixPlayer->resampler[0] )
+        resampler_clear( gpRixPlayer->resampler[0] );
+    if ( gpRixPlayer->resampler[1] )
+        resampler_clear( gpRixPlayer->resampler[1] );
+//    gpRixPlayer->rix->rewind(iNumRIX);
 
    DWORD t = SDL_GetTicks();
    gpRixPlayer->fNextLoop = fLoop;

+ 1 - 1
script.c

@@ -3534,7 +3534,7 @@ begin:
 		  if (wEventObjectID & PAL_ITEM_DESC_BOTTOM)
 		  {
 #  ifdef PAL_UNICODE
-			  int YOffset = gpGlobals->dwExtraDescLines * 16;
+			  int YOffset = gpGlobals->dwExtraItemDescLines * 16;
 #  else
 			  int YOffset = 0;
 #  endif

+ 11 - 2
sdlpal.vcxproj

@@ -179,7 +179,7 @@
     <Link>
       <AdditionalDependencies>odbc32.lib;odbccp32.lib;winmm.lib;sdl2.lib;sdl2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <SuppressStartupBanner>true</SuppressStartupBanner>
-      <AdditionalLibraryDirectories>..\SDL2-2.0.3\lib\x86;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>D:\Develop\SDL2-2.0.3\VisualC\SDL\Win32\Debug\;D:\Develop\SDL2-2.0.3\VisualC\SDLMain\Win32\Debug\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <SubSystem>Windows</SubSystem>
       <TargetMachine>MachineX86</TargetMachine>
@@ -217,7 +217,7 @@
     <Link>
       <AdditionalDependencies>odbc32.lib;odbccp32.lib;winmm.lib;sdl2.lib;sdl2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <SuppressStartupBanner>true</SuppressStartupBanner>
-      <AdditionalLibraryDirectories>..\SDL2-2.0.3\lib\x86;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>D:\Develop\SDL2-2.0.3\VisualC\SDL\Win32\Debug\;D:\Develop\SDL2-2.0.3\VisualC\SDLMain\Win32\Debug\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <SubSystem>Windows</SubSystem>
       <TargetMachine>MachineX86</TargetMachine>
@@ -228,6 +228,8 @@
     </Bscmake>
   </ItemDefinitionGroup>
   <ItemGroup>
+    <ClCompile Include="adplug\adlibemu.c" />
+    <ClCompile Include="adplug\demuopl.cpp" />
     <ClCompile Include="battle.c">
       <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug_WIN95|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
@@ -398,6 +400,7 @@
       <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release_WIN95|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
     </ClCompile>
+    <ClCompile Include="resampler.c" />
     <ClCompile Include="rixplay.cpp">
       <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug_WIN95|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
@@ -547,6 +550,9 @@
       <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release_WIN95|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release_WIN95|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug_WIN95|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release_WIN95|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
     </ClCompile>
     <ClCompile Include="adplug\emuopl.cpp">
       <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
@@ -740,6 +746,8 @@
     </ClCompile>
   </ItemGroup>
   <ItemGroup>
+    <ClInclude Include="adplug\adlibemu.h" />
+    <ClInclude Include="adplug\kemuopl.h" />
     <ClInclude Include="ascii.h" />
     <ClInclude Include="battle.h" />
     <ClInclude Include="big5font.h" />
@@ -763,6 +771,7 @@
     <ClInclude Include="palette.h" />
     <ClInclude Include="play.h" />
     <ClInclude Include="res.h" />
+    <ClInclude Include="resampler.h" />
     <ClInclude Include="rixplay.h" />
     <ClInclude Include="rngplay.h" />
     <ClInclude Include="scene.h" />

+ 18 - 0
sdlpal.vcxproj.filters

@@ -176,6 +176,15 @@
     <ClCompile Include="libmad\timer.c">
       <Filter>libmad</Filter>
     </ClCompile>
+    <ClCompile Include="resampler.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="adplug\adlibemu.c">
+      <Filter>adplug</Filter>
+    </ClCompile>
+    <ClCompile Include="adplug\demuopl.cpp">
+      <Filter>adplug</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="ascii.h">
@@ -361,6 +370,15 @@
     <ClInclude Include="big5font.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="resampler.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="adplug\adlibemu.h">
+      <Filter>adplug</Filter>
+    </ClInclude>
+    <ClInclude Include="adplug\kemuopl.h">
+      <Filter>adplug</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="sdlpal.ico">

+ 1 - 1
sound.h

@@ -29,7 +29,7 @@
 #endif
 
 #ifndef PAL_CHANNELS
-#define PAL_CHANNELS        1
+#define PAL_CHANNELS        2
 #endif
 
 #ifdef __cplusplus