added an old version of mikmod for dos
[dosdemo] / libs / oldmik / src / virtch.c
diff --git a/libs/oldmik/src/virtch.c b/libs/oldmik/src/virtch.c
new file mode 100644 (file)
index 0000000..8f7e357
--- /dev/null
@@ -0,0 +1,852 @@
+/*\r
+\r
+Name:\r
+VIRTCH.C\r
+\r
+Description:\r
+All-c sample mixing routines, using a 32 bits mixing buffer\r
+\r
+Portability:\r
+All systems - all compilers\r
+\r
+*/\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <malloc.h>\r
+#include "mikmod.h"\r
+\r
+#define FRACBITS 11\r
+#define FRACMASK ((1L<<FRACBITS)-1)\r
+\r
+#define TICKLSIZE 3600\r
+#define TICKWSIZE (TICKLSIZE*2)\r
+#define TICKBSIZE (TICKWSIZE*2)\r
+static SLONG VC_TICKBUF[TICKLSIZE];\r
+\r
+#ifndef min\r
+#define min(a,b) (((a)<(b)) ? (a) : (b))\r
+#endif\r
+\r
+typedef struct{\r
+       UBYTE kick;                     /* =1 -> sample has to be restarted */\r
+       UBYTE active;                   /* =1 -> sample is playing */\r
+       UWORD flags;                    /* 16/8 bits looping/one-shot */\r
+       SWORD handle;                  /* identifies the sample */\r
+       ULONG start;                    /* start index */\r
+       ULONG size;                     /* samplesize */\r
+       ULONG reppos;                   /* loop start */\r
+       ULONG repend;                   /* loop end */\r
+       ULONG frq;                      /* current frequency */\r
+       UBYTE vol;                      /* current volume */\r
+       UBYTE pan;                                              /* current panning position */\r
+       SLONG current;                  /* current index in the sample */\r
+       SLONG increment;                /* fixed-point increment value */\r
+#ifdef __WATCOMC__\r
+       UWORD lvolsel;                                  /* left volume table selector */\r
+       UWORD rvolsel;                                  /* right volume table selector */\r
+#else\r
+       SLONG lvolmul;                                  /* left volume multiply */\r
+       SLONG rvolmul;                                  /* right volume multiply */\r
+#endif\r
+} VINFO;\r
+\r
+\r
+static VINFO vinf[32];\r
+static VINFO *vnf;\r
+\r
+static UWORD samplesthatfit;\r
+static SLONG idxsize,idxlpos,idxlend,maxvol;\r
+\r
+static long per256;\r
+static int ampshift;\r
+\r
+\r
+#ifdef __WATCOMC__\r
+\r
+static SLONG voltab[65][256];\r
+static UWORD volsel[65];\r
+\r
+#ifdef __cplusplus\r
+extern "C" {\r
+#endif\r
+\r
+UWORD lvolsel,rvolsel;\r
+\r
+void AsmStereoNormal(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,ULONG todo);\r
+#pragma aux AsmStereoNormal    \\r
+               parm [esi] [edi] [ebx] [ecx] [edx] \\r
+               modify [eax];\r
+\r
+\r
+void AsmMonoNormal(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,ULONG todo);\r
+#pragma aux AsmMonoNormal     \\r
+        parm [esi] [edi] [ebx] [ecx] [edx] \\r
+        modify [eax];\r
+\r
+#ifdef __cplusplus\r
+}\r
+#endif\r
+\r
+void freedescriptor(unsigned short selector);\r
+#pragma aux freedescriptor =    \\r
+       "mov    ax,0001h"       \\r
+       "int    31h"            \\r
+       parm    [bx]            \\r
+       modify  [ax];\r
+\r
+unsigned short getalias(void);\r
+#pragma aux getalias =          \\r
+       "mov    ax,cs   "       \\r
+       "mov    bx,ax   "       \\r
+       "mov    ax,000ah"       \\r
+       "int    31h     "       \\r
+       "jnc    isok    "       \\r
+       "xor    ax,ax   "       \\r
+       "isok:          "       \\r
+       modify  [bx]            \\r
+       value   [ax];\r
+\r
+void setbase(unsigned short selector,unsigned long offset);\r
+#pragma aux setbase =           \\r
+       "mov    ax,0007h"       \\r
+       "mov    ecx,edx"        \\r
+       "ror    ecx,16"         \\r
+       "int    31h"            \\r
+       parm    [bx] [edx]      \\r
+       modify  [ax ecx] ;\r
+\r
+void VC_Sample32To16Copy(SLONG *srce,SWORD *dest,ULONG count,UBYTE shift);\r
+#pragma aux VC_Sample32To16Copy =      \\r
+"again:"                                       \\r
+       "mov   eax,[esi]"               \\r
+       "sar   eax,cl"          \\r
+       "cmp   eax,32767"               \\r
+       "jg    toobig"                  \\r
+       "cmp   eax,-32768"              \\r
+       "jl    toosmall"                \\r
+"write:"                                       \\r
+       "mov   [edi],ax"                \\r
+       "add   esi,4"           \\r
+       "add   edi,2"                   \\r
+       "dec   edx"             \\r
+       "jnz   again"           \\r
+       "jmp   ready"                   \\r
+"toobig:"                   \\r
+       "mov   eax,32767"               \\r
+       "jmp   write"                   \\r
+"toosmall:"                 \\r
+       "mov   eax,-32768"              \\r
+       "jmp   write"                   \\r
+"ready:"                                       \\r
+       parm [esi] [edi] [edx] [cl]     \\r
+       modify [eax] ;\r
+\r
+\r
+void VC_Sample32To8Copy(SLONG *srce,SBYTE *dest,ULONG count,UBYTE shift);\r
+#pragma aux VC_Sample32To8Copy =       \\r
+"again:"                       \\r
+       "mov   eax,[esi]"               \\r
+       "sar   eax,cl"          \\r
+       "cmp   eax,127"         \\r
+       "jg    toobig"                  \\r
+       "cmp   eax,-128"        \\r
+       "jl    toosmall"                \\r
+"write:"                    \\r
+       "add   al,080h"         \\r
+       "mov   [edi],al"        \\r
+       "add   esi,4"           \\r
+       "inc   edi"             \\r
+       "dec   edx"             \\r
+       "jnz   again"                   \\r
+       "jmp   ready"                   \\r
+"toobig:"                   \\r
+       "mov   eax,127"         \\r
+       "jmp   write"                   \\r
+"toosmall:"                 \\r
+       "mov   eax,-128"        \\r
+       "jmp   write"                   \\r
+"ready:"                    \\r
+       parm [esi] [edi] [edx] [cl]     \\r
+       modify [eax] ;\r
+\r
+\r
+#else\r
+\r
+\r
+static SLONG lvolmul,rvolmul;\r
+\r
+\r
+static void VC_Sample32To8Copy(SLONG *srce,SBYTE *dest,ULONG count,UBYTE shift)\r
+{\r
+       SLONG c;\r
+       int shift=(24-ampshift);\r
+\r
+       while(count--){\r
+               c=*srce >> shift;\r
+               if(c>127) c=127;\r
+               else if(c<-128) c=-128;\r
+               *dest++=c+128;\r
+               srce++;\r
+       }\r
+}\r
+\r
+\r
+static void VC_Sample32To16Copy(SLONG *srce,SWORD *dest,ULONG count,UBYTE shift)\r
+{\r
+       SLONG c;\r
+       int shift=(16-ampshift);\r
+\r
+       while(count--){\r
+               c=*srce >> shift;\r
+               if(c>32767) c=32767;\r
+               else if(c<-32768) c=-32768;\r
+               *dest++=c;\r
+               srce++;\r
+       }\r
+}\r
+\r
+#endif\r
+\r
+\r
+static SLONG fraction2long(ULONG dividend,UWORD divisor)\r
+/*\r
+       Converts the fraction 'dividend/divisor' into a fixed point longword.\r
+*/\r
+{\r
+       ULONG whole,part;\r
+\r
+       whole=dividend/divisor;\r
+       part=((dividend%divisor)<<FRACBITS)/divisor;\r
+\r
+       return((whole<<FRACBITS)|part);\r
+}\r
+\r
+\r
+static UWORD samples2bytes(UWORD samples)\r
+{\r
+       if(md_mode & DMODE_16BITS) samples<<=1;\r
+       if(md_mode & DMODE_STEREO) samples<<=1;\r
+       return samples;\r
+}\r
+\r
+\r
+static UWORD bytes2samples(UWORD bytes)\r
+{\r
+       if(md_mode & DMODE_16BITS) bytes>>=1;\r
+       if(md_mode & DMODE_STEREO) bytes>>=1;\r
+       return bytes;\r
+}\r
+\r
+\r
+/**************************************************\r
+***************************************************\r
+***************************************************\r
+**************************************************/\r
+\r
+\r
+static SBYTE *Samples[MAXSAMPLEHANDLES];\r
+\r
+\r
+BOOL LargeRead(SBYTE *buffer,ULONG size)\r
+{\r
+       int t;\r
+       ULONG todo;\r
+\r
+       while(size){\r
+               /* how many bytes to load (in chunks of 8000) ? */\r
+\r
+               todo=(size>8000)?8000:size;\r
+\r
+               /* read data */\r
+\r
+               SL_Load(buffer,todo);\r
+               /* and update pointers.. */\r
+\r
+               size-=todo;\r
+               buffer+=todo;\r
+       }\r
+       return 1;\r
+}\r
+\r
+\r
+\r
+SWORD VC_SampleLoad(FILE *fp,ULONG length,ULONG reppos,ULONG repend,UWORD flags)\r
+{\r
+       int handle;\r
+       ULONG t;\r
+\r
+       SL_Init(fp,flags,(flags|SF_SIGNED)&~SF_16BITS);\r
+\r
+       /* Find empty slot to put sample address in */\r
+\r
+       for(handle=0;handle<MAXSAMPLEHANDLES;handle++){\r
+               if(Samples[handle]==NULL) break;\r
+       }\r
+\r
+       if(handle==MAXSAMPLEHANDLES){\r
+               myerr=ERROR_OUT_OF_HANDLES;\r
+               return -1;\r
+       }\r
+\r
+        if((Samples[handle]=(SBYTE *)malloc(length+16))==NULL){\r
+               myerr=ERROR_SAMPLE_TOO_BIG;\r
+               return -1;\r
+       }\r
+\r
+       /* read sample into buffer. */\r
+       LargeRead(Samples[handle],length);\r
+\r
+       /* Unclick samples: */\r
+\r
+       if(flags & SF_LOOP){\r
+               if(flags & SF_BIDI)\r
+                       for(t=0;t<16;t++) Samples[handle][repend+t]=Samples[handle][(repend-t)-1];\r
+               else\r
+                       for(t=0;t<16;t++) Samples[handle][repend+t]=Samples[handle][t+reppos];\r
+       }\r
+       else{\r
+               for(t=0;t<16;t++) Samples[handle][t+length]=0;\r
+       }\r
+\r
+       return handle;\r
+}\r
+\r
+\r
+\r
+void VC_SampleUnload(SWORD handle)\r
+{\r
+       void *sampleadr=Samples[handle];\r
+\r
+       free(sampleadr);\r
+       Samples[handle]=NULL;\r
+}\r
+\r
+\r
+/**************************************************\r
+***************************************************\r
+***************************************************\r
+**************************************************/\r
+\r
+\r
+#ifndef __WATCOMC__\r
+\r
+\r
+static void (*SampleMix)(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,UWORD todo);\r
+\r
+\r
+static void MixStereoNormal(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,UWORD todo)\r
+{\r
+       SBYTE sample;\r
+\r
+       while(todo>0){\r
+               sample=srce[index>>FRACBITS];\r
+               *(dest++)+=lvolmul*sample;\r
+               *(dest++)+=rvolmul*sample;\r
+               index+=increment;\r
+               todo--;\r
+       }\r
+}\r
+\r
+\r
+static void MixMonoNormal(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,UWORD todo)\r
+{\r
+       SBYTE sample;\r
+\r
+       while(todo>0){\r
+               sample=srce[index>>FRACBITS];\r
+               *(dest++)+=lvolmul*sample;\r
+               index+=increment;\r
+               todo--;\r
+       }\r
+}\r
+\r
+\r
+static void MixStereoInterp(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,UWORD todo)\r
+{\r
+       SWORD sample,a,b;\r
+\r
+       while(todo>0){\r
+               a=srce[index>>FRACBITS];\r
+               b=srce[1+(index>>FRACBITS)];\r
+               sample=a+(((long)(b-a)*(index&FRACMASK))>>FRACBITS);\r
+\r
+               *(dest++)+=lvolmul*sample;\r
+               *(dest++)+=rvolmul*sample;\r
+               index+=increment;\r
+               todo--;\r
+       }\r
+}\r
+\r
+\r
+static void MixMonoInterp(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,UWORD todo)\r
+{\r
+       SWORD sample,a,b;\r
+\r
+       while(todo>0){\r
+               a=srce[index>>FRACBITS];\r
+               b=srce[1+(index>>FRACBITS)];\r
+               sample=a+(((long)(b-a)*(index&FRACMASK))>>FRACBITS);\r
+\r
+               *(dest++)+=lvolmul*sample;\r
+\r
+               index+=increment;\r
+               todo--;\r
+       }\r
+}\r
+\r
+#endif\r
+\r
+\r
+static UWORD NewPredict(SLONG index,SLONG end,SLONG increment,UWORD todo)\r
+/*\r
+       This functions returns the number of resamplings we can do so that:\r
+\r
+               - it never accesses indexes bigger than index 'end'\r
+               - it doesn't do more than 'todo' resamplings\r
+*/\r
+{\r
+       SLONG di;\r
+\r
+       di=(end-index)/increment;\r
+       index+=(di*increment);\r
+\r
+       if(increment<0){\r
+               while(index>=end){\r
+                       index+=increment;\r
+                       di++;\r
+               }\r
+       }\r
+       else{\r
+               while(index<=end){\r
+                       index+=increment;\r
+                       di++;\r
+               }\r
+       }\r
+       return ((di<todo) ? di : todo);\r
+}\r
+\r
+\r
+static void VC_AddChannel(SLONG *ptr,UWORD todo)\r
+/*\r
+       Mixes 'todo' stereo or mono samples of the current channel to the tickbuffer.\r
+*/\r
+{\r
+       SLONG end;\r
+       UWORD done,needs;\r
+        SBYTE *s;\r
+\r
+       while(todo>0){\r
+\r
+               /* update the 'current' index so the sample loops, or\r
+                  stops playing if it reached the end of the sample */\r
+\r
+               if(vnf->flags&SF_REVERSE){\r
+\r
+                       /* The sample is playing in reverse */\r
+\r
+                               if(vnf->flags&SF_LOOP){\r
+\r
+                                       /* the sample is looping, so check if\r
+                                          it reached the loopstart index */\r
+\r
+                                       if(vnf->current<idxlpos){\r
+                                       if(vnf->flags&SF_BIDI){\r
+\r
+                                               /* sample is doing bidirectional loops, so 'bounce'\r
+                                                       the current index against the idxlpos */\r
+\r
+                                               vnf->current=idxlpos+(idxlpos-vnf->current);\r
+                                               vnf->flags&=~SF_REVERSE;\r
+                                               vnf->increment=-vnf->increment;\r
+                                       }\r
+                                       else\r
+                                               /* normal backwards looping, so set the\r
+                                                       current position to loopend index */\r
+\r
+                                                       vnf->current=idxlend-(idxlpos-vnf->current);\r
+                                       }\r
+                               }\r
+                               else{\r
+\r
+                                       /* the sample is not looping, so check\r
+                                               if it reached index 0 */\r
+\r
+                                       if(vnf->current<0){\r
+\r
+                                               /* playing index reached 0, so stop\r
+                                                       playing this sample */\r
+\r
+                                               vnf->current=0;\r
+                                               vnf->active=0;\r
+                                               break;\r
+                                       }\r
+                               }\r
+               }\r
+               else{\r
+\r
+                       /* The sample is playing forward */\r
+\r
+                               if(vnf->flags&SF_LOOP){\r
+\r
+                                       /* the sample is looping, so check if\r
+                                               it reached the loopend index */\r
+\r
+                                       if(vnf->current>idxlend){\r
+                                               if(vnf->flags&SF_BIDI){\r
+\r
+                                               /* sample is doing bidirectional loops, so 'bounce'\r
+                                                       the current index against the idxlend */\r
+\r
+                                                       vnf->flags|=SF_REVERSE;\r
+                                                       vnf->increment=-vnf->increment;\r
+                                                       vnf->current=idxlend-(vnf->current-idxlend); /* ?? */\r
+                                               }\r
+                                               else\r
+                                               /* normal backwards looping, so set the\r
+                                                       current position to loopend index */\r
+\r
+                                                       vnf->current=idxlpos+(vnf->current-idxlend);\r
+                                       }\r
+                               }\r
+                               else{\r
+\r
+                                       /* sample is not looping, so check\r
+                                               if it reached the last position */\r
+\r
+                                       if(vnf->current>idxsize){\r
+\r
+                                               /* yes, so stop playing this sample */\r
+\r
+                                               vnf->current=0;\r
+                                               vnf->active=0;\r
+                                               break;\r
+                                       }\r
+                               }\r
+               }\r
+\r
+               /* Vraag een far ptr op van het sampleadres\r
+                       op byte offset vnf->current, en hoeveel samples\r
+                       daarvan geldig zijn (VOORDAT segment overschrijding optreed) */\r
+\r
+               if(!(s=Samples[vnf->handle])){\r
+                       vnf->current=0;\r
+                       vnf->active=0;\r
+                       break;\r
+               }\r
+\r
+               if(vnf->flags & SF_REVERSE)\r
+                       end = (vnf->flags & SF_LOOP) ? idxlpos : 0;\r
+               else\r
+                       end = (vnf->flags & SF_LOOP) ? idxlend : idxsize;\r
+\r
+               /* Als de sample simpelweg niet beschikbaar is, of als\r
+                       sample gestopt moet worden sample stilleggen en stoppen */\r
+               /* mix 'em: */\r
+\r
+               done=NewPredict(vnf->current,end,vnf->increment,todo);\r
+\r
+               if(!done){\r
+/*                     printf("predict stopped it. current %ld, end %ld\n",vnf->current,end);\r
+*/                     vnf->active=0;\r
+                       break;\r
+               }\r
+\r
+               /* optimisation: don't mix anything if volume is zero */\r
+\r
+               if(vnf->vol){\r
+#ifdef __WATCOMC__\r
+                       if(md_mode & DMODE_STEREO)\r
+                               AsmStereoNormal(s,ptr,vnf->current,vnf->increment,done);\r
+                       else\r
+                               AsmMonoNormal(s,ptr,vnf->current,vnf->increment,done);\r
+#else\r
+                       SampleMix(s,ptr,vnf->current,vnf->increment,done);\r
+#endif\r
+               }\r
+               vnf->current+=(vnf->increment*done);\r
+\r
+               todo-=done;\r
+               ptr+=(md_mode & DMODE_STEREO) ? (done<<1) : done;\r
+       }\r
+}\r
+\r
+\r
+\r
+\r
+static void VC_FillTick(SBYTE *buf,UWORD todo)\r
+/*\r
+       Mixes 'todo' samples to 'buf'.. The number of samples has\r
+       to fit into the tickbuffer.\r
+*/\r
+{\r
+       int t;\r
+\r
+       /* clear the mixing buffer: */\r
+\r
+       memset(VC_TICKBUF,0,(md_mode & DMODE_STEREO) ? todo<<3 : todo<<2);\r
+\r
+       for(t=0;t<md_numchn;t++){\r
+               vnf=&vinf[t];\r
+\r
+               if(vnf->active){\r
+                       idxsize=(vnf->size<<FRACBITS)-1;\r
+                       idxlpos=vnf->reppos<<FRACBITS;\r
+                       idxlend=(vnf->repend<<FRACBITS)-1;\r
+#ifdef __WATCOMC__\r
+                        lvolsel=vnf->lvolsel;\r
+                        rvolsel=vnf->rvolsel;\r
+#else\r
+                       lvolmul=vnf->lvolmul;\r
+                       rvolmul=vnf->rvolmul;\r
+#endif\r
+                       VC_AddChannel(VC_TICKBUF,todo);\r
+               }\r
+       }\r
+\r
+       if(md_mode & DMODE_16BITS)\r
+               VC_Sample32To16Copy(VC_TICKBUF,(SWORD *)buf,(md_mode & DMODE_STEREO) ? todo<<1 : todo,16-ampshift);\r
+       else\r
+               VC_Sample32To8Copy(VC_TICKBUF,buf,(md_mode & DMODE_STEREO) ? todo<<1 : todo,24-ampshift);\r
+}\r
+\r
+\r
+\r
+static void VC_WritePortion(SBYTE *buf,UWORD todo)\r
+/*\r
+       Writes 'todo' mixed SAMPLES (!!) to 'buf'. When todo is bigger than the\r
+       number of samples that fit into VC_TICKBUF, the mixing operation is split\r
+       up into a number of smaller chunks.\r
+*/\r
+{\r
+       UWORD part;\r
+\r
+       /* write 'part' samples to the buffer */\r
+\r
+       while(todo){\r
+               part=min(todo,samplesthatfit);\r
+               VC_FillTick(buf,part);\r
+               buf+=samples2bytes(part);\r
+               todo-=part;\r
+       }\r
+}\r
+\r
+\r
+static UWORD TICKLEFT;\r
+\r
+\r
+void VC_WriteSamples(SBYTE *buf,UWORD todo)\r
+{\r
+       int t;\r
+       UWORD part;\r
+\r
+       while(todo>0){\r
+\r
+               if(TICKLEFT==0){\r
+                       md_player();\r
+\r
+                       TICKLEFT=(125L*md_mixfreq)/(50L*md_bpm);\r
+\r
+                       /* compute volume, frequency counter & panning parameters for each channel. */\r
+\r
+                       for(t=0;t<md_numchn;t++){\r
+                               int pan,vol,lvol,rvol;\r
+\r
+                               if(vinf[t].kick){\r
+                                       vinf[t].current=(vinf[t].start << FRACBITS);\r
+                                       vinf[t].active=1;\r
+                                       vinf[t].kick=0;\r
+                               }\r
+\r
+                               if(vinf[t].frq==0) vinf[t].active=0;\r
+\r
+                               if(vinf[t].active){\r
+                                       vinf[t].increment=fraction2long(vinf[t].frq,md_mixfreq);\r
+\r
+                                       if(vinf[t].flags & SF_REVERSE) vinf[t].increment=-vinf[t].increment;\r
+\r
+                                       vol=vinf[t].vol;\r
+                                       pan=vinf[t].pan;\r
+\r
+#ifdef __WATCOMC__\r
+                                       if(md_mode & DMODE_STEREO){\r
+                                               lvol= ( vol * (255-pan) ) / 255;\r
+                                               rvol= ( vol * pan ) / 255;\r
+                                               vinf[t].lvolsel=volsel[lvol];\r
+                                               vinf[t].rvolsel=volsel[rvol];\r
+                                       }\r
+                                       else{\r
+                                               vinf[t].lvolsel=volsel[vol];\r
+                                       }\r
+#else\r
+                                       if(md_mode & DMODE_STEREO){\r
+                                               lvol= ( vol * (255-pan) ) / 255;\r
+                                               rvol= ( vol * pan ) / 255;\r
+                                               vinf[t].lvolmul=(maxvol*lvol)/64;\r
+                                               vinf[t].rvolmul=(maxvol*rvol)/64;\r
+                                       }\r
+                                       else{\r
+                                               vinf[t].lvolmul=(maxvol*vol)/64;\r
+                                       }\r
+#endif\r
+                               }\r
+                       }\r
+               }\r
+\r
+               part=min(TICKLEFT,todo);\r
+\r
+               VC_WritePortion(buf,part);\r
+\r
+               TICKLEFT-=part;\r
+               todo-=part;\r
+\r
+               buf+=samples2bytes(part);\r
+       }\r
+}\r
+\r
+\r
+UWORD VC_WriteBytes(SBYTE *buf,UWORD todo)\r
+/*\r
+       Writes 'todo' mixed SBYTES (!!) to 'buf'. It returns the number of\r
+       SBYTES actually written to 'buf' (which is rounded to number of samples\r
+       that fit into 'todo' bytes).\r
+*/\r
+{\r
+       todo=bytes2samples(todo);\r
+       VC_WriteSamples(buf,todo);\r
+       return samples2bytes(todo);\r
+}\r
+\r
+\r
+void VC_SilenceBytes(SBYTE *buf,UWORD todo)\r
+/*\r
+       Fill the buffer with 'todo' bytes of silence (it depends on the mixing\r
+       mode how the buffer is filled)\r
+*/\r
+{\r
+       /* clear the buffer to zero (16 bits\r
+          signed ) or 0x80 (8 bits unsigned) */\r
+\r
+       if(md_mode & DMODE_16BITS)\r
+               memset(buf,0,todo);\r
+       else\r
+               memset(buf,0x80,todo);\r
+}\r
+\r
+\r
+void VC_PlayStart(void)\r
+{\r
+       int t;\r
+\r
+       maxvol=16777216L / md_numchn;\r
+\r
+#ifdef __WATCOMC__\r
+       for(t=0;t<65;t++){\r
+               int c;\r
+               SLONG volmul=(maxvol*t)/64;\r
+               for(c=-128;c<128;c++){\r
+                       voltab[t][(UBYTE)c]=(SLONG)c*volmul;\r
+               }\r
+       }\r
+#endif\r
+\r
+       /* instead of using a amplifying lookup table, I'm using a simple shift\r
+          amplify now.. amplifying doubles with every extra 4 channels, and also\r
+          doubles in stereo mode.. this seems to give similar volume levels\r
+          across the channel range */\r
+\r
+       ampshift=md_numchn/8;\r
+       if(md_mode & DMODE_STEREO) ampshift++;\r
+\r
+#ifndef __WATCOMC__\r
+       if(md_mode & DMODE_INTERP)\r
+               SampleMix=(md_mode & DMODE_STEREO) ? MixStereoInterp : MixMonoInterp;\r
+       else\r
+               SampleMix=(md_mode & DMODE_STEREO) ? MixStereoNormal : MixMonoNormal;\r
+#endif\r
+\r
+       samplesthatfit=TICKLSIZE;\r
+       if(md_mode & DMODE_STEREO) samplesthatfit>>=1;\r
+       TICKLEFT=0;\r
+}\r
+\r
+\r
+void VC_PlayStop(void)\r
+{\r
+}\r
+\r
+\r
+BOOL VC_Init(void)\r
+{\r
+       int t;\r
+       for(t=0;t<32;t++){\r
+               vinf[t].current=0;\r
+               vinf[t].flags=0;\r
+               vinf[t].handle=0;\r
+               vinf[t].kick=0;\r
+               vinf[t].active=0;\r
+               vinf[t].frq=10000;\r
+               vinf[t].vol=0;\r
+               vinf[t].pan=(t&1)?0:255;\r
+       }\r
+\r
+#ifdef __WATCOMC__\r
+       if(md_mode & DMODE_INTERP) md_mode&=~DMODE_INTERP;\r
+\r
+       for(t=0;t<65;t++) volsel[t]=0;\r
+\r
+       for(t=0;t<65;t++){\r
+               if(!(volsel[t]=getalias())) return 0;\r
+               setbase(volsel[t],(ULONG)voltab[t]);\r
+       }\r
+#endif\r
+\r
+       return 1;\r
+}\r
+\r
+\r
+void VC_Exit(void)\r
+{\r
+#ifdef __WATCOMC__\r
+       int t;\r
+       for(t=0;t<65;t++){\r
+               if(volsel[t]) freedescriptor(volsel[t]);\r
+       }\r
+#endif\r
+}\r
+\r
+\r
+void VC_VoiceSetVolume(UBYTE voice,UBYTE vol)\r
+{\r
+       vinf[voice].vol=vol;\r
+}\r
+\r
+\r
+void VC_VoiceSetFrequency(UBYTE voice,ULONG frq)\r
+{\r
+       vinf[voice].frq=frq;\r
+}\r
+\r
+\r
+void VC_VoiceSetPanning(UBYTE voice,UBYTE pan)\r
+{\r
+       vinf[voice].pan=pan;\r
+}\r
+\r
+\r
+void VC_VoicePlay(UBYTE voice,SWORD handle,ULONG start,ULONG size,ULONG reppos,ULONG repend,UWORD flags)\r
+{\r
+       if(start>=size) return;\r
+\r
+       if(flags&SF_LOOP){\r
+               if(repend>size) repend=size;    /* repend can't be bigger than size */\r
+       }\r
+\r
+       vinf[voice].flags=flags;\r
+       vinf[voice].handle=handle;\r
+       vinf[voice].start=start;\r
+       vinf[voice].size=size;\r
+       vinf[voice].reppos=reppos;\r
+       vinf[voice].repend=repend;\r
+       vinf[voice].kick=1;\r
+}\r