changeset 6:d14fd386d182

Initial entry of mikmod into the CVS tree.
author darius
date Fri, 23 Jan 1998 16:05:09 +0000
parents 42e11dc15457
children de95ce2eacfd
files playercode/munitrk.c playercode/mwav.c playercode/npertab.c playercode/resample.S playercode/resample.asm playercode/s3m_it.c playercode/sloader.c playercode/virtch.c
diffstat 8 files changed, 3045 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/playercode/munitrk.c	Fri Jan 23 16:05:09 1998 +0000
@@ -0,0 +1,307 @@
+/*
+
+Name:
+MUNITRK.C
+
+Description:
+All routines dealing with the manipulation of UNITRK(tm) streams
+
+Portability:
+All systems - all compilers
+
+*/
+
+#include <string.h>
+#include "mikmod.h"
+
+#define BUFPAGE  128            // smallest unibuffer size
+#define TRESHOLD 16
+
+UWORD unioperands[256] =
+{   0,              // not used
+    1,              // UNI_NOTE
+    1,              // UNI_INSTRUMENT
+    1,              // UNI_PTEFFECT0
+    1,              // UNI_PTEFFECT1
+    1,              // UNI_PTEFFECT2
+    1,              // UNI_PTEFFECT3
+    1,              // UNI_PTEFFECT4
+    1,              // UNI_PTEFFECT5
+    1,              // UNI_PTEFFECT6
+    1,              // UNI_PTEFFECT7
+    1,              // UNI_PTEFFECT8
+    1,              // UNI_PTEFFECT9
+    1,              // UNI_PTEFFECTA
+    1,              // UNI_PTEFFECTB
+    1,              // UNI_PTEFFECTC
+    1,              // UNI_PTEFFECTD
+    1,              // UNI_PTEFFECTE
+    1,              // UNI_PTEFFECTF
+    1,              // UNI_S3MEFFECTA
+    1,              // UNI_S3MEFFECTD
+    1,              // UNI_S3MEFFECTE
+    1,              // UNI_S3MEFFECTF
+    1,              // UNI_S3MEFFECTI
+    1,              // UNI_S3MEFFECTQ
+    1,              // UNI_S3MEFFECTR
+    1,              // UNI_S3MEFFECTT
+    1,              // UNI_S3MEFFECTU
+    0,              // UNI_KEYOFF
+    1,              // UNI_KEYFADE
+    2,              // UNI_VOLEFFECTS
+    1,              // UNI_XMEFFECT4
+    1,              // UNI_XMEFFECTA
+    1,              // UNI_XMEFFECTE1
+    1,              // UNI_XMEFFECTE2
+    1,              // UNI_XMEFFECTEA
+    1,              // UNI_XMEFFECTEB
+    1,              // UNI_XMEFFECTG
+    1,              // UNI_XMEFFECTH
+    1,              // UNI_XMEFFECTL
+    1,              // UNI_XMEFFECTP
+    1,              // UNI_XMEFFECTX1
+    1,              // UNI_XMEFFECTX2
+    1,              // UNI_ITEFFECTG
+    1,              // UNI_ITEFFECTH
+    1,              // UNI_ITEFFECTI
+    1,              // UNI_ITEFFECTM
+    1,              // UNI_ITEFFECTN
+    1,              // UNI_ITEFFECTP
+    1,              // UNI_ITEFFECTU
+    1,              // UNI_ITEFFECTW
+    1,              // UNI_ITEFFECTY
+    1               // UNI_ITEFFECTS0
+};
+
+
+// unibuffer is increased by BUFPAGE
+// bytes when unipc reaches unimax-TRESHOLD
+
+
+/*
+    Ok.. I'll try to explain the new internal module format.. so here it goes:
+
+
+    The UNITRK(tm) Format:
+    ======================
+
+    A UNITRK stream is an array of bytes representing a single track
+    of a pattern. It's made up of 'repeat/length' bytes, opcodes and
+    operands (sort of a assembly language):
+
+    rrrlllll
+    [REP/LEN][OPCODE][OPERAND][OPCODE][OPERAND] [REP/LEN][OPCODE][OPERAND]..
+    ^                                         ^ ^
+    |-------ROWS 0 - 0+REP of a track---------| |-------ROWS xx - xx+REP of a track...
+
+
+    The rep/len byte contains the number of bytes in the current row,
+    _including_ the length byte itself (So the LENGTH byte of row 0 in the
+    previous example would have a value of 5). This makes it easy to search
+    through a stream for a particular row. A track is concluded by a 0-value
+    length byte.
+
+    The upper 3 bits of the rep/len byte contain the number of times -1 this
+    row is repeated for this track. (so a value of 7 means this row is repeated
+    8 times)
+
+    Opcodes can range from 1 to 255 but currently only opcodes 1 to 45 are
+    being used. Each opcode can have a different number of operands. You can
+    find the number of operands to a particular opcode by using the opcode
+    as an index into the 'unioperands' table.
+
+*/
+
+
+/***************************************************************************
+>>>>>>>>>>> Next are the routines for reading a UNITRK stream: <<<<<<<<<<<<<
+***************************************************************************/
+
+
+static UBYTE *rowstart;     // startadress of a row
+static UBYTE *rowend;       // endaddress of a row (exclusive)
+static UBYTE *rowpc;        // current unimod(tm) programcounter
+
+
+void UniSetRow(UBYTE *t)
+{
+    rowstart = t;
+    rowpc    = rowstart;
+    rowend   = rowstart+(*(rowpc++)&0x1f);
+}
+
+
+UBYTE UniGetByte(void)
+{
+    return (rowpc<rowend) ? *(rowpc++) : 0;
+}
+
+
+void UniSkipOpcode(UBYTE op)
+{
+    UWORD t = unioperands[op];
+    while(t--) UniGetByte();
+}
+
+
+UBYTE *UniFindRow(UBYTE *t, UWORD row)
+//  Finds the address of row number 'row' in the UniMod(tm) stream 't'
+//  returns NULL if the row can't be found.
+{
+    UBYTE c,l;
+
+    while(1)
+    {   c = *t;                  // get rep/len byte
+        if(!c) return NULL;      // zero ? -> end of track..
+        l = (c>>5)+1;            // extract repeat value
+        if(l>row) break;         // reached wanted row? -> return pointer
+        row -= l;                // haven't reached row yet.. update row
+        t += c&0x1f;             // point t to the next row
+    }
+
+    return t;
+}
+
+
+
+/***************************************************************************
+>>>>>>>>>>> Next are the routines for CREATING UNITRK streams: <<<<<<<<<<<<<
+***************************************************************************/
+
+
+static UBYTE *unibuf;       // pointer to the temporary unitrk buffer
+static UWORD unimax;        // maximum number of bytes to be written to this buffer
+
+static UWORD unipc;         // index in the buffer where next opcode will be written
+static UWORD unitt;         // holds index of the rep/len byte of a row
+static UWORD lastp;         // holds index to the previous row (needed for compressing)
+
+
+void UniReset(void)
+// Resets index-pointers to create a new track.
+{
+    unitt     = 0;   // reset index to rep/len byte
+    unipc     = 1;   // first opcode will be written to index 1
+    lastp     = 0;   // no previous row yet
+    unibuf[0] = 0;   // clear rep/len byte
+}
+
+
+void UniWrite(UBYTE data)
+// Appends one byte of data to the current row of a track.
+{
+    // write byte to current position and update
+
+    unibuf[unipc++] = data;
+
+    // Check if we've reached the end of the buffer
+
+    if(unipc > (unimax-TRESHOLD))
+    {   UBYTE *newbuf;
+
+        // We've reached the end of the buffer, so expand
+        // the buffer by BUFPAGE bytes
+
+        newbuf = (UBYTE *)realloc(unibuf, unimax+BUFPAGE);
+
+        // Check if realloc succeeded
+
+        if(newbuf!=NULL)
+        {   unibuf = newbuf;
+            unimax+=BUFPAGE;
+        } else
+        {   // realloc failed, so decrease unipc so we won't write beyond
+            // the end of the buffer.. I don't report the out-of-memory
+            // here; the UniDup() will fail anyway so that's where the
+            // loader sees that something went wrong
+
+            unipc--;
+        }
+    }
+}
+
+
+BOOL MyCmp(UBYTE *a, UBYTE *b, UWORD l)
+{
+    UWORD t;
+
+    for(t=0; t<l; t++)
+        if(*(a++) != *(b++)) return 0;
+    return 1;
+}
+
+
+void UniNewline(void)
+// Closes the current row of a unitrk stream (updates the rep/len byte)
+// and sets pointers to start a new row.
+{
+    UWORD n,l,len;
+
+    n = (unibuf[lastp]>>5)+1;     // repeat of previous row
+    l = (unibuf[lastp]&0x1f);     // length of previous row
+
+    len = unipc-unitt;            // length of current row
+
+    // Now, check if the previous and the current row are identical..
+    // when they are, just increase the repeat field of the previous row
+
+    if(n<8 && len==l && MyCmp(&unibuf[lastp+1],&unibuf[unitt+1],len-1))
+    {   unibuf[lastp]+=0x20;
+        unipc = unitt+1;
+    } else
+    {   // current and previous row aren't equal.. so just update the pointers
+
+        unibuf[unitt] = len;
+        lastp = unitt;
+        unitt = unipc;
+        unipc++;
+    }
+}
+
+
+UBYTE *UniDup(void)
+// Terminates the current unitrk stream and returns a pointer
+// to a copy of the stream.
+{
+    UBYTE *d;
+
+    unibuf[unitt] = 0;
+
+    if((d=(UBYTE *)_mm_malloc(unipc))==NULL) return NULL;
+    memcpy(d,unibuf,unipc);
+
+    return d;
+}
+
+
+UWORD TrkLen(UBYTE *t)
+// Determines the length (in rows) of a unitrk stream 't'
+{
+    UWORD len = 0;
+    UBYTE c;
+
+    while(c = *t & 0x1f)
+    {   len += c;
+        t   += c;
+    }
+    len++;
+
+    return len;
+}
+
+
+BOOL UniInit(void)
+{
+    unimax = BUFPAGE;
+
+    if(!(unibuf=(UBYTE *)_mm_malloc(unimax)))  return 0;
+    return 1;
+}
+
+
+void UniCleanup(void)
+{
+    if(unibuf!=NULL) free(unibuf);
+    unibuf = NULL;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/playercode/mwav.c	Fri Jan 23 16:05:09 1998 +0000
@@ -0,0 +1,133 @@
+/*
+
+ Name:  MWAV.C
+
+ Description:
+ WAV sample loader
+ Stereo .WAV files are not yet supported as samples.
+
+ Portability:
+ All compilers -- All systems (hopefully)
+
+*/
+
+#include <string.h>
+#include "mikmod.h"
+
+
+typedef struct WAV
+{   CHAR  rID[4];
+    ULONG rLen;
+    CHAR  wID[4];
+    CHAR  fID[4];
+    ULONG fLen;
+    UWORD wFormatTag;
+    UWORD nChannels;
+    ULONG nSamplesPerSec;
+    ULONG nAvgBytesPerSec;
+    UWORD nBlockAlign;
+    UWORD nFormatSpecific;
+} WAV;
+
+
+SAMPLE *WAV_LoadFP(FILE *fp)
+{
+    SAMPLE     *si;
+    static WAV  wh;
+    static CHAR dID[4];
+
+    // read wav header
+
+    _mm_read_string(wh.rID,4,fp);
+    wh.rLen = _mm_read_I_ULONG(fp);
+    _mm_read_string(wh.wID,4,fp);
+
+    while(1)
+    {   _mm_read_string(wh.fID,4,fp);
+        wh.fLen = _mm_read_I_ULONG(fp);
+        if(memcmp(wh.fID,"fmt ",4) == 0) break;
+        _mm_fseek(fp,wh.fLen,SEEK_CUR);
+    }
+
+    if( feof(fp) ||
+        memcmp(wh.rID,"RIFF",4) ||
+        memcmp(wh.wID,"WAVE",4))
+    {
+        _mm_errno = MMERR_UNKNOWN_WAVE_TYPE;
+        return NULL;
+    }
+
+    wh.wFormatTag      = _mm_read_I_UWORD(fp);
+    wh.nChannels       = _mm_read_I_UWORD(fp);
+    wh.nSamplesPerSec  = _mm_read_I_ULONG(fp);
+    wh.nAvgBytesPerSec = _mm_read_I_ULONG(fp);
+    wh.nBlockAlign     = _mm_read_I_UWORD(fp);
+    wh.nFormatSpecific = _mm_read_I_UWORD(fp);
+
+    // check it
+
+    if(feof(fp))
+    {   _mm_errno = MMERR_UNKNOWN_WAVE_TYPE;
+        return NULL;
+    }
+
+    // skip other crap
+
+    _mm_fseek(fp,wh.fLen-16,SEEK_CUR);
+    _mm_read_string(dID,4,fp);
+
+    if(memcmp(dID,"data",4))
+    {   _mm_errno = MMERR_UNKNOWN_WAVE_TYPE;
+        return NULL;
+    }
+
+    if(wh.nChannels > 1)
+    {   _mm_errno = MMERR_UNKNOWN_WAVE_TYPE;
+        return NULL;
+    }
+
+//  printf("wFormatTag: %x\n",wh.wFormatTag);
+//  printf("blockalign: %x\n",wh.nBlockAlign);
+//  prinff("nFormatSpc: %x\n",wh.nFormatSpecific);
+
+    if((si=(SAMPLE *)_mm_calloc(1,sizeof(SAMPLE)))==NULL) return NULL;
+
+    si->speed  = wh.nSamplesPerSec;
+    si->volume = 64;
+    si->length = _mm_read_I_ULONG(fp);
+
+    if(wh.nBlockAlign == 2)
+    {   si->flags    = SF_16BITS | SF_SIGNED;
+        si->length >>= 1;
+    }
+
+    SL_RegisterSample(si,MD_SNDFX,fp);
+
+    return si;
+}
+
+
+SAMPLE *WAV_LoadFN(CHAR *filename)
+{
+    FILE   *fp;
+    SAMPLE *si;
+
+    if(!(md_mode & DMODE_SOFT_SNDFX)) return NULL;
+    if((fp=_mm_fopen(filename,"rb"))==NULL) return NULL;
+
+    si = WAV_LoadFP(fp);
+    SL_LoadSamples();
+    fclose(fp);
+
+    return si;
+}
+
+
+void WAV_Free(SAMPLE *si)
+{
+    if(si!=NULL)
+    {   MD_SampleUnLoad(si->handle);
+        free(si);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/playercode/npertab.c	Fri Jan 23 16:05:09 1998 +0000
@@ -0,0 +1,14 @@
+//  MOD format period table.  Used by both the MOD and
+//  M15 (15-inst mod) Loaders.
+
+#include "tdefs.h"
+
+UWORD npertab[60] =
+{   // -> Tuning 0
+    1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,906,
+    856,808,762,720,678,640,604,570,538,508,480,453,
+    428,404,381,360,339,320,302,285,269,254,240,226,
+    214,202,190,180,170,160,151,143,135,127,120,113,
+    107,101,95,90,85,80,75,71,67,63,60,56
+};
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/playercode/resample.S	Fri Jan 23 16:05:09 1998 +0000
@@ -0,0 +1,316 @@
+/* GCC port of mixing code by Barubary (barubary@dal.net)
+   This should work on both Linux and DJGPP...  I have confirmed that
+   self-modifying code works just fine in Linux without causing signals.
+   This may change in the future, so I recommend that the Linux MikMod
+   developers look out for new kernel releases.  The critical thing is that
+   CS.base = DS.base in Linux, which makes DS an alias for CS, and that the
+   code pages remain writable. */
+
+.data          /* self-modifying code... keep in data segment */
+
+#define _AsmStereoNormal AsmStereoNormal
+#define _AsmStereoSurround AsmStereoSurround
+#define _AsmMonoNormal AsmMonoNormal
+#define _AsmMix32To16Surround AsmMix32To16Surround
+#define _AsmMix32To8Surround AsmMix32To8Surround
+#define _bitshift bitshift
+#define _lvoltab lvoltab
+#define _rvoltab rvoltab
+
+    .globl  _AsmStereoNormal
+    .globl  _AsmStereoSurround
+    .globl  _AsmMonoNormal
+    .globl  _AsmMix32To16Surround
+    .globl  _AsmMix32To8Surround
+
+
+#define STUBSTART \
+    pushl   %eax; \
+    pushl   %ebx; \
+    pushl   %ecx; \
+    pushl   %edx; \
+    pushl   %esi; \
+    pushl   %edi; \
+    pushl   %ebp
+
+#define STUBEND \
+    popl    %ebp; \
+    popl    %edi; \
+    popl    %esi; \
+    popl    %edx; \
+    popl    %ecx; \
+    popl    %ebx; \
+    popl    %eax
+
+#define SS2F(index,lab1,lab2,lab3,lab4) \
+    movb (%edx),%al; \
+    .byte 0x081,0x0c3; \
+    lab1: .int 0; \
+    .byte 0x081,0x0d2; \
+    lab2: .int 0; \
+    movl index*8(%edi),%esi; \
+    .byte 0x08b,0x0c,0x085; \
+    lab3: .int 0; \
+    movl index*8+4(%edi),%ebp; \
+    addl %ecx,%esi; \
+    .byte 0x08b,0x0c,0x085; \
+    lab4: .int 0; \
+    movl %esi,index*8(%edi); \
+    addl %ecx,%ebp
+
+#define SS2M(index,lab1,lab2,lab3,lab4) \
+    movb (%edx),%al; \
+    movl %ebp,index*8-4(%edi); \
+    .byte 0x081,0x0c3; \
+    lab1: .int 0; \
+    .byte 0x081,0x0d2; \
+    lab2: .int 0; \
+    movl index*8(%edi),%esi; \
+    .byte 0x08b,0x0c,0x085; \
+    lab3: .int 0; \
+    movl index*8+4(%edi),%ebp; \
+    addl %ecx,%esi; \
+    .byte 0x08b,0x0c,0x085; \
+    lab4: .int 0; \
+    movl %esi,index*8(%edi); \
+    addl %ecx,%ebp
+
+#define SS3F(index,lab1,lab2,lab3) \
+    movb (%edx),%al; \
+    .byte 0x081,0x0c3; \
+    lab1: .int 0; \
+    .byte 0x081,0x0d2; \
+    lab2: .int 0; \
+    movl index*8(%edi),%esi; \
+    .byte 0x08b,0x0c,0x085; \
+    lab3: .int 0; \
+    movl index*8+4(%edi),%ebp; \
+    addl %ecx,%esi; \
+    subl %ecx,%ebp; \
+    movl %esi,index*8(%edi); \
+
+#define SS3M(index,lab1,lab2,lab3) \
+    movb (%edx),%al; \
+    movl %ebp,index*8-4(%edi); \
+    .byte 0x081,0x0c3; \
+    lab1: .int 0; \
+    .byte 0x081,0x0d2; \
+    lab2: .int 0; \
+    movl index*8(%edi),%esi; \
+    .byte 0x08b,0x0c,0x085; \
+    lab3: .int 0; \
+    movl index*8+4(%edi),%ebp; \
+    addl %ecx,%esi; \
+    subl %ecx,%ebp; \
+    movl %esi,index*8(%edi); \
+
+#define SS2L(index) \
+    movl %ebp,index*8-4(%edi)
+
+#define SM2F(index,lab1,lab2,lab3) \
+    movb (%edx),%al; \
+    .byte 0x081,0x0c3; \
+    lab1: .int 0; \
+    .byte 0x081,0x0d2; \
+    lab2: .int 0; \
+    movl index*4(%edi),%esi; \
+    .byte 0x08b,0x0c,0x085; \
+    lab3: .int 0
+
+#define SM2M(index,lab1,lab2,lab3) \
+    movb (%edx),%al; \
+    addl %ecx,%esi; \
+    .byte 0x081,0x0c3; \
+    lab1: .int 0; \
+    movl %esi,index*4-4(%edi); \
+    .byte 0x081,0x0d2; \
+    lab2: .int 0; \
+    movl index*4(%edi),%esi; \
+    .byte 0x08b,0x0c,0x085; \
+    lab3: .int 0
+
+#define SM2L(index) \
+    addl %ecx,%esi; \
+    movl %esi,index*4-4(%edi)
+
+_AsmStereoNormal:
+    STUBSTART       
+    movl    32(%esp),%esi   /* get src */
+    movl    36(%esp),%edi   /* get dst */
+    movl    40(%esp),%ebx   /* get index */
+    movl    44(%esp),%ecx   /* get increment */
+    movl    48(%esp),%ebp   /* get todo */
+    movl    %ecx,%eax
+    movl    %ebx,%edx
+    sarl    $31,%eax
+    sarl    $31,%edx
+    shldl   $21,%ecx,%eax   /* convert to 32:32 */
+    shll    $21,%ecx
+    shldl   $21,%ebx,%edx   /* convert to 32:32 */
+    shll    $21,%ebx
+    addl    %esi,%edx
+    movl    %eax,shi1
+    movl    %eax,shi2
+    movl    %eax,shi3
+    movl    %eax,shi4
+    movl    %eax,shi5
+    movl    _lvoltab,%eax
+    movl    %ecx,slo1
+    movl    %ecx,slo2
+    movl    %ecx,slo3
+    movl    %ecx,slo4
+    movl    %ecx,slo5
+    movl    %eax,sltab1
+    movl    %eax,sltab2
+    movl    %eax,sltab3
+    movl    %eax,sltab4
+    movl    %eax,sltab5
+    movl    _rvoltab,%eax
+    pushl   %ebp
+    movl    %eax,srtab1
+    movl    %eax,srtab2
+    movl    %eax,srtab3
+    movl    %eax,srtab4
+    movl    %eax,srtab5
+    xorl    %eax,%eax
+    jmp s1  /* flush code cache */
+s1:             
+    shrl    $2,%ebp
+    jz      sskip16
+    pushl   %ebp
+sagain16:               
+    SS2F(0,slo1,shi1,sltab1,srtab1)
+    SS2M(1,slo2,shi2,sltab2,srtab2)
+    SS2M(2,slo3,shi3,sltab3,srtab3)
+    SS2M(3,slo4,shi4,sltab4,srtab4)
+    SS2L(4)
+    addl    $32,%edi
+    decl    (%esp)
+    jnz     sagain16
+    popl    %ebp
+sskip16:                
+    popl    %ebp
+    andl    $3,%ebp
+    jz      sskip1
+    pushl   %ebp
+sagain1:                
+    SS2F(0,slo5,shi5,sltab5,srtab5)
+    SS2L(1)
+    addl    $8,%edi
+    decl (%esp)
+    jnz     sagain1
+    popl    %ebp
+sskip1:         
+    STUBEND 
+    ret     
+
+
+_AsmStereoSurround:
+    STUBSTART       
+    movl    32(%esp),%esi   /* get src */
+    movl    36(%esp),%edi   /* get dst */
+    movl    40(%esp),%ebx   /* get index */
+    movl    44(%esp),%ecx   /* get increment */
+    movl    48(%esp),%ebp   /* get todo */
+    movl    %ecx,%eax
+    movl    %ebx,%edx
+    sarl    $31,%eax
+    sarl    $31,%edx
+    shldl   $21,%ecx,%eax   /* convert to 32:32 */
+    shll    $21,%ecx
+    shldl   $21,%ebx,%edx   /* convert to 32:32 */
+    shll    $21,%ebx
+    addl    %esi,%edx
+    movl    %eax,s2hi1
+    movl    %eax,s2hi2
+    movl    %eax,s2hi3
+    movl    %eax,s2hi4
+    movl    %eax,s2hi5
+    movl    _lvoltab,%eax
+    movl    %ecx,s2lo1
+    movl    %ecx,s2lo2
+    movl    %ecx,s2lo3
+    movl    %ecx,s2lo4
+    movl    %ecx,s2lo5
+    movl    %eax,s2ltab1
+    movl    %eax,s2ltab2
+    movl    %eax,s2ltab3
+    movl    %eax,s2ltab4
+    movl    %eax,s2ltab5
+    pushl   %ebp
+    xorl    %eax,%eax
+    jmp s3  /* flush code cache */
+s3:             
+    shrl    $2,%ebp
+    jz      s2skip16
+    pushl   %ebp
+s2again16:               
+    SS3F(0,s2lo1,s2hi1,s2ltab1)
+    SS3M(1,s2lo2,s2hi2,s2ltab2)
+    SS3M(2,s2lo3,s2hi3,s2ltab3)
+    SS3M(3,s2lo4,s2hi4,s2ltab4)
+    SS2L(4)
+    addl    $32,%edi
+    decl    (%esp)
+    jnz     s2again16
+    popl    %ebp
+s2skip16:                
+    popl    %ebp
+    andl    $3,%ebp
+    jz      s2skip1
+    pushl   %ebp
+s2again1:                
+    SS3F(0,s2lo5,s2hi5,s2ltab5)
+    SS2L(1)
+    addl    $8,%edi
+    decl    (%esp)
+    jnz     s2again1
+    popl    %ebp
+s2skip1:         
+    STUBEND 
+    ret     
+
+
+_AsmMonoNormal:
+    STUBSTART       
+    movl    32(%esp),%esi   /* get src */
+    movl    36(%esp),%edi   /* get dst */
+    movl    40(%esp),%ebx   /* get index */
+    movl    44(%esp),%ecx   /* get increment */
+    movl    48(%esp),%ebp   /* get todo */
+    movl    %ecx,%eax
+    movl    %ebx,%edx
+    sarl    $31,%eax
+    sarl    $31,%edx
+    shldl   $21,%ecx,%eax   /* convert to 32:32 */
+    shll    $21,%ecx
+    shldl   $21,%ebx,%edx   /* convert to 32:32 */
+    shll    $21,%ebx
+    addl    %esi,%edx
+    movl    %eax,mhi1
+    movl    %eax,mhi2
+    movl    %eax,mhi3
+    movl    %eax,mhi4
+    movl    %eax,mhi5
+    movl    _lvoltab,%eax
+    movl    %ecx,mlo1
+    movl    %ecx,mlo2
+    movl    %ecx,mlo3
+    movl    %ecx,mlo4
+    movl    %ecx,mlo5
+    movl    %eax,mltab1
+    movl    %eax,mltab2
+    movl    %eax,mltab3
+    movl    %eax,mltab4
+    movl    %eax,mltab5
+    xorl    %eax,%eax
+
+    jmp m1  /* flush code cache */
+m1:             
+    pushl   %ebp
+    shrl    $2,%ebp
+    jz      mskip16
+magain16:               
+    SM2F(0,mlo1,mhi1,mltab1)
+    SM2M(1,mlo2,mhi2,mltab2)
+   
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/playercode/resample.asm	Fri Jan 23 16:05:09 1998 +0000
@@ -0,0 +1,455 @@
+.386p
+.model flat
+.data ; self-modifying code... keep in data segment
+
+        NAME    resample
+        EXTRN   _rvoltab :DWORD
+        EXTRN   _lvoltab :DWORD
+        EXTRN   _bitshift :BYTE
+
+        PUBLIC  _AsmStereoNormal
+        PUBLIC  _AsmStereoSurround
+        PUBLIC  _AsmMonoNormal
+IFDEF __ASMSTUFF__
+        PUBLIC  _AsmMix32To16_Normal
+        PUBLIC  _AsmMix32To8_Normal
+ENDIF
+
+
+STUBSTART macro
+  push eax
+  push ebx
+  push ecx
+  push edx
+  push esi
+  push edi
+  push ebp
+  endm
+
+
+STUBEND macro
+  pop ebp
+  pop edi
+  pop esi
+  pop edx
+  pop ecx
+  pop ebx
+  pop eax
+  endm
+
+
+SS2F MACRO index,lab1,lab2,lab3,lab4 ; 486+
+ mov al,[edx]
+ db 081h,0c3h
+lab1 dd 0 ; add ebx,lo
+ db 081h,0d2h
+lab2 dd 0 ; adc edx,hi
+ mov esi,(index*8)[edi]
+ db 08bh,0ch,085h
+lab3 dd 0 ; mov ecx,[eax*4+table1]
+ mov ebp,(index*8+4)[edi]
+ add esi,ecx
+ db 08bh,0ch,085h
+lab4 dd 0 ; mov ecx,[eax*4+table2]
+ mov (index*8)[edi],esi
+ add ebp,ecx
+ ENDM
+
+SS2M MACRO index,lab1,lab2,lab3,lab4 ; 486+
+ mov al,[edx]
+ mov (index*8-4)[edi],ebp
+ db 081h,0c3h
+lab1 dd 0 ; add ebx,lo
+ db 081h,0d2h
+lab2 dd 0 ; adc edx,hi
+ mov esi,(index*8)[edi]
+ db 08bh,0ch,085h
+lab3 dd 0 ; mov ecx,[eax*4+table1]
+ mov ebp,(index*8+4)[edi]
+ add esi,ecx
+ db 08bh,0ch,085h
+lab4 dd 0 ; mov ecx,[eax*4+table2]
+ mov (index*8)[edi],esi
+ add ebp,ecx
+ ENDM
+
+SS3F MACRO index,lab1,lab2,lab3 ; 486+
+ mov al,[edx]
+ db 081h,0c3h
+lab1 dd 0 ; add ebx,lo
+ db 081h,0d2h
+lab2 dd 0 ; adc edx,hi
+ mov esi,(index*8)[edi]
+ db 08bh,0ch,085h
+lab3 dd 0 ; mov ecx,[eax*4+table1]
+ mov ebp,(index*8+4)[edi]
+ add esi,ecx
+ sub ebp,ecx
+ mov (index*8)[edi],esi
+ ENDM
+
+SS3M MACRO index,lab1,lab2,lab3 ; 486+
+ mov al,[edx]
+ mov (index*8-4)[edi],ebp
+ db 081h,0c3h
+lab1 dd 0 ; add ebx,lo
+ db 081h,0d2h
+lab2 dd 0 ; adc edx,hi
+ mov esi,(index*8)[edi]
+ db 08bh,0ch,085h
+lab3 dd 0 ; mov ecx,[eax*4+table1]
+ mov ebp,(index*8+4)[edi]
+ add esi,ecx
+ sub ebp,ecx
+ mov (index*8)[edi],esi
+ ENDM
+
+SS2L MACRO index ; 486+
+ mov (index*8-4)[edi],ebp
+ ENDM
+
+SM2F MACRO index,lab1,lab2,lab3 ; 486+
+ mov al,[edx] ; AGI-3
+ db 081h,0c3h
+lab1 dd 0 ; add ebx,lo
+ db 081h,0d2h
+lab2 dd 0 ; adc edx,hi
+ mov esi,(index*4)[edi]
+ db 08bh,0ch,085h
+lab3 dd 0 ; mov ecx,[eax*4+table1]
+ ENDM
+
+SM2M MACRO index,lab1,lab2,lab3 ; 486+
+ mov al,[edx] ; AGI-3
+ add esi,ecx
+ db 081h,0c3h
+lab1 dd 0 ; add ebx,lo
+ mov (index*4-4)[edi],esi
+ db 081h,0d2h
+lab2 dd 0 ; adc edx,hi
+ mov esi,(index*4)[edi]
+ db 08bh,0ch,085h
+lab3 dd 0 ; mov ecx,[eax*4+table1]
+ ENDM
+
+SM2L MACRO index ; 486+
+ add esi,ecx
+ mov (index*4-4)[edi],esi
+ ENDM
+
+;SS2 ; 386
+;mov al,[edx+source]
+;add ebx,lo
+;adc edx,hi
+;mov ecx,[eax*4+table1]
+;mov ebp,[eax*4+table2]
+;add [edi],ecx
+;add [edi+4],ebp
+;ENDM
+
+;SS2M ; 486+
+;mov al,[edx+source]
+;mov [edi-8+4],ebp
+;add ebx,lo
+;adc edx,hi
+;mov esi,[edi]
+;mov ecx,[eax*4+table1]
+;mov ebp,[edi+4]
+;add esi,ecx
+;mov ecx,[eax*4+table2]
+;mov [edi],esi
+;add ebp,ecx
+;ENDM
+
+;SM2M ; 386
+;add ebx,lo
+;adc edx,hi
+;mov ecx,[eax*4+table1]
+;mov al,[edx+source]
+;add [edi],ecx
+;ENDM
+
+;SM2M ; 486+
+;add ebx,lo
+;mov [edi-4],esi
+;adc edx,hi
+;mov esi,[edi]
+;mov ecx,[eax*4+table1]
+;mov al,[edx+source] ; AGI-3
+;add esi,ecx
+;ENDM
+
+
+_AsmStereoNormal:
+        STUBSTART
+        mov    esi,[esp+32] ; get src
+        mov    edi,[esp+36] ; get dst
+        mov    ebx,[esp+40] ; get index
+        mov    ecx,[esp+44] ; get increment
+        mov    ebp,[esp+48] ; get todo
+        mov    eax,ecx
+        mov    edx,ebx
+        sar    eax,31
+        sar    edx,31
+        shld   eax,ecx,21 ; convert to 32:32
+        shl    ecx,21
+        shld   edx,ebx,21 ; convert to 32:32
+        shl    ebx,21
+        add    edx,esi
+        mov    shi1,eax
+        mov    shi2,eax
+        mov    shi3,eax
+        mov    shi4,eax
+        mov    shi5,eax
+        mov    eax,_lvoltab
+        mov    slo1,ecx
+        mov    slo2,ecx
+        mov    slo3,ecx
+        mov    slo4,ecx
+        mov    slo5,ecx
+        mov    sltab1,eax
+        mov    sltab2,eax
+        mov    sltab3,eax
+        mov    sltab4,eax
+        mov    sltab5,eax
+        mov    eax,_rvoltab
+        push   ebp
+        mov    srtab1,eax
+        mov    srtab2,eax
+        mov    srtab3,eax
+        mov    srtab4,eax
+        mov    srtab5,eax
+        xor    eax,eax
+        jmp    s1 ; flush code cache
+s1:
+        shr    ebp,2
+        jz     sskip16
+        push   ebp
+sagain16:
+        SS2F   0,slo1,shi1,sltab1,srtab1
+        SS2M   1,slo2,shi2,sltab2,srtab2
+        SS2M   2,slo3,shi3,sltab3,srtab3
+        SS2M   3,slo4,shi4,sltab4,srtab4
+        SS2L   4
+        add    edi,(4*8)
+        dec    dword ptr [esp]
+        jnz    sagain16
+        pop    ebp
+sskip16:
+        pop    ebp
+        and    ebp,3
+        jz     sskip1
+        push   ebp
+sagain1:
+        SS2F   0,slo5,shi5,sltab5,srtab5
+        SS2L   1
+        add    edi,8
+        dec    dword ptr [esp]
+        jnz    sagain1
+        pop    ebp
+sskip1:
+        STUBEND
+        ret
+
+
+_AsmStereoSurround:
+        STUBSTART
+        mov    esi,[esp+32] ; get src
+        mov    edi,[esp+36] ; get dst
+        mov    ebx,[esp+40] ; get index
+        mov    ecx,[esp+44] ; get increment
+        mov    ebp,[esp+48] ; get todo
+        mov    eax,ecx
+        mov    edx,ebx
+        sar    eax,31
+        sar    edx,31
+        shld   eax,ecx,21 ; convert to 32:32
+        shl    ecx,21
+        shld   edx,ebx,21 ; convert to 32:32
+        shl    ebx,21
+        add    edx,esi
+        mov    s2hi1,eax
+        mov    s2hi2,eax
+        mov    s2hi3,eax
+        mov    s2hi4,eax
+        mov    s2hi5,eax
+        mov    eax,_lvoltab
+        mov    s2lo1,ecx
+        mov    s2lo2,ecx
+        mov    s2lo3,ecx
+        mov    s2lo4,ecx
+        mov    s2lo5,ecx
+        mov    s2ltab1,eax
+        mov    s2ltab2,eax
+        mov    s2ltab3,eax
+        mov    s2ltab4,eax
+        mov    s2ltab5,eax
+        ;mov    eax,_rvoltab
+        push   ebp
+        ;mov    s2rtab1,eax
+        ;mov    s2rtab2,eax
+        ;mov    s2rtab3,eax
+        ;mov    s2rtab4,eax
+        ;mov    s2rtab5,eax
+        xor    eax,eax
+        jmp    s3 ; flush code cache
+s3:
+        shr    ebp,2
+        jz     s2skip16
+        push   ebp
+s2again16:
+        SS3F   0,s2lo1,s2hi1,s2ltab1  ;,s2rtab1
+        SS3M   1,s2lo2,s2hi2,s2ltab2  ;,s2rtab2
+        SS3M   2,s2lo3,s2hi3,s2ltab3  ;,s2rtab3
+        SS3M   3,s2lo4,s2hi4,s2ltab4  ;,s2rtab4
+        SS2L   4
+        add    edi,(4*8)
+        dec    dword ptr [esp]
+        jnz    s2again16
+        pop    ebp
+s2skip16:
+        pop    ebp
+        and    ebp,3
+        jz     s2skip1
+        push   ebp
+s2again1:
+        SS3F   0,s2lo5,s2hi5,s2ltab5  ;,s2rtab5
+        SS2L   1
+        add    edi,8
+        dec    dword ptr [esp]
+        jnz    s2again1
+        pop    ebp
+s2skip1:
+        STUBEND
+        ret
+
+
+_AsmMonoNormal:
+        STUBSTART
+        mov    esi,[esp+32] ; get src
+        mov    edi,[esp+36] ; get dst
+        mov    ebx,[esp+40] ; get index
+        mov    ecx,[esp+44] ; get increment
+        mov    ebp,[esp+48] ; get todo
+        mov    eax,ecx
+        mov    edx,ebx
+        sar    eax,31
+        sar    edx,31
+        shld   eax,ecx,21 ; convert to 32:32
+        shl    ecx,21
+        shld   edx,ebx,21 ; convert to 32:32
+        shl    ebx,21
+        add    edx,esi
+        mov    mhi1,eax
+        mov    mhi2,eax
+        mov    mhi3,eax
+        mov    mhi4,eax
+        mov    mhi5,eax
+        mov    eax,_lvoltab
+        mov    mlo1,ecx
+        mov    mlo2,ecx
+        mov    mlo3,ecx
+        mov    mlo4,ecx
+        mov    mlo5,ecx
+        mov    mltab1,eax
+        mov    mltab2,eax
+        mov    mltab3,eax
+        mov    mltab4,eax
+        mov    mltab5,eax
+        xor    eax,eax
+
+        jmp    m1 ; flush code cache
+m1:
+        push   ebp
+        shr    ebp,2
+        jz     mskip16
+magain16:
+        SM2F   0,mlo1,mhi1,mltab1
+        SM2M   1,mlo2,mhi2,mltab2
+        SM2M   2,mlo3,mhi3,mltab3
+        SM2M   3,mlo4,mhi4,mltab4
+        SM2L   4
+        add    edi,(4*4)
+        dec    ebp
+        jnz    magain16
+mskip16:
+        pop    ebp
+        and    ebp,3
+        jz     mskip1
+magain1:
+        SM2F   0,mlo5,mhi5,mltab5
+        SM2L   1
+        add    edi,4
+        dec    ebp
+        jnz    magain1
+mskip1:
+        STUBEND
+        ret
+
+
+
+IFDEF __ASMSTUFF__
+_AsmMix32To16_Normal:
+        STUBSTART
+        mov   edi,[esp+32] ; get dest
+        mov   esi,[esp+36] ; get src
+        mov   edx,[esp+40] ; get count
+        mov    cl,[_bitshift]
+again:
+        mov   eax,[esi]
+        sar   eax,cl
+        cmp   eax,32767
+        jg    toobig
+        cmp   eax,-32768
+        jl    toosmall
+write:
+        mov   [edi],ax
+        add   esi,4
+        add   edi,2
+        dec   edx
+        jnz   again
+        jmp   ready
+toobig:
+        mov   eax,32767
+        jmp   write
+toosmall:
+        mov   eax,-32768
+        jmp   write
+ready:
+        STUBEND
+        ret
+
+
+_AsmMix32To8_Normal:
+        STUBSTART
+        mov   edi,[esp+32] ; get dest
+        mov   esi,[esp+36] ; get src
+        mov   edx,[esp+40] ; get count
+        mov    cl,[_bitshift]
+cagain:
+        mov   eax,[esi]
+        sar   eax,cl
+        cmp   eax,127
+        jg    ctoobig
+        cmp   eax,-128
+        jl    ctoosmall
+cwrite:
+        add   al,080h
+        add   esi,4
+        mov   [edi],al
+        inc   edi
+        dec   edx
+        jnz   cagain
+        jmp   cready
+ctoobig:
+        mov   eax,127
+        jmp   cwrite
+ctoosmall:
+        mov   eax,-128
+        jmp   cwrite
+cready:
+        STUBEND
+        ret
+ENDIF
+
+        END
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/playercode/s3m_it.c	Fri Jan 23 16:05:09 1998 +0000
@@ -0,0 +1,187 @@
+/*
+
+Name:
+S3M_IT.C
+
+Description:
+Commonfunctions between S3Ms and ITs  (smaller .EXE! :)
+
+Portability:
+All systems - all compilers (hopefully)
+
+*/
+
+#include "mikmod.h"
+
+UBYTE  *poslookup=NULL;// S3M/IT fix - removing blank patterns needs a
+                       // lookup table to fix position-jump commands
+SBYTE   remap[64];     // for removing empty channels
+SBYTE   isused[64];    // for removing empty channels
+
+
+void S3MIT_ProcessCmd(UBYTE cmd,UBYTE inf,BOOL oldeffect)
+{
+    UBYTE hi,lo;
+
+    lo=inf&0xf;
+    hi=inf>>4;
+
+    // process S3M / IT specific command structure
+
+   if(cmd!=255)
+   {   switch(cmd)
+       {   case 1:                 // Axx set speed to xx
+              UniWrite(UNI_S3MEFFECTA);
+              UniWrite(inf);
+           break;
+
+           case 2:                 // Bxx position jump
+              UniPTEffect(0xb,poslookup[inf]);
+           break;
+
+           case 3:                 // Cxx patternbreak to row xx
+               if(oldeffect & 1)
+                   UniPTEffect(0xd,(((inf&0xf0)>>4)*10)+(inf&0xf));
+               else
+                   UniPTEffect(0xd,inf);
+           break;
+
+           case 4:                 // Dxy volumeslide
+               UniWrite(UNI_S3MEFFECTD);
+               UniWrite(inf);
+           break;
+
+           case 5:                 // Exy toneslide down
+               UniWrite(UNI_S3MEFFECTE);
+               UniWrite(inf);
+           break;
+
+           case 6:                 // Fxy toneslide up
+               UniWrite(UNI_S3MEFFECTF);
+               UniWrite(inf);
+           break;
+                 
+           case 7:                 // Gxx Tone portamento, speed xx
+               UniWrite(UNI_ITEFFECTG);
+               UniWrite(inf);
+           break;
+
+           case 8:                 // Hxy vibrato
+               if(oldeffect & 1)
+                  UniPTEffect(0x4,inf);
+               else
+               {   UniWrite(UNI_ITEFFECTH);
+                   UniWrite(inf);
+               }
+           break;
+
+           case 9:                 // Ixy tremor, ontime x, offtime y
+               if(oldeffect & 1)
+                   UniWrite(UNI_S3MEFFECTI);
+               else                     
+                   UniWrite(UNI_ITEFFECTI);
+               UniWrite(inf);
+           break;
+
+           case 0xa:               // Jxy arpeggio
+               UniPTEffect(0x0,inf);
+           break;
+
+           case 0xb:               // Kxy Dual command H00 & Dxy
+               if(oldeffect & 1)
+                   UniPTEffect(0x4,0);    
+               else
+               {   UniWrite(UNI_ITEFFECTH);
+                   UniWrite(0);
+               }
+               UniWrite(UNI_S3MEFFECTD);
+               UniWrite(inf);
+           break;
+
+           case 0xc:               // Lxy Dual command G00 & Dxy
+               if(oldeffect & 1)
+                  UniPTEffect(0x3,0);
+               else
+               {   UniWrite(UNI_ITEFFECTG);
+                   UniWrite(0);
+               }
+               UniWrite(UNI_S3MEFFECTD);
+               UniWrite(inf);
+           break;
+
+           case 0xd:               // Mxx Set Channel Volume
+               UniWrite(UNI_ITEFFECTM);
+               UniWrite(inf);
+           break;       
+
+           case 0xe:               // Nxy Slide Channel Volume
+               UniWrite(UNI_ITEFFECTN);
+               UniWrite(inf);
+           break;
+
+           case 0xf:               // Oxx set sampleoffset xx00h
+               UniPTEffect(0x9,inf);
+           break;
+
+           case 0x10:              // Pxy Slide Panning Commands
+               UniWrite(UNI_ITEFFECTP);
+               UniWrite(inf);
+           break;
+
+           case 0x11:              // Qxy Retrig (+volumeslide)
+               UniWrite(UNI_S3MEFFECTQ);
+               if((inf!=0) && (lo==0) && !(oldeffect & 1))
+                   UniWrite(1);
+               else
+                   UniWrite(inf); 
+           break;
+
+           case 0x12:              // Rxy tremolo speed x, depth y
+               UniWrite(UNI_S3MEFFECTR);
+               UniWrite(inf);
+           break;
+
+           case 0x13:              // Sxx special commands
+               UniWrite(UNI_ITEFFECTS0);
+               UniWrite(inf);
+           break;
+
+           case 0x14:      // Txx tempo
+               if(inf>0x20)
+               {   UniWrite(UNI_S3MEFFECTT);
+                   UniWrite(inf);
+               }
+           break;
+
+           case 0x15:      // Uxy Fine Vibrato speed x, depth y
+               if(oldeffect & 1)
+                   UniWrite(UNI_S3MEFFECTU);
+               else
+                   UniWrite(UNI_ITEFFECTU);
+               UniWrite(inf);
+           break;
+
+           case 0x16:      // Vxx Set Global Volume
+               UniWrite(UNI_XMEFFECTG);
+               UniWrite(inf);
+           break;
+
+           case 0x17:      // Wxy Global Volume Slide
+               UniWrite(UNI_ITEFFECTW);
+               UniWrite(inf);  
+           break;
+
+           case 0x18:      // Xxx amiga command 8xx
+               if(oldeffect & 1)
+                   UniPTEffect(0x8,((inf<<1) == 256) ? 255 : (inf<<1));
+               else
+                   UniPTEffect(0x8,inf);                                          
+           break;
+
+           case 0x19:      // Yxy Panbrello  speed x, depth y
+               UniWrite(UNI_ITEFFECTY);
+               UniWrite(inf);
+           break;
+       }
+   }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/playercode/sloader.c	Fri Jan 23 16:05:09 1998 +0000
@@ -0,0 +1,407 @@
+/*
+ --> Sample Loaders and Sample Processing
+
+Name: sloader.c
+
+Description:
+Routines for loading samples.  The sample loader utilizes the routines
+provided by the "registered" sample loader.  See SAMPLELOADER in
+MIKMOD.H for the sample loader structure.
+
+Portability:
+All systems - all compilers
+
+*/
+
+#include "mikmod.h"
+
+
+static int   sl_rlength;
+static SWORD sl_old;
+static SWORD *sl_buffer = NULL;
+static SAMPLOAD *musiclist = NULL,
+                *sndfxlist = NULL;
+
+
+
+BOOL SL_Init(SAMPLOAD *s)        // returns 0 on error!
+{
+    if(sl_buffer==NULL)
+         if((sl_buffer=_mm_malloc(4100)) == NULL) return 0;
+
+    sl_rlength = s->length;
+    if(s->infmt & SF_16BITS) sl_rlength>>=1;
+    sl_old = 0;
+
+    return 1;
+}
+
+
+void SL_Exit(SAMPLOAD *s)
+{
+    if(sl_rlength > 0) _mm_fseek(s->fp,sl_rlength,SEEK_CUR);
+}
+
+
+void SL_Reset(void)
+{
+    sl_old = 0;
+}
+
+
+void SL_Load(void *buffer, SAMPLOAD *smp, int length)
+{
+    UWORD infmt = smp->infmt, outfmt = smp->outfmt;
+    SBYTE *bptr = (SBYTE *)buffer;
+    SWORD *wptr = (SWORD *)buffer;
+    int   stodo;
+    int   t, u;
+
+    while(length)
+    {   stodo = (length<2048) ? length : 2048;
+
+        if(infmt & SF_16BITS)
+        {   if(infmt & SF_BIG_ENDIAN)
+                _mm_read_M_SWORDS(sl_buffer,stodo,smp->fp);
+            else
+                _mm_read_I_SWORDS(sl_buffer,stodo,smp->fp);
+        } else
+        {   SBYTE  *s;
+            SWORD  *d;
+
+            fread(sl_buffer,sizeof(SBYTE),stodo,smp->fp);
+
+            s  = (SBYTE *)sl_buffer;
+            d  = sl_buffer;
+            s += stodo;
+            d += stodo;
+
+            for(t=0; t<stodo; t++)
+            {   s--;
+                d--;
+                *d = (*s)<<8;
+            }
+        }
+
+        if(infmt & SF_DELTA)
+        {   for(t=0; t<stodo; t++)
+            {   sl_buffer[t] += sl_old;
+                sl_old = sl_buffer[t];
+            }
+        }
+
+        if((infmt^outfmt) & SF_SIGNED)
+        {   for(t=0; t<stodo; t++)
+               sl_buffer[t] ^= 0x8000;
+        }
+
+        if(smp->scalefactor)
+        {   int   idx = 0;
+            SLONG scaleval;
+
+            // Sample Scaling... average values for better results.
+            t = 0;
+            while(t<stodo && length)
+            {   scaleval = 0;
+                for(u=smp->scalefactor; u && t<stodo; u--, t++)
+                    scaleval += sl_buffer[t];
+                sl_buffer[idx++] = scaleval / (smp->scalefactor-u);
+                length--;
+            }
+            sl_rlength -= stodo;
+            stodo = idx;
+        } else
+        {   length -= stodo;
+            sl_rlength -= stodo;
+        }
+
+        if(outfmt & SF_16BITS)
+        {   for(t=0; t<stodo; t++) *(wptr++) = sl_buffer[t];
+        } else
+        {   for(t=0; t<stodo; t++) *(bptr++) = sl_buffer[t]>>8;
+        }
+    }
+}
+
+
+void SL_LoadStream(void *buffer, UWORD infmt, UWORD outfmt, int length, FILE *fp)
+
+// This is like SL_Load, but does not perform sample scaling, and does not
+// require calls to SL_Init / SL_Exit.
+
+{
+    SBYTE *bptr = (SBYTE *)buffer;
+    SWORD *wptr = (SWORD *)buffer;
+    int   stodo;
+    int   t;
+
+    // compute number of samples to load
+
+    if(sl_buffer==NULL)
+         if((sl_buffer=_mm_malloc(4100)) == NULL) return;
+
+    while(length)
+    {   stodo = (length<2048) ? length : 2048;
+
+        if(infmt & SF_16BITS)
+        {   if(infmt & SF_BIG_ENDIAN)
+                _mm_read_M_SWORDS(sl_buffer,stodo,fp);
+            else
+                _mm_read_I_SWORDS(sl_buffer,stodo,fp);
+        } else
+        {   SBYTE  *s;
+            SWORD  *d;
+
+            fread(sl_buffer,sizeof(SBYTE),stodo,fp);
+
+            s  = (SBYTE *)sl_buffer;
+            d  = sl_buffer;
+            s += stodo;
+            d += stodo;
+
+            for(t=0; t<stodo; t++)
+            {   s--;
+                d--;
+                *d = (*s)<<8;
+            }
+        }
+
+        if(infmt & SF_DELTA)
+        {   for(t=0; t<stodo; t++)
+            {   sl_buffer[t] += sl_old;
+                sl_old = sl_buffer[t];
+            }
+        }
+
+        if((infmt^outfmt) & SF_SIGNED)
+        {   for(t=0; t<stodo; t++)
+               sl_buffer[t] ^= 0x8000;
+        }
+
+        length -= stodo;
+
+        if((infmt & SF_STEREO) && !(outfmt & SF_STEREO))
+        {   // Dither stereo to mono .. average together every two samples
+            SLONG avgval;
+            int   idx = 0;
+
+            t = 0;
+            while(t<stodo && length)
+            {   avgval  = sl_buffer[t++];
+                avgval += sl_buffer[t++];
+                sl_buffer[idx++] = avgval >> 1; 
+                length-=2;
+            }
+            stodo = idx;
+        }
+
+        if(outfmt & SF_16BITS)
+        {   for(t=0; t<stodo; t++) *(wptr++) = sl_buffer[t];
+        } else
+        {   for(t=0; t<stodo; t++) *(bptr++) = sl_buffer[t]>>8;
+        }
+    }
+}
+
+
+SAMPLOAD *SL_RegisterSample(SAMPLE *s, int type, FILE *fp)    // Returns 1 on error!
+
+// Registers a sample for loading when SL_LoadSamples() is called.
+//   type  -  type of sample to be loaded ..
+//             MD_MUSIC, MD_SNDFX
+
+{
+    SAMPLOAD *news, **samplist, *cruise;
+
+    if(type==MD_MUSIC)
+    {   samplist = & musiclist;
+        cruise = musiclist;
+    } else
+    {   samplist = &sndfxlist;
+        cruise = sndfxlist;
+    }
+
+    // Allocate and add structure to the END of the list
+
+    if((news=(SAMPLOAD *)_mm_calloc(1,sizeof(SAMPLOAD))) == NULL) return NULL;
+
+    if(cruise!=NULL)
+    {   while(cruise->next!=NULL)  cruise = cruise->next;
+        cruise->next = news;
+    } else
+        *samplist = news;
+
+    news->infmt     = s->flags & 31;
+    news->outfmt    = news->infmt;
+    news->fp        = fp;
+    news->sample    = s;
+    news->length    = s->length;
+    news->loopstart = s->loopstart;
+    news->loopend   = s->loopend;
+
+    return news;
+}
+
+
+static void FreeSampleList(SAMPLOAD *s)
+{
+    SAMPLOAD *old;
+
+    while(s!=NULL)
+    {   old = s;
+        s = s->next;
+        free(old);
+    }
+}
+
+
+static ULONG SampleTotal(SAMPLOAD *samplist, int type)
+// Returns the total amount of memory required by the samplelist queue.
+{
+    int total = 0;
+
+    while(samplist!=NULL)
+    {   samplist->sample->flags = (samplist->sample->flags&~31) | samplist->outfmt;
+        total += MD_SampleLength(type,samplist->sample);
+        samplist = samplist->next;
+    }
+
+    return total;
+}
+
+
+static ULONG RealSpeed(SAMPLOAD *s)
+{
+    return(s->sample->speed / ((s->scalefactor==0) ? 1 : s->scalefactor));
+}    
+
+
+static BOOL DitherSamples(SAMPLOAD *samplist, int type)
+{
+    SAMPLOAD  *c2smp;
+    ULONG     maxsize, speed;
+    SAMPLOAD  *s;
+
+    if(samplist==NULL) return 0;
+
+    // make sure the samples will fit inside available RAM
+    if((maxsize = MD_SampleSpace(type)*1024) != 0)
+    {   while(SampleTotal(samplist, type) > maxsize)
+        {   // First Pass - check for any 16 bit samples
+            s = samplist;
+            while(s!=NULL)
+            {   if(s->outfmt & SF_16BITS)
+                {   SL_Sample16to8(s);
+                    break;
+                }
+                s = s->next;
+            }
+    
+            // Second pass (if no 16bits found above) is to take the sample
+            // with the highest speed and dither it by half.
+            if(s==NULL)
+            {   s = samplist;
+                speed = 0;
+                while(s!=NULL)
+                {   if((s->sample->length) && (RealSpeed(s) > speed))
+                    {   speed = RealSpeed(s);
+                        c2smp = s;
+                    }
+                    s = s->next;
+                }
+                SL_HalveSample(c2smp);
+            }
+        }
+    }
+
+
+    // Samples dithered, now load them!
+    // ================================
+
+    s = samplist;
+    while(s != NULL)
+    {   // sample has to be loaded ? -> increase number of
+        // samples, allocate memory and load sample.
+
+        if(s->sample->length)
+        {   if(s->sample->seekpos)
+               _mm_fseek(s->fp, s->sample->seekpos, SEEK_SET);
+
+            // Call the sample load routine of the driver module.
+            // It has to return a 'handle' (>=0) that identifies
+            // the sample.
+
+            s->sample->handle = MD_SampleLoad(s, type, s->fp);
+            s->sample->flags  = (s->sample->flags & ~31) | s->outfmt;
+            if(s->sample->handle < 0)
+            {   FreeSampleList(samplist);
+                if(_mm_errorhandler!=NULL) _mm_errorhandler();
+                return 1;
+            }
+        }
+        s = s->next;
+    }
+
+    FreeSampleList(samplist);
+    return 0;
+}
+
+
+BOOL SL_LoadSamples(void)      // Returns 1 on error!
+{
+    BOOL ok;
+
+    _mm_critical = 0;
+
+    if((musiclist==NULL) && (sndfxlist==NULL)) return 0;
+//    MikMod_Exit();
+//    exit(1);
+    ok = DitherSamples(musiclist,MD_MUSIC) || DitherSamples(sndfxlist,MD_SNDFX);
+
+    musiclist = sndfxlist = NULL;
+
+    return ok;
+}
+
+
+void SL_Sample16to8(SAMPLOAD *s)
+{
+    s->outfmt &= ~SF_16BITS;
+    s->sample->flags = (s->sample->flags&~31) | s->outfmt;
+}
+
+
+void SL_Sample8to16(SAMPLOAD *s)
+{
+    s->outfmt |= SF_16BITS;
+    s->sample->flags = (s->sample->flags&~31) | s->outfmt;
+}
+
+
+void SL_SampleSigned(SAMPLOAD *s)
+{
+    s->outfmt |= SF_SIGNED;
+    s->sample->flags = (s->sample->flags&~31) | s->outfmt;
+}
+
+
+void SL_SampleUnsigned(SAMPLOAD *s)
+{
+    s->outfmt &= ~SF_SIGNED;
+    s->sample->flags = (s->sample->flags&~31) | s->outfmt;
+}
+
+
+void SL_HalveSample(SAMPLOAD *s)
+{
+    if(s->scalefactor)
+        s->scalefactor++;
+    else
+        s->scalefactor = 2;
+
+    s->sample->divfactor = s->scalefactor;
+    s->sample->length    = s->length / s->scalefactor;
+    s->sample->loopstart = s->loopstart / s->scalefactor;
+    s->sample->loopend   = s->loopend / s->scalefactor;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/playercode/virtch.c	Fri Jan 23 16:05:09 1998 +0000
@@ -0,0 +1,1226 @@
+/*
+
+Name:  VIRTCH.C
+
+Description:
+ Sample mixing routines, using a 32 bits mixing buffer.
+
+ Optional features include:
+   (a) 4-step reverb (for 16 bit output only)
+   (b) Interpolation of sample data during mixing
+   (c) Dolby Surround Sound
+   (d) Optimized assembly mixers for the Intel platform
+   (e) Optional high-speed or high-quality modes
+
+C Mixer Portability:
+ All Systems -- All compilers.
+
+Assembly Mixer Portability:
+
+ MSDOS:  BC(?)   Watcom(y)   DJGPP(y)
+ Win95:  ?
+ Os2:    ?
+ Linux:  y
+
+ (y) - yes
+ (n) - no (not possible or not useful)
+ (?) - may be possible, but not tested
+
+*/
+
+#include <stddef.h>
+#include <string.h>
+#include "mikmod.h"
+
+// REVERBERATION : Larger numbers result in shorter reverb duration.
+#define REVERBERATION  110000l
+
+
+#ifdef __GNUC__
+#define __cdecl
+#endif
+
+#ifdef __WATCOMC__
+#define   inline
+#endif
+
+
+// for PC-assembly mixing
+// ======================
+//
+// Uncomment both lines below for assembly mixing under WATCOM or GCC for
+// Linux.
+// Note that there is no 16 bit mixers for assembly yet (C only), so
+// defining __ASSEMBLY__ if not defining __FASTMIXER__ will lead to compiler
+// errors.
+
+#define __FASTMIXER__
+//#define __ASSEMBLY__
+
+#define FRACBITS 11
+#define FRACMASK ((1l<<FRACBITS)-1)
+
+#define TICKLSIZE 3600
+#define TICKWSIZE (TICKLSIZE*2)
+#define TICKBSIZE (TICKWSIZE*2)
+
+#ifndef MIN
+#define MIN(a,b) (((a)<(b)) ? (a) : (b))
+#endif
+
+#ifndef MAX
+#define MAX(a,b) (((a)>(b))?(a):(b))
+#endif
+
+
+typedef struct
+{   BOOL   active;
+    UWORD  infmt;
+    UWORD  flags;
+#ifdef __FASTMIXER__
+    UBYTE  *buffer;
+#else
+    UWORD  *buffer;
+#endif
+    ULONG  size;
+    ULONG  speed;
+    ULONG  speedfactor;
+    SLONG  current;
+    SLONG  increment;
+    SLONG  writepos;
+} VSTREAM;
+
+
+typedef struct
+{   UBYTE  kick;                  // =1 -> sample has to be restarted
+    UBYTE  active;                // =1 -> sample is playing
+    UWORD  flags;                 // 16/8 bits looping/one-shot
+    SWORD  handle;                // identifies the sample
+    ULONG  start;                 // start index
+    ULONG  size;                  // samplesize
+    ULONG  reppos;                // loop start
+    ULONG  repend;                // loop end
+    ULONG  frq;                   // current frequency
+    UWORD  vol;                   // current volume
+    UWORD  pan;                   // current panning position
+    SLONG  current;               // current index in the sample
+    SLONG  increment;             // fixed-point increment value
+} VINFO;
+
+
+#ifdef __FASTMIXER__
+static SBYTE **Samples;
+#else
+static SWORD **Samples;
+#endif
+
+// Volume table for 8 bit sample mixing
+#ifdef __FASTMIXER__
+static SLONG  **voltab;
+#endif
+
+static VINFO   *vinf = NULL, *vnf;
+static VSTREAM vstream;
+static ULONG   samplesthatfit;
+static BOOL    vc_stream = 0;
+static int     vc_memory, vc_softchn;
+static SLONG   idxsize,idxlpos,idxlend;
+static SLONG   TICKLEFT, *VC_TICKBUF = NULL;
+static UWORD   vc_mode;
+
+
+// Reverb control variables
+// ========================
+
+static int     RVc1, RVc2, RVc3, RVc4;
+static ULONG   RVRindex;
+
+
+// For Mono or Left Channel
+
+static SLONG  *RVbuf1 = NULL, *RVbuf2 = NULL, *RVbuf3 = NULL,
+              *RVbuf4 = NULL;
+
+// For Stereo only (Right Channel)
+//   Values start at 9 to leave room for expanding this to 8-step
+//   reverb in the future.
+
+static SLONG  *RVbuf9 = NULL, *RVbuf10 = NULL, *RVbuf11 = NULL,
+              *RVbuf12 = NULL;
+
+
+int    bitshift;           // Amplification shift (amount to decrease 32 bit mixing buffer by!)
+
+#ifdef __FASTMIXER__
+SLONG         *lvoltab, *rvoltab; // Volume Table values for use by 8 bit mixers
+#else
+static SLONG  lvolsel, rvolsel;   // Volume Selectors for 16 bit mixers.
+#endif
+
+
+
+// Define external Assembly Language Prototypes
+// ============================================
+
+#ifdef __ASSEMBLY__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __GNUC__
+#define __cdecl
+#endif
+
+void __cdecl AsmStereoNormal(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,SLONG todo);
+void __cdecl AsmStereoSurround(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,SLONG todo);
+void __cdecl AsmMonoNormal(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,SLONG todo);
+
+#ifdef __cplusplus
+};
+#endif
+
+#else
+
+#ifdef __FASTMIXER__
+
+// ==============================================================
+//  8 bit sample mixers!
+
+static SLONG MixStereoNormal(SBYTE *srce, SLONG *dest, SLONG index, SLONG increment, SLONG todo)
+{
+    UBYTE  sample1, sample2, sample3, sample4;
+    int    remain;
+
+    remain = todo & 3;
+    
+    for(todo>>=2; todo; todo--)
+    {
+        sample1 = srce[index >> FRACBITS];
+        index  += increment;
+        sample2 = srce[index >> FRACBITS];
+        index  += increment;
+        sample3 = srce[index >> FRACBITS];
+        index  += increment;
+        sample4 = srce[index >> FRACBITS];
+        index  += increment;
+        
+        *dest++ += lvoltab[sample1];
+        *dest++ += rvoltab[sample1];
+        *dest++ += lvoltab[sample2];
+        *dest++ += rvoltab[sample2];
+        *dest++ += lvoltab[sample3];
+        *dest++ += rvoltab[sample3];
+        *dest++ += lvoltab[sample4];
+        *dest++ += rvoltab[sample4];
+    }
+
+    for(; remain--; )
+    {
+        sample1    = srce[index >> FRACBITS];
+        index     += increment;
+        *dest++   += lvoltab[sample1];
+        *dest++   += rvoltab[sample1];
+    }
+    
+    return index;
+}
+
+
+static SLONG MixStereoSurround(SBYTE *srce, SLONG *dest, SLONG index, SLONG increment, SLONG todo)
+{
+    SLONG sample1, sample2, sample3, sample4;
+    int   remain;
+
+    remain = todo & 3;
+    
+    for(todo>>=2; todo; todo--)
+    {
+        sample1 = lvoltab[(UBYTE)srce[index >> FRACBITS]];
+        index  += increment;
+        sample2 = lvoltab[(UBYTE)srce[index >> FRACBITS]];
+        index  += increment;
+        sample3 = lvoltab[(UBYTE)srce[index >> FRACBITS]];
+        index  += increment;
+        sample4 = lvoltab[(UBYTE)srce[index >> FRACBITS]];
+        index  += increment;
+        
+        *dest++ += sample1;
+        *dest++ -= sample1;
+        *dest++ += sample2;
+        *dest++ -= sample2;
+        *dest++ += sample3;
+        *dest++ -= sample3;
+        *dest++ += sample4;
+        *dest++ -= sample4;
+    }
+
+    for(; remain--; )
+    {   sample1   = lvoltab[(UBYTE)srce[index >> FRACBITS]];
+        index    += increment;
+        *dest++  += sample1;
+        *dest++  -= sample1;
+    }
+
+    return index;
+}
+
+
+static SLONG MixMonoNormal(SBYTE *srce, SLONG *dest, SLONG index, SLONG increment, SLONG todo)
+{
+    UBYTE  sample1, sample2, sample3, sample4;
+    int    remain;
+
+    remain = todo & 3;
+    
+    for(todo>>=2; todo; todo--)
+    {
+        sample1 = srce[index >> FRACBITS];
+        index += increment;
+        sample2 = srce[index >> FRACBITS];
+        index += increment;
+        sample3 = srce[index >> FRACBITS];
+        index += increment;
+        sample4 = srce[index >> FRACBITS];
+        index += increment;
+
+        *dest++ += lvoltab[sample1];
+        *dest++ += lvoltab[sample2];
+        *dest++ += lvoltab[sample3];
+        *dest++ += lvoltab[sample4];
+    }
+
+    for(; remain--;)
+    {   sample1    = srce[index >> FRACBITS];
+        index     += increment;
+        *dest++   -= lvoltab[sample1];
+    }
+
+    return index;
+}
+
+#else   // not __FASTMIXER__
+
+// ==============================================================
+//  16 bit sample mixers!
+
+static SLONG MixStereoNormal(SWORD *srce, SLONG *dest, SLONG index, SLONG increment, ULONG todo)
+{
+    SWORD  sample;
+
+    for(; todo; todo--)
+    {
+        sample = srce[index >> FRACBITS];
+        index += increment;
+
+        *dest++ += lvolsel * sample;
+        *dest++ += rvolsel * sample;
+    }
+    
+    return index;
+}
+
+
+static SLONG MixStereoSurround(SWORD *srce, SLONG *dest, SLONG index, SLONG increment, ULONG todo)
+{
+    SWORD  sample;
+
+    for (; todo; todo--)
+    {
+        sample = srce[index >> FRACBITS];
+        index += increment;
+
+        *dest++ += lvolsel * sample;
+        *dest++ -= lvolsel * sample;
+    }
+    
+    return index;
+}
+
+
+static SLONG MixMonoNormal(SWORD *srce, SLONG *dest, SLONG index, SLONG increment, SLONG todo)
+{
+    SWORD  sample;
+    
+    for(; todo; todo--)
+    {
+        sample = srce[index >> FRACBITS];
+        index += increment;
+
+        *dest++ += lvolsel * sample;
+    }
+
+    return index;
+}
+
+#endif   // __FASTMIXER__
+#endif   // __ASSEMBLY__
+
+static void (*MixReverb16)(SLONG *srce, SLONG count);
+
+static void MixReverb16_Normal(SLONG *srce, SLONG count)
+{
+    unsigned int  speedup;
+    int           ReverbPct;
+    unsigned int  loc1, loc2, loc3, loc4;
+
+    ReverbPct = 63 + (md_reverb*4);
+
+    loc1 = RVRindex % RVc1;
+    loc2 = RVRindex % RVc2;
+    loc3 = RVRindex % RVc3;
+    loc4 = RVRindex % RVc4;
+
+    for(; count; count--)
+    {
+        // Compute the LEFT CHANNEL echo buffers!
+
+        speedup = *srce >> 3;
+
+        RVbuf1[loc1] = speedup + ((ReverbPct * RVbuf1[loc1]) / 128l);
+        RVbuf2[loc2] = speedup + ((ReverbPct * RVbuf2[loc2]) / 128l);
+        RVbuf3[loc3] = speedup + ((ReverbPct * RVbuf3[loc3]) / 128l);
+        RVbuf4[loc4] = speedup + ((ReverbPct * RVbuf4[loc4]) / 128l);
+
+        // Prepare to compute actual finalized data!
+
+        RVRindex++;
+        loc1 = RVRindex % RVc1;
+        loc2 = RVRindex % RVc2;
+        loc3 = RVRindex % RVc3;
+        loc4 = RVRindex % RVc4;
+
+        // Left Channel!
+        
+        *srce++ += RVbuf1[loc1] - RVbuf2[loc2] + RVbuf3[loc3] - RVbuf4[loc4];
+    }
+}
+
+
+static void MixReverb16_Stereo(SLONG *srce, SLONG count)
+{
+    unsigned int  speedup;
+    int           ReverbPct;
+    unsigned int  loc1, loc2, loc3, loc4;
+
+    ReverbPct = 63 + (md_reverb*4);
+
+    loc1 = RVRindex % RVc1;
+    loc2 = RVRindex % RVc2;
+    loc3 = RVRindex % RVc3;
+    loc4 = RVRindex % RVc4;
+
+    for(; count; count--)
+    {
+        // Compute the LEFT CHANNEL echo buffers!
+
+        speedup = *srce >> 3;
+
+        RVbuf1[loc1] = speedup + ((ReverbPct * RVbuf1[loc1]) / 128l);
+        RVbuf2[loc2] = speedup + ((ReverbPct * RVbuf2[loc2]) / 128l);
+        RVbuf3[loc3] = speedup + ((ReverbPct * RVbuf3[loc3]) / 128l);
+        RVbuf4[loc4] = speedup + ((ReverbPct * RVbuf4[loc4]) / 128l);
+
+        // Compute the RIGHT CHANNEL echo buffers!
+        
+        speedup = srce[1] >> 3;
+
+        RVbuf9[loc1]  = speedup + ((ReverbPct * RVbuf9[loc1]) / 128l);
+        RVbuf10[loc2] = speedup + ((ReverbPct * RVbuf11[loc2]) / 128l);
+        RVbuf11[loc3] = speedup + ((ReverbPct * RVbuf12[loc3]) / 128l);
+        RVbuf12[loc4] = speedup + ((ReverbPct * RVbuf12[loc4]) / 128l);
+
+        // Prepare to compute actual finalized data!
+
+        RVRindex++;
+        loc1 = RVRindex % RVc1;
+        loc2 = RVRindex % RVc2;
+        loc3 = RVRindex % RVc3;
+        loc4 = RVRindex % RVc4;
+
+        // Left Channel!
+        
+        *srce++ += RVbuf1[loc1] - RVbuf2[loc2] + RVbuf3[loc3] - RVbuf4[loc4];
+
+        // Right Channel!
+
+        *srce++ += RVbuf9[loc1] - RVbuf10[loc2] + RVbuf11[loc3] - RVbuf12[loc4];
+    }
+}
+
+
+static void Mix32To16(SWORD *dste, SLONG *srce, SLONG count)
+{
+    SLONG   x1, x2, x3, x4;
+    int     remain;
+    
+    remain = count & 3;
+    
+    for(count>>=2; count; count--)
+    {   x1 = *srce++ >> bitshift;
+        x2 = *srce++ >> bitshift;
+        x3 = *srce++ >> bitshift;
+        x4 = *srce++ >> bitshift;
+        x1 = (x1 > 32767) ? 32767 : (x1 < -32768) ? -32768 : x1;
+        x2 = (x2 > 32767) ? 32767 : (x2 < -32768) ? -32768 : x2;
+        x3 = (x3 > 32767) ? 32767 : (x3 < -32768) ? -32768 : x3;
+        x4 = (x4 > 32767) ? 32767 : (x4 < -32768) ? -32768 : x4;
+        *dste++ = x1;
+        *dste++ = x2;
+        *dste++ = x3;
+        *dste++ = x4;
+    }
+
+    for(; remain; remain--)
+    {   x1 = *srce++ >> bitshift;
+        x1 = (x1 > 32767) ? 32767 : (x1 < -32768) ? -32768 : x1;
+        *dste++ = x1;
+    }
+}
+
+
+static void Mix32To8(SBYTE *dste, SLONG *srce, SLONG count)
+{
+    int   x1, x2, x3, x4;
+    int   remain;
+    
+    remain = count & 3;
+    
+    for(count>>=2; count; count--)
+    {   x1 = *srce++ >> bitshift;
+        x2 = *srce++ >> bitshift;
+        x3 = *srce++ >> bitshift;
+        x4 = *srce++ >> bitshift;
+
+        x1 = (x1 > 127) ? 127 : (x1 < -128) ? -128 : x1;
+        x2 = (x2 > 127) ? 127 : (x2 < -128) ? -128 : x2;
+        x3 = (x3 > 127) ? 127 : (x3 < -128) ? -128 : x3;
+        x4 = (x4 > 127) ? 127 : (x4 < -128) ? -128 : x4;
+
+        *dste++ = x1 + 128;
+        *dste++ = x2 + 128;
+        *dste++ = x3 + 128;
+        *dste++ = x4 + 128;
+    }
+
+    for(; remain; remain--)
+    {   x1 = *srce++ >> bitshift;
+        x1 = (x1 > 127) ? 127 : (x1 < -128) ? -128 : x1;
+        *dste++ = x1 + 128;
+    }
+}
+
+
+static ULONG samples2bytes(ULONG samples)
+{
+    if(vc_mode & DMODE_16BITS) samples <<= 1;
+    if(vc_mode & DMODE_STEREO) samples <<= 1;
+    return samples;
+}
+
+
+static ULONG bytes2samples(ULONG bytes)
+{
+    if(vc_mode & DMODE_16BITS) bytes >>= 1;
+    if(vc_mode & DMODE_STEREO) bytes >>= 1;
+    return bytes;
+}
+
+
+static void AddChannel(SLONG *ptr, SLONG todo)
+{
+    SLONG  end, done;
+#ifdef __FASTMIXER__
+    SBYTE  *s;
+#else
+    SWORD  *s;
+#endif
+
+    while(todo > 0)
+    {   // update the 'current' index so the sample loops, or
+        // stops playing if it reached the end of the sample
+
+        if(vnf->flags & SF_REVERSE)
+        {
+            // The sample is playing in reverse
+
+            if((vnf->flags & SF_LOOP) && (vnf->current < idxlpos))
+            {
+                // the sample is looping, and it has
+                // reached the loopstart index
+
+                if(vnf->flags & SF_BIDI)
+                {
+                    // sample is doing bidirectional loops, so 'bounce'
+                    // the current index against the idxlpos
+
+                    vnf->current   = idxlpos+(idxlpos-vnf->current);
+                    vnf->flags    &=~SF_REVERSE;
+                    vnf->increment =-vnf->increment;
+                } else
+                    // normal backwards looping, so set the
+                    // current position to loopend index
+
+                   vnf->current = idxlend-(idxlpos-vnf->current);
+            } else
+            {
+                // the sample is not looping, so check
+                // if it reached index 0
+
+                if(vnf->current < 0)
+                {
+                    // playing index reached 0, so stop
+                    // playing this sample
+
+                    vnf->current = 0;
+                    vnf->active  = 0;
+                    break;
+                }
+            }
+        } else
+        {
+            // The sample is playing forward
+
+            if((vnf->flags & SF_LOOP) && (vnf->current > idxlend))
+            {
+                 // the sample is looping, so check if
+                 // it reached the loopend index
+
+                 if(vnf->flags & SF_BIDI)
+                 {
+                     // sample is doing bidirectional loops, so 'bounce'
+                     //  the current index against the idxlend
+
+                     vnf->flags    |=SF_REVERSE;
+                     vnf->increment =-vnf->increment;
+                     vnf->current   =idxlend-(vnf->current-idxlend);
+                 } else
+                     // normal backwards looping, so set the
+                     // current position to loopend index
+
+                     vnf->current = idxlpos + (vnf->current-idxlend);
+            } else
+            {
+                // sample is not looping, so check
+                // if it reached the last position
+
+                if(vnf->current > idxsize)
+                {
+                    // yes, so stop playing this sample
+
+                    vnf->current = 0;
+                    vnf->active  = 0;
+                    break;
+                }
+            }
+        }
+
+        if(!(s=Samples[vnf->handle]))
+        {   vnf->current = 0;
+            vnf->active  = 0;
+            break;
+        }
+
+        end = (vnf->flags & SF_REVERSE) ? 
+                (vnf->flags & SF_LOOP) ? idxlpos : 0 :
+                (vnf->flags & SF_LOOP) ? idxlend : idxsize;
+
+        done = MIN((end - vnf->current) / vnf->increment + 1, todo);
+
+        if(!done)
+        {   vnf->active = 0;
+            break;
+        }
+
+        if(vnf->vol)
+        {
+#ifdef __ASSEMBLY__
+            if(vc_mode & DMODE_STEREO)
+                if((vnf->pan == PAN_SURROUND) && (vc_mode & DMODE_SURROUND))
+                    AsmStereoSurround(s,ptr,vnf->current,vnf->increment,done);
+                else
+                    AsmStereoNormal(s,ptr,vnf->current,vnf->increment,done);
+            else
+                AsmMonoNormal(s,ptr,vnf->current,vnf->increment,done);
+            vnf->current += (vnf->increment*done);
+#else
+            if(vc_mode & DMODE_STEREO)
+                if((vnf->pan == PAN_SURROUND) && (vc_mode & DMODE_SURROUND))
+                    vnf->current = MixStereoSurround(s,ptr,vnf->current,vnf->increment,done);
+                else
+                    vnf->current = MixStereoNormal(s,ptr,vnf->current,vnf->increment,done);
+            else
+                vnf->current = MixMonoNormal(s,ptr,vnf->current,vnf->increment,done);
+#endif
+        }
+        todo -= done;
+        ptr  += (vc_mode & DMODE_STEREO) ? (done<<1) : done;
+    }
+}
+
+
+void VC_WriteSamples(SBYTE *buf, ULONG todo)
+{
+    int    left, portion = 0, count;
+    SBYTE  *buffer, *samplebuf;
+    int    t;
+    int    pan, vol;
+    int    sampletodo;
+
+    samplebuf  = buf;
+    sampletodo = todo;
+
+    while(todo)
+    {   if(TICKLEFT==0)
+        {   if(vc_mode & DMODE_SOFT_MUSIC) md_player();
+            TICKLEFT = (md_mixfreq*125l) / (md_bpm*50L);
+        }
+
+        left = MIN(TICKLEFT, todo);
+        
+        buffer    = buf;
+        TICKLEFT -= left;
+        todo     -= left;
+
+        buf += samples2bytes(left);
+
+        while(left)
+        {   portion = MIN(left, samplesthatfit);
+            count   = (vc_mode & DMODE_STEREO) ? (portion<<1) : portion;
+            
+            memset(VC_TICKBUF, 0, count<<2);
+
+            for(t=0; t<vc_softchn; t++)
+            {   vnf = &vinf[t];
+
+                if(vnf->kick)
+                {   vnf->current = vnf->start << FRACBITS;
+                    vnf->kick    = 0;
+                    vnf->active  = 1;
+                }
+                
+                if(vnf->frq == 0) vnf->active = 0;
+                
+                if(vnf->active)
+                {   vnf->increment = (vnf->frq<<FRACBITS) / md_mixfreq;
+                    if(vnf->flags & SF_REVERSE) vnf->increment=-vnf->increment;
+
+                    vol = vnf->vol;  pan = vnf->pan;
+
+                    if(vc_mode & DMODE_STEREO)
+                    {   if(pan != PAN_SURROUND)
+                        {
+                            #ifdef __FASTMIXER__
+                            lvoltab = voltab[(vol * (255-pan)) / 1024];
+                            rvoltab = voltab[(vol * pan) / 1024];
+                            #else
+                            lvolsel = (vol * (255-pan)) >> 8;
+                            rvolsel = (vol * pan) >> 8;
+                            #endif
+                        } else
+                        {
+                            #ifdef __FASTMIXER__
+                            lvoltab = voltab[(vol+1)>>3];
+                            #else
+                            lvolsel = vol/2;
+                            #endif
+                        }
+                    } else
+                    {
+                        #ifdef __FASTMIXER__
+                        lvoltab = voltab[(vol+1)>>2];
+                        #else
+                        lvolsel = vol;
+                        #endif
+                    }
+                    idxsize = (vnf->size)   ? (vnf->size << FRACBITS)-1 : 0;
+                    idxlend = (vnf->repend) ? (vnf->repend << FRACBITS)-1 : 0;                    
+                    idxlpos = vnf->reppos << FRACBITS;
+                    AddChannel(VC_TICKBUF, portion);
+                }
+            }
+
+            if(md_reverb) MixReverb16(VC_TICKBUF, portion);
+
+            if(vc_mode & DMODE_16BITS)
+                Mix32To16((SWORD *) buffer, VC_TICKBUF, count);
+            else
+                Mix32To8((SBYTE *) buffer, VC_TICKBUF, count);
+
+            buffer += samples2bytes(portion);
+            left   -= portion;
+        }
+    }
+}
+
+
+void VC_SilenceBytes(SBYTE *buf, ULONG todo)
+
+//  Fill the buffer with 'todo' bytes of silence (it depends on the mixing
+//  mode how the buffer is filled)
+
+{
+    // clear the buffer to zero (16 bits
+    // signed ) or 0x80 (8 bits unsigned)
+
+    if(vc_mode & DMODE_16BITS)
+        memset(buf,0,todo);
+    else
+        memset(buf,0x80,todo);
+}
+
+
+ULONG VC_WriteBytes(SBYTE *buf, ULONG todo)
+
+//  Writes 'todo' mixed SBYTES (!!) to 'buf'. It returns the number of
+//  SBYTES actually written to 'buf' (which is rounded to number of samples
+//  that fit into 'todo' bytes).
+
+{
+    if(vc_softchn == 0)
+    {   VC_SilenceBytes(buf,todo);
+        return todo;
+    }
+
+    todo = bytes2samples(todo);
+    VC_WriteSamples(buf,todo);
+
+    return samples2bytes(todo);
+}
+
+
+static UBYTE log2(ULONG x)
+{
+    UBYTE result = 0;
+    while (x>>=1) result++;
+
+    return result;
+}
+
+
+BOOL VC_PlayStart(void)
+{
+    int    t, numchn;
+
+    numchn = md_softchn;
+    if(vc_stream) numchn++;
+
+    if(numchn > 0)
+    {
+#ifdef __FASTMIXER__
+        int    c;
+        SLONG  maxvol, volmul;
+
+        if(vc_stream) numchn++;
+        maxvol = 16777216L / (numchn+6);
+
+        for(t=0; t<65; t++)
+        {   volmul = (maxvol*t) / 64;
+            for(c=-128; c<128; c++)
+                voltab[t][(UBYTE)c] = (SLONG)c*volmul;
+        }
+
+        bitshift = 16 - log2(numchn);
+#else
+        bitshift = (log2(numchn)>>3) + 7;
+        
+#endif
+
+        if (!(vc_mode & DMODE_16BITS))
+            bitshift += 8;
+    }
+
+    samplesthatfit = TICKLSIZE;
+    if(vc_mode & DMODE_STEREO) samplesthatfit >>= 1;
+    TICKLEFT = 0;
+
+
+    RVc1 = (5000L * md_mixfreq) / REVERBERATION;
+    RVc2 = (5078L * md_mixfreq) / REVERBERATION;
+    RVc3 = (5313L * md_mixfreq) / REVERBERATION;
+    RVc4 = (5703L * md_mixfreq) / REVERBERATION;
+
+    if((RVbuf1 = (SLONG *)_mm_calloc((RVc1+1),sizeof(SLONG))) == NULL) return 1;
+    if((RVbuf2 = (SLONG *)_mm_calloc((RVc2+1),sizeof(SLONG))) == NULL) return 1;
+    if((RVbuf3 = (SLONG *)_mm_calloc((RVc3+1),sizeof(SLONG))) == NULL) return 1;
+    if((RVbuf4 = (SLONG *)_mm_calloc((RVc4+1),sizeof(SLONG))) == NULL) return 1;
+   
+    if(vc_mode & DMODE_STEREO)
+    {   if((RVbuf9 = (SLONG *)_mm_calloc((RVc1+1),sizeof(SLONG))) == NULL) return 1;
+        if((RVbuf10 = (SLONG *)_mm_calloc((RVc2+1),sizeof(SLONG))) == NULL) return 1;
+        if((RVbuf11 = (SLONG *)_mm_calloc((RVc3+1),sizeof(SLONG))) == NULL) return 1;
+        if((RVbuf12 = (SLONG *)_mm_calloc((RVc4+1),sizeof(SLONG))) == NULL) return 1;
+    }
+
+    RVRindex = 0;
+
+    return 0;
+}
+
+
+void VC_PlayStop(void)
+{
+    if(RVbuf1  != NULL) free(RVbuf1);
+    if(RVbuf2  != NULL) free(RVbuf2);
+    if(RVbuf3  != NULL) free(RVbuf3);
+    if(RVbuf4  != NULL) free(RVbuf4);
+    if(RVbuf9  != NULL) free(RVbuf9);
+    if(RVbuf10 != NULL) free(RVbuf10);
+    if(RVbuf11 != NULL) free(RVbuf11);
+    if(RVbuf12 != NULL) free(RVbuf12);
+
+    RVbuf1  = NULL;  RVbuf2  = NULL;  RVbuf3  = NULL;  RVbuf4  = NULL;
+    RVbuf9  = NULL;  RVbuf10 = NULL;  RVbuf11 = NULL;  RVbuf12 = NULL;
+}
+
+
+BOOL VC_Init(void)
+{
+
+#ifdef __FASTMIXER__
+    int t;
+
+    _mm_errno = MMERR_INITIALIZING_MIXER;
+    if((voltab = (SLONG **)calloc(65,sizeof(SLONG *))) == NULL) return 1;
+    for(t=0; t<65; t++)
+       if((voltab[t] = (SLONG *)calloc(256,sizeof(SLONG))) == NULL) return 1;
+
+    if((Samples = (SBYTE **)calloc(MAXSAMPLEHANDLES, sizeof(SBYTE *))) == NULL) return 1;
+#else
+    _mm_errno = MMERR_INITIALIZING_MIXER;
+    if((Samples = (SWORD **)calloc(MAXSAMPLEHANDLES, sizeof(SWORD *))) == NULL) return 1;
+#endif
+
+    if(VC_TICKBUF==NULL) if((VC_TICKBUF=(SLONG *)malloc((TICKLSIZE+32) * sizeof(SLONG))) == NULL) return 1;
+    if(md_mode & DMODE_INTERP) md_mode &= ~DMODE_INTERP;
+
+    MixReverb16 = (md_mode & DMODE_STEREO) ? MixReverb16_Stereo : MixReverb16_Normal;
+    vc_mode = md_mode;
+
+
+    _mm_errno = 0;
+    return 0;
+}
+
+
+void VC_Exit(void)
+{
+#ifdef __FASTMIXER__
+    int t;
+    if(voltab!=NULL)
+    {   for(t=0; t<65; t++) if(voltab[t]!=NULL) free(voltab[t]);
+        free(voltab); voltab = NULL;
+    }
+#endif
+
+    //if(VC_TICKBUF!=NULL) free(VC_TICKBUF);
+    if(vinf!=NULL) free(vinf);
+    if(Samples!=NULL) free(Samples);
+
+    //VC_TICKBUF      = NULL;
+    vinf            = NULL;
+    Samples         = NULL;
+}
+
+
+BOOL VC_SetNumVoices(void)
+{
+    int t;
+
+    if((vc_softchn = md_softchn) == 0) return 0;
+
+    if(vinf!=NULL) free(vinf);
+    if((vinf = _mm_calloc(sizeof(VINFO),vc_softchn)) == NULL) return 1;
+    
+    for(t=0; t<vc_softchn; t++)
+    {   vinf[t].frq = 10000;
+        vinf[t].pan = (t&1) ? 0 : 255;
+    }
+
+    return 0;
+}
+
+
+void VC_VoiceSetVolume(UBYTE voice, UWORD vol)
+{
+    vinf[voice].vol = vol;
+}
+
+
+void VC_VoiceSetFrequency(UBYTE voice, ULONG frq)
+{
+    vinf[voice].frq = frq;
+}
+
+
+void VC_VoiceSetPanning(UBYTE voice, ULONG pan)
+{
+    vinf[voice].pan = pan;
+}
+
+
+void VC_VoicePlay(UBYTE voice, SWORD handle, ULONG start, ULONG size, ULONG reppos, ULONG repend, UWORD flags)
+{
+    vinf[voice].flags    = flags;
+    vinf[voice].handle   = handle;
+    vinf[voice].start    = start;
+    vinf[voice].size     = size;
+    vinf[voice].reppos   = reppos;
+    vinf[voice].repend   = repend;
+    vinf[voice].kick     = 1;
+}
+
+
+void VC_VoiceStop(UBYTE voice)
+{
+    vinf[voice].active = 0;
+}  
+
+
+BOOL VC_VoiceStopped(UBYTE voice)
+{
+    return(vinf[voice].active==0);
+}
+
+
+void VC_VoiceReleaseSustain(UBYTE voice)
+{
+
+}
+
+
+SLONG VC_VoiceGetPosition(UBYTE voice)
+{
+    return(vinf[voice].current>>FRACBITS);
+}
+
+
+/**************************************************
+***************************************************
+***************************************************
+**************************************************/
+
+
+void VC_SampleUnload(SWORD handle)
+{
+    void *sampleadr = Samples[handle];
+
+    free(sampleadr);
+    Samples[handle] = NULL;
+}
+
+
+SWORD VC_SampleLoad(SAMPLOAD *sload, int type, FILE *fp)
+{
+    SAMPLE *s = sload->sample;
+    int    handle;
+    ULONG  t, length,loopstart,loopend;
+
+    if(type==MD_HARDWARE) return -1;
+
+    // Find empty slot to put sample address in
+    for(handle=0; handle<MAXSAMPLEHANDLES; handle++)
+        if(Samples[handle]==NULL) break;
+
+    if(handle==MAXSAMPLEHANDLES)
+    {   _mm_errno = MMERR_OUT_OF_HANDLES;
+        return -1;
+    }
+
+    length    = s->length;
+    loopstart = s->loopstart;
+    loopend   = s->loopend;
+
+    SL_SampleSigned(sload);
+
+#ifdef __FASTMIXER__
+    SL_Sample16to8(sload);
+    if((Samples[handle]=(SBYTE *)malloc(length+16))==NULL)
+    {   _mm_errno = MMERR_SAMPLE_TOO_BIG;
+        return -1;
+    }
+    // read sample into buffer.
+    SL_Load(Samples[handle],sload,length);
+#else
+    SL_Sample8to16(sload);
+    if((Samples[handle]=(SWORD *)malloc((length+16)<<1))==NULL)
+    {   _mm_errno = MMERR_SAMPLE_TOO_BIG;
+        return -1;
+    }
+    // read sample into buffer.
+    SL_Load(Samples[handle],sload,length);
+#endif
+
+
+    // Unclick samples:
+
+    if(s->flags & SF_LOOP)
+    {   if(s->flags & SF_BIDI)
+            for(t=0; t<16; t++) Samples[handle][loopend+t] = Samples[handle][(loopend-t)-1];
+        else
+            for(t=0; t<16; t++) Samples[handle][loopend+t] = Samples[handle][t+loopstart];
+    } else
+        for(t=0; t<16; t++) Samples[handle][t+length] = 0;
+
+    return handle;
+}
+
+
+ULONG VC_SampleSpace(int type)
+{
+    return vc_memory;
+}
+
+
+ULONG VC_SampleLength(int type, SAMPLE *s)
+{
+#ifdef __FASTMIXER__
+    return s->length + 16;
+#else
+    return (s->length * ((s->flags&SF_16BITS) ? 2 : 1)) + 16;
+#endif
+}
+
+
+/**************************************************
+***************************************************
+***************************************************
+**************************************************/
+
+
+ULONG VC_VoiceRealVolume(UBYTE voice)
+{
+    ULONG i,s,size;
+    int k,j;
+#ifdef __FASTMIXER__
+    SBYTE *smp;
+#else
+    SWORD *smp;
+#endif
+    SLONG t;
+                    
+    t = vinf[voice].current>>FRACBITS;
+    if(vinf[voice].active==0) return 0;
+
+    s    = vinf[voice].handle;
+    size = vinf[voice].size;
+
+    i=64; t-=64; k=0; j=0;
+    if(i>size) i = size;
+    if(t<0) t = 0;
+    if(t+i > size) t = size-i;
+
+    i &= ~1;  // make sure it's EVEN.
+
+    smp = &Samples[s][t];
+    for(; i; i--, smp++)
+    {   if(k<*smp) k = *smp;
+        if(j>*smp) j = *smp;
+    }
+
+#ifdef __FASTMIXER__
+    k = abs(k-j)<<8;
+#else
+    k = abs(k-j);
+#endif
+
+    return k;
+}
+
+
+BOOL VC_StreamInit(ULONG speed, UWORD flags)
+
+// flags - Disk Format - SF_STEREO, SF_16BITS, etc.
+// speed - speed at which to replay sample
+//
+// Returns - TRUE if init failed
+
+{
+    ULONG   tmp;
+
+#ifdef __FASTMIXER__
+    tmp = stream_bufsize * speed * (((flags & SF_STEREO) && (vc_mode & DMODE_STEREO)) ? 2 : 1);
+#else
+    tmp = stream_bufsize * speed * (((flags & SF_STEREO) && (vc_mode & DMODE_STEREO)) ? 2 : 1)
+          * ((flags & SF_16BITS) && (vc_mode & DMODE_16BITS)) ? 2 : 1;
+#endif
+    if((flags & SF_STEREO) && (vc_mode & DMODE_STEREO)) tmp <<= 1;
+
+    vstream.size = tmp;
+    if((vstream.buffer=_mm_calloc(vstream.size,1024)) == NULL) return 1;
+
+    vstream.speed       = speed;
+    vstream.speedfactor = (md_mixfreq / speed);
+    if(!((vstream.speedfactor==2) || (vstream.speedfactor==4)))
+        vstream.speedfactor = 1;
+
+    vstream.infmt     = flags;
+    vstream.flags     = flags;
+#ifdef __FASTMIXER__
+    vstream.flags     = flags &= ~SF_16BITS;
+#else
+    vstream.flags     = flags |= SF_16BITS;
+#endif
+    if(!(vc_mode&DMODE_STEREO)) vstream.flags &= ~SF_STEREO;
+
+    vstream.active    = 0;
+    vstream.current   = 0;
+    vstream.increment = 0;
+
+    vc_stream = 1;
+    VC_PlayStart();
+
+    return 0;
+}
+
+
+void VC_StreamExit(void)
+{
+    vstream.active = 0;
+    if(vstream.buffer != NULL) free(vstream.buffer);
+    vstream.buffer = NULL;
+    vc_stream = 0;
+    VC_PlayStart();
+}
+
+
+void VC_StreamSetSpeed(ULONG speed)
+{
+    vstream.speed = speed;
+    vstream.speedfactor = (md_mixfreq/speed);
+    if(!((vstream.speedfactor==2) || (vstream.speedfactor==4)))
+        vstream.speedfactor = 1;
+}
+
+
+SLONG VC_StreamGetPosition(void)
+{
+    return(vstream.current >> FRACBITS);
+}
+
+
+void VC_StreamStart(void)
+{
+    if(vstream.buffer!=NULL) vstream.active = 1;
+}
+
+
+void VC_StreamStop(void)
+{
+    vstream.active = 0;
+}
+
+
+void VC_StreamCommit(void *sample, ULONG size)
+
+// Read 'size' bytes from the specified buffer and commit them to
+// the streaming audio buffer.
+
+{
+    //ULONG last, curr;
+    //ULONG todo;
+
+    if(vstream.buffer==NULL) return;
+
+}
+