#include #include #include #include #include #include #include #include "mems.h" #include "mdriver.h" #include "vc.h" extern char far ampbuf[16384]; extern void VC_Sample16To8Copy(WORD *srce,BYTE *dest,UWORD count); extern void VC_MemSet(void *buf,UWORD data,UWORD count); extern UWORD VC_ResampleMixMono( void *srce, void *dest, void *volt, UWORD todo, ULONG incr, UWORD *itrr ); extern UWORD VC_ResampleMixStereo( void *srce, void *dest, void *lvolt, void *rvolt, UWORD todo, ULONG incr, UWORD *itrr ); #define MAXTICKSIZE 1800 // max number of samples in temporary buffer #define samples2bytes(x) (x<>samplesize[md_mode]) WORD VC_TICKBUF[MAXTICKSIZE*2]; // tickbuffer int samplesize[]={ 0,1,1,2 }; GHOLD *ghl; WORD far voltab[65][256]; void far *normalize(void far *p){ return MK_FP(FP_SEG(p)+(FP_OFF(p)>>4),FP_OFF(p)&15); } ULONG fraction2long(ULONG dividend,UWORD divisor) { asm{ mov dx,word ptr [dividend+2] mov ax,word ptr [dividend] // dx:ax is dividend div divisor // dx=dividend%divisor; ax=dividend/divisor push ax // store whole part xor ax,ax // dx:ax = mod*65536 div divisor // ax=65536 based fraction increment pop dx // dx:ax is fixed point increment } } /************************************************** *************************************************** *************************************************** **************************************************/ #define MAXHANDLE 160 // should be enough for now int ahandle=-1; char far *Samples[MAXHANDLE]; int old; BOOL LargeRead(char far *buffer,ULONG size) { int t; ULONG todo; // check if 'buffer' is a ems-handle.. if(FP_SEG(buffer)==1){ // Yes it is! long offset=0; int emshandle; char far *emsptr; // So extract the ems-handle out of the 'buffer' pointer emshandle=FP_OFF(buffer); while(size){ // Map a page of ems.. EMS_Map(emshandle,offset>>14,0); // How many bytes may we load to this page ? todo=(size>16384) ? 16384:size; // Build a pointer to the physically mapped ems-page: emsptr=MK_FP(ems_frameseg,0); // Read data SL_Load(emsptr,todo); // and update pointers.. size-=todo; offset+=todo; } } else{ // No ems.. so do a normal, dull memory load.. while(size){ /* first we have to normalize the pointer.. we don't want any segment wrapping */ buffer=normalize(buffer); // how many bytes to load (in chunks of 8000) ? todo=(size>8000)?8000:size; // read data SL_Load(buffer,todo); // and update pointers.. size-=todo; buffer+=todo; } } return 1; } ULONG VC_GetSampleAddress(UWORD sample,ULONG index,char far **s) { char huge *sampleadr; sampleadr=Samples[sample]; if(sampleadr==NULL) return 0; if(FP_SEG(sampleadr)==1){ EMS_Map(FP_OFF(sampleadr),index>>14,0); index&=0x3fff; *s=MK_FP(ems_frameseg,index); return(16384-index); } sampleadr+=index; *s=normalize(sampleadr); return(65536-FP_OFF(*s)); } WORD VC_SampleLoad(FILE *fp,ULONG length,ULONG reppos,ULONG repend,UWORD flags) { int handle,emshandle,t; SL_Init(fp,flags,(flags|SF_SIGNED)&~SF_16BITS); // Find empty slot to put sample address in for(handle=0;handleflags&SF_LOOP){ if(ghl->current>=ghl->repend){ ghl->current=ghl->reppos; } } else{ if(ghl->current>=ghl->size){ ghl->current=0; ghl->active=0; ghl->iter=0; return 0; } } /* avail1 is the number of samples thats available before segment wrap */ avail1=VC_GetSampleAddress(ghl->handle,ghl->current,s); /* avail2 is the number of samples available before the end of the sample, or before the end of the loop */ if(ghl->flags&SF_LOOP){ avail2=(ghl->repend)-(ghl->current); } else{ avail2=(ghl->size)-(ghl->current); } return((avail1 < avail2) ? avail1 : avail2); } UWORD NewPredict(ULONG avail,UWORD todo,ULONG increment,UWORD iter) /* The returnvalue is the number of times we can resample a sample so that: - the number of samples written doesn't exceed 'todo' - the number of samples read doesn't exceed 'avail' */ { long tmp; ULONG di=0; if(avail==0) return 0; if(todo==0) return 0; tmp=(avail<<16)-iter; di=tmp/increment; tmp-=(di*increment); while(tmp>0){ di++; tmp-=increment; } /* di is het aantal keren dat ik increment van avail:-iter kan aftrekken zodat het resultaat <= 0 is */ return( (di0){ /* Vraag een far ptr op van het sampleadres op byte offset ghl->current, en hoeveel samples daarvan geldig zijn (VOORDAT segment overschrijding optreed) */ avail=VC_NewSampleAddress(&s); /* Als de sample simpelweg niet beschikbaar is, of als sample gestopt moet worden sample stilleggen en stoppen */ if(!avail){ ghl->active=0; break; } /* we overschrijden wel het sampleeinde of segmentgrens, dus we samplen eerst zoveel bytes als er beschikbaar zijn */ done=NewPredict(avail,todo,ghl->increment,ghl->iter); // mix 'em: ghl->current+=VC_ResampleMixStereo(s,ptr,ghl->lvoltab,ghl->rvoltab,done,ghl->increment,&ghl->iter); todo-=done; ptr+=done; } } void VC_AddChannelMono(WORD far *ptr,UWORD todo) /* Mixes 'todo' mono samples of the current channel to the tickbuffer. */ { UWORD avail,done,needs; char far *s; while(todo>0){ /* Vraag een far ptr op van het sampleadres op byte offset ghl->current, en hoeveel samples daarvan geldig zijn (VOORDAT segment overschrijding optreed) */ avail=VC_NewSampleAddress(&s); /* Als de sample simpelweg niet beschikbaar is, of als sample gestopt moet worden sample stilleggen en stoppen */ if(!avail){ ghl->active=0; break; } /* we overschrijden wel het sampleeinde of segmentgrens, dus we samplen eerst zoveel bytes als er beschikbaar zijn */ done=NewPredict(avail,todo,ghl->increment,ghl->iter); // mix 'em: ghl->current+=VC_ResampleMixMono(s,ptr,ghl->lvoltab,done,ghl->increment,&ghl->iter); todo-=done; ptr+=done; } } void VC_FillTickStereo(WORD far *buf,UWORD todo) /* Fills 'buf' with 'todo' 16 bits stereo samples. */ { int t; // Dan voor ieder kanaal de tickbuffer vullen for(t=0;tactive){ VC_AddChannelStereo((LONG far *)buf,todo); } } } void VC_FillTickMono(WORD far *buf,UWORD todo) /* Fills 'buf' with 'todo' 16 bits mono samples. */ { int t; // Dan voor ieder kanaal de tickbuffer vullen for(t=0;tactive){ VC_AddChannelMono(buf,todo); } } } void VC_FillTick(char far *buf,UWORD todo) /* Mixes 'todo' samples to 'buf'. todo has to be <= MAXTICKSIZE */ { switch(md_mode){ case 0: // mono, 8 bits VC_MemSet(VC_TICKBUF,0x2000,todo); VC_FillTickMono(VC_TICKBUF,todo); VC_Sample16To8Copy(VC_TICKBUF,buf,todo); break; case 1: // stereo, 8 bits VC_MemSet(VC_TICKBUF,0x2000,todo<<1); VC_FillTickStereo(VC_TICKBUF,todo); VC_Sample16To8Copy(VC_TICKBUF,buf,todo<<1); break; case 2: // mono,16 bits VC_MemSet(buf,0x0000,todo); VC_FillTickMono((WORD far *)buf,todo); break; case 3: // stereo,16 bits VC_MemSet(buf,0x0000,todo<<1); VC_FillTickStereo((WORD far *)buf,todo); break; } } void VC_WriteSamples(char far *buf,UWORD todo) /* Writes 'todo' mixed SAMPLES (!!) to 'buf'. When todo is bigger than the number of samples that fit into VC_TICKBUF, the mixing operation is split up into a number of smaller chunks. */ { int t; UWORD part; EMSSaveMap(); // compute volume, frequency counter & panning parameters for each channel. for(t=0;t>=2; for(c=0;c<=64;c++){ for(t=-128;t<128;t++) voltab[c][(UBYTE)t]=((long)(t*maxvol)*c)/64; } /* I assume that each channel can be amplified to a 30% higher volume than the original volume without noticable clipping.. and 30% = 76/256 I use this amplification when doing 8-bit mixing so I get a decent volume-level, even on 16-channel mods.. this is what OC calls 'autogain' I think, but I don't know if he uses the same 20% factor */ per256=256+(76U*md_numchn); for(q=-8192;q<=8191;q++){ c=(q*per256) >> 14; // /(64*256); if(c<-128) c=-128; else if(c>127) c=127; ampbuf[q+8192]=c+128; } } void VC_PlayStop(void) { } BOOL VC_Init(void) { return 1; } void VC_Exit(void) { }