home *** CD-ROM | disk | FTP | other *** search
/ The Games Machine 25 / GNOME_DEMO.iso / amiga / music / mikmod.lzx / mikmod / virtch.c < prev    next >
C/C++ Source or Header  |  1999-01-06  |  13KB  |  646 lines

  1. /*
  2.  
  3. Name:
  4. VIRTCH.C
  5.  
  6. Description:
  7. All-c sample mixing routines, using a 32 bits mixing buffer
  8.  
  9. Portability:
  10. All systems - all compilers
  11.  
  12. */
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. #include <string.h>
  16. /* #include <malloc.h> */
  17. #include "mikmod.h"
  18.  
  19. #define FRACBITS 11
  20. #define FRACMASK ((1L<<FRACBITS)-1)
  21.  
  22. #define TICKLSIZE 3600
  23. #define TICKWSIZE (TICKLSIZE*2)
  24. #define TICKBSIZE (TICKWSIZE*2)
  25. SLONG VC_TICKBUF[TICKLSIZE];
  26.  
  27. #ifndef min
  28. #define min(a,b) (((a)<(b)) ? (a) : (b))
  29. #endif
  30.  
  31. typedef struct{
  32.     UBYTE kick;                     /* =1 -> sample has to be restarted */
  33.     UBYTE active;                   /* =1 -> sample is playing */
  34.     UWORD flags;                    /* 16/8 bits looping/one-shot */
  35.     SWORD handle;                  /* identifies the sample */
  36.     ULONG start;                    /* start index */
  37.     ULONG size;                     /* samplesize */
  38.     ULONG reppos;                   /* loop start */
  39.     ULONG repend;                   /* loop end */
  40.     ULONG frq;                      /* current frequency */
  41.     UBYTE vol;                      /* current volume */
  42.     UBYTE pan;                        /* current panning position */
  43.     SLONG current;                  /* current index in the sample */
  44.     SLONG increment;                 /* fixed-point increment value */
  45.     SLONG lvolmul;                    /* left volume multiply */
  46.     SLONG rvolmul;                    /* right volume multiply */
  47. } VINFO;
  48.  
  49. VINFO vinf[32];
  50. VINFO *vnf;
  51.  
  52.  
  53. UWORD samplesthatfit;
  54. SLONG idxsize,idxlpos,idxlend,maxvol;
  55.  
  56. long per256;
  57. int ampshift;
  58.  
  59.  
  60. void VC_Sample32To8Copy(SLONG *srce,SBYTE *dest,UWORD count)
  61. {
  62.     SLONG c;
  63.     int shift=(24-ampshift);
  64.  
  65.     while(count--){
  66.         c=*srce >> shift;
  67.         if(c>127) c=127;
  68.         else if(c<-128) c=-128;
  69.         *dest++=c+128;
  70.         srce++;
  71.     }
  72. }
  73.  
  74.  
  75. void VC_Sample32To16Copy(SLONG *srce,SWORD *dest,UWORD count)
  76. {
  77.     SLONG c;
  78.     int shift=(16-ampshift);
  79.  
  80.     while(count--){
  81.         c=*srce >> shift;
  82.         if(c>32767) c=32767;
  83.         else if(c<-32768) c=-32768;
  84.         *dest++=c;
  85.         srce++;
  86.     }
  87. }
  88.  
  89.  
  90. SLONG fraction2long(ULONG dividend,UWORD divisor)
  91. /*
  92.     Converts the fraction 'dividend/divisor' into a fixed point longword.
  93. */
  94. {
  95.     ULONG whole,part;
  96.  
  97.     whole=dividend/divisor;
  98.     part=((dividend%divisor)<<FRACBITS)/divisor;
  99.  
  100.     return((whole<<FRACBITS)|part);
  101. }
  102.  
  103. UWORD samples2bytes(UWORD samples)
  104. {
  105.     if(md_mode & DMODE_16BITS) samples<<=1;
  106.     if(md_mode & DMODE_STEREO) samples<<=1;
  107.     return samples;
  108. }
  109.  
  110. UWORD bytes2samples(UWORD bytes)
  111. {
  112.     if(md_mode & DMODE_16BITS) bytes>>=1;
  113.     if(md_mode & DMODE_STEREO) bytes>>=1;
  114.     return bytes;
  115. }
  116.  
  117. /**************************************************
  118. ***************************************************
  119. ***************************************************
  120. **************************************************/
  121.  
  122.  
  123. char *Samples[MAXSAMPLEHANDLES];
  124.  
  125.  
  126. BOOL LargeRead(char *buffer,ULONG size)
  127. {
  128.     int t;
  129.     ULONG todo;
  130.  
  131.     while(size){
  132.         /* how many bytes to load (in chunks of 8000) ? */
  133.  
  134.         todo=(size>8000)?8000:size;
  135.  
  136.         /* read data */
  137.  
  138.         SL_Load(buffer,todo);
  139.         /* and update pointers.. */
  140.  
  141.         size-=todo;
  142.         buffer+=todo;
  143.     }
  144.     return 1;
  145. }
  146.  
  147.  
  148.  
  149. SWORD VC_SampleLoad(FILE *fp,ULONG length,ULONG reppos,ULONG repend,UWORD flags)
  150. {
  151.     int handle;
  152.     ULONG t;
  153.  
  154.     SL_Init(fp,flags,(flags|SF_SIGNED)&~SF_16BITS);
  155.  
  156.     /* Find empty slot to put sample address in */
  157.  
  158.     for(handle=0;handle<MAXSAMPLEHANDLES;handle++){
  159.         if(Samples[handle]==NULL) break;
  160.     }
  161.  
  162.     if(handle==MAXSAMPLEHANDLES){
  163.         myerr=ERROR_OUT_OF_HANDLES;
  164.         return -1;
  165.     }
  166.  
  167.     if((Samples[handle]=malloc(length+16))==NULL){
  168.         myerr=ERROR_SAMPLE_TOO_BIG;
  169.         return -1;
  170.     }
  171.  
  172.     /* read sample into buffer. */
  173.     LargeRead(Samples[handle],length);
  174.  
  175.     /* Unclick samples: */
  176.  
  177.     if(flags & SF_LOOP){
  178.         if(flags & SF_BIDI)
  179.             for(t=0;t<16;t++) Samples[handle][repend+t]=Samples[handle][(repend-t)-1];
  180.         else
  181.             for(t=0;t<16;t++) Samples[handle][repend+t]=Samples[handle][t+reppos];
  182.     }
  183.     else{
  184.         for(t=0;t<16;t++) Samples[handle][t+length]=0;
  185.     }
  186.  
  187.     return handle;
  188. }
  189.  
  190.  
  191.  
  192. void VC_SampleUnload(SWORD handle)
  193. {
  194.     void *sampleadr=Samples[handle];
  195.  
  196.     free(sampleadr);
  197.     Samples[handle]=NULL;
  198. }
  199.  
  200.  
  201. /**************************************************
  202. ***************************************************
  203. ***************************************************
  204. **************************************************/
  205.  
  206. void (*SampleMix)(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,SLONG lvolt,SLONG rvolt,UWORD todo);
  207.  
  208.  
  209. void MixStereoNormal(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,SLONG lvolt,SLONG rvolt,UWORD todo)
  210. {
  211.     SBYTE sample;
  212.  
  213.     while(todo>0){
  214.         sample=srce[index>>FRACBITS];
  215.         *(dest++)+=lvolt*sample;
  216.         *(dest++)+=rvolt*sample;
  217.         index+=increment;
  218.         todo--;
  219.     }
  220. }
  221.  
  222.  
  223. void MixMonoNormal(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,SLONG lvolt,SLONG rvolt,UWORD todo)
  224. {
  225.     SBYTE sample;
  226.  
  227.     while(todo>0){
  228.         sample=srce[index>>FRACBITS];
  229.         *(dest++)+=lvolt*sample;
  230.         index+=increment;
  231.         todo--;
  232.     }
  233. }
  234.  
  235.  
  236. void MixStereoInterp(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,SLONG lvolt,SLONG rvolt,UWORD todo)
  237. {
  238.     SWORD sample,a,b;
  239.  
  240.     while(todo>0){
  241.         a=srce[index>>FRACBITS];
  242.         b=srce[1+(index>>FRACBITS)];
  243.         sample=a+(((long)(b-a)*(index&FRACMASK))>>FRACBITS);
  244.  
  245.         *(dest++)+=lvolt*sample;
  246.         *(dest++)+=rvolt*sample;
  247.         index+=increment;
  248.         todo--;
  249.     }
  250. }
  251.  
  252.  
  253. void MixMonoInterp(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,SLONG lvolt,SLONG rvolt,UWORD todo)
  254. {
  255.     SWORD sample,a,b;
  256.  
  257.     while(todo>0){
  258.         a=srce[index>>FRACBITS];
  259.         b=srce[1+(index>>FRACBITS)];
  260.         sample=a+(((long)(b-a)*(index&FRACMASK))>>FRACBITS);
  261.  
  262.         *(dest++)+=lvolt*sample;
  263.  
  264.         index+=increment;
  265.         todo--;
  266.     }
  267. }
  268.  
  269.  
  270. UWORD NewPredict(SLONG index,SLONG end,SLONG increment,UWORD todo)
  271. /*
  272.     This functions returns the number of resamplings we can do so that:
  273.  
  274.         - it never accesses indexes bigger than index 'end'
  275.         - it doesn't do more than 'todo' resamplings
  276. */
  277. {
  278.     SLONG di;
  279.  
  280.     di=(end-index)/increment;
  281.     index+=(di*increment);
  282.  
  283.     if(increment<0){
  284.         while(index>=end){
  285.             index+=increment;
  286.             di++;
  287.         }
  288.     }
  289.     else{
  290.         while(index<=end){
  291.             index+=increment;
  292.             di++;
  293.         }
  294.     }
  295.     return ((di<todo) ? di : todo);
  296. }
  297.  
  298.  
  299. void VC_AddChannel(SLONG *ptr,UWORD todo)
  300. /*
  301.     Mixes 'todo' stereo or mono samples of the current channel to the tickbuffer.
  302. */
  303. {
  304.     SLONG end;
  305.     UWORD done,needs;
  306.     char *s;
  307.  
  308.     while(todo>0){
  309.  
  310.         /* update the 'current' index so the sample loops, or
  311.            stops playing if it reached the end of the sample */
  312.  
  313.         if(vnf->flags&SF_REVERSE){
  314.  
  315.             /* The sample is playing in reverse */
  316.  
  317.                 if(vnf->flags&SF_LOOP){
  318.  
  319.                     /* the sample is looping, so check if
  320.                        it reached the loopstart index */
  321.  
  322.                     if(vnf->current<idxlpos){
  323.                     if(vnf->flags&SF_BIDI){
  324.  
  325.                         /* sample is doing bidirectional loops, so 'bounce'
  326.                             the current index against the idxlpos */
  327.  
  328.                         vnf->current=idxlpos+(idxlpos-vnf->current);
  329.                         vnf->flags&=~SF_REVERSE;
  330.                         vnf->increment=-vnf->increment;
  331.                     }
  332.                     else
  333.                         /* normal backwards looping, so set the
  334.                             current position to loopend index */
  335.  
  336.                             vnf->current=idxlend-(idxlpos-vnf->current);
  337.                     }
  338.                 }
  339.                 else{
  340.  
  341.                     /* the sample is not looping, so check
  342.                         if it reached index 0 */
  343.  
  344.                     if(vnf->current<0){
  345.  
  346.                         /* playing index reached 0, so stop
  347.                             playing this sample */
  348.  
  349.                         vnf->current=0;
  350.                         vnf->active=0;
  351.                         break;
  352.                     }
  353.                 }
  354.         }
  355.         else{
  356.  
  357.             /* The sample is playing forward */
  358.  
  359.                 if(vnf->flags&SF_LOOP){
  360.  
  361.                     /* the sample is looping, so check if
  362.                         it reached the loopend index */
  363.  
  364.                     if(vnf->current>idxlend){
  365.                         if(vnf->flags&SF_BIDI){
  366.  
  367.                         /* sample is doing bidirectional loops, so 'bounce'
  368.                             the current index against the idxlend */
  369.  
  370.                             vnf->flags|=SF_REVERSE;
  371.                             vnf->increment=-vnf->increment;
  372.                             vnf->current=idxlend-(vnf->current-idxlend); /* ?? */
  373.                         }
  374.                         else
  375.                         /* normal backwards looping, so set the
  376.                             current position to loopend index */
  377.  
  378.                             vnf->current=idxlpos+(vnf->current-idxlend);
  379.                     }
  380.                 }
  381.                 else{
  382.  
  383.                     /* sample is not looping, so check
  384.                         if it reached the last position */
  385.  
  386.                     if(vnf->current>idxsize){
  387.  
  388.                         /* yes, so stop playing this sample */
  389.  
  390.                         vnf->current=0;
  391.                         vnf->active=0;
  392.                         break;
  393.                     }
  394.                 }
  395.         }
  396.  
  397.         /* Vraag een far ptr op van het sampleadres
  398.             op byte offset vnf->current, en hoeveel samples
  399.             daarvan geldig zijn (VOORDAT segment overschrijding optreed) */
  400.  
  401.         if(!(s=Samples[vnf->handle])){
  402.             vnf->current=0;
  403.             vnf->active=0;
  404.             break;
  405.         }
  406.  
  407.         if(vnf->flags & SF_REVERSE)
  408.             end = (vnf->flags & SF_LOOP) ? idxlpos : 0;
  409.         else
  410.             end = (vnf->flags & SF_LOOP) ? idxlend : idxsize;
  411.  
  412.         /* Als de sample simpelweg niet beschikbaar is, of als
  413.             sample gestopt moet worden sample stilleggen en stoppen */
  414.         /* mix 'em: */
  415.  
  416.         done=NewPredict(vnf->current,end,vnf->increment,todo);
  417.  
  418.         if(!done){
  419.             printf("predict stopped it. current %ld, end %ld\n",vnf->current,end);
  420.             vnf->active=0;
  421.             break;
  422.         }
  423.  
  424.         SampleMix(s,ptr,vnf->current,vnf->increment,vnf->lvolmul,vnf->rvolmul,done);
  425.         vnf->current+=(vnf->increment*done);
  426.  
  427.         todo-=done;
  428.         ptr+=(md_mode & DMODE_STEREO) ? (done<<1) : done;
  429.     }
  430. }
  431.  
  432.  
  433.  
  434.  
  435. void VC_FillTick(char *buf,UWORD todo)
  436. /*
  437.     Mixes 'todo' samples to 'buf'.. The number of samples has to fit into the tickbuffer.
  438. */
  439. {
  440.     int t;
  441.  
  442.     /* clear the mixing buffer: */
  443.  
  444.     memset(VC_TICKBUF,0,(md_mode & DMODE_STEREO) ? todo<<3 : todo<<2);
  445.  
  446.     for(t=0;t<md_numchn;t++){
  447.         vnf=&vinf[t];
  448.         vnf=&vinf[t];
  449.  
  450.         idxsize=(vnf->size<<FRACBITS)-1;
  451.         idxlpos=vnf->reppos<<FRACBITS;
  452.         idxlend=(vnf->repend<<FRACBITS)-1;
  453.  
  454.         if(vnf->active){
  455.             VC_AddChannel(VC_TICKBUF,todo);
  456.         }
  457.     }
  458.  
  459.     if(md_mode & DMODE_16BITS)
  460.         VC_Sample32To16Copy(VC_TICKBUF,(SWORD *)buf,(md_mode & DMODE_STEREO) ? todo<<1 : todo);
  461.     else
  462.         VC_Sample32To8Copy(VC_TICKBUF,buf,(md_mode & DMODE_STEREO) ? todo<<1 : todo);
  463. }
  464.  
  465.  
  466. void VC_WritePortion(char *buf,UWORD todo)
  467. /*
  468.     Writes 'todo' mixed SAMPLES (!!) to 'buf'. When todo is bigger than the
  469.     number of samples that fit into VC_TICKBUF, the mixing operation is split
  470.     up into a number of smaller chunks.
  471. */
  472. {
  473.     UWORD part;
  474.  
  475.     /* write 'part' samples to the buffer */
  476.  
  477.     while(todo){
  478.         part=min(todo,samplesthatfit);
  479.         VC_FillTick(buf,part);
  480.         buf+=samples2bytes(part);
  481.         todo-=part;
  482.     }
  483. }
  484.  
  485.  
  486. static UWORD TICKLEFT;
  487.  
  488.  
  489. void VC_WriteSamples(char *buf,UWORD todo)
  490. {
  491.     int t;
  492.     UWORD part;
  493.  
  494.     while(todo>0){
  495.  
  496.         if(TICKLEFT==0){
  497.             md_player();
  498.  
  499.             TICKLEFT=(125L*md_mixfreq)/(50L*md_bpm);
  500.  
  501.             /* compute volume, frequency counter & panning parameters for each channel. */
  502.  
  503.             for(t=0;t<md_numchn;t++){
  504.                 int pan,vol,lvol,rvol;
  505.  
  506.                 if(vinf[t].kick){
  507.                     vinf[t].current=(vinf[t].start << FRACBITS);
  508.                     vinf[t].active=1;
  509.                     vinf[t].kick=0;
  510.                 }
  511.  
  512.                 if(vinf[t].frq==0) vinf[t].active=0;
  513.  
  514.                 if(vinf[t].active){
  515.                     vinf[t].increment=fraction2long(vinf[t].frq,md_mixfreq);
  516.  
  517.                     if(vinf[t].flags & SF_REVERSE) vinf[t].increment=-vinf[t].increment;
  518.  
  519.                     vol=vinf[t].vol;
  520.                     pan=vinf[t].pan;
  521.  
  522.                     if(md_mode & DMODE_STEREO){
  523.                         lvol= ( vol * (255-pan) ) / 255;
  524.                         rvol= ( vol * pan ) / 255;
  525.                         vinf[t].lvolmul=(maxvol*lvol)/64;
  526.                         vinf[t].rvolmul=(maxvol*rvol)/64;
  527.                     }
  528.                     else{
  529.                         vinf[t].lvolmul=(maxvol*vol)/64;
  530.                     }
  531.                 }
  532.             }
  533.         }
  534.  
  535.         part=min(TICKLEFT,todo);
  536.  
  537.         VC_WritePortion(buf,part);
  538.  
  539.         TICKLEFT-=part;
  540.         todo-=part;
  541.  
  542.         buf+=samples2bytes(part);
  543.     }
  544. }
  545.  
  546.  
  547. UWORD VC_WriteBytes(char *buf,UWORD todo)
  548. /*
  549.     Writes 'todo' mixed SBYTES (!!) to 'buf'. It returns the number of
  550.     SBYTES actually written to 'buf' (which is rounded to number of samples
  551.     that fit into 'todo' bytes).
  552. */
  553. {
  554.     todo=bytes2samples(todo);
  555.     VC_WriteSamples(buf,todo);
  556.     return samples2bytes(todo);
  557. }
  558.  
  559.  
  560.  
  561. void VC_PlayStart(void)
  562. {
  563.     int t;
  564.     long q,c;
  565.     UWORD i;
  566.  
  567.     maxvol=16777200L / md_numchn;
  568.  
  569.     /* instead of using a amplifying lookup table, I'm using a simple shift
  570.        amplify now.. amplifying doubles with every extra 4 channels, and also
  571.        doubles in stereo mode.. this seems to give similar volume levels
  572.        across the channel range */
  573.  
  574.     ampshift=md_numchn/8;
  575.     if(md_mode & DMODE_STEREO) ampshift++;
  576.  
  577.     if(md_mode & DMODE_INTERP)
  578.         SampleMix=(md_mode & DMODE_STEREO) ? MixStereoInterp : MixMonoInterp;
  579.     else
  580.         SampleMix=(md_mode & DMODE_STEREO) ? MixStereoNormal : MixMonoNormal;
  581.  
  582.     samplesthatfit=TICKLSIZE;
  583.     if(md_mode & DMODE_STEREO) samplesthatfit>>=1;
  584.     TICKLEFT=0;
  585. }
  586.  
  587.  
  588. void VC_PlayStop(void)
  589. {
  590. }
  591.  
  592.  
  593. BOOL VC_Init(void)
  594. {
  595.     int t;
  596.     for(t=0;t<md_numchn;t++){
  597.         vinf[t].current=0;
  598.         vinf[t].flags=0;
  599.         vinf[t].handle=0;
  600.         vinf[t].kick=0;
  601.         vinf[t].active=0;
  602.         vinf[t].frq=10000;
  603.         vinf[t].vol=0;
  604.         vinf[t].pan=(t&1)?0:255;
  605.     }
  606.     return 1;
  607. }
  608.  
  609.  
  610. void VC_Exit(void)
  611. {
  612. }
  613.  
  614. void VC_VoiceSetVolume(UBYTE voice,UBYTE vol)
  615. {
  616.     vinf[voice].vol=vol;
  617. }
  618.  
  619.  
  620. void VC_VoiceSetFrequency(UBYTE voice,ULONG frq)
  621. {
  622.     vinf[voice].frq=frq;
  623. }
  624.  
  625. void VC_VoiceSetPanning(UBYTE voice,ULONG pan)
  626. {
  627.     vinf[voice].pan=pan;
  628. }
  629.  
  630. void VC_VoicePlay(UBYTE voice,SWORD handle,ULONG start,ULONG size,ULONG reppos,ULONG repend,UWORD flags)
  631. {
  632.     if(start>=size) return;
  633.  
  634.     if(flags&SF_LOOP){
  635.         if(repend>size) repend=size;    /* repend can't be bigger than size */
  636.     }
  637.  
  638.     vinf[voice].flags=flags;
  639.     vinf[voice].handle=handle;
  640.     vinf[voice].start=start;
  641.     vinf[voice].size=size;
  642.     vinf[voice].reppos=reppos;
  643.     vinf[voice].repend=repend;
  644.     vinf[voice].kick=1;
  645. }
  646.