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