#include #include #include #include #include #include #include #include "mdriver.h" #include "vc.h" char ampbuf[16384]; WORD voltab[65][256]; #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; void VC_MemSet(UWORD *buf,UWORD data,UWORD count) { while(count--) *(buf++)=data; } void VC_Sample16To8Copy(UWORD *srce,BYTE *dest,UWORD count) { while(count){ *dest=ampbuf[*srce]; dest++; srce++; count--; } } UWORD VC_ResampleMixMono(UBYTE *srce,WORD *dest,WORD *volt,UWORD todo,ULONG incr,UWORD *itrr) { register ULONG index=*itrr; while(todo){ *(dest++)+=volt[srce[index>>16]]; index+=incr; todo--; } *itrr=(index&0xffff); return (index>>16); } UWORD VC_ResampleMixStereo(UBYTE *srce,WORD *dest,WORD *lvolt,WORD *rvolt,UWORD todo,ULONG incr,UWORD *itrr) { register ULONG index=*itrr; register UBYTE sample; while(todo){ sample=srce[index>>16]; *(dest++)+=lvolt[sample]; *(dest++)+=rvolt[sample]; index+=incr; todo--; } *itrr=(index&0xffff); return (index>>16); } ULONG fraction2long(ULONG dividend,UWORD divisor) { ULONG whole,part; whole=dividend/divisor; part=((dividend%divisor)<<16)/divisor; return((whole<<16)|part); } /************************************************** *************************************************** *************************************************** **************************************************/ #define MAXHANDLE 160 // should be enough for now char *Samples[MAXHANDLE]; BOOL LargeRead(char *buffer,ULONG size) { int t; ULONG todo; // No ems.. so do a normal, dull memory load.. while(size){ // 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; } 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 */ *s=Samples[ghl->handle]+ghl->current; /* 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(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 *ptr,UWORD todo) /* Mixes 'todo' mono samples of the current channel to the tickbuffer. */ { UWORD avail,done,needs; char *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 *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 *)buf,todo); } } } void VC_FillTickMono(WORD *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 *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 *)buf,todo); break; case 3: // stereo,16 bits VC_MemSet(buf,0x0000,todo<<1); VC_FillTickStereo((WORD *)buf,todo); break; } } void VC_WriteSamples(char *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; // 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) { }