builds with djgpp but crashes
[dosdemo] / libs / oldmik / src / virtch.c
1 /*\r
2 \r
3 Name:\r
4 VIRTCH.C\r
5 \r
6 Description:\r
7 All-c sample mixing routines, using a 32 bits mixing buffer\r
8 \r
9 Portability:\r
10 All systems - all compilers\r
11 \r
12 */\r
13 #include <stdio.h>\r
14 #include <stdlib.h>\r
15 #include <string.h>\r
16 #include <malloc.h>\r
17 #include "mikmod.h"\r
18 \r
19 #define FRACBITS 11\r
20 #define FRACMASK ((1L<<FRACBITS)-1)\r
21 \r
22 #define TICKLSIZE 3600\r
23 #define TICKWSIZE (TICKLSIZE*2)\r
24 #define TICKBSIZE (TICKWSIZE*2)\r
25 static SLONG VC_TICKBUF[TICKLSIZE];\r
26 \r
27 #ifndef min\r
28 #define min(a,b) (((a)<(b)) ? (a) : (b))\r
29 #endif\r
30 \r
31 typedef struct{\r
32         UBYTE kick;                     /* =1 -> sample has to be restarted */\r
33         UBYTE active;                   /* =1 -> sample is playing */\r
34         UWORD flags;                    /* 16/8 bits looping/one-shot */\r
35         SWORD handle;                  /* identifies the sample */\r
36         ULONG start;                    /* start index */\r
37         ULONG size;                     /* samplesize */\r
38         ULONG reppos;                   /* loop start */\r
39         ULONG repend;                   /* loop end */\r
40         ULONG frq;                      /* current frequency */\r
41         UBYTE vol;                      /* current volume */\r
42         UBYTE pan;                                              /* current panning position */\r
43         SLONG current;                  /* current index in the sample */\r
44         SLONG increment;                /* fixed-point increment value */\r
45 #ifdef __WATCOMC__\r
46         UWORD lvolsel;                                  /* left volume table selector */\r
47         UWORD rvolsel;                                  /* right volume table selector */\r
48 #else\r
49         SLONG lvolmul;                                  /* left volume multiply */\r
50         SLONG rvolmul;                                  /* right volume multiply */\r
51 #endif\r
52 } VINFO;\r
53 \r
54 \r
55 static VINFO vinf[32];\r
56 static VINFO *vnf;\r
57 \r
58 static UWORD samplesthatfit;\r
59 static SLONG idxsize,idxlpos,idxlend,maxvol;\r
60 \r
61 static long per256;\r
62 static int ampshift;\r
63 \r
64 \r
65 #ifdef __WATCOMC__\r
66 \r
67 static SLONG voltab[65][256];\r
68 static UWORD volsel[65];\r
69 \r
70 #ifdef __cplusplus\r
71 extern "C" {\r
72 #endif\r
73 \r
74 UWORD lvolsel,rvolsel;\r
75 \r
76 #if __WATCOMC__ >= 1200 /* OpenWatcom 1.0+ */\r
77 #define AsmStereoNormal _AsmStereoNormal\r
78 #define AsmMonoNormal _AsmMonoNormal\r
79 #endif\r
80 \r
81 void AsmStereoNormal(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,ULONG todo);\r
82 #pragma aux AsmStereoNormal     \\r
83                 parm [esi] [edi] [ebx] [ecx] [edx] \\r
84                 modify [eax];\r
85 \r
86 \r
87 void AsmMonoNormal(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,ULONG todo);\r
88 #pragma aux AsmMonoNormal     \\r
89         parm [esi] [edi] [ebx] [ecx] [edx] \\r
90         modify [eax];\r
91 \r
92 #ifdef __cplusplus\r
93 }\r
94 #endif\r
95 \r
96 void freedescriptor(unsigned short selector);\r
97 #pragma aux freedescriptor =    \\r
98         "mov    ax,0001h"       \\r
99         "int    31h"            \\r
100         parm    [bx]            \\r
101         modify  [ax];\r
102 \r
103 unsigned short getalias(void);\r
104 #pragma aux getalias =          \\r
105         "mov    ax,cs   "       \\r
106         "mov    bx,ax   "       \\r
107         "mov    ax,000ah"       \\r
108         "int    31h     "       \\r
109         "jnc    isok    "       \\r
110         "xor    ax,ax   "       \\r
111         "isok:          "       \\r
112         modify  [bx]            \\r
113         value   [ax];\r
114 \r
115 void setbase(unsigned short selector,unsigned long offset);\r
116 #pragma aux setbase =           \\r
117         "mov    ax,0007h"       \\r
118         "mov    ecx,edx"        \\r
119         "ror    ecx,16"         \\r
120         "int    31h"            \\r
121         parm    [bx] [edx]      \\r
122         modify  [ax ecx] ;\r
123 \r
124 void VC_Sample32To16Copy(SLONG *srce,SWORD *dest,ULONG count,UBYTE shift);\r
125 #pragma aux VC_Sample32To16Copy =       \\r
126 "again:"                                        \\r
127         "mov   eax,[esi]"               \\r
128         "sar   eax,cl"          \\r
129         "cmp   eax,32767"               \\r
130         "jg    toobig"                  \\r
131         "cmp   eax,-32768"              \\r
132         "jl    toosmall"                \\r
133 "write:"                                        \\r
134         "mov   [edi],ax"                \\r
135         "add   esi,4"           \\r
136         "add   edi,2"                   \\r
137         "dec   edx"             \\r
138         "jnz   again"           \\r
139         "jmp   ready"                   \\r
140 "toobig:"                   \\r
141         "mov   eax,32767"               \\r
142         "jmp   write"                   \\r
143 "toosmall:"                 \\r
144         "mov   eax,-32768"              \\r
145         "jmp   write"                   \\r
146 "ready:"                                        \\r
147         parm [esi] [edi] [edx] [cl]     \\r
148         modify [eax] ;\r
149 \r
150 \r
151 void VC_Sample32To8Copy(SLONG *srce,SBYTE *dest,ULONG count,UBYTE shift);\r
152 #pragma aux VC_Sample32To8Copy =        \\r
153 "again:"                        \\r
154         "mov   eax,[esi]"               \\r
155         "sar   eax,cl"          \\r
156         "cmp   eax,127"         \\r
157         "jg    toobig"                  \\r
158         "cmp   eax,-128"        \\r
159         "jl    toosmall"                \\r
160 "write:"                    \\r
161         "add   al,080h"         \\r
162         "mov   [edi],al"        \\r
163         "add   esi,4"           \\r
164         "inc   edi"             \\r
165         "dec   edx"             \\r
166         "jnz   again"                   \\r
167         "jmp   ready"                   \\r
168 "toobig:"                   \\r
169         "mov   eax,127"         \\r
170         "jmp   write"                   \\r
171 "toosmall:"                 \\r
172         "mov   eax,-128"        \\r
173         "jmp   write"                   \\r
174 "ready:"                    \\r
175         parm [esi] [edi] [edx] [cl]     \\r
176         modify [eax] ;\r
177 \r
178 \r
179 #else\r
180 \r
181 \r
182 static SLONG lvolmul,rvolmul;\r
183 \r
184 \r
185 static void VC_Sample32To8Copy(SLONG *srce,SBYTE *dest,ULONG count,UBYTE shift)\r
186 {\r
187         SLONG c;\r
188         int fooshift=(24-ampshift);\r
189 \r
190         while(count--){\r
191                 c=*srce >> fooshift;\r
192                 if(c>127) c=127;\r
193                 else if(c<-128) c=-128;\r
194                 *dest++=c+128;\r
195                 srce++;\r
196         }\r
197 }\r
198 \r
199 \r
200 static void VC_Sample32To16Copy(SLONG *srce,SWORD *dest,ULONG count,UBYTE shift)\r
201 {\r
202         SLONG c;\r
203         int fooshift=(16-ampshift);\r
204 \r
205         while(count--){\r
206                 c=*srce >> fooshift;\r
207                 if(c>32767) c=32767;\r
208                 else if(c<-32768) c=-32768;\r
209                 *dest++=c;\r
210                 srce++;\r
211         }\r
212 }\r
213 \r
214 #endif\r
215 \r
216 \r
217 static SLONG fraction2long(ULONG dividend,UWORD divisor)\r
218 /*\r
219         Converts the fraction 'dividend/divisor' into a fixed point longword.\r
220 */\r
221 {\r
222         ULONG whole,part;\r
223 \r
224         whole=dividend/divisor;\r
225         part=((dividend%divisor)<<FRACBITS)/divisor;\r
226 \r
227         return((whole<<FRACBITS)|part);\r
228 }\r
229 \r
230 \r
231 static UWORD samples2bytes(UWORD samples)\r
232 {\r
233         if(md_mode & DMODE_16BITS) samples<<=1;\r
234         if(md_mode & DMODE_STEREO) samples<<=1;\r
235         return samples;\r
236 }\r
237 \r
238 \r
239 static UWORD bytes2samples(UWORD bytes)\r
240 {\r
241         if(md_mode & DMODE_16BITS) bytes>>=1;\r
242         if(md_mode & DMODE_STEREO) bytes>>=1;\r
243         return bytes;\r
244 }\r
245 \r
246 \r
247 /**************************************************\r
248 ***************************************************\r
249 ***************************************************\r
250 **************************************************/\r
251 \r
252 \r
253 static SBYTE *Samples[MAXSAMPLEHANDLES];\r
254 \r
255 \r
256 BOOL LargeRead(SBYTE *buffer,ULONG size)\r
257 {\r
258         int t;\r
259         ULONG todo;\r
260 \r
261         while(size){\r
262                 /* how many bytes to load (in chunks of 8000) ? */\r
263 \r
264                 todo=(size>8000)?8000:size;\r
265 \r
266                 /* read data */\r
267 \r
268                 SL_Load(buffer,todo);\r
269                 /* and update pointers.. */\r
270 \r
271                 size-=todo;\r
272                 buffer+=todo;\r
273         }\r
274         return 1;\r
275 }\r
276 \r
277 \r
278 \r
279 SWORD VC_SampleLoad(FILE *fp,ULONG length,ULONG reppos,ULONG repend,UWORD flags)\r
280 {\r
281         int handle;\r
282         ULONG t;\r
283 \r
284         SL_Init(fp,flags,(flags|SF_SIGNED)&~SF_16BITS);\r
285 \r
286         /* Find empty slot to put sample address in */\r
287 \r
288         for(handle=0;handle<MAXSAMPLEHANDLES;handle++){\r
289                 if(Samples[handle]==NULL) break;\r
290         }\r
291 \r
292         if(handle==MAXSAMPLEHANDLES){\r
293                 myerr=ERROR_OUT_OF_HANDLES;\r
294                 return -1;\r
295         }\r
296 \r
297         if((Samples[handle]=(SBYTE *)malloc(length+16))==NULL){\r
298                 myerr=ERROR_SAMPLE_TOO_BIG;\r
299                 return -1;\r
300         }\r
301 \r
302         /* read sample into buffer. */\r
303         LargeRead(Samples[handle],length);\r
304 \r
305         /* Unclick samples: */\r
306 \r
307         if(flags & SF_LOOP){\r
308                 if(flags & SF_BIDI)\r
309                         for(t=0;t<16;t++) Samples[handle][repend+t]=Samples[handle][(repend-t)-1];\r
310                 else\r
311                         for(t=0;t<16;t++) Samples[handle][repend+t]=Samples[handle][t+reppos];\r
312         }\r
313         else{\r
314                 for(t=0;t<16;t++) Samples[handle][t+length]=0;\r
315         }\r
316 \r
317         return handle;\r
318 }\r
319 \r
320 \r
321 \r
322 void VC_SampleUnload(SWORD handle)\r
323 {\r
324         void *sampleadr=Samples[handle];\r
325 \r
326         free(sampleadr);\r
327         Samples[handle]=NULL;\r
328 }\r
329 \r
330 \r
331 /**************************************************\r
332 ***************************************************\r
333 ***************************************************\r
334 **************************************************/\r
335 \r
336 \r
337 #ifndef __WATCOMC__\r
338 \r
339 \r
340 static void (*SampleMix)(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,UWORD todo);\r
341 \r
342 \r
343 static void MixStereoNormal(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,UWORD todo)\r
344 {\r
345         SBYTE sample;\r
346 \r
347         while(todo>0){\r
348                 sample=srce[index>>FRACBITS];\r
349                 *(dest++)+=lvolmul*sample;\r
350                 *(dest++)+=rvolmul*sample;\r
351                 index+=increment;\r
352                 todo--;\r
353         }\r
354 }\r
355 \r
356 \r
357 static void MixMonoNormal(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,UWORD todo)\r
358 {\r
359         SBYTE sample;\r
360 \r
361         while(todo>0){\r
362                 sample=srce[index>>FRACBITS];\r
363                 *(dest++)+=lvolmul*sample;\r
364                 index+=increment;\r
365                 todo--;\r
366         }\r
367 }\r
368 \r
369 \r
370 static void MixStereoInterp(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,UWORD todo)\r
371 {\r
372         SWORD sample,a,b;\r
373 \r
374         while(todo>0){\r
375                 a=srce[index>>FRACBITS];\r
376                 b=srce[1+(index>>FRACBITS)];\r
377                 sample=a+(((long)(b-a)*(index&FRACMASK))>>FRACBITS);\r
378 \r
379                 *(dest++)+=lvolmul*sample;\r
380                 *(dest++)+=rvolmul*sample;\r
381                 index+=increment;\r
382                 todo--;\r
383         }\r
384 }\r
385 \r
386 \r
387 static void MixMonoInterp(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,UWORD todo)\r
388 {\r
389         SWORD sample,a,b;\r
390 \r
391         while(todo>0){\r
392                 a=srce[index>>FRACBITS];\r
393                 b=srce[1+(index>>FRACBITS)];\r
394                 sample=a+(((long)(b-a)*(index&FRACMASK))>>FRACBITS);\r
395 \r
396                 *(dest++)+=lvolmul*sample;\r
397 \r
398                 index+=increment;\r
399                 todo--;\r
400         }\r
401 }\r
402 \r
403 #endif\r
404 \r
405 \r
406 static UWORD NewPredict(SLONG index,SLONG end,SLONG increment,UWORD todo)\r
407 /*\r
408         This functions returns the number of resamplings we can do so that:\r
409 \r
410                 - it never accesses indexes bigger than index 'end'\r
411                 - it doesn't do more than 'todo' resamplings\r
412 */\r
413 {\r
414         SLONG di;\r
415 \r
416         di=(end-index)/increment;\r
417         index+=(di*increment);\r
418 \r
419         if(increment<0){\r
420                 while(index>=end){\r
421                         index+=increment;\r
422                         di++;\r
423                 }\r
424         }\r
425         else{\r
426                 while(index<=end){\r
427                         index+=increment;\r
428                         di++;\r
429                 }\r
430         }\r
431         return ((di<todo) ? di : todo);\r
432 }\r
433 \r
434 \r
435 static void VC_AddChannel(SLONG *ptr,UWORD todo)\r
436 /*\r
437         Mixes 'todo' stereo or mono samples of the current channel to the tickbuffer.\r
438 */\r
439 {\r
440         SLONG end;\r
441         UWORD done,needs;\r
442         SBYTE *s;\r
443 \r
444         while(todo>0){\r
445 \r
446                 /* update the 'current' index so the sample loops, or\r
447                    stops playing if it reached the end of the sample */\r
448 \r
449                 if(vnf->flags&SF_REVERSE){\r
450 \r
451                         /* The sample is playing in reverse */\r
452 \r
453                                 if(vnf->flags&SF_LOOP){\r
454 \r
455                                         /* the sample is looping, so check if\r
456                                            it reached the loopstart index */\r
457 \r
458                                         if(vnf->current<idxlpos){\r
459                                         if(vnf->flags&SF_BIDI){\r
460 \r
461                                                 /* sample is doing bidirectional loops, so 'bounce'\r
462                                                         the current index against the idxlpos */\r
463 \r
464                                                 vnf->current=idxlpos+(idxlpos-vnf->current);\r
465                                                 vnf->flags&=~SF_REVERSE;\r
466                                                 vnf->increment=-vnf->increment;\r
467                                         }\r
468                                         else\r
469                                                 /* normal backwards looping, so set the\r
470                                                         current position to loopend index */\r
471 \r
472                                                         vnf->current=idxlend-(idxlpos-vnf->current);\r
473                                         }\r
474                                 }\r
475                                 else{\r
476 \r
477                                         /* the sample is not looping, so check\r
478                                                 if it reached index 0 */\r
479 \r
480                                         if(vnf->current<0){\r
481 \r
482                                                 /* playing index reached 0, so stop\r
483                                                         playing this sample */\r
484 \r
485                                                 vnf->current=0;\r
486                                                 vnf->active=0;\r
487                                                 break;\r
488                                         }\r
489                                 }\r
490                 }\r
491                 else{\r
492 \r
493                         /* The sample is playing forward */\r
494 \r
495                                 if(vnf->flags&SF_LOOP){\r
496 \r
497                                         /* the sample is looping, so check if\r
498                                                 it reached the loopend index */\r
499 \r
500                                         if(vnf->current>idxlend){\r
501                                                 if(vnf->flags&SF_BIDI){\r
502 \r
503                                                 /* sample is doing bidirectional loops, so 'bounce'\r
504                                                         the current index against the idxlend */\r
505 \r
506                                                         vnf->flags|=SF_REVERSE;\r
507                                                         vnf->increment=-vnf->increment;\r
508                                                         vnf->current=idxlend-(vnf->current-idxlend); /* ?? */\r
509                                                 }\r
510                                                 else\r
511                                                 /* normal backwards looping, so set the\r
512                                                         current position to loopend index */\r
513 \r
514                                                         vnf->current=idxlpos+(vnf->current-idxlend);\r
515                                         }\r
516                                 }\r
517                                 else{\r
518 \r
519                                         /* sample is not looping, so check\r
520                                                 if it reached the last position */\r
521 \r
522                                         if(vnf->current>idxsize){\r
523 \r
524                                                 /* yes, so stop playing this sample */\r
525 \r
526                                                 vnf->current=0;\r
527                                                 vnf->active=0;\r
528                                                 break;\r
529                                         }\r
530                                 }\r
531                 }\r
532 \r
533                 /* Vraag een far ptr op van het sampleadres\r
534                         op byte offset vnf->current, en hoeveel samples\r
535                         daarvan geldig zijn (VOORDAT segment overschrijding optreed) */\r
536 \r
537                 if(!(s=Samples[vnf->handle])){\r
538                         vnf->current=0;\r
539                         vnf->active=0;\r
540                         break;\r
541                 }\r
542 \r
543                 if(vnf->flags & SF_REVERSE)\r
544                         end = (vnf->flags & SF_LOOP) ? idxlpos : 0;\r
545                 else\r
546                         end = (vnf->flags & SF_LOOP) ? idxlend : idxsize;\r
547 \r
548                 /* Als de sample simpelweg niet beschikbaar is, of als\r
549                         sample gestopt moet worden sample stilleggen en stoppen */\r
550                 /* mix 'em: */\r
551 \r
552                 done=NewPredict(vnf->current,end,vnf->increment,todo);\r
553 \r
554                 if(!done){\r
555 /*                      printf("predict stopped it. current %ld, end %ld\n",vnf->current,end);\r
556 */                      vnf->active=0;\r
557                         break;\r
558                 }\r
559 \r
560                 /* optimisation: don't mix anything if volume is zero */\r
561 \r
562                 if(vnf->vol){\r
563 #ifdef __WATCOMC__\r
564                         if(md_mode & DMODE_STEREO)\r
565                                 AsmStereoNormal(s,ptr,vnf->current,vnf->increment,done);\r
566                         else\r
567                                 AsmMonoNormal(s,ptr,vnf->current,vnf->increment,done);\r
568 #else\r
569                         SampleMix(s,ptr,vnf->current,vnf->increment,done);\r
570 #endif\r
571                 }\r
572                 vnf->current+=(vnf->increment*done);\r
573 \r
574                 todo-=done;\r
575                 ptr+=(md_mode & DMODE_STEREO) ? (done<<1) : done;\r
576         }\r
577 }\r
578 \r
579 \r
580 \r
581 \r
582 static void VC_FillTick(SBYTE *buf,UWORD todo)\r
583 /*\r
584         Mixes 'todo' samples to 'buf'.. The number of samples has\r
585         to fit into the tickbuffer.\r
586 */\r
587 {\r
588         int t;\r
589 \r
590         /* clear the mixing buffer: */\r
591 \r
592         memset(VC_TICKBUF,0,(md_mode & DMODE_STEREO) ? todo<<3 : todo<<2);\r
593 \r
594         for(t=0;t<md_numchn;t++){\r
595                 vnf=&vinf[t];\r
596 \r
597                 if(vnf->active){\r
598                         idxsize=(vnf->size<<FRACBITS)-1;\r
599                         idxlpos=vnf->reppos<<FRACBITS;\r
600                         idxlend=(vnf->repend<<FRACBITS)-1;\r
601 #ifdef __WATCOMC__\r
602                         lvolsel=vnf->lvolsel;\r
603                         rvolsel=vnf->rvolsel;\r
604 #else\r
605                         lvolmul=vnf->lvolmul;\r
606                         rvolmul=vnf->rvolmul;\r
607 #endif\r
608                         VC_AddChannel(VC_TICKBUF,todo);\r
609                 }\r
610         }\r
611 \r
612         if(md_mode & DMODE_16BITS)\r
613                 VC_Sample32To16Copy(VC_TICKBUF,(SWORD *)buf,(md_mode & DMODE_STEREO) ? todo<<1 : todo,16-ampshift);\r
614         else\r
615                 VC_Sample32To8Copy(VC_TICKBUF,buf,(md_mode & DMODE_STEREO) ? todo<<1 : todo,24-ampshift);\r
616 }\r
617 \r
618 \r
619 \r
620 static void VC_WritePortion(SBYTE *buf,UWORD todo)\r
621 /*\r
622         Writes 'todo' mixed SAMPLES (!!) to 'buf'. When todo is bigger than the\r
623         number of samples that fit into VC_TICKBUF, the mixing operation is split\r
624         up into a number of smaller chunks.\r
625 */\r
626 {\r
627         UWORD part;\r
628 \r
629         /* write 'part' samples to the buffer */\r
630 \r
631         while(todo){\r
632                 part=min(todo,samplesthatfit);\r
633                 VC_FillTick(buf,part);\r
634                 buf+=samples2bytes(part);\r
635                 todo-=part;\r
636         }\r
637 }\r
638 \r
639 \r
640 static UWORD TICKLEFT;\r
641 \r
642 \r
643 void VC_WriteSamples(SBYTE *buf,UWORD todo)\r
644 {\r
645         int t;\r
646         UWORD part;\r
647 \r
648         while(todo>0){\r
649 \r
650                 if(TICKLEFT==0){\r
651                         md_player();\r
652 \r
653                         TICKLEFT=(125L*md_mixfreq)/(50L*md_bpm);\r
654 \r
655                         /* compute volume, frequency counter & panning parameters for each channel. */\r
656 \r
657                         for(t=0;t<md_numchn;t++){\r
658                                 int pan,vol,lvol,rvol;\r
659 \r
660                                 if(vinf[t].kick){\r
661                                         vinf[t].current=(vinf[t].start << FRACBITS);\r
662                                         vinf[t].active=1;\r
663                                         vinf[t].kick=0;\r
664                                 }\r
665 \r
666                                 if(vinf[t].frq==0) vinf[t].active=0;\r
667 \r
668                                 if(vinf[t].active){\r
669                                         vinf[t].increment=fraction2long(vinf[t].frq,md_mixfreq);\r
670 \r
671                                         if(vinf[t].flags & SF_REVERSE) vinf[t].increment=-vinf[t].increment;\r
672 \r
673                                         vol=vinf[t].vol;\r
674                                         pan=vinf[t].pan;\r
675 \r
676 #ifdef __WATCOMC__\r
677                                         if(md_mode & DMODE_STEREO){\r
678                                                 lvol= ( vol * (255-pan) ) / 255;\r
679                                                 rvol= ( vol * pan ) / 255;\r
680                                                 vinf[t].lvolsel=volsel[lvol];\r
681                                                 vinf[t].rvolsel=volsel[rvol];\r
682                                         }\r
683                                         else{\r
684                                                 vinf[t].lvolsel=volsel[vol];\r
685                                         }\r
686 #else\r
687                                         if(md_mode & DMODE_STEREO){\r
688                                                 lvol= ( vol * (255-pan) ) / 255;\r
689                                                 rvol= ( vol * pan ) / 255;\r
690                                                 vinf[t].lvolmul=(maxvol*lvol)/64;\r
691                                                 vinf[t].rvolmul=(maxvol*rvol)/64;\r
692                                         }\r
693                                         else{\r
694                                                 vinf[t].lvolmul=(maxvol*vol)/64;\r
695                                         }\r
696 #endif\r
697                                 }\r
698                         }\r
699                 }\r
700 \r
701                 part=min(TICKLEFT,todo);\r
702 \r
703                 VC_WritePortion(buf,part);\r
704 \r
705                 TICKLEFT-=part;\r
706                 todo-=part;\r
707 \r
708                 buf+=samples2bytes(part);\r
709         }\r
710 }\r
711 \r
712 \r
713 UWORD VC_WriteBytes(SBYTE *buf,UWORD todo)\r
714 /*\r
715         Writes 'todo' mixed SBYTES (!!) to 'buf'. It returns the number of\r
716         SBYTES actually written to 'buf' (which is rounded to number of samples\r
717         that fit into 'todo' bytes).\r
718 */\r
719 {\r
720         todo=bytes2samples(todo);\r
721         VC_WriteSamples(buf,todo);\r
722         return samples2bytes(todo);\r
723 }\r
724 \r
725 \r
726 void VC_SilenceBytes(SBYTE *buf,UWORD todo)\r
727 /*\r
728         Fill the buffer with 'todo' bytes of silence (it depends on the mixing\r
729         mode how the buffer is filled)\r
730 */\r
731 {\r
732         /* clear the buffer to zero (16 bits\r
733            signed ) or 0x80 (8 bits unsigned) */\r
734 \r
735         if(md_mode & DMODE_16BITS)\r
736                 memset(buf,0,todo);\r
737         else\r
738                 memset(buf,0x80,todo);\r
739 }\r
740 \r
741 \r
742 void VC_PlayStart(void)\r
743 {\r
744         int t;\r
745 \r
746         maxvol=16777216L / md_numchn;\r
747 \r
748 #ifdef __WATCOMC__\r
749         for(t=0;t<65;t++){\r
750                 int c;\r
751                 SLONG volmul=(maxvol*t)/64;\r
752                 for(c=-128;c<128;c++){\r
753                         voltab[t][(UBYTE)c]=(SLONG)c*volmul;\r
754                 }\r
755         }\r
756 #endif\r
757 \r
758         /* instead of using a amplifying lookup table, I'm using a simple shift\r
759            amplify now.. amplifying doubles with every extra 4 channels, and also\r
760            doubles in stereo mode.. this seems to give similar volume levels\r
761            across the channel range */\r
762 \r
763         ampshift=md_numchn/8;\r
764         if(md_mode & DMODE_STEREO) ampshift++;\r
765 \r
766 #ifndef __WATCOMC__\r
767         if(md_mode & DMODE_INTERP)\r
768                 SampleMix=(md_mode & DMODE_STEREO) ? MixStereoInterp : MixMonoInterp;\r
769         else\r
770                 SampleMix=(md_mode & DMODE_STEREO) ? MixStereoNormal : MixMonoNormal;\r
771 #endif\r
772 \r
773         samplesthatfit=TICKLSIZE;\r
774         if(md_mode & DMODE_STEREO) samplesthatfit>>=1;\r
775         TICKLEFT=0;\r
776 }\r
777 \r
778 \r
779 void VC_PlayStop(void)\r
780 {\r
781 }\r
782 \r
783 \r
784 BOOL VC_Init(void)\r
785 {\r
786         int t;\r
787         for(t=0;t<32;t++){\r
788                 vinf[t].current=0;\r
789                 vinf[t].flags=0;\r
790                 vinf[t].handle=0;\r
791                 vinf[t].kick=0;\r
792                 vinf[t].active=0;\r
793                 vinf[t].frq=10000;\r
794                 vinf[t].vol=0;\r
795                 vinf[t].pan=(t&1)?0:255;\r
796         }\r
797 \r
798 #ifdef __WATCOMC__\r
799         if(md_mode & DMODE_INTERP) md_mode&=~DMODE_INTERP;\r
800 \r
801         for(t=0;t<65;t++) volsel[t]=0;\r
802 \r
803         for(t=0;t<65;t++){\r
804                 if(!(volsel[t]=getalias())) return 0;\r
805                 setbase(volsel[t],(ULONG)voltab[t]);\r
806         }\r
807 #endif\r
808 \r
809         return 1;\r
810 }\r
811 \r
812 \r
813 void VC_Exit(void)\r
814 {\r
815 #ifdef __WATCOMC__\r
816         int t;\r
817         for(t=0;t<65;t++){\r
818                 if(volsel[t]) freedescriptor(volsel[t]);\r
819         }\r
820 #endif\r
821 }\r
822 \r
823 \r
824 void VC_VoiceSetVolume(UBYTE voice,UBYTE vol)\r
825 {\r
826         vinf[voice].vol=vol;\r
827 }\r
828 \r
829 \r
830 void VC_VoiceSetFrequency(UBYTE voice,ULONG frq)\r
831 {\r
832         vinf[voice].frq=frq;\r
833 }\r
834 \r
835 \r
836 void VC_VoiceSetPanning(UBYTE voice,UBYTE pan)\r
837 {\r
838         vinf[voice].pan=pan;\r
839 }\r
840 \r
841 \r
842 void VC_VoicePlay(UBYTE voice,SWORD handle,ULONG start,ULONG size,ULONG reppos,ULONG repend,UWORD flags)\r
843 {\r
844         if(start>=size) return;\r
845 \r
846         if(flags&SF_LOOP){\r
847                 if(repend>size) repend=size;    /* repend can't be bigger than size */\r
848         }\r
849 \r
850         vinf[voice].flags=flags;\r
851         vinf[voice].handle=handle;\r
852         vinf[voice].start=start;\r
853         vinf[voice].size=size;\r
854         vinf[voice].reppos=reppos;\r
855         vinf[voice].repend=repend;\r
856         vinf[voice].kick=1;\r
857 }\r