home *** CD-ROM | disk | FTP | other *** search
/ PC Loisirs 18 / cd.iso / sharewar / mikm202 / source / virtch16 / virtch16.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-09-18  |  11.7 KB  |  610 lines

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <dos.h>
  5. #include <io.h>
  6. #include <alloc.h>
  7. #include <mem.h>
  8. #include "mems.h"
  9. #include "mdriver.h"
  10.  
  11. #include "vc.h"
  12.  
  13. extern char far ampbuf[16384];
  14.  
  15. extern void VC_Sample16To8Copy(WORD *srce,BYTE *dest,UWORD count);
  16. extern void VC_MemSet(void *buf,UWORD data,UWORD count);
  17.  
  18. extern UWORD VC_ResampleMixMono(
  19.                 void *srce,
  20.                 void *dest,
  21.                 void *volt,
  22.                 UWORD todo,
  23.                 ULONG incr,
  24.                 UWORD *itrr
  25.             );
  26.  
  27. extern UWORD VC_ResampleMixStereo(
  28.                 void *srce,
  29.                 void *dest,
  30.                 void *lvolt,
  31.                 void *rvolt,
  32.                 UWORD todo,
  33.                 ULONG incr,
  34.                 UWORD *itrr
  35.             );
  36.  
  37.  
  38. #define MAXTICKSIZE 1800        // max number of samples in temporary buffer
  39.  
  40. #define samples2bytes(x) (x<<samplesize[md_mode])
  41. #define bytes2samples(x) (x>>samplesize[md_mode])
  42.  
  43. WORD VC_TICKBUF[MAXTICKSIZE*2];    // tickbuffer
  44.  
  45. int samplesize[]={ 0,1,1,2 };
  46.  
  47. GHOLD *ghl;
  48.  
  49.  
  50. WORD far voltab[65][256];
  51.  
  52.  
  53. void far *normalize(void far *p){
  54.     return MK_FP(FP_SEG(p)+(FP_OFF(p)>>4),FP_OFF(p)&15);
  55. }
  56.  
  57.  
  58. ULONG fraction2long(ULONG dividend,UWORD divisor)
  59. {
  60.     asm{
  61.         mov  dx,word ptr [dividend+2]
  62.         mov  ax,word ptr [dividend]        // dx:ax is dividend
  63.         div  divisor                    // dx=dividend%divisor; ax=dividend/divisor
  64.         push ax                            // store whole part
  65.         xor  ax,ax                        // dx:ax = mod*65536
  66.         div  divisor                    // ax=65536 based fraction increment
  67.         pop  dx                            // dx:ax is fixed point increment
  68.     }
  69. }
  70.  
  71. /**************************************************
  72. ***************************************************
  73. ***************************************************
  74. **************************************************/
  75.  
  76.  
  77. #define MAXHANDLE 160        // should be enough for now
  78.  
  79.  
  80. int ahandle=-1;
  81.  
  82. char far *Samples[MAXHANDLE];
  83. int old;
  84.  
  85. BOOL LargeRead(char far *buffer,ULONG size)
  86. {
  87.     int t;
  88.     ULONG todo;
  89.  
  90.     // check if 'buffer' is a ems-handle..
  91.  
  92.     if(FP_SEG(buffer)==1){
  93.  
  94.         // Yes it is!
  95.  
  96.         long offset=0;
  97.         int emshandle;
  98.         char far *emsptr;
  99.  
  100.         // So extract the ems-handle out of the 'buffer' pointer
  101.  
  102.         emshandle=FP_OFF(buffer);
  103.  
  104.         while(size){
  105.  
  106.             // Map a page of ems..
  107.  
  108.             EMS_Map(emshandle,offset>>14,0);
  109.  
  110.             // How many bytes may we load to this page ?
  111.  
  112.             todo=(size>16384) ? 16384:size;
  113.  
  114.             // Build a pointer to the physically mapped ems-page:
  115.  
  116.             emsptr=MK_FP(ems_frameseg,0);
  117.  
  118.             // Read data
  119.  
  120.             SL_Load(emsptr,todo);
  121.  
  122.             // and update pointers..
  123.  
  124.             size-=todo;
  125.             offset+=todo;
  126.         }
  127.     }
  128.     else{
  129.  
  130.         // No ems.. so do a normal, dull memory load..
  131.  
  132.         while(size){
  133.  
  134.             /* first we have to normalize the pointer.. we
  135.                don't want any segment wrapping */
  136.  
  137.             buffer=normalize(buffer);
  138.  
  139.             // how many bytes to load (in chunks of 8000) ?
  140.  
  141.             todo=(size>8000)?8000:size;
  142.  
  143.             // read data
  144.  
  145.             SL_Load(buffer,todo);
  146.  
  147.             // and update pointers..
  148.  
  149.             size-=todo;
  150.             buffer+=todo;
  151.         }
  152.     }
  153.     return 1;
  154. }
  155.  
  156.  
  157.  
  158. ULONG VC_GetSampleAddress(UWORD sample,ULONG index,char far **s)
  159. {
  160.     char huge *sampleadr;
  161.  
  162.     sampleadr=Samples[sample];
  163.     if(sampleadr==NULL) return 0;
  164.  
  165.     if(FP_SEG(sampleadr)==1){
  166.         EMS_Map(FP_OFF(sampleadr),index>>14,0);
  167.         index&=0x3fff;
  168.         *s=MK_FP(ems_frameseg,index);
  169.         return(16384-index);
  170.     }
  171.  
  172.     sampleadr+=index;
  173.     *s=normalize(sampleadr);
  174.     return(65536-FP_OFF(*s));
  175. }
  176.  
  177.  
  178.  
  179. WORD VC_SampleLoad(FILE *fp,ULONG length,ULONG reppos,ULONG repend,UWORD flags)
  180. {
  181.     int handle,emshandle,t;
  182.  
  183.     SL_Init(fp,flags,(flags|SF_SIGNED)&~SF_16BITS);
  184.  
  185.     // Find empty slot to put sample address in
  186.  
  187.     for(handle=0;handle<MAXHANDLE;handle++){
  188.         if(Samples[handle]==NULL) break;
  189.     }
  190.  
  191.     if(handle==MAXHANDLE){
  192.         myerr=ERROR_OUT_OF_HANDLES;
  193.         return -1;
  194.     }
  195.  
  196.     // Try to allocate 'length' bytes of ems memory:
  197.  
  198.     emshandle=(ems_active) ? EMS_AllocMem(length) : -1;
  199.  
  200.     if(emshandle==-1){
  201.  
  202.         /* Couldn't allocate that much ems, try
  203.         allocating normal memory instead */
  204.  
  205.         if((Samples[handle]=farmalloc(length))==NULL){
  206.             myerr=ERROR_SAMPLE_TOO_BIG;
  207.             return -1;
  208.         }
  209.     }
  210.     else{
  211.  
  212.         /* disquize the ems handle as a far pointer.. we can identify it
  213.            as a emshandle by looking at the segment of the pointer.. if
  214.            it's 1, then the offset containts a ems handle */
  215.  
  216.         Samples[handle]=MK_FP(1,emshandle);
  217.         ahandle=emshandle;
  218.     }
  219.  
  220.     old=0;
  221.  
  222.     // read sample into buffer.
  223.  
  224.     LargeRead(Samples[handle],length);
  225.  
  226.     return handle;
  227. }
  228.  
  229.  
  230.  
  231. void VC_SampleUnload(WORD handle)
  232. {
  233.     void far *sampleadr=Samples[handle];
  234.  
  235.     if(FP_SEG(sampleadr)==1)
  236.         EMS_Free(FP_OFF(sampleadr));
  237.     else
  238.         farfree(sampleadr);
  239.  
  240.     Samples[handle]=NULL;
  241.     ahandle=-1;
  242. }
  243.  
  244.  
  245. void EMSSaveMap(void)
  246. {
  247.     if(ahandle!=-1) EMS_Save_Map(ahandle);
  248.  
  249. }
  250.  
  251.  
  252. void EMSRestoreMap(void)
  253. {
  254.     if(ahandle!=-1) EMS_Restore_Map(ahandle);
  255. }
  256.  
  257.  
  258.  
  259. /**************************************************
  260. ***************************************************
  261. ***************************************************
  262. **************************************************/
  263.  
  264.  
  265.  
  266. ULONG VC_NewSampleAddress(char far **s)
  267. {
  268.     ULONG avail1=0,avail2;
  269.  
  270.     if(ghl->flags&SF_LOOP){
  271.         if(ghl->current>=ghl->repend){
  272.             ghl->current=ghl->reppos;
  273.         }
  274.     }
  275.     else{
  276.         if(ghl->current>=ghl->size){
  277.             ghl->current=0;
  278.             ghl->active=0;
  279.             ghl->iter=0;
  280.             return 0;
  281.         }
  282.     }
  283.  
  284.     /* avail1 is the number of samples thats available before
  285.        segment wrap */
  286.  
  287.     avail1=VC_GetSampleAddress(ghl->handle,ghl->current,s);
  288.  
  289.     /* avail2 is the number of samples available before the end
  290.        of the sample, or before the end of the loop */
  291.  
  292.     if(ghl->flags&SF_LOOP){
  293.         avail2=(ghl->repend)-(ghl->current);
  294.     }
  295.     else{
  296.         avail2=(ghl->size)-(ghl->current);
  297.     }
  298.  
  299.     return((avail1 < avail2) ? avail1 : avail2);
  300. }
  301.  
  302.  
  303.  
  304. UWORD NewPredict(ULONG avail,UWORD todo,ULONG increment,UWORD iter)
  305. /*
  306.     The returnvalue is the number of times we can resample a sample so that:
  307.  
  308.         - the number of samples written doesn't exceed 'todo'
  309.         - the number of samples read doesn't exceed 'avail'
  310. */
  311. {
  312.     long tmp;
  313.     ULONG di=0;
  314.  
  315.     if(avail==0) return 0;
  316.     if(todo==0) return 0;
  317.  
  318.     tmp=(avail<<16)-iter;
  319.  
  320.     di=tmp/increment;
  321.     tmp-=(di*increment);
  322.  
  323.     while(tmp>0){
  324.         di++;
  325.         tmp-=increment;
  326.     }
  327.  
  328.     /* di is het aantal keren dat ik increment van avail:-iter
  329.        kan aftrekken zodat het resultaat <= 0 is */
  330.  
  331.     return( (di<todo) ? di : todo );
  332. }
  333.  
  334.  
  335.  
  336. void VC_AddChannelStereo(LONG far *ptr,UWORD todo)
  337. /*
  338.     Mixes 'todo' stereo samples of the current channel to the tickbuffer.
  339. */
  340. {
  341.     ULONG avail;
  342.     UWORD done,needs;
  343.     char far *s;
  344.  
  345.     while(todo>0){
  346.  
  347.         /* Vraag een far ptr op van het sampleadres
  348.            op byte offset ghl->current, en hoeveel samples
  349.            daarvan geldig zijn (VOORDAT segment overschrijding optreed) */
  350.  
  351.         avail=VC_NewSampleAddress(&s);
  352.  
  353.         /* Als de sample simpelweg niet beschikbaar is, of als
  354.            sample gestopt moet worden sample stilleggen en stoppen */
  355.  
  356.         if(!avail){
  357.             ghl->active=0;
  358.             break;
  359.         }
  360.  
  361.         /* we overschrijden wel het sampleeinde of segmentgrens, dus
  362.            we samplen eerst zoveel bytes als er beschikbaar zijn */
  363.  
  364.         done=NewPredict(avail,todo,ghl->increment,ghl->iter);
  365.  
  366.         // mix 'em:
  367.  
  368.         ghl->current+=VC_ResampleMixStereo(s,ptr,ghl->lvoltab,ghl->rvoltab,done,ghl->increment,&ghl->iter);
  369.  
  370.         todo-=done;
  371.         ptr+=done;
  372.     }
  373. }
  374.  
  375.  
  376.  
  377. void VC_AddChannelMono(WORD far *ptr,UWORD todo)
  378. /*
  379.     Mixes 'todo' mono samples of the current channel to the tickbuffer.
  380. */
  381. {
  382.     UWORD avail,done,needs;
  383.     char far *s;
  384.  
  385.     while(todo>0){
  386.  
  387.         /* Vraag een far ptr op van het sampleadres
  388.            op byte offset ghl->current, en hoeveel samples
  389.            daarvan geldig zijn (VOORDAT segment overschrijding optreed) */
  390.  
  391.         avail=VC_NewSampleAddress(&s);
  392.  
  393.         /* Als de sample simpelweg niet beschikbaar is, of als
  394.            sample gestopt moet worden sample stilleggen en stoppen */
  395.  
  396.         if(!avail){
  397.             ghl->active=0;
  398.             break;
  399.         }
  400.  
  401.         /* we overschrijden wel het sampleeinde of segmentgrens, dus
  402.            we samplen eerst zoveel bytes als er beschikbaar zijn */
  403.  
  404.         done=NewPredict(avail,todo,ghl->increment,ghl->iter);
  405.  
  406.         // mix 'em:
  407.  
  408.         ghl->current+=VC_ResampleMixMono(s,ptr,ghl->lvoltab,done,ghl->increment,&ghl->iter);
  409.  
  410.         todo-=done;
  411.         ptr+=done;
  412.     }
  413. }
  414.  
  415.  
  416.  
  417. void VC_FillTickStereo(WORD far *buf,UWORD todo)
  418. /*
  419.     Fills 'buf' with 'todo' 16 bits stereo samples.
  420. */
  421. {
  422.     int t;
  423.  
  424.     // Dan voor ieder kanaal de tickbuffer vullen
  425.  
  426.     for(t=0;t<md_numchn;t++){
  427.         ghl=&ghld[t];
  428.  
  429.         if(ghl->active){
  430.             VC_AddChannelStereo((LONG far *)buf,todo);
  431.         }
  432.     }
  433. }
  434.  
  435.  
  436.  
  437.  
  438. void VC_FillTickMono(WORD far *buf,UWORD todo)
  439. /*
  440.     Fills 'buf' with 'todo' 16 bits mono samples.
  441. */
  442. {
  443.     int t;
  444.  
  445.     // Dan voor ieder kanaal de tickbuffer vullen
  446.  
  447.     for(t=0;t<md_numchn;t++){
  448.         ghl=&ghld[t];
  449.  
  450.         if(ghl->active){
  451.             VC_AddChannelMono(buf,todo);
  452.         }
  453.     }
  454. }
  455.  
  456.  
  457.  
  458.  
  459. void VC_FillTick(char far *buf,UWORD todo)
  460. /*
  461.     Mixes 'todo' samples to 'buf'. todo has to be <= MAXTICKSIZE
  462. */
  463. {
  464.     switch(md_mode){
  465.  
  466.         case 0:        // mono, 8 bits
  467.             VC_MemSet(VC_TICKBUF,0x2000,todo);
  468.             VC_FillTickMono(VC_TICKBUF,todo);
  469.             VC_Sample16To8Copy(VC_TICKBUF,buf,todo);
  470.             break;
  471.  
  472.         case 1:        // stereo, 8 bits
  473.             VC_MemSet(VC_TICKBUF,0x2000,todo<<1);
  474.             VC_FillTickStereo(VC_TICKBUF,todo);
  475.             VC_Sample16To8Copy(VC_TICKBUF,buf,todo<<1);
  476.             break;
  477.  
  478.         case 2:        // mono,16 bits
  479.             VC_MemSet(buf,0x0000,todo);
  480.             VC_FillTickMono((WORD far *)buf,todo);
  481.             break;
  482.  
  483.         case 3:           // stereo,16 bits
  484.             VC_MemSet(buf,0x0000,todo<<1);
  485.             VC_FillTickStereo((WORD far *)buf,todo);
  486.             break;
  487.     }
  488. }
  489.  
  490.  
  491. void VC_WriteSamples(char far *buf,UWORD todo)
  492. /*
  493.     Writes 'todo' mixed SAMPLES (!!) to 'buf'. When todo is bigger than the
  494.     number of samples that fit into VC_TICKBUF, the mixing operation is split
  495.     up into a number of smaller chunks.
  496. */
  497. {
  498.     int t;
  499.     UWORD part;
  500.  
  501.     EMSSaveMap();
  502.  
  503.     // compute volume, frequency counter & panning parameters for each channel.
  504.  
  505.     for(t=0;t<md_numchn;t++){
  506.         int pan,vol,lvol,rvol;
  507.  
  508.         if(ghld[t].kick){
  509.             ghld[t].current=ghld[t].start;
  510.             ghld[t].iter=0;
  511.             ghld[t].active=1;
  512.             ghld[t].kick=0;
  513.         }
  514.  
  515.         ghld[t].increment=fraction2long(ghld[t].frq,md_mixfreq); // & 0xfffffffc;
  516.  
  517.         vol=ghld[t].vol;
  518.         pan=ghld[t].pan;
  519.  
  520.         if(md_mode & DMODE_STEREO){
  521.             lvol= ( vol * (255-pan) ) / 255;
  522.             rvol= ( vol * pan ) / 255;
  523.             ghld[t].lvoltab=normalize(voltab[lvol]);
  524.             ghld[t].rvoltab=normalize(voltab[rvol]);
  525.         }
  526.         else{
  527.             ghld[t].lvoltab=normalize(voltab[vol]);
  528.         }
  529.  
  530.         if(FP_OFF(ghld[t].lvoltab)) return;
  531.     }
  532.  
  533.     // write 'part' samples to the buffer
  534.  
  535.     while(todo){
  536.         part=min(todo,MAXTICKSIZE);
  537.         VC_FillTick(buf,part);
  538.         buf+=bytes2samples(part);
  539.         todo-=part;
  540.     }
  541.  
  542.     EMSRestoreMap();
  543. }
  544.  
  545.  
  546.  
  547. UWORD VC_WriteBytes(char far *buf,UWORD todo)
  548. /*
  549.     Writes 'todo' mixed BYTES (!!) to 'buf'. It returns the number of
  550.     BYTES 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,c,maxvol,per256;
  564.     long q;
  565.     UWORD i;
  566.  
  567.     maxvol=256 / md_numchn;
  568.  
  569.     if(!(md_mode & DMODE_16BITS)) maxvol>>=2;
  570.  
  571.     for(c=0;c<=64;c++){
  572.         for(t=-128;t<128;t++) voltab[c][(UBYTE)t]=((long)(t*maxvol)*c)/64;
  573.     }
  574.  
  575.     /* I assume that each channel can be amplified to a 30% higher volume
  576.        than the original volume without noticable clipping..
  577.        and 30% = 76/256
  578.  
  579.        I use this amplification when doing 8-bit mixing so I get a decent
  580.        volume-level, even on 16-channel mods.. this is what OC calls
  581.        'autogain' I think, but I don't know if he uses the same 20% factor */
  582.  
  583.     per256=256+(76U*md_numchn);
  584.  
  585.     for(q=-8192;q<=8191;q++){
  586.  
  587.         c=(q*per256) >> 14;        // /(64*256);
  588.         if(c<-128) c=-128;
  589.         else if(c>127) c=127;
  590.  
  591.         ampbuf[q+8192]=c+128;
  592.     }
  593. }
  594.  
  595.  
  596. void VC_PlayStop(void)
  597. {
  598. }
  599.  
  600.  
  601. BOOL VC_Init(void)
  602. {
  603.     return 1;
  604. }
  605.  
  606.  
  607. void VC_Exit(void)
  608. {
  609. }
  610.