diff playercode/virtch.c @ 6:d14fd386d182

Initial entry of mikmod into the CVS tree.
author darius
date Fri, 23 Jan 1998 16:05:09 +0000
parents
children
line wrap: on
line diff
--- /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;
+
+}
+