# HG changeset patch # User darius # Date 885571509 0 # Node ID d14fd386d182b2604580bad697dee461bf8e8669 # Parent 42e11dc15457a31793f9f87d1734b134df3fdade Initial entry of mikmod into the CVS tree. diff -r 42e11dc15457 -r d14fd386d182 playercode/munitrk.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/playercode/munitrk.c Fri Jan 23 16:05:09 1998 +0000 @@ -0,0 +1,307 @@ +/* + +Name: +MUNITRK.C + +Description: +All routines dealing with the manipulation of UNITRK(tm) streams + +Portability: +All systems - all compilers + +*/ + +#include +#include "mikmod.h" + +#define BUFPAGE 128 // smallest unibuffer size +#define TRESHOLD 16 + +UWORD unioperands[256] = +{ 0, // not used + 1, // UNI_NOTE + 1, // UNI_INSTRUMENT + 1, // UNI_PTEFFECT0 + 1, // UNI_PTEFFECT1 + 1, // UNI_PTEFFECT2 + 1, // UNI_PTEFFECT3 + 1, // UNI_PTEFFECT4 + 1, // UNI_PTEFFECT5 + 1, // UNI_PTEFFECT6 + 1, // UNI_PTEFFECT7 + 1, // UNI_PTEFFECT8 + 1, // UNI_PTEFFECT9 + 1, // UNI_PTEFFECTA + 1, // UNI_PTEFFECTB + 1, // UNI_PTEFFECTC + 1, // UNI_PTEFFECTD + 1, // UNI_PTEFFECTE + 1, // UNI_PTEFFECTF + 1, // UNI_S3MEFFECTA + 1, // UNI_S3MEFFECTD + 1, // UNI_S3MEFFECTE + 1, // UNI_S3MEFFECTF + 1, // UNI_S3MEFFECTI + 1, // UNI_S3MEFFECTQ + 1, // UNI_S3MEFFECTR + 1, // UNI_S3MEFFECTT + 1, // UNI_S3MEFFECTU + 0, // UNI_KEYOFF + 1, // UNI_KEYFADE + 2, // UNI_VOLEFFECTS + 1, // UNI_XMEFFECT4 + 1, // UNI_XMEFFECTA + 1, // UNI_XMEFFECTE1 + 1, // UNI_XMEFFECTE2 + 1, // UNI_XMEFFECTEA + 1, // UNI_XMEFFECTEB + 1, // UNI_XMEFFECTG + 1, // UNI_XMEFFECTH + 1, // UNI_XMEFFECTL + 1, // UNI_XMEFFECTP + 1, // UNI_XMEFFECTX1 + 1, // UNI_XMEFFECTX2 + 1, // UNI_ITEFFECTG + 1, // UNI_ITEFFECTH + 1, // UNI_ITEFFECTI + 1, // UNI_ITEFFECTM + 1, // UNI_ITEFFECTN + 1, // UNI_ITEFFECTP + 1, // UNI_ITEFFECTU + 1, // UNI_ITEFFECTW + 1, // UNI_ITEFFECTY + 1 // UNI_ITEFFECTS0 +}; + + +// unibuffer is increased by BUFPAGE +// bytes when unipc reaches unimax-TRESHOLD + + +/* + Ok.. I'll try to explain the new internal module format.. so here it goes: + + + The UNITRK(tm) Format: + ====================== + + A UNITRK stream is an array of bytes representing a single track + of a pattern. It's made up of 'repeat/length' bytes, opcodes and + operands (sort of a assembly language): + + rrrlllll + [REP/LEN][OPCODE][OPERAND][OPCODE][OPERAND] [REP/LEN][OPCODE][OPERAND].. + ^ ^ ^ + |-------ROWS 0 - 0+REP of a track---------| |-------ROWS xx - xx+REP of a track... + + + The rep/len byte contains the number of bytes in the current row, + _including_ the length byte itself (So the LENGTH byte of row 0 in the + previous example would have a value of 5). This makes it easy to search + through a stream for a particular row. A track is concluded by a 0-value + length byte. + + The upper 3 bits of the rep/len byte contain the number of times -1 this + row is repeated for this track. (so a value of 7 means this row is repeated + 8 times) + + Opcodes can range from 1 to 255 but currently only opcodes 1 to 45 are + being used. Each opcode can have a different number of operands. You can + find the number of operands to a particular opcode by using the opcode + as an index into the 'unioperands' table. + +*/ + + +/*************************************************************************** +>>>>>>>>>>> Next are the routines for reading a UNITRK stream: <<<<<<<<<<<<< +***************************************************************************/ + + +static UBYTE *rowstart; // startadress of a row +static UBYTE *rowend; // endaddress of a row (exclusive) +static UBYTE *rowpc; // current unimod(tm) programcounter + + +void UniSetRow(UBYTE *t) +{ + rowstart = t; + rowpc = rowstart; + rowend = rowstart+(*(rowpc++)&0x1f); +} + + +UBYTE UniGetByte(void) +{ + return (rowpc end of track.. + l = (c>>5)+1; // extract repeat value + if(l>row) break; // reached wanted row? -> return pointer + row -= l; // haven't reached row yet.. update row + t += c&0x1f; // point t to the next row + } + + return t; +} + + + +/*************************************************************************** +>>>>>>>>>>> Next are the routines for CREATING UNITRK streams: <<<<<<<<<<<<< +***************************************************************************/ + + +static UBYTE *unibuf; // pointer to the temporary unitrk buffer +static UWORD unimax; // maximum number of bytes to be written to this buffer + +static UWORD unipc; // index in the buffer where next opcode will be written +static UWORD unitt; // holds index of the rep/len byte of a row +static UWORD lastp; // holds index to the previous row (needed for compressing) + + +void UniReset(void) +// Resets index-pointers to create a new track. +{ + unitt = 0; // reset index to rep/len byte + unipc = 1; // first opcode will be written to index 1 + lastp = 0; // no previous row yet + unibuf[0] = 0; // clear rep/len byte +} + + +void UniWrite(UBYTE data) +// Appends one byte of data to the current row of a track. +{ + // write byte to current position and update + + unibuf[unipc++] = data; + + // Check if we've reached the end of the buffer + + if(unipc > (unimax-TRESHOLD)) + { UBYTE *newbuf; + + // We've reached the end of the buffer, so expand + // the buffer by BUFPAGE bytes + + newbuf = (UBYTE *)realloc(unibuf, unimax+BUFPAGE); + + // Check if realloc succeeded + + if(newbuf!=NULL) + { unibuf = newbuf; + unimax+=BUFPAGE; + } else + { // realloc failed, so decrease unipc so we won't write beyond + // the end of the buffer.. I don't report the out-of-memory + // here; the UniDup() will fail anyway so that's where the + // loader sees that something went wrong + + unipc--; + } + } +} + + +BOOL MyCmp(UBYTE *a, UBYTE *b, UWORD l) +{ + UWORD t; + + for(t=0; t>5)+1; // repeat of previous row + l = (unibuf[lastp]&0x1f); // length of previous row + + len = unipc-unitt; // length of current row + + // Now, check if the previous and the current row are identical.. + // when they are, just increase the repeat field of the previous row + + if(n<8 && len==l && MyCmp(&unibuf[lastp+1],&unibuf[unitt+1],len-1)) + { unibuf[lastp]+=0x20; + unipc = unitt+1; + } else + { // current and previous row aren't equal.. so just update the pointers + + unibuf[unitt] = len; + lastp = unitt; + unitt = unipc; + unipc++; + } +} + + +UBYTE *UniDup(void) +// Terminates the current unitrk stream and returns a pointer +// to a copy of the stream. +{ + UBYTE *d; + + unibuf[unitt] = 0; + + if((d=(UBYTE *)_mm_malloc(unipc))==NULL) return NULL; + memcpy(d,unibuf,unipc); + + return d; +} + + +UWORD TrkLen(UBYTE *t) +// Determines the length (in rows) of a unitrk stream 't' +{ + UWORD len = 0; + UBYTE c; + + while(c = *t & 0x1f) + { len += c; + t += c; + } + len++; + + return len; +} + + +BOOL UniInit(void) +{ + unimax = BUFPAGE; + + if(!(unibuf=(UBYTE *)_mm_malloc(unimax))) return 0; + return 1; +} + + +void UniCleanup(void) +{ + if(unibuf!=NULL) free(unibuf); + unibuf = NULL; +} + diff -r 42e11dc15457 -r d14fd386d182 playercode/mwav.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/playercode/mwav.c Fri Jan 23 16:05:09 1998 +0000 @@ -0,0 +1,133 @@ +/* + + Name: MWAV.C + + Description: + WAV sample loader + Stereo .WAV files are not yet supported as samples. + + Portability: + All compilers -- All systems (hopefully) + +*/ + +#include +#include "mikmod.h" + + +typedef struct WAV +{ CHAR rID[4]; + ULONG rLen; + CHAR wID[4]; + CHAR fID[4]; + ULONG fLen; + UWORD wFormatTag; + UWORD nChannels; + ULONG nSamplesPerSec; + ULONG nAvgBytesPerSec; + UWORD nBlockAlign; + UWORD nFormatSpecific; +} WAV; + + +SAMPLE *WAV_LoadFP(FILE *fp) +{ + SAMPLE *si; + static WAV wh; + static CHAR dID[4]; + + // read wav header + + _mm_read_string(wh.rID,4,fp); + wh.rLen = _mm_read_I_ULONG(fp); + _mm_read_string(wh.wID,4,fp); + + while(1) + { _mm_read_string(wh.fID,4,fp); + wh.fLen = _mm_read_I_ULONG(fp); + if(memcmp(wh.fID,"fmt ",4) == 0) break; + _mm_fseek(fp,wh.fLen,SEEK_CUR); + } + + if( feof(fp) || + memcmp(wh.rID,"RIFF",4) || + memcmp(wh.wID,"WAVE",4)) + { + _mm_errno = MMERR_UNKNOWN_WAVE_TYPE; + return NULL; + } + + wh.wFormatTag = _mm_read_I_UWORD(fp); + wh.nChannels = _mm_read_I_UWORD(fp); + wh.nSamplesPerSec = _mm_read_I_ULONG(fp); + wh.nAvgBytesPerSec = _mm_read_I_ULONG(fp); + wh.nBlockAlign = _mm_read_I_UWORD(fp); + wh.nFormatSpecific = _mm_read_I_UWORD(fp); + + // check it + + if(feof(fp)) + { _mm_errno = MMERR_UNKNOWN_WAVE_TYPE; + return NULL; + } + + // skip other crap + + _mm_fseek(fp,wh.fLen-16,SEEK_CUR); + _mm_read_string(dID,4,fp); + + if(memcmp(dID,"data",4)) + { _mm_errno = MMERR_UNKNOWN_WAVE_TYPE; + return NULL; + } + + if(wh.nChannels > 1) + { _mm_errno = MMERR_UNKNOWN_WAVE_TYPE; + return NULL; + } + +// printf("wFormatTag: %x\n",wh.wFormatTag); +// printf("blockalign: %x\n",wh.nBlockAlign); +// prinff("nFormatSpc: %x\n",wh.nFormatSpecific); + + if((si=(SAMPLE *)_mm_calloc(1,sizeof(SAMPLE)))==NULL) return NULL; + + si->speed = wh.nSamplesPerSec; + si->volume = 64; + si->length = _mm_read_I_ULONG(fp); + + if(wh.nBlockAlign == 2) + { si->flags = SF_16BITS | SF_SIGNED; + si->length >>= 1; + } + + SL_RegisterSample(si,MD_SNDFX,fp); + + return si; +} + + +SAMPLE *WAV_LoadFN(CHAR *filename) +{ + FILE *fp; + SAMPLE *si; + + if(!(md_mode & DMODE_SOFT_SNDFX)) return NULL; + if((fp=_mm_fopen(filename,"rb"))==NULL) return NULL; + + si = WAV_LoadFP(fp); + SL_LoadSamples(); + fclose(fp); + + return si; +} + + +void WAV_Free(SAMPLE *si) +{ + if(si!=NULL) + { MD_SampleUnLoad(si->handle); + free(si); + } +} + diff -r 42e11dc15457 -r d14fd386d182 playercode/npertab.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/playercode/npertab.c Fri Jan 23 16:05:09 1998 +0000 @@ -0,0 +1,14 @@ +// MOD format period table. Used by both the MOD and +// M15 (15-inst mod) Loaders. + +#include "tdefs.h" + +UWORD npertab[60] = +{ // -> Tuning 0 + 1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,906, + 856,808,762,720,678,640,604,570,538,508,480,453, + 428,404,381,360,339,320,302,285,269,254,240,226, + 214,202,190,180,170,160,151,143,135,127,120,113, + 107,101,95,90,85,80,75,71,67,63,60,56 +}; + diff -r 42e11dc15457 -r d14fd386d182 playercode/resample.S --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/playercode/resample.S Fri Jan 23 16:05:09 1998 +0000 @@ -0,0 +1,316 @@ +/* GCC port of mixing code by Barubary (barubary@dal.net) + This should work on both Linux and DJGPP... I have confirmed that + self-modifying code works just fine in Linux without causing signals. + This may change in the future, so I recommend that the Linux MikMod + developers look out for new kernel releases. The critical thing is that + CS.base = DS.base in Linux, which makes DS an alias for CS, and that the + code pages remain writable. */ + +.data /* self-modifying code... keep in data segment */ + +#define _AsmStereoNormal AsmStereoNormal +#define _AsmStereoSurround AsmStereoSurround +#define _AsmMonoNormal AsmMonoNormal +#define _AsmMix32To16Surround AsmMix32To16Surround +#define _AsmMix32To8Surround AsmMix32To8Surround +#define _bitshift bitshift +#define _lvoltab lvoltab +#define _rvoltab rvoltab + + .globl _AsmStereoNormal + .globl _AsmStereoSurround + .globl _AsmMonoNormal + .globl _AsmMix32To16Surround + .globl _AsmMix32To8Surround + + +#define STUBSTART \ + pushl %eax; \ + pushl %ebx; \ + pushl %ecx; \ + pushl %edx; \ + pushl %esi; \ + pushl %edi; \ + pushl %ebp + +#define STUBEND \ + popl %ebp; \ + popl %edi; \ + popl %esi; \ + popl %edx; \ + popl %ecx; \ + popl %ebx; \ + popl %eax + +#define SS2F(index,lab1,lab2,lab3,lab4) \ + movb (%edx),%al; \ + .byte 0x081,0x0c3; \ + lab1: .int 0; \ + .byte 0x081,0x0d2; \ + lab2: .int 0; \ + movl index*8(%edi),%esi; \ + .byte 0x08b,0x0c,0x085; \ + lab3: .int 0; \ + movl index*8+4(%edi),%ebp; \ + addl %ecx,%esi; \ + .byte 0x08b,0x0c,0x085; \ + lab4: .int 0; \ + movl %esi,index*8(%edi); \ + addl %ecx,%ebp + +#define SS2M(index,lab1,lab2,lab3,lab4) \ + movb (%edx),%al; \ + movl %ebp,index*8-4(%edi); \ + .byte 0x081,0x0c3; \ + lab1: .int 0; \ + .byte 0x081,0x0d2; \ + lab2: .int 0; \ + movl index*8(%edi),%esi; \ + .byte 0x08b,0x0c,0x085; \ + lab3: .int 0; \ + movl index*8+4(%edi),%ebp; \ + addl %ecx,%esi; \ + .byte 0x08b,0x0c,0x085; \ + lab4: .int 0; \ + movl %esi,index*8(%edi); \ + addl %ecx,%ebp + +#define SS3F(index,lab1,lab2,lab3) \ + movb (%edx),%al; \ + .byte 0x081,0x0c3; \ + lab1: .int 0; \ + .byte 0x081,0x0d2; \ + lab2: .int 0; \ + movl index*8(%edi),%esi; \ + .byte 0x08b,0x0c,0x085; \ + lab3: .int 0; \ + movl index*8+4(%edi),%ebp; \ + addl %ecx,%esi; \ + subl %ecx,%ebp; \ + movl %esi,index*8(%edi); \ + +#define SS3M(index,lab1,lab2,lab3) \ + movb (%edx),%al; \ + movl %ebp,index*8-4(%edi); \ + .byte 0x081,0x0c3; \ + lab1: .int 0; \ + .byte 0x081,0x0d2; \ + lab2: .int 0; \ + movl index*8(%edi),%esi; \ + .byte 0x08b,0x0c,0x085; \ + lab3: .int 0; \ + movl index*8+4(%edi),%ebp; \ + addl %ecx,%esi; \ + subl %ecx,%ebp; \ + movl %esi,index*8(%edi); \ + +#define SS2L(index) \ + movl %ebp,index*8-4(%edi) + +#define SM2F(index,lab1,lab2,lab3) \ + movb (%edx),%al; \ + .byte 0x081,0x0c3; \ + lab1: .int 0; \ + .byte 0x081,0x0d2; \ + lab2: .int 0; \ + movl index*4(%edi),%esi; \ + .byte 0x08b,0x0c,0x085; \ + lab3: .int 0 + +#define SM2M(index,lab1,lab2,lab3) \ + movb (%edx),%al; \ + addl %ecx,%esi; \ + .byte 0x081,0x0c3; \ + lab1: .int 0; \ + movl %esi,index*4-4(%edi); \ + .byte 0x081,0x0d2; \ + lab2: .int 0; \ + movl index*4(%edi),%esi; \ + .byte 0x08b,0x0c,0x085; \ + lab3: .int 0 + +#define SM2L(index) \ + addl %ecx,%esi; \ + movl %esi,index*4-4(%edi) + +_AsmStereoNormal: + STUBSTART + movl 32(%esp),%esi /* get src */ + movl 36(%esp),%edi /* get dst */ + movl 40(%esp),%ebx /* get index */ + movl 44(%esp),%ecx /* get increment */ + movl 48(%esp),%ebp /* get todo */ + movl %ecx,%eax + movl %ebx,%edx + sarl $31,%eax + sarl $31,%edx + shldl $21,%ecx,%eax /* convert to 32:32 */ + shll $21,%ecx + shldl $21,%ebx,%edx /* convert to 32:32 */ + shll $21,%ebx + addl %esi,%edx + movl %eax,shi1 + movl %eax,shi2 + movl %eax,shi3 + movl %eax,shi4 + movl %eax,shi5 + movl _lvoltab,%eax + movl %ecx,slo1 + movl %ecx,slo2 + movl %ecx,slo3 + movl %ecx,slo4 + movl %ecx,slo5 + movl %eax,sltab1 + movl %eax,sltab2 + movl %eax,sltab3 + movl %eax,sltab4 + movl %eax,sltab5 + movl _rvoltab,%eax + pushl %ebp + movl %eax,srtab1 + movl %eax,srtab2 + movl %eax,srtab3 + movl %eax,srtab4 + movl %eax,srtab5 + xorl %eax,%eax + jmp s1 /* flush code cache */ +s1: + shrl $2,%ebp + jz sskip16 + pushl %ebp +sagain16: + SS2F(0,slo1,shi1,sltab1,srtab1) + SS2M(1,slo2,shi2,sltab2,srtab2) + SS2M(2,slo3,shi3,sltab3,srtab3) + SS2M(3,slo4,shi4,sltab4,srtab4) + SS2L(4) + addl $32,%edi + decl (%esp) + jnz sagain16 + popl %ebp +sskip16: + popl %ebp + andl $3,%ebp + jz sskip1 + pushl %ebp +sagain1: + SS2F(0,slo5,shi5,sltab5,srtab5) + SS2L(1) + addl $8,%edi + decl (%esp) + jnz sagain1 + popl %ebp +sskip1: + STUBEND + ret + + +_AsmStereoSurround: + STUBSTART + movl 32(%esp),%esi /* get src */ + movl 36(%esp),%edi /* get dst */ + movl 40(%esp),%ebx /* get index */ + movl 44(%esp),%ecx /* get increment */ + movl 48(%esp),%ebp /* get todo */ + movl %ecx,%eax + movl %ebx,%edx + sarl $31,%eax + sarl $31,%edx + shldl $21,%ecx,%eax /* convert to 32:32 */ + shll $21,%ecx + shldl $21,%ebx,%edx /* convert to 32:32 */ + shll $21,%ebx + addl %esi,%edx + movl %eax,s2hi1 + movl %eax,s2hi2 + movl %eax,s2hi3 + movl %eax,s2hi4 + movl %eax,s2hi5 + movl _lvoltab,%eax + movl %ecx,s2lo1 + movl %ecx,s2lo2 + movl %ecx,s2lo3 + movl %ecx,s2lo4 + movl %ecx,s2lo5 + movl %eax,s2ltab1 + movl %eax,s2ltab2 + movl %eax,s2ltab3 + movl %eax,s2ltab4 + movl %eax,s2ltab5 + pushl %ebp + xorl %eax,%eax + jmp s3 /* flush code cache */ +s3: + shrl $2,%ebp + jz s2skip16 + pushl %ebp +s2again16: + SS3F(0,s2lo1,s2hi1,s2ltab1) + SS3M(1,s2lo2,s2hi2,s2ltab2) + SS3M(2,s2lo3,s2hi3,s2ltab3) + SS3M(3,s2lo4,s2hi4,s2ltab4) + SS2L(4) + addl $32,%edi + decl (%esp) + jnz s2again16 + popl %ebp +s2skip16: + popl %ebp + andl $3,%ebp + jz s2skip1 + pushl %ebp +s2again1: + SS3F(0,s2lo5,s2hi5,s2ltab5) + SS2L(1) + addl $8,%edi + decl (%esp) + jnz s2again1 + popl %ebp +s2skip1: + STUBEND + ret + + +_AsmMonoNormal: + STUBSTART + movl 32(%esp),%esi /* get src */ + movl 36(%esp),%edi /* get dst */ + movl 40(%esp),%ebx /* get index */ + movl 44(%esp),%ecx /* get increment */ + movl 48(%esp),%ebp /* get todo */ + movl %ecx,%eax + movl %ebx,%edx + sarl $31,%eax + sarl $31,%edx + shldl $21,%ecx,%eax /* convert to 32:32 */ + shll $21,%ecx + shldl $21,%ebx,%edx /* convert to 32:32 */ + shll $21,%ebx + addl %esi,%edx + movl %eax,mhi1 + movl %eax,mhi2 + movl %eax,mhi3 + movl %eax,mhi4 + movl %eax,mhi5 + movl _lvoltab,%eax + movl %ecx,mlo1 + movl %ecx,mlo2 + movl %ecx,mlo3 + movl %ecx,mlo4 + movl %ecx,mlo5 + movl %eax,mltab1 + movl %eax,mltab2 + movl %eax,mltab3 + movl %eax,mltab4 + movl %eax,mltab5 + xorl %eax,%eax + + jmp m1 /* flush code cache */ +m1: + pushl %ebp + shrl $2,%ebp + jz mskip16 +magain16: + SM2F(0,mlo1,mhi1,mltab1) + SM2M(1,mlo2,mhi2,mltab2) + diff -r 42e11dc15457 -r d14fd386d182 playercode/resample.asm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/playercode/resample.asm Fri Jan 23 16:05:09 1998 +0000 @@ -0,0 +1,455 @@ +.386p +.model flat +.data ; self-modifying code... keep in data segment + + NAME resample + EXTRN _rvoltab :DWORD + EXTRN _lvoltab :DWORD + EXTRN _bitshift :BYTE + + PUBLIC _AsmStereoNormal + PUBLIC _AsmStereoSurround + PUBLIC _AsmMonoNormal +IFDEF __ASMSTUFF__ + PUBLIC _AsmMix32To16_Normal + PUBLIC _AsmMix32To8_Normal +ENDIF + + +STUBSTART macro + push eax + push ebx + push ecx + push edx + push esi + push edi + push ebp + endm + + +STUBEND macro + pop ebp + pop edi + pop esi + pop edx + pop ecx + pop ebx + pop eax + endm + + +SS2F MACRO index,lab1,lab2,lab3,lab4 ; 486+ + mov al,[edx] + db 081h,0c3h +lab1 dd 0 ; add ebx,lo + db 081h,0d2h +lab2 dd 0 ; adc edx,hi + mov esi,(index*8)[edi] + db 08bh,0ch,085h +lab3 dd 0 ; mov ecx,[eax*4+table1] + mov ebp,(index*8+4)[edi] + add esi,ecx + db 08bh,0ch,085h +lab4 dd 0 ; mov ecx,[eax*4+table2] + mov (index*8)[edi],esi + add ebp,ecx + ENDM + +SS2M MACRO index,lab1,lab2,lab3,lab4 ; 486+ + mov al,[edx] + mov (index*8-4)[edi],ebp + db 081h,0c3h +lab1 dd 0 ; add ebx,lo + db 081h,0d2h +lab2 dd 0 ; adc edx,hi + mov esi,(index*8)[edi] + db 08bh,0ch,085h +lab3 dd 0 ; mov ecx,[eax*4+table1] + mov ebp,(index*8+4)[edi] + add esi,ecx + db 08bh,0ch,085h +lab4 dd 0 ; mov ecx,[eax*4+table2] + mov (index*8)[edi],esi + add ebp,ecx + ENDM + +SS3F MACRO index,lab1,lab2,lab3 ; 486+ + mov al,[edx] + db 081h,0c3h +lab1 dd 0 ; add ebx,lo + db 081h,0d2h +lab2 dd 0 ; adc edx,hi + mov esi,(index*8)[edi] + db 08bh,0ch,085h +lab3 dd 0 ; mov ecx,[eax*4+table1] + mov ebp,(index*8+4)[edi] + add esi,ecx + sub ebp,ecx + mov (index*8)[edi],esi + ENDM + +SS3M MACRO index,lab1,lab2,lab3 ; 486+ + mov al,[edx] + mov (index*8-4)[edi],ebp + db 081h,0c3h +lab1 dd 0 ; add ebx,lo + db 081h,0d2h +lab2 dd 0 ; adc edx,hi + mov esi,(index*8)[edi] + db 08bh,0ch,085h +lab3 dd 0 ; mov ecx,[eax*4+table1] + mov ebp,(index*8+4)[edi] + add esi,ecx + sub ebp,ecx + mov (index*8)[edi],esi + ENDM + +SS2L MACRO index ; 486+ + mov (index*8-4)[edi],ebp + ENDM + +SM2F MACRO index,lab1,lab2,lab3 ; 486+ + mov al,[edx] ; AGI-3 + db 081h,0c3h +lab1 dd 0 ; add ebx,lo + db 081h,0d2h +lab2 dd 0 ; adc edx,hi + mov esi,(index*4)[edi] + db 08bh,0ch,085h +lab3 dd 0 ; mov ecx,[eax*4+table1] + ENDM + +SM2M MACRO index,lab1,lab2,lab3 ; 486+ + mov al,[edx] ; AGI-3 + add esi,ecx + db 081h,0c3h +lab1 dd 0 ; add ebx,lo + mov (index*4-4)[edi],esi + db 081h,0d2h +lab2 dd 0 ; adc edx,hi + mov esi,(index*4)[edi] + db 08bh,0ch,085h +lab3 dd 0 ; mov ecx,[eax*4+table1] + ENDM + +SM2L MACRO index ; 486+ + add esi,ecx + mov (index*4-4)[edi],esi + ENDM + +;SS2 ; 386 +;mov al,[edx+source] +;add ebx,lo +;adc edx,hi +;mov ecx,[eax*4+table1] +;mov ebp,[eax*4+table2] +;add [edi],ecx +;add [edi+4],ebp +;ENDM + +;SS2M ; 486+ +;mov al,[edx+source] +;mov [edi-8+4],ebp +;add ebx,lo +;adc edx,hi +;mov esi,[edi] +;mov ecx,[eax*4+table1] +;mov ebp,[edi+4] +;add esi,ecx +;mov ecx,[eax*4+table2] +;mov [edi],esi +;add ebp,ecx +;ENDM + +;SM2M ; 386 +;add ebx,lo +;adc edx,hi +;mov ecx,[eax*4+table1] +;mov al,[edx+source] +;add [edi],ecx +;ENDM + +;SM2M ; 486+ +;add ebx,lo +;mov [edi-4],esi +;adc edx,hi +;mov esi,[edi] +;mov ecx,[eax*4+table1] +;mov al,[edx+source] ; AGI-3 +;add esi,ecx +;ENDM + + +_AsmStereoNormal: + STUBSTART + mov esi,[esp+32] ; get src + mov edi,[esp+36] ; get dst + mov ebx,[esp+40] ; get index + mov ecx,[esp+44] ; get increment + mov ebp,[esp+48] ; get todo + mov eax,ecx + mov edx,ebx + sar eax,31 + sar edx,31 + shld eax,ecx,21 ; convert to 32:32 + shl ecx,21 + shld edx,ebx,21 ; convert to 32:32 + shl ebx,21 + add edx,esi + mov shi1,eax + mov shi2,eax + mov shi3,eax + mov shi4,eax + mov shi5,eax + mov eax,_lvoltab + mov slo1,ecx + mov slo2,ecx + mov slo3,ecx + mov slo4,ecx + mov slo5,ecx + mov sltab1,eax + mov sltab2,eax + mov sltab3,eax + mov sltab4,eax + mov sltab5,eax + mov eax,_rvoltab + push ebp + mov srtab1,eax + mov srtab2,eax + mov srtab3,eax + mov srtab4,eax + mov srtab5,eax + xor eax,eax + jmp s1 ; flush code cache +s1: + shr ebp,2 + jz sskip16 + push ebp +sagain16: + SS2F 0,slo1,shi1,sltab1,srtab1 + SS2M 1,slo2,shi2,sltab2,srtab2 + SS2M 2,slo3,shi3,sltab3,srtab3 + SS2M 3,slo4,shi4,sltab4,srtab4 + SS2L 4 + add edi,(4*8) + dec dword ptr [esp] + jnz sagain16 + pop ebp +sskip16: + pop ebp + and ebp,3 + jz sskip1 + push ebp +sagain1: + SS2F 0,slo5,shi5,sltab5,srtab5 + SS2L 1 + add edi,8 + dec dword ptr [esp] + jnz sagain1 + pop ebp +sskip1: + STUBEND + ret + + +_AsmStereoSurround: + STUBSTART + mov esi,[esp+32] ; get src + mov edi,[esp+36] ; get dst + mov ebx,[esp+40] ; get index + mov ecx,[esp+44] ; get increment + mov ebp,[esp+48] ; get todo + mov eax,ecx + mov edx,ebx + sar eax,31 + sar edx,31 + shld eax,ecx,21 ; convert to 32:32 + shl ecx,21 + shld edx,ebx,21 ; convert to 32:32 + shl ebx,21 + add edx,esi + mov s2hi1,eax + mov s2hi2,eax + mov s2hi3,eax + mov s2hi4,eax + mov s2hi5,eax + mov eax,_lvoltab + mov s2lo1,ecx + mov s2lo2,ecx + mov s2lo3,ecx + mov s2lo4,ecx + mov s2lo5,ecx + mov s2ltab1,eax + mov s2ltab2,eax + mov s2ltab3,eax + mov s2ltab4,eax + mov s2ltab5,eax + ;mov eax,_rvoltab + push ebp + ;mov s2rtab1,eax + ;mov s2rtab2,eax + ;mov s2rtab3,eax + ;mov s2rtab4,eax + ;mov s2rtab5,eax + xor eax,eax + jmp s3 ; flush code cache +s3: + shr ebp,2 + jz s2skip16 + push ebp +s2again16: + SS3F 0,s2lo1,s2hi1,s2ltab1 ;,s2rtab1 + SS3M 1,s2lo2,s2hi2,s2ltab2 ;,s2rtab2 + SS3M 2,s2lo3,s2hi3,s2ltab3 ;,s2rtab3 + SS3M 3,s2lo4,s2hi4,s2ltab4 ;,s2rtab4 + SS2L 4 + add edi,(4*8) + dec dword ptr [esp] + jnz s2again16 + pop ebp +s2skip16: + pop ebp + and ebp,3 + jz s2skip1 + push ebp +s2again1: + SS3F 0,s2lo5,s2hi5,s2ltab5 ;,s2rtab5 + SS2L 1 + add edi,8 + dec dword ptr [esp] + jnz s2again1 + pop ebp +s2skip1: + STUBEND + ret + + +_AsmMonoNormal: + STUBSTART + mov esi,[esp+32] ; get src + mov edi,[esp+36] ; get dst + mov ebx,[esp+40] ; get index + mov ecx,[esp+44] ; get increment + mov ebp,[esp+48] ; get todo + mov eax,ecx + mov edx,ebx + sar eax,31 + sar edx,31 + shld eax,ecx,21 ; convert to 32:32 + shl ecx,21 + shld edx,ebx,21 ; convert to 32:32 + shl ebx,21 + add edx,esi + mov mhi1,eax + mov mhi2,eax + mov mhi3,eax + mov mhi4,eax + mov mhi5,eax + mov eax,_lvoltab + mov mlo1,ecx + mov mlo2,ecx + mov mlo3,ecx + mov mlo4,ecx + mov mlo5,ecx + mov mltab1,eax + mov mltab2,eax + mov mltab3,eax + mov mltab4,eax + mov mltab5,eax + xor eax,eax + + jmp m1 ; flush code cache +m1: + push ebp + shr ebp,2 + jz mskip16 +magain16: + SM2F 0,mlo1,mhi1,mltab1 + SM2M 1,mlo2,mhi2,mltab2 + SM2M 2,mlo3,mhi3,mltab3 + SM2M 3,mlo4,mhi4,mltab4 + SM2L 4 + add edi,(4*4) + dec ebp + jnz magain16 +mskip16: + pop ebp + and ebp,3 + jz mskip1 +magain1: + SM2F 0,mlo5,mhi5,mltab5 + SM2L 1 + add edi,4 + dec ebp + jnz magain1 +mskip1: + STUBEND + ret + + + +IFDEF __ASMSTUFF__ +_AsmMix32To16_Normal: + STUBSTART + mov edi,[esp+32] ; get dest + mov esi,[esp+36] ; get src + mov edx,[esp+40] ; get count + mov cl,[_bitshift] +again: + mov eax,[esi] + sar eax,cl + cmp eax,32767 + jg toobig + cmp eax,-32768 + jl toosmall +write: + mov [edi],ax + add esi,4 + add edi,2 + dec edx + jnz again + jmp ready +toobig: + mov eax,32767 + jmp write +toosmall: + mov eax,-32768 + jmp write +ready: + STUBEND + ret + + +_AsmMix32To8_Normal: + STUBSTART + mov edi,[esp+32] ; get dest + mov esi,[esp+36] ; get src + mov edx,[esp+40] ; get count + mov cl,[_bitshift] +cagain: + mov eax,[esi] + sar eax,cl + cmp eax,127 + jg ctoobig + cmp eax,-128 + jl ctoosmall +cwrite: + add al,080h + add esi,4 + mov [edi],al + inc edi + dec edx + jnz cagain + jmp cready +ctoobig: + mov eax,127 + jmp cwrite +ctoosmall: + mov eax,-128 + jmp cwrite +cready: + STUBEND + ret +ENDIF + + END diff -r 42e11dc15457 -r d14fd386d182 playercode/s3m_it.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/playercode/s3m_it.c Fri Jan 23 16:05:09 1998 +0000 @@ -0,0 +1,187 @@ +/* + +Name: +S3M_IT.C + +Description: +Commonfunctions between S3Ms and ITs (smaller .EXE! :) + +Portability: +All systems - all compilers (hopefully) + +*/ + +#include "mikmod.h" + +UBYTE *poslookup=NULL;// S3M/IT fix - removing blank patterns needs a + // lookup table to fix position-jump commands +SBYTE remap[64]; // for removing empty channels +SBYTE isused[64]; // for removing empty channels + + +void S3MIT_ProcessCmd(UBYTE cmd,UBYTE inf,BOOL oldeffect) +{ + UBYTE hi,lo; + + lo=inf&0xf; + hi=inf>>4; + + // process S3M / IT specific command structure + + if(cmd!=255) + { switch(cmd) + { case 1: // Axx set speed to xx + UniWrite(UNI_S3MEFFECTA); + UniWrite(inf); + break; + + case 2: // Bxx position jump + UniPTEffect(0xb,poslookup[inf]); + break; + + case 3: // Cxx patternbreak to row xx + if(oldeffect & 1) + UniPTEffect(0xd,(((inf&0xf0)>>4)*10)+(inf&0xf)); + else + UniPTEffect(0xd,inf); + break; + + case 4: // Dxy volumeslide + UniWrite(UNI_S3MEFFECTD); + UniWrite(inf); + break; + + case 5: // Exy toneslide down + UniWrite(UNI_S3MEFFECTE); + UniWrite(inf); + break; + + case 6: // Fxy toneslide up + UniWrite(UNI_S3MEFFECTF); + UniWrite(inf); + break; + + case 7: // Gxx Tone portamento, speed xx + UniWrite(UNI_ITEFFECTG); + UniWrite(inf); + break; + + case 8: // Hxy vibrato + if(oldeffect & 1) + UniPTEffect(0x4,inf); + else + { UniWrite(UNI_ITEFFECTH); + UniWrite(inf); + } + break; + + case 9: // Ixy tremor, ontime x, offtime y + if(oldeffect & 1) + UniWrite(UNI_S3MEFFECTI); + else + UniWrite(UNI_ITEFFECTI); + UniWrite(inf); + break; + + case 0xa: // Jxy arpeggio + UniPTEffect(0x0,inf); + break; + + case 0xb: // Kxy Dual command H00 & Dxy + if(oldeffect & 1) + UniPTEffect(0x4,0); + else + { UniWrite(UNI_ITEFFECTH); + UniWrite(0); + } + UniWrite(UNI_S3MEFFECTD); + UniWrite(inf); + break; + + case 0xc: // Lxy Dual command G00 & Dxy + if(oldeffect & 1) + UniPTEffect(0x3,0); + else + { UniWrite(UNI_ITEFFECTG); + UniWrite(0); + } + UniWrite(UNI_S3MEFFECTD); + UniWrite(inf); + break; + + case 0xd: // Mxx Set Channel Volume + UniWrite(UNI_ITEFFECTM); + UniWrite(inf); + break; + + case 0xe: // Nxy Slide Channel Volume + UniWrite(UNI_ITEFFECTN); + UniWrite(inf); + break; + + case 0xf: // Oxx set sampleoffset xx00h + UniPTEffect(0x9,inf); + break; + + case 0x10: // Pxy Slide Panning Commands + UniWrite(UNI_ITEFFECTP); + UniWrite(inf); + break; + + case 0x11: // Qxy Retrig (+volumeslide) + UniWrite(UNI_S3MEFFECTQ); + if((inf!=0) && (lo==0) && !(oldeffect & 1)) + UniWrite(1); + else + UniWrite(inf); + break; + + case 0x12: // Rxy tremolo speed x, depth y + UniWrite(UNI_S3MEFFECTR); + UniWrite(inf); + break; + + case 0x13: // Sxx special commands + UniWrite(UNI_ITEFFECTS0); + UniWrite(inf); + break; + + case 0x14: // Txx tempo + if(inf>0x20) + { UniWrite(UNI_S3MEFFECTT); + UniWrite(inf); + } + break; + + case 0x15: // Uxy Fine Vibrato speed x, depth y + if(oldeffect & 1) + UniWrite(UNI_S3MEFFECTU); + else + UniWrite(UNI_ITEFFECTU); + UniWrite(inf); + break; + + case 0x16: // Vxx Set Global Volume + UniWrite(UNI_XMEFFECTG); + UniWrite(inf); + break; + + case 0x17: // Wxy Global Volume Slide + UniWrite(UNI_ITEFFECTW); + UniWrite(inf); + break; + + case 0x18: // Xxx amiga command 8xx + if(oldeffect & 1) + UniPTEffect(0x8,((inf<<1) == 256) ? 255 : (inf<<1)); + else + UniPTEffect(0x8,inf); + break; + + case 0x19: // Yxy Panbrello speed x, depth y + UniWrite(UNI_ITEFFECTY); + UniWrite(inf); + break; + } + } +} diff -r 42e11dc15457 -r d14fd386d182 playercode/sloader.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/playercode/sloader.c Fri Jan 23 16:05:09 1998 +0000 @@ -0,0 +1,407 @@ +/* + --> Sample Loaders and Sample Processing + +Name: sloader.c + +Description: +Routines for loading samples. The sample loader utilizes the routines +provided by the "registered" sample loader. See SAMPLELOADER in +MIKMOD.H for the sample loader structure. + +Portability: +All systems - all compilers + +*/ + +#include "mikmod.h" + + +static int sl_rlength; +static SWORD sl_old; +static SWORD *sl_buffer = NULL; +static SAMPLOAD *musiclist = NULL, + *sndfxlist = NULL; + + + +BOOL SL_Init(SAMPLOAD *s) // returns 0 on error! +{ + if(sl_buffer==NULL) + if((sl_buffer=_mm_malloc(4100)) == NULL) return 0; + + sl_rlength = s->length; + if(s->infmt & SF_16BITS) sl_rlength>>=1; + sl_old = 0; + + return 1; +} + + +void SL_Exit(SAMPLOAD *s) +{ + if(sl_rlength > 0) _mm_fseek(s->fp,sl_rlength,SEEK_CUR); +} + + +void SL_Reset(void) +{ + sl_old = 0; +} + + +void SL_Load(void *buffer, SAMPLOAD *smp, int length) +{ + UWORD infmt = smp->infmt, outfmt = smp->outfmt; + SBYTE *bptr = (SBYTE *)buffer; + SWORD *wptr = (SWORD *)buffer; + int stodo; + int t, u; + + while(length) + { stodo = (length<2048) ? length : 2048; + + if(infmt & SF_16BITS) + { if(infmt & SF_BIG_ENDIAN) + _mm_read_M_SWORDS(sl_buffer,stodo,smp->fp); + else + _mm_read_I_SWORDS(sl_buffer,stodo,smp->fp); + } else + { SBYTE *s; + SWORD *d; + + fread(sl_buffer,sizeof(SBYTE),stodo,smp->fp); + + s = (SBYTE *)sl_buffer; + d = sl_buffer; + s += stodo; + d += stodo; + + for(t=0; tscalefactor) + { int idx = 0; + SLONG scaleval; + + // Sample Scaling... average values for better results. + t = 0; + while(tscalefactor; u && tscalefactor-u); + length--; + } + sl_rlength -= stodo; + stodo = idx; + } else + { length -= stodo; + sl_rlength -= stodo; + } + + if(outfmt & SF_16BITS) + { for(t=0; t>8; + } + } +} + + +void SL_LoadStream(void *buffer, UWORD infmt, UWORD outfmt, int length, FILE *fp) + +// This is like SL_Load, but does not perform sample scaling, and does not +// require calls to SL_Init / SL_Exit. + +{ + SBYTE *bptr = (SBYTE *)buffer; + SWORD *wptr = (SWORD *)buffer; + int stodo; + int t; + + // compute number of samples to load + + if(sl_buffer==NULL) + if((sl_buffer=_mm_malloc(4100)) == NULL) return; + + while(length) + { stodo = (length<2048) ? length : 2048; + + if(infmt & SF_16BITS) + { if(infmt & SF_BIG_ENDIAN) + _mm_read_M_SWORDS(sl_buffer,stodo,fp); + else + _mm_read_I_SWORDS(sl_buffer,stodo,fp); + } else + { SBYTE *s; + SWORD *d; + + fread(sl_buffer,sizeof(SBYTE),stodo,fp); + + s = (SBYTE *)sl_buffer; + d = sl_buffer; + s += stodo; + d += stodo; + + for(t=0; t> 1; + length-=2; + } + stodo = idx; + } + + if(outfmt & SF_16BITS) + { for(t=0; t>8; + } + } +} + + +SAMPLOAD *SL_RegisterSample(SAMPLE *s, int type, FILE *fp) // Returns 1 on error! + +// Registers a sample for loading when SL_LoadSamples() is called. +// type - type of sample to be loaded .. +// MD_MUSIC, MD_SNDFX + +{ + SAMPLOAD *news, **samplist, *cruise; + + if(type==MD_MUSIC) + { samplist = & musiclist; + cruise = musiclist; + } else + { samplist = &sndfxlist; + cruise = sndfxlist; + } + + // Allocate and add structure to the END of the list + + if((news=(SAMPLOAD *)_mm_calloc(1,sizeof(SAMPLOAD))) == NULL) return NULL; + + if(cruise!=NULL) + { while(cruise->next!=NULL) cruise = cruise->next; + cruise->next = news; + } else + *samplist = news; + + news->infmt = s->flags & 31; + news->outfmt = news->infmt; + news->fp = fp; + news->sample = s; + news->length = s->length; + news->loopstart = s->loopstart; + news->loopend = s->loopend; + + return news; +} + + +static void FreeSampleList(SAMPLOAD *s) +{ + SAMPLOAD *old; + + while(s!=NULL) + { old = s; + s = s->next; + free(old); + } +} + + +static ULONG SampleTotal(SAMPLOAD *samplist, int type) +// Returns the total amount of memory required by the samplelist queue. +{ + int total = 0; + + while(samplist!=NULL) + { samplist->sample->flags = (samplist->sample->flags&~31) | samplist->outfmt; + total += MD_SampleLength(type,samplist->sample); + samplist = samplist->next; + } + + return total; +} + + +static ULONG RealSpeed(SAMPLOAD *s) +{ + return(s->sample->speed / ((s->scalefactor==0) ? 1 : s->scalefactor)); +} + + +static BOOL DitherSamples(SAMPLOAD *samplist, int type) +{ + SAMPLOAD *c2smp; + ULONG maxsize, speed; + SAMPLOAD *s; + + if(samplist==NULL) return 0; + + // make sure the samples will fit inside available RAM + if((maxsize = MD_SampleSpace(type)*1024) != 0) + { while(SampleTotal(samplist, type) > maxsize) + { // First Pass - check for any 16 bit samples + s = samplist; + while(s!=NULL) + { if(s->outfmt & SF_16BITS) + { SL_Sample16to8(s); + break; + } + s = s->next; + } + + // Second pass (if no 16bits found above) is to take the sample + // with the highest speed and dither it by half. + if(s==NULL) + { s = samplist; + speed = 0; + while(s!=NULL) + { if((s->sample->length) && (RealSpeed(s) > speed)) + { speed = RealSpeed(s); + c2smp = s; + } + s = s->next; + } + SL_HalveSample(c2smp); + } + } + } + + + // Samples dithered, now load them! + // ================================ + + s = samplist; + while(s != NULL) + { // sample has to be loaded ? -> increase number of + // samples, allocate memory and load sample. + + if(s->sample->length) + { if(s->sample->seekpos) + _mm_fseek(s->fp, s->sample->seekpos, SEEK_SET); + + // Call the sample load routine of the driver module. + // It has to return a 'handle' (>=0) that identifies + // the sample. + + s->sample->handle = MD_SampleLoad(s, type, s->fp); + s->sample->flags = (s->sample->flags & ~31) | s->outfmt; + if(s->sample->handle < 0) + { FreeSampleList(samplist); + if(_mm_errorhandler!=NULL) _mm_errorhandler(); + return 1; + } + } + s = s->next; + } + + FreeSampleList(samplist); + return 0; +} + + +BOOL SL_LoadSamples(void) // Returns 1 on error! +{ + BOOL ok; + + _mm_critical = 0; + + if((musiclist==NULL) && (sndfxlist==NULL)) return 0; +// MikMod_Exit(); +// exit(1); + ok = DitherSamples(musiclist,MD_MUSIC) || DitherSamples(sndfxlist,MD_SNDFX); + + musiclist = sndfxlist = NULL; + + return ok; +} + + +void SL_Sample16to8(SAMPLOAD *s) +{ + s->outfmt &= ~SF_16BITS; + s->sample->flags = (s->sample->flags&~31) | s->outfmt; +} + + +void SL_Sample8to16(SAMPLOAD *s) +{ + s->outfmt |= SF_16BITS; + s->sample->flags = (s->sample->flags&~31) | s->outfmt; +} + + +void SL_SampleSigned(SAMPLOAD *s) +{ + s->outfmt |= SF_SIGNED; + s->sample->flags = (s->sample->flags&~31) | s->outfmt; +} + + +void SL_SampleUnsigned(SAMPLOAD *s) +{ + s->outfmt &= ~SF_SIGNED; + s->sample->flags = (s->sample->flags&~31) | s->outfmt; +} + + +void SL_HalveSample(SAMPLOAD *s) +{ + if(s->scalefactor) + s->scalefactor++; + else + s->scalefactor = 2; + + s->sample->divfactor = s->scalefactor; + s->sample->length = s->length / s->scalefactor; + s->sample->loopstart = s->loopstart / s->scalefactor; + s->sample->loopend = s->loopend / s->scalefactor; +} + diff -r 42e11dc15457 -r d14fd386d182 playercode/virtch.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/playercode/virtch.c Fri Jan 23 16:05:09 1998 +0000 @@ -0,0 +1,1226 @@ +/* + +Name: VIRTCH.C + +Description: + Sample mixing routines, using a 32 bits mixing buffer. + + Optional features include: + (a) 4-step reverb (for 16 bit output only) + (b) Interpolation of sample data during mixing + (c) Dolby Surround Sound + (d) Optimized assembly mixers for the Intel platform + (e) Optional high-speed or high-quality modes + +C Mixer Portability: + All Systems -- All compilers. + +Assembly Mixer Portability: + + MSDOS: BC(?) Watcom(y) DJGPP(y) + Win95: ? + Os2: ? + Linux: y + + (y) - yes + (n) - no (not possible or not useful) + (?) - may be possible, but not tested + +*/ + +#include +#include +#include "mikmod.h" + +// REVERBERATION : Larger numbers result in shorter reverb duration. +#define REVERBERATION 110000l + + +#ifdef __GNUC__ +#define __cdecl +#endif + +#ifdef __WATCOMC__ +#define inline +#endif + + +// for PC-assembly mixing +// ====================== +// +// Uncomment both lines below for assembly mixing under WATCOM or GCC for +// Linux. +// Note that there is no 16 bit mixers for assembly yet (C only), so +// defining __ASSEMBLY__ if not defining __FASTMIXER__ will lead to compiler +// errors. + +#define __FASTMIXER__ +//#define __ASSEMBLY__ + +#define FRACBITS 11 +#define FRACMASK ((1l<(b))?(a):(b)) +#endif + + +typedef struct +{ BOOL active; + UWORD infmt; + UWORD flags; +#ifdef __FASTMIXER__ + UBYTE *buffer; +#else + UWORD *buffer; +#endif + ULONG size; + ULONG speed; + ULONG speedfactor; + SLONG current; + SLONG increment; + SLONG writepos; +} VSTREAM; + + +typedef struct +{ UBYTE kick; // =1 -> sample has to be restarted + UBYTE active; // =1 -> sample is playing + UWORD flags; // 16/8 bits looping/one-shot + SWORD handle; // identifies the sample + ULONG start; // start index + ULONG size; // samplesize + ULONG reppos; // loop start + ULONG repend; // loop end + ULONG frq; // current frequency + UWORD vol; // current volume + UWORD pan; // current panning position + SLONG current; // current index in the sample + SLONG increment; // fixed-point increment value +} VINFO; + + +#ifdef __FASTMIXER__ +static SBYTE **Samples; +#else +static SWORD **Samples; +#endif + +// Volume table for 8 bit sample mixing +#ifdef __FASTMIXER__ +static SLONG **voltab; +#endif + +static VINFO *vinf = NULL, *vnf; +static VSTREAM vstream; +static ULONG samplesthatfit; +static BOOL vc_stream = 0; +static int vc_memory, vc_softchn; +static SLONG idxsize,idxlpos,idxlend; +static SLONG TICKLEFT, *VC_TICKBUF = NULL; +static UWORD vc_mode; + + +// Reverb control variables +// ======================== + +static int RVc1, RVc2, RVc3, RVc4; +static ULONG RVRindex; + + +// For Mono or Left Channel + +static SLONG *RVbuf1 = NULL, *RVbuf2 = NULL, *RVbuf3 = NULL, + *RVbuf4 = NULL; + +// For Stereo only (Right Channel) +// Values start at 9 to leave room for expanding this to 8-step +// reverb in the future. + +static SLONG *RVbuf9 = NULL, *RVbuf10 = NULL, *RVbuf11 = NULL, + *RVbuf12 = NULL; + + +int bitshift; // Amplification shift (amount to decrease 32 bit mixing buffer by!) + +#ifdef __FASTMIXER__ +SLONG *lvoltab, *rvoltab; // Volume Table values for use by 8 bit mixers +#else +static SLONG lvolsel, rvolsel; // Volume Selectors for 16 bit mixers. +#endif + + + +// Define external Assembly Language Prototypes +// ============================================ + +#ifdef __ASSEMBLY__ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __GNUC__ +#define __cdecl +#endif + +void __cdecl AsmStereoNormal(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,SLONG todo); +void __cdecl AsmStereoSurround(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,SLONG todo); +void __cdecl AsmMonoNormal(SBYTE *srce,SLONG *dest,SLONG index,SLONG increment,SLONG todo); + +#ifdef __cplusplus +}; +#endif + +#else + +#ifdef __FASTMIXER__ + +// ============================================================== +// 8 bit sample mixers! + +static SLONG MixStereoNormal(SBYTE *srce, SLONG *dest, SLONG index, SLONG increment, SLONG todo) +{ + UBYTE sample1, sample2, sample3, sample4; + int remain; + + remain = todo & 3; + + for(todo>>=2; todo; todo--) + { + sample1 = srce[index >> FRACBITS]; + index += increment; + sample2 = srce[index >> FRACBITS]; + index += increment; + sample3 = srce[index >> FRACBITS]; + index += increment; + sample4 = srce[index >> FRACBITS]; + index += increment; + + *dest++ += lvoltab[sample1]; + *dest++ += rvoltab[sample1]; + *dest++ += lvoltab[sample2]; + *dest++ += rvoltab[sample2]; + *dest++ += lvoltab[sample3]; + *dest++ += rvoltab[sample3]; + *dest++ += lvoltab[sample4]; + *dest++ += rvoltab[sample4]; + } + + for(; remain--; ) + { + sample1 = srce[index >> FRACBITS]; + index += increment; + *dest++ += lvoltab[sample1]; + *dest++ += rvoltab[sample1]; + } + + return index; +} + + +static SLONG MixStereoSurround(SBYTE *srce, SLONG *dest, SLONG index, SLONG increment, SLONG todo) +{ + SLONG sample1, sample2, sample3, sample4; + int remain; + + remain = todo & 3; + + for(todo>>=2; todo; todo--) + { + sample1 = lvoltab[(UBYTE)srce[index >> FRACBITS]]; + index += increment; + sample2 = lvoltab[(UBYTE)srce[index >> FRACBITS]]; + index += increment; + sample3 = lvoltab[(UBYTE)srce[index >> FRACBITS]]; + index += increment; + sample4 = lvoltab[(UBYTE)srce[index >> FRACBITS]]; + index += increment; + + *dest++ += sample1; + *dest++ -= sample1; + *dest++ += sample2; + *dest++ -= sample2; + *dest++ += sample3; + *dest++ -= sample3; + *dest++ += sample4; + *dest++ -= sample4; + } + + for(; remain--; ) + { sample1 = lvoltab[(UBYTE)srce[index >> FRACBITS]]; + index += increment; + *dest++ += sample1; + *dest++ -= sample1; + } + + return index; +} + + +static SLONG MixMonoNormal(SBYTE *srce, SLONG *dest, SLONG index, SLONG increment, SLONG todo) +{ + UBYTE sample1, sample2, sample3, sample4; + int remain; + + remain = todo & 3; + + for(todo>>=2; todo; todo--) + { + sample1 = srce[index >> FRACBITS]; + index += increment; + sample2 = srce[index >> FRACBITS]; + index += increment; + sample3 = srce[index >> FRACBITS]; + index += increment; + sample4 = srce[index >> FRACBITS]; + index += increment; + + *dest++ += lvoltab[sample1]; + *dest++ += lvoltab[sample2]; + *dest++ += lvoltab[sample3]; + *dest++ += lvoltab[sample4]; + } + + for(; remain--;) + { sample1 = srce[index >> FRACBITS]; + index += increment; + *dest++ -= lvoltab[sample1]; + } + + return index; +} + +#else // not __FASTMIXER__ + +// ============================================================== +// 16 bit sample mixers! + +static SLONG MixStereoNormal(SWORD *srce, SLONG *dest, SLONG index, SLONG increment, ULONG todo) +{ + SWORD sample; + + for(; todo; todo--) + { + sample = srce[index >> FRACBITS]; + index += increment; + + *dest++ += lvolsel * sample; + *dest++ += rvolsel * sample; + } + + return index; +} + + +static SLONG MixStereoSurround(SWORD *srce, SLONG *dest, SLONG index, SLONG increment, ULONG todo) +{ + SWORD sample; + + for (; todo; todo--) + { + sample = srce[index >> FRACBITS]; + index += increment; + + *dest++ += lvolsel * sample; + *dest++ -= lvolsel * sample; + } + + return index; +} + + +static SLONG MixMonoNormal(SWORD *srce, SLONG *dest, SLONG index, SLONG increment, SLONG todo) +{ + SWORD sample; + + for(; todo; todo--) + { + sample = srce[index >> FRACBITS]; + index += increment; + + *dest++ += lvolsel * sample; + } + + return index; +} + +#endif // __FASTMIXER__ +#endif // __ASSEMBLY__ + +static void (*MixReverb16)(SLONG *srce, SLONG count); + +static void MixReverb16_Normal(SLONG *srce, SLONG count) +{ + unsigned int speedup; + int ReverbPct; + unsigned int loc1, loc2, loc3, loc4; + + ReverbPct = 63 + (md_reverb*4); + + loc1 = RVRindex % RVc1; + loc2 = RVRindex % RVc2; + loc3 = RVRindex % RVc3; + loc4 = RVRindex % RVc4; + + for(; count; count--) + { + // Compute the LEFT CHANNEL echo buffers! + + speedup = *srce >> 3; + + RVbuf1[loc1] = speedup + ((ReverbPct * RVbuf1[loc1]) / 128l); + RVbuf2[loc2] = speedup + ((ReverbPct * RVbuf2[loc2]) / 128l); + RVbuf3[loc3] = speedup + ((ReverbPct * RVbuf3[loc3]) / 128l); + RVbuf4[loc4] = speedup + ((ReverbPct * RVbuf4[loc4]) / 128l); + + // Prepare to compute actual finalized data! + + RVRindex++; + loc1 = RVRindex % RVc1; + loc2 = RVRindex % RVc2; + loc3 = RVRindex % RVc3; + loc4 = RVRindex % RVc4; + + // Left Channel! + + *srce++ += RVbuf1[loc1] - RVbuf2[loc2] + RVbuf3[loc3] - RVbuf4[loc4]; + } +} + + +static void MixReverb16_Stereo(SLONG *srce, SLONG count) +{ + unsigned int speedup; + int ReverbPct; + unsigned int loc1, loc2, loc3, loc4; + + ReverbPct = 63 + (md_reverb*4); + + loc1 = RVRindex % RVc1; + loc2 = RVRindex % RVc2; + loc3 = RVRindex % RVc3; + loc4 = RVRindex % RVc4; + + for(; count; count--) + { + // Compute the LEFT CHANNEL echo buffers! + + speedup = *srce >> 3; + + RVbuf1[loc1] = speedup + ((ReverbPct * RVbuf1[loc1]) / 128l); + RVbuf2[loc2] = speedup + ((ReverbPct * RVbuf2[loc2]) / 128l); + RVbuf3[loc3] = speedup + ((ReverbPct * RVbuf3[loc3]) / 128l); + RVbuf4[loc4] = speedup + ((ReverbPct * RVbuf4[loc4]) / 128l); + + // Compute the RIGHT CHANNEL echo buffers! + + speedup = srce[1] >> 3; + + RVbuf9[loc1] = speedup + ((ReverbPct * RVbuf9[loc1]) / 128l); + RVbuf10[loc2] = speedup + ((ReverbPct * RVbuf11[loc2]) / 128l); + RVbuf11[loc3] = speedup + ((ReverbPct * RVbuf12[loc3]) / 128l); + RVbuf12[loc4] = speedup + ((ReverbPct * RVbuf12[loc4]) / 128l); + + // Prepare to compute actual finalized data! + + RVRindex++; + loc1 = RVRindex % RVc1; + loc2 = RVRindex % RVc2; + loc3 = RVRindex % RVc3; + loc4 = RVRindex % RVc4; + + // Left Channel! + + *srce++ += RVbuf1[loc1] - RVbuf2[loc2] + RVbuf3[loc3] - RVbuf4[loc4]; + + // Right Channel! + + *srce++ += RVbuf9[loc1] - RVbuf10[loc2] + RVbuf11[loc3] - RVbuf12[loc4]; + } +} + + +static void Mix32To16(SWORD *dste, SLONG *srce, SLONG count) +{ + SLONG x1, x2, x3, x4; + int remain; + + remain = count & 3; + + for(count>>=2; count; count--) + { x1 = *srce++ >> bitshift; + x2 = *srce++ >> bitshift; + x3 = *srce++ >> bitshift; + x4 = *srce++ >> bitshift; + x1 = (x1 > 32767) ? 32767 : (x1 < -32768) ? -32768 : x1; + x2 = (x2 > 32767) ? 32767 : (x2 < -32768) ? -32768 : x2; + x3 = (x3 > 32767) ? 32767 : (x3 < -32768) ? -32768 : x3; + x4 = (x4 > 32767) ? 32767 : (x4 < -32768) ? -32768 : x4; + *dste++ = x1; + *dste++ = x2; + *dste++ = x3; + *dste++ = x4; + } + + for(; remain; remain--) + { x1 = *srce++ >> bitshift; + x1 = (x1 > 32767) ? 32767 : (x1 < -32768) ? -32768 : x1; + *dste++ = x1; + } +} + + +static void Mix32To8(SBYTE *dste, SLONG *srce, SLONG count) +{ + int x1, x2, x3, x4; + int remain; + + remain = count & 3; + + for(count>>=2; count; count--) + { x1 = *srce++ >> bitshift; + x2 = *srce++ >> bitshift; + x3 = *srce++ >> bitshift; + x4 = *srce++ >> bitshift; + + x1 = (x1 > 127) ? 127 : (x1 < -128) ? -128 : x1; + x2 = (x2 > 127) ? 127 : (x2 < -128) ? -128 : x2; + x3 = (x3 > 127) ? 127 : (x3 < -128) ? -128 : x3; + x4 = (x4 > 127) ? 127 : (x4 < -128) ? -128 : x4; + + *dste++ = x1 + 128; + *dste++ = x2 + 128; + *dste++ = x3 + 128; + *dste++ = x4 + 128; + } + + for(; remain; remain--) + { x1 = *srce++ >> bitshift; + x1 = (x1 > 127) ? 127 : (x1 < -128) ? -128 : x1; + *dste++ = x1 + 128; + } +} + + +static ULONG samples2bytes(ULONG samples) +{ + if(vc_mode & DMODE_16BITS) samples <<= 1; + if(vc_mode & DMODE_STEREO) samples <<= 1; + return samples; +} + + +static ULONG bytes2samples(ULONG bytes) +{ + if(vc_mode & DMODE_16BITS) bytes >>= 1; + if(vc_mode & DMODE_STEREO) bytes >>= 1; + return bytes; +} + + +static void AddChannel(SLONG *ptr, SLONG todo) +{ + SLONG end, done; +#ifdef __FASTMIXER__ + SBYTE *s; +#else + SWORD *s; +#endif + + while(todo > 0) + { // update the 'current' index so the sample loops, or + // stops playing if it reached the end of the sample + + if(vnf->flags & SF_REVERSE) + { + // The sample is playing in reverse + + if((vnf->flags & SF_LOOP) && (vnf->current < idxlpos)) + { + // the sample is looping, and it has + // reached the loopstart index + + if(vnf->flags & SF_BIDI) + { + // sample is doing bidirectional loops, so 'bounce' + // the current index against the idxlpos + + vnf->current = idxlpos+(idxlpos-vnf->current); + vnf->flags &=~SF_REVERSE; + vnf->increment =-vnf->increment; + } else + // normal backwards looping, so set the + // current position to loopend index + + vnf->current = idxlend-(idxlpos-vnf->current); + } else + { + // the sample is not looping, so check + // if it reached index 0 + + if(vnf->current < 0) + { + // playing index reached 0, so stop + // playing this sample + + vnf->current = 0; + vnf->active = 0; + break; + } + } + } else + { + // The sample is playing forward + + if((vnf->flags & SF_LOOP) && (vnf->current > idxlend)) + { + // the sample is looping, so check if + // it reached the loopend index + + if(vnf->flags & SF_BIDI) + { + // sample is doing bidirectional loops, so 'bounce' + // the current index against the idxlend + + vnf->flags |=SF_REVERSE; + vnf->increment =-vnf->increment; + vnf->current =idxlend-(vnf->current-idxlend); + } else + // normal backwards looping, so set the + // current position to loopend index + + vnf->current = idxlpos + (vnf->current-idxlend); + } else + { + // sample is not looping, so check + // if it reached the last position + + if(vnf->current > idxsize) + { + // yes, so stop playing this sample + + vnf->current = 0; + vnf->active = 0; + break; + } + } + } + + if(!(s=Samples[vnf->handle])) + { vnf->current = 0; + vnf->active = 0; + break; + } + + end = (vnf->flags & SF_REVERSE) ? + (vnf->flags & SF_LOOP) ? idxlpos : 0 : + (vnf->flags & SF_LOOP) ? idxlend : idxsize; + + done = MIN((end - vnf->current) / vnf->increment + 1, todo); + + if(!done) + { vnf->active = 0; + break; + } + + if(vnf->vol) + { +#ifdef __ASSEMBLY__ + if(vc_mode & DMODE_STEREO) + if((vnf->pan == PAN_SURROUND) && (vc_mode & DMODE_SURROUND)) + AsmStereoSurround(s,ptr,vnf->current,vnf->increment,done); + else + AsmStereoNormal(s,ptr,vnf->current,vnf->increment,done); + else + AsmMonoNormal(s,ptr,vnf->current,vnf->increment,done); + vnf->current += (vnf->increment*done); +#else + if(vc_mode & DMODE_STEREO) + if((vnf->pan == PAN_SURROUND) && (vc_mode & DMODE_SURROUND)) + vnf->current = MixStereoSurround(s,ptr,vnf->current,vnf->increment,done); + else + vnf->current = MixStereoNormal(s,ptr,vnf->current,vnf->increment,done); + else + vnf->current = MixMonoNormal(s,ptr,vnf->current,vnf->increment,done); +#endif + } + todo -= done; + ptr += (vc_mode & DMODE_STEREO) ? (done<<1) : done; + } +} + + +void VC_WriteSamples(SBYTE *buf, ULONG todo) +{ + int left, portion = 0, count; + SBYTE *buffer, *samplebuf; + int t; + int pan, vol; + int sampletodo; + + samplebuf = buf; + sampletodo = todo; + + while(todo) + { if(TICKLEFT==0) + { if(vc_mode & DMODE_SOFT_MUSIC) md_player(); + TICKLEFT = (md_mixfreq*125l) / (md_bpm*50L); + } + + left = MIN(TICKLEFT, todo); + + buffer = buf; + TICKLEFT -= left; + todo -= left; + + buf += samples2bytes(left); + + while(left) + { portion = MIN(left, samplesthatfit); + count = (vc_mode & DMODE_STEREO) ? (portion<<1) : portion; + + memset(VC_TICKBUF, 0, count<<2); + + for(t=0; tkick) + { vnf->current = vnf->start << FRACBITS; + vnf->kick = 0; + vnf->active = 1; + } + + if(vnf->frq == 0) vnf->active = 0; + + if(vnf->active) + { vnf->increment = (vnf->frq<flags & SF_REVERSE) vnf->increment=-vnf->increment; + + vol = vnf->vol; pan = vnf->pan; + + if(vc_mode & DMODE_STEREO) + { if(pan != PAN_SURROUND) + { + #ifdef __FASTMIXER__ + lvoltab = voltab[(vol * (255-pan)) / 1024]; + rvoltab = voltab[(vol * pan) / 1024]; + #else + lvolsel = (vol * (255-pan)) >> 8; + rvolsel = (vol * pan) >> 8; + #endif + } else + { + #ifdef __FASTMIXER__ + lvoltab = voltab[(vol+1)>>3]; + #else + lvolsel = vol/2; + #endif + } + } else + { + #ifdef __FASTMIXER__ + lvoltab = voltab[(vol+1)>>2]; + #else + lvolsel = vol; + #endif + } + idxsize = (vnf->size) ? (vnf->size << FRACBITS)-1 : 0; + idxlend = (vnf->repend) ? (vnf->repend << FRACBITS)-1 : 0; + idxlpos = vnf->reppos << FRACBITS; + AddChannel(VC_TICKBUF, portion); + } + } + + if(md_reverb) MixReverb16(VC_TICKBUF, portion); + + if(vc_mode & DMODE_16BITS) + Mix32To16((SWORD *) buffer, VC_TICKBUF, count); + else + Mix32To8((SBYTE *) buffer, VC_TICKBUF, count); + + buffer += samples2bytes(portion); + left -= portion; + } + } +} + + +void VC_SilenceBytes(SBYTE *buf, ULONG todo) + +// Fill the buffer with 'todo' bytes of silence (it depends on the mixing +// mode how the buffer is filled) + +{ + // clear the buffer to zero (16 bits + // signed ) or 0x80 (8 bits unsigned) + + if(vc_mode & DMODE_16BITS) + memset(buf,0,todo); + else + memset(buf,0x80,todo); +} + + +ULONG VC_WriteBytes(SBYTE *buf, ULONG todo) + +// Writes 'todo' mixed SBYTES (!!) to 'buf'. It returns the number of +// SBYTES actually written to 'buf' (which is rounded to number of samples +// that fit into 'todo' bytes). + +{ + if(vc_softchn == 0) + { VC_SilenceBytes(buf,todo); + return todo; + } + + todo = bytes2samples(todo); + VC_WriteSamples(buf,todo); + + return samples2bytes(todo); +} + + +static UBYTE log2(ULONG x) +{ + UBYTE result = 0; + while (x>>=1) result++; + + return result; +} + + +BOOL VC_PlayStart(void) +{ + int t, numchn; + + numchn = md_softchn; + if(vc_stream) numchn++; + + if(numchn > 0) + { +#ifdef __FASTMIXER__ + int c; + SLONG maxvol, volmul; + + if(vc_stream) numchn++; + maxvol = 16777216L / (numchn+6); + + for(t=0; t<65; t++) + { volmul = (maxvol*t) / 64; + for(c=-128; c<128; c++) + voltab[t][(UBYTE)c] = (SLONG)c*volmul; + } + + bitshift = 16 - log2(numchn); +#else + bitshift = (log2(numchn)>>3) + 7; + +#endif + + if (!(vc_mode & DMODE_16BITS)) + bitshift += 8; + } + + samplesthatfit = TICKLSIZE; + if(vc_mode & DMODE_STEREO) samplesthatfit >>= 1; + TICKLEFT = 0; + + + RVc1 = (5000L * md_mixfreq) / REVERBERATION; + RVc2 = (5078L * md_mixfreq) / REVERBERATION; + RVc3 = (5313L * md_mixfreq) / REVERBERATION; + RVc4 = (5703L * md_mixfreq) / REVERBERATION; + + if((RVbuf1 = (SLONG *)_mm_calloc((RVc1+1),sizeof(SLONG))) == NULL) return 1; + if((RVbuf2 = (SLONG *)_mm_calloc((RVc2+1),sizeof(SLONG))) == NULL) return 1; + if((RVbuf3 = (SLONG *)_mm_calloc((RVc3+1),sizeof(SLONG))) == NULL) return 1; + if((RVbuf4 = (SLONG *)_mm_calloc((RVc4+1),sizeof(SLONG))) == NULL) return 1; + + if(vc_mode & DMODE_STEREO) + { if((RVbuf9 = (SLONG *)_mm_calloc((RVc1+1),sizeof(SLONG))) == NULL) return 1; + if((RVbuf10 = (SLONG *)_mm_calloc((RVc2+1),sizeof(SLONG))) == NULL) return 1; + if((RVbuf11 = (SLONG *)_mm_calloc((RVc3+1),sizeof(SLONG))) == NULL) return 1; + if((RVbuf12 = (SLONG *)_mm_calloc((RVc4+1),sizeof(SLONG))) == NULL) return 1; + } + + RVRindex = 0; + + return 0; +} + + +void VC_PlayStop(void) +{ + if(RVbuf1 != NULL) free(RVbuf1); + if(RVbuf2 != NULL) free(RVbuf2); + if(RVbuf3 != NULL) free(RVbuf3); + if(RVbuf4 != NULL) free(RVbuf4); + if(RVbuf9 != NULL) free(RVbuf9); + if(RVbuf10 != NULL) free(RVbuf10); + if(RVbuf11 != NULL) free(RVbuf11); + if(RVbuf12 != NULL) free(RVbuf12); + + RVbuf1 = NULL; RVbuf2 = NULL; RVbuf3 = NULL; RVbuf4 = NULL; + RVbuf9 = NULL; RVbuf10 = NULL; RVbuf11 = NULL; RVbuf12 = NULL; +} + + +BOOL VC_Init(void) +{ + +#ifdef __FASTMIXER__ + int t; + + _mm_errno = MMERR_INITIALIZING_MIXER; + if((voltab = (SLONG **)calloc(65,sizeof(SLONG *))) == NULL) return 1; + for(t=0; t<65; t++) + if((voltab[t] = (SLONG *)calloc(256,sizeof(SLONG))) == NULL) return 1; + + if((Samples = (SBYTE **)calloc(MAXSAMPLEHANDLES, sizeof(SBYTE *))) == NULL) return 1; +#else + _mm_errno = MMERR_INITIALIZING_MIXER; + if((Samples = (SWORD **)calloc(MAXSAMPLEHANDLES, sizeof(SWORD *))) == NULL) return 1; +#endif + + if(VC_TICKBUF==NULL) if((VC_TICKBUF=(SLONG *)malloc((TICKLSIZE+32) * sizeof(SLONG))) == NULL) return 1; + if(md_mode & DMODE_INTERP) md_mode &= ~DMODE_INTERP; + + MixReverb16 = (md_mode & DMODE_STEREO) ? MixReverb16_Stereo : MixReverb16_Normal; + vc_mode = md_mode; + + + _mm_errno = 0; + return 0; +} + + +void VC_Exit(void) +{ +#ifdef __FASTMIXER__ + int t; + if(voltab!=NULL) + { for(t=0; t<65; t++) if(voltab[t]!=NULL) free(voltab[t]); + free(voltab); voltab = NULL; + } +#endif + + //if(VC_TICKBUF!=NULL) free(VC_TICKBUF); + if(vinf!=NULL) free(vinf); + if(Samples!=NULL) free(Samples); + + //VC_TICKBUF = NULL; + vinf = NULL; + Samples = NULL; +} + + +BOOL VC_SetNumVoices(void) +{ + int t; + + if((vc_softchn = md_softchn) == 0) return 0; + + if(vinf!=NULL) free(vinf); + if((vinf = _mm_calloc(sizeof(VINFO),vc_softchn)) == NULL) return 1; + + for(t=0; t>FRACBITS); +} + + +/************************************************** +*************************************************** +*************************************************** +**************************************************/ + + +void VC_SampleUnload(SWORD handle) +{ + void *sampleadr = Samples[handle]; + + free(sampleadr); + Samples[handle] = NULL; +} + + +SWORD VC_SampleLoad(SAMPLOAD *sload, int type, FILE *fp) +{ + SAMPLE *s = sload->sample; + int handle; + ULONG t, length,loopstart,loopend; + + if(type==MD_HARDWARE) return -1; + + // Find empty slot to put sample address in + for(handle=0; handlelength; + loopstart = s->loopstart; + loopend = s->loopend; + + SL_SampleSigned(sload); + +#ifdef __FASTMIXER__ + SL_Sample16to8(sload); + if((Samples[handle]=(SBYTE *)malloc(length+16))==NULL) + { _mm_errno = MMERR_SAMPLE_TOO_BIG; + return -1; + } + // read sample into buffer. + SL_Load(Samples[handle],sload,length); +#else + SL_Sample8to16(sload); + if((Samples[handle]=(SWORD *)malloc((length+16)<<1))==NULL) + { _mm_errno = MMERR_SAMPLE_TOO_BIG; + return -1; + } + // read sample into buffer. + SL_Load(Samples[handle],sload,length); +#endif + + + // Unclick samples: + + if(s->flags & SF_LOOP) + { if(s->flags & SF_BIDI) + for(t=0; t<16; t++) Samples[handle][loopend+t] = Samples[handle][(loopend-t)-1]; + else + for(t=0; t<16; t++) Samples[handle][loopend+t] = Samples[handle][t+loopstart]; + } else + for(t=0; t<16; t++) Samples[handle][t+length] = 0; + + return handle; +} + + +ULONG VC_SampleSpace(int type) +{ + return vc_memory; +} + + +ULONG VC_SampleLength(int type, SAMPLE *s) +{ +#ifdef __FASTMIXER__ + return s->length + 16; +#else + return (s->length * ((s->flags&SF_16BITS) ? 2 : 1)) + 16; +#endif +} + + +/************************************************** +*************************************************** +*************************************************** +**************************************************/ + + +ULONG VC_VoiceRealVolume(UBYTE voice) +{ + ULONG i,s,size; + int k,j; +#ifdef __FASTMIXER__ + SBYTE *smp; +#else + SWORD *smp; +#endif + SLONG t; + + t = vinf[voice].current>>FRACBITS; + if(vinf[voice].active==0) return 0; + + s = vinf[voice].handle; + size = vinf[voice].size; + + i=64; t-=64; k=0; j=0; + if(i>size) i = size; + if(t<0) t = 0; + if(t+i > size) t = size-i; + + i &= ~1; // make sure it's EVEN. + + smp = &Samples[s][t]; + for(; i; i--, smp++) + { if(k<*smp) k = *smp; + if(j>*smp) j = *smp; + } + +#ifdef __FASTMIXER__ + k = abs(k-j)<<8; +#else + k = abs(k-j); +#endif + + return k; +} + + +BOOL VC_StreamInit(ULONG speed, UWORD flags) + +// flags - Disk Format - SF_STEREO, SF_16BITS, etc. +// speed - speed at which to replay sample +// +// Returns - TRUE if init failed + +{ + ULONG tmp; + +#ifdef __FASTMIXER__ + tmp = stream_bufsize * speed * (((flags & SF_STEREO) && (vc_mode & DMODE_STEREO)) ? 2 : 1); +#else + tmp = stream_bufsize * speed * (((flags & SF_STEREO) && (vc_mode & DMODE_STEREO)) ? 2 : 1) + * ((flags & SF_16BITS) && (vc_mode & DMODE_16BITS)) ? 2 : 1; +#endif + if((flags & SF_STEREO) && (vc_mode & DMODE_STEREO)) tmp <<= 1; + + vstream.size = tmp; + if((vstream.buffer=_mm_calloc(vstream.size,1024)) == NULL) return 1; + + vstream.speed = speed; + vstream.speedfactor = (md_mixfreq / speed); + if(!((vstream.speedfactor==2) || (vstream.speedfactor==4))) + vstream.speedfactor = 1; + + vstream.infmt = flags; + vstream.flags = flags; +#ifdef __FASTMIXER__ + vstream.flags = flags &= ~SF_16BITS; +#else + vstream.flags = flags |= SF_16BITS; +#endif + if(!(vc_mode&DMODE_STEREO)) vstream.flags &= ~SF_STEREO; + + vstream.active = 0; + vstream.current = 0; + vstream.increment = 0; + + vc_stream = 1; + VC_PlayStart(); + + return 0; +} + + +void VC_StreamExit(void) +{ + vstream.active = 0; + if(vstream.buffer != NULL) free(vstream.buffer); + vstream.buffer = NULL; + vc_stream = 0; + VC_PlayStart(); +} + + +void VC_StreamSetSpeed(ULONG speed) +{ + vstream.speed = speed; + vstream.speedfactor = (md_mixfreq/speed); + if(!((vstream.speedfactor==2) || (vstream.speedfactor==4))) + vstream.speedfactor = 1; +} + + +SLONG VC_StreamGetPosition(void) +{ + return(vstream.current >> FRACBITS); +} + + +void VC_StreamStart(void) +{ + if(vstream.buffer!=NULL) vstream.active = 1; +} + + +void VC_StreamStop(void) +{ + vstream.active = 0; +} + + +void VC_StreamCommit(void *sample, ULONG size) + +// Read 'size' bytes from the specified buffer and commit them to +// the streaming audio buffer. + +{ + //ULONG last, curr; + //ULONG todo; + + if(vstream.buffer==NULL) return; + +} +