home *** CD-ROM | disk | FTP | other *** search
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <dos.h>
- #include <io.h>
- #include <alloc.h>
- #include <mem.h>
- #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])
- #define bytes2samples(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;handle<MAXHANDLE;handle++){
- if(Samples[handle]==NULL) break;
- }
-
- if(handle==MAXHANDLE){
- myerr=ERROR_OUT_OF_HANDLES;
- return -1;
- }
-
- // Try to allocate 'length' bytes of ems memory:
-
- emshandle=(ems_active) ? EMS_AllocMem(length) : -1;
-
- if(emshandle==-1){
-
- /* Couldn't allocate that much ems, try
- allocating normal memory instead */
-
- if((Samples[handle]=farmalloc(length))==NULL){
- myerr=ERROR_SAMPLE_TOO_BIG;
- return -1;
- }
- }
- else{
-
- /* disquize the ems handle as a far pointer.. we can identify it
- as a emshandle by looking at the segment of the pointer.. if
- it's 1, then the offset containts a ems handle */
-
- Samples[handle]=MK_FP(1,emshandle);
- ahandle=emshandle;
- }
-
- old=0;
-
- // read sample into buffer.
-
- LargeRead(Samples[handle],length);
-
- return handle;
- }
-
-
-
- void VC_SampleUnload(WORD handle)
- {
- void far *sampleadr=Samples[handle];
-
- if(FP_SEG(sampleadr)==1)
- EMS_Free(FP_OFF(sampleadr));
- else
- farfree(sampleadr);
-
- Samples[handle]=NULL;
- ahandle=-1;
- }
-
-
- void EMSSaveMap(void)
- {
- if(ahandle!=-1) EMS_Save_Map(ahandle);
-
- }
-
-
- void EMSRestoreMap(void)
- {
- if(ahandle!=-1) EMS_Restore_Map(ahandle);
- }
-
-
-
- /**************************************************
- ***************************************************
- ***************************************************
- **************************************************/
-
-
-
- ULONG VC_NewSampleAddress(char far **s)
- {
- ULONG avail1=0,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 */
-
- 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( (di<todo) ? di : todo );
- }
-
-
-
- void VC_AddChannelStereo(LONG far *ptr,UWORD todo)
- /*
- Mixes 'todo' stereo samples of the current channel to the tickbuffer.
- */
- {
- ULONG avail;
- UWORD 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_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;t<md_numchn;t++){
- ghl=&ghld[t];
-
- if(ghl->active){
- 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;t<md_numchn;t++){
- ghl=&ghld[t];
-
- if(ghl->active){
- 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<md_numchn;t++){
- int 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=normalize(voltab[lvol]);
- ghld[t].rvoltab=normalize(voltab[rvol]);
- }
- else{
- ghld[t].lvoltab=normalize(voltab[vol]);
- }
-
- if(FP_OFF(ghld[t].lvoltab)) return;
- }
-
- // write 'part' samples to the buffer
-
- while(todo){
- part=min(todo,MAXTICKSIZE);
- VC_FillTick(buf,part);
- buf+=bytes2samples(part);
- todo-=part;
- }
-
- EMSRestoreMap();
- }
-
-
-
- UWORD VC_WriteBytes(char far *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)
- {
- }
-