home *** CD-ROM | disk | FTP | other *** search
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <dos.h>
- #include <io.h>
- #include <malloc.h>
- #include <mem.h>
- #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])
- #define bytes2samples(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;handle<MAXHANDLE;handle++){
- if(Samples[handle]==NULL) break;
- }
-
- if(handle==MAXHANDLE){
- myerr=ERROR_OUT_OF_HANDLES;
- return -1;
- }
-
- if((Samples[handle]=malloc(length))==NULL){
- myerr=ERROR_SAMPLE_TOO_BIG;
- return -1;
- }
-
- // read sample into buffer.
-
- LargeRead(Samples[handle],length);
-
- return handle;
- }
-
-
- void VC_SampleUnload(WORD handle)
- {
- free(Samples[handle]);
- Samples[handle]=NULL;
- }
-
-
- /**************************************************
- ***************************************************
- ***************************************************
- **************************************************/
-
-
-
- ULONG VC_NewSampleAddress(char **s)
- {
- ULONG avail2;
-
- if(ghl->flags&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( (di<todo) ? di : todo );
- }
-
-
-
- void VC_AddChannelStereo(LONG *ptr,UWORD todo)
- /*
- Mixes 'todo' stereo samples of the current channel to the tickbuffer.
- */
- {
- ULONG avail;
- UWORD 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_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;t<md_numchn;t++){
- ghl=&ghld[t];
-
- if(ghl->active){
- 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;t<md_numchn;t++){
- ghl=&ghld[t];
-
- if(ghl->active){
- 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<md_numchn;t++){
- WORD pan,vol,lvol,rvol;
-
- if(ghld[t].kick){
- ghld[t].current=ghld[t].start;
- ghld[t].iter=0;
- ghld[t].active=1;
- ghld[t].kick=0;
- }
-
- ghld[t].increment=fraction2long(ghld[t].frq,md_mixfreq); // & 0xfffffffc;
-
- vol=ghld[t].vol;
- pan=ghld[t].pan;
-
- if(md_mode & DMODE_STEREO){
- lvol= ( vol * (255-pan) ) / 255;
- rvol= ( vol * pan ) / 255;
- ghld[t].lvoltab=voltab[lvol];
- ghld[t].rvoltab=voltab[rvol];
- }
- else{
- ghld[t].lvoltab=voltab[vol];
- }
- }
-
- // write 'part' samples to the buffer
-
- while(todo){
- part=min(todo,MAXTICKSIZE);
- VC_FillTick(buf,part);
- buf+=bytes2samples(part);
- todo-=part;
- }
- }
-
-
-
- UWORD VC_WriteBytes(char *buf,UWORD todo)
- /*
- Writes 'todo' mixed BYTES (!!) to 'buf'. It returns the number of
- BYTES actually written to 'buf' (which is rounded to number of samples
- that fit into 'todo' bytes).
- */
- {
- todo=bytes2samples(todo);
- VC_WriteSamples(buf,todo);
- return samples2bytes(todo);
- }
-
-
-
- void VC_PlayStart(void)
- {
- int t,c,maxvol,per256;
- long q;
- UWORD i;
-
- maxvol=256 / md_numchn;
-
- if(!(md_mode & DMODE_16BITS)) maxvol>>=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)
- {
- }
-