www.pudn.com > vmix_1.013.zip > WAVE.C


;   /*\ 
;---|*|----====< Play >====---- 
;---|*| 
;---|*| play small a list of wave files 
;---|*| 
;---|*| Copyright (c) 1993,1994  V.E.S.A, Inc. All Rights Reserved. 
;---|*| 
;---|*| VBE/AI 1.0 Specification 
;---|*|    April 6, 1994. 1.00 release 
;---|*| 
;---|*| ******************************************************************* 
;---|*| This program is modified by Joel Chen to do mixing waves output. 
;---|*| You are free to use it anyway you like.  Giving me some credit 
;---|*| would be nice if you use it. 
;---|*| #include  
;---|*| Anything that can possibly happen is not my fault, including 
;---|*| events such as your red hot CPU suddenly becomes a mini-bomb and 
;---|*| blows up your entire house/building/apt/whatever. 
;---|*| The vbe/ai sdk can be obtained from the game programming site 
;---|*| x2ftp.oulu.fi: /pub/msdos/programming/mxlibs/vaisdk.zip 
;---|*|                                                     joel... 
;---|*|                                                     Jan 17, 1996 
Note: 
This program and the original play.exe from the vbe/ai sdk failed to play 
waves correctly on my friend's computer with a Thunder Board.  It looks 
like that the dma is not autoinit and no interrupt is generated.  Strangely, 
I remember that it used to work. 
;   \*/ 
 
#include  
#include  
#include  
#include  
#include  
#include  
 
#include "vbeai.h" 
#include "vesa.h" 
#include "wave.h" 
 
 
;   /*\ 
;---|*| Public Global Variables 
;   \*/ 
    PUBLIC int SoundPlaying = 0; 
 
;   /*\ 
;---|*|----------------------=======================--------------------------- 
;---|*|----------------------====< Private stuff >====------------------------- 
;---|*|----------------------=======================--------------------------- 
;   \*/ 
;   /*\ 
;---|*| Private Local Variables 
;   \*/ 
	#include "wavetype.h" 
 
	PRIVATE int  WAVEPoweredUp = 0; 
#if VERBOSE_MODE 
	PRIVATE int  WaveCallBackOccured  = 0; 
#endif 
    PRIVATE int  InWaveCallBack  = 0; 
	PRIVATE int  WaveErrorCallBackOccured = 0; 
 
	PRIVATE VESAHANDLE  hWAVE   = 0; 
	PRIVATE fpWAVServ wfn;          // pointer to wave functions 
 
	PRIVATE char far *DMAptr = 0; 
	PRIVATE char far *dmaptr = 0; 
	PRIVATE char far *WAVmemptr = 0; 
	PRIVATE int  MixBuf[MIX_LEN]; 
    PRIVATE int  SilentBuf[MIX_LEN]; 
	PRIVATE int  TotalSounds = -1; 
	PRIVATE int  MySoundPlaying = 0; 
	PRIVATE int  SoundDone = 0; 
	PRIVATE Sound MixSnds[MIX_SNDS]; 
 
#define UseQueue 0 
 
#if UseQueue 
	typedef struct { 
		int I; 
		int rep; 
		wave *Wav; 
	} SoundQueue; 
	PRIVATE SoundQueue MixQueue[MIX_SNDS]; 
	PRIVATE int  QueueIdx = 0; 
#endif 
 
 
;   /*\ 
;---|*| Private prototypes 
;   \*/ 
 
	void far pascal OurWAVECallback  ( int, void far *, long, long ); 
	void far pascal ErrorWAVECallback( int, void far *, long, long ); 
 
	PRIVATE int PASCAL MixSounds( void ); 
	PRIVATE int PASCAL InitSound( void ); 
	PRIVATE VESAHANDLE PASCAL WAVEOpenTheDriver     ( int ); 
	PRIVATE int PASCAL ProcessWAVHeader         ( int, wave *); 
	PRIVATE int PASCAL WAVEDriverError          ( void ); 
 
#if VERBOSE_MODE 
	PRIVATE void PASCAL ReportWave              ( void ); 
#endif 
 
    // only allows us to run on this version of the VBE interface. This 
    // will be removed after the interface is ratified. Changes may be made 
    // that would cause this code to crash if run on other version. Again, 
    // this will be removed in the final software. 
 
        PRIVATE int  WAVEVersionControl = 0x0100; 
 
;   /*\ 
;---|*|------------------==============================------------------- 
;---|*|------------------====< Public Subroutines >====------------------- 
;---|*|------------------==============================------------------- 
;   \*/ 
 
#define TEST_WAVE 0 
 
#if TEST_WAVE 
void DoExit( void ) 
{ 
#if VERBOSE_MODE 
    printf("Shuting down the wave driver.\n"); 
#endif 
    ShutDownWAVE(); 
} 
 
main( int argc, char **argv ) 
{ 
    int i, n, quit = 0; 
    wave Waves[10]; 
 
    if ( argc < 2 ) { 
        printf( "usage: wave list of wave files.\n"); 
        return 0; 
    } 
 
    atexit(DoExit); 
    if ( PowerUpWAVE() == -1 ) exit(-1); 
    argc --; 
	for( n = 0; n < argc; n ++ ) { 
// 
// Very important!  The next line tells LoadWave don't try to free 
// the memory pointed to by wave.ptr because there isn't any yet. 
// 
		Waves[n].ptr = 0; 
 
        if ( LoadWave(argv[n+1], &Waves[n]) == 0 ) { 
            printf("Loading wave %s failed.\n", argv[n+1]); 
            exit(-2); 
        } 
	} 
    // Play all sounds using parameters from this wave 
    SetToWaveParam( &Waves[0] ); 
    printf( "\nPress a number will play the corresponding wave.\n"); 
    while(!quit) { 
    // Keeps the WAVE driver alive.  Specifically, stop playing if 
    // SoundDone is true.  Somehow the program crashes if I try to 
    // do it in the timer call back function.  Probably has something 
    // to do with some registers.  If you fix it and get it to work, 
    // make sure you send me the changes. :) 
        UpdateWAVE(); 
 
        if ( kbhit()) { 
            switch(i = getch()) { 
                case 27: 
                    quit = 1; 
                    break; 
                case 'r': 
                case 'R': 
                // Remove all the sounds playing 
                    for( i = 0; i < MIX_SNDS; i ++ ) { 
                        RemoveSound(i); 
                    } 
                    break; 
                default: 
                    if ( isdigit(i)) { 
                        i -= '0'; 
                        if ( i < n ) { 
                            if ( AddSound( &Waves[i], 0 ) >= 0 ) 
                                printf("Playing %s.\n", argv[i+1]); 
                        } 
                    } 
                    break; 
            } 
        } 
    } 
    exit(0); 
} 
#endif 
 
PUBLIC int PASCAL PowerUpWAVE( void ) 
{ 
    if ( WAVEPoweredUp ) return 0; 
    if ( !WAVEOpenTheDriver(0)) { 
        return -1; 
    } 
    WAVEPoweredUp = 1; 
    return 1; 
} 
 
; 
;   /*\ 
;---|*|----====< ShutDownWAVE >====---- 
;---|*| 
;---|*| Shut Wave player down 
;---|*| 
;   \*/ 
PUBLIC void PASCAL ShutDownWAVE( void ) 
{ 
// close the device if already opened 
    if (wfn) { 
        VESACloseDevice(hWAVE);     // close the device 
        wfn = 0; 
    } 
// Free allocated memory 
    if ( dmaptr ) { 
        DelBuf(dmaptr); 
        dmaptr = 0; 
    } 
 
    if ( WAVmemptr ) { 
        DelBuf(WAVmemptr); 
        WAVmemptr = 0; 
    } 
} 
 
 
PUBLIC int PASCAL  SetToWaveParam( wave *Wav ) 
{ 
	if ( !WAVEPoweredUp ) return 0; 
	// set the sample rate, etc. 
	(wfn->wsPCMInfo) ( Wav->ch, Wav->sr, Wav->cp, 0, Wav->sz ); 
	return 1; 
} 
 
 
PUBLIC int PASCAL  LoadWave( char *WAVFileName, wave *Wav ) 
{ 
    int fhan, siz; 
    long i; 
    char far *ptr; 
 
	if ( !WAVEPoweredUp ) return 0; 
 
    Wav->fname = WAVFileName; 
    if ((fhan = open(WAVFileName, O_BINARY|O_RDONLY)) == -1) { 
        perror("open wave file"); 
        return 0; 
    } 
    lseek( fhan, 0, SEEK_SET ); 
    if ( ProcessWAVHeader(fhan, Wav) != VALIDWAVE ) { 
        printf("Invalid .WAV file: %s.\n", WAVFileName ); 
        return 0; 
	} 
    if ( Wav->ptr ) DelBuf( Wav->ptr ); 
    Wav->ptr = NewBuf(Wav->len); 
    if ( Wav->ptr == 0 ) { 
        printf( "Ran out of memory loading wave.\n"); 
        return 0; 
    } 
	siz = readblock( fhan, Wav->ptr, (int)Wav->len ); 
    close(fhan); 
 
// convert to sign format 
    ptr = Wav->ptr; 
    for( i = 0; i < Wav->len; i ++ ) { 
		*ptr = (char)((byte)*ptr) - 128; 
        ptr ++; 
    } 
 
	return siz; 
} 
 
 
PUBLIC int PASCAL AddSound( wave *wav, int rep ) 
{ 
    int I; 
 
    if ( TotalSounds >= MIX_SNDS ) return -1; 
	if ( TotalSounds < 0 ) InitSound(); 
 
	for( I = 0; I < MIX_SNDS; I ++ ) { 
		if ( MixSnds[I].valid == 0 ) break; 
	} 
	if ( MixSnds[I].valid ) return -1;  // sanity 
	TotalSounds ++; 
#if VERBOSE_MODE 
    printf("TotalSounds: %d\n", TotalSounds ); 
#endif 
 
#if UseQueue 
	if ( !MySoundPlaying ) { 
//        printf("First sound.\n"); 
#endif 
 
		MixSnds[I].ptr = wav->ptr; 
		MixSnds[I].valid = 1; 
		MixSnds[I].repeat = rep; 
		MixSnds[I].len = wav->len; 
		MixSnds[I].pos = 0; 
#if !UseQueue 
	if ( !MySoundPlaying ) { 
    #if VERBOSE_MODE 
        printf("First sound.\n"); 
    #endif 
#endif  /* !UseQueue */ 
 
		SoundDone = 0; 
		MySoundPlaying = SoundPlaying = 1; 
		// Start playing 
		DMAptr = dmaptr; 
		MixSounds();    // Mix into first half of the buffer 
		DMAptr += MIX_LEN; 
		MixSounds();    // Mix into second half of the buffer 
		(wfn->wsPlayCont)( dmaptr, (MIX_LEN*2), MIX_LEN ); 
	} 
 
#if UseQueue 
	 else { 
		MixSnds[I].valid = 2; 
		MixQueue[QueueIdx].Wav = wav; 
		MixQueue[QueueIdx].I = I; 
		MixQueue[QueueIdx].rep = rep; 
		QueueIdx ++; 
	} 
#endif 
 
	return I; 
} 
 
 
PUBLIC int PASCAL RemoveSound( int which ) 
{ 
    if ( which >= 0 && which < MIX_SNDS ) { 
        if ( MixSnds[which].valid ) { 
            MixSnds[which].valid = 0; 
            TotalSounds --; 
            if ( TotalSounds == 0 ) { 
                (wfn->wsStopIO)(0); 
                MySoundPlaying = SoundPlaying = 0; 
            } 
        } 
    } else 
        return 0; 
    return 1; 
} 
 
 
PUBLIC void PASCAL UpdateWAVE( void ) 
{ 
#if VERBOSE_MODE 
    if ( WaveCallBackOccured ) { 
        WaveCallBackOccured --; 
		ReportWave(); 
	} 
#endif 
	if ( SoundDone ) { 
		(wfn->wsStopIO)(0); 
		SoundDone = 0; 
	} 
} 
 
;   /*\ 
;---|*|----------------------=======================---------------------- 
;---|*|----------------------====< Private Subroutines >====-------------- 
;---|*|----------------------=======================---------------------- 
;   \*/ 
; 
;   /*\ 
;---|*|----====< WAVEDriverError >====---- 
;---|*| 
;---|*| Report any errors by the driver 
;---|*| 
;   \*/ 
PRIVATE int PASCAL WAVEDriverError( void ) 
{ 
    int err; 
    if ( (err = (wfn->wsGetLastError)()) ) { 
        printf ("Driver reported an error! (code=%02X)\n",err); 
        return (err); 
    } 
    return(0); 
} 
 
; 
;   /*\ 
;---|*|----====< ErrorWAVECallback >====---- 
;---|*| 
;---|*| If we get this, we received the wrong callback! 
;---|*| 
;   \*/ 
 
void far pascal ErrorWAVECallback( han, fptr, len, filler ) 
    int han;        // device handle 
    void far *fptr; // buffer that just played 
    long len;       // length of completed transfer 
    long filler;    // reserved... 
{ 
    // setup our data segment 
        _asm { 
            push    ds 
			mov     ax,seg WaveErrorCallBackOccured 
			mov     ds,ax 
			inc     [WaveErrorCallBackOccured] 
            pop     ds 
        } 
} 
 
; 
;   /*\ 
;---|*|----====< WAVEOpenTheDriver >====---- 
;---|*| 
;---|*| Find the driver with the highest user preference, and return it to 
;---|*| the caller 
;---|*| 
;   \*/ 
PRIVATE int PASCAL WAVEOpenTheDriver(pref) 
    int pref; 
{ 
	long l; 
	int try = 0; 
	int driverpref = 256;   // real low preference 
	GeneralDeviceClass gdc; // receives a copy of the VESA driver info block 
 
    // find a matching driver 
        do { 
            try ++; 
            // Find one DAC device, else bail if non found 
                if ((hWAVE = VESAFindADevice(WAVDEVICE)) == 0) continue; 
            // get the device information 
                if (VESAQueryDevice(hWAVE, VESAQUERY2 ,&gdc) == 0) { 
                    printf ("Cannot query the installed VAI devices!\n"); 
                    return 0; 
                } 
            // make sure its a wave device 
                if (gdc.gdclassid != WAVDEVICE) { 
                    printf ("The VESA find device query returned a NON DAC device!\n"); 
                    return 0; 
                } 
            // make sure it's matches the beta version # 
                if (gdc.gdvbever != WAVEVersionControl) { 
                    printf ("The VESA device version # does not match, cannot continue!\n"); 
                    return 0; 
                } 
            // get the drivers user preference level 
				driverpref = gdc.u.gdwi.widevpref; 
			// if the caller is not expressing a preference, then use this one 
                if (pref == -1) break; 
        } while (driverpref != pref && try < 4); 
 
        if ( hWAVE == 0 ) { 
            printf ("Cannot find any installed VBE/AI WAVE devices!\n"); 
            return(0); 
        } 
    // get the memory needed by the device 
        if (!(WAVmemptr = NewBuf(gdc.u.gdwi.wimemreq))) { 
			printf ("We don't have memory for the wave device!\n"); 
            return (0); 
		} 
	// if the DAC device doesn't open, bomb out... 
		if ((wfn = (fpWAVServ)VESAOpenADevice(hWAVE,0,WAVmemptr)) == 0) { 
            printf ("Cannot Open the installed wave devices!\n"); 
            return (0); 
        } 
    // setup the record and playback callbacks 
		wfn->wsApplPSyncCB = &OurWAVECallback; 
		wfn->wsApplRSyncCB = &ErrorWAVECallback; 
    // return the handle 
        return(hWAVE); 
} 
 
 
PRIVATE int PASCAL MixSounds( void ) 
{ 
    register int I; 
    long J; 
    int *MixPtr; 
    char far *DataPtr; 
 
//    for( I = 0; I < MIX_LEN; I ++ ) MixBuf[I] = 128; 
	memcpy( (void*)MixBuf, (const void*)SilentBuf, MIX_LEN*sizeof(int) ); 
    for( I = 0; I < MIX_SNDS; I ++ ) { 
		if ( MixSnds[I].valid == 1 ) { 
            DataPtr = MixSnds[I].ptr + MixSnds[I].pos; 
            if ( MixSnds[I].len - MixSnds[I].pos <= MIX_LEN ) { 
                MixPtr = MixBuf; 
                J = MixSnds[I].pos; 
                for( ; J != MixSnds[I].len; J ++ ) { 
					*MixPtr++ += (char)*DataPtr++; 
                } 
                // repeat here. 
                if ( MixSnds[I].repeat ) { 
                    MixSnds[I].pos = 0; 
                } else { 
                    MixSnds[I].valid = 0; 
                    TotalSounds --; 
				} 
			} else { // Play a chunk 
				for( J = 0; J < MIX_LEN; J ++ ) { 
					MixBuf[J] += (char)*DataPtr++; 
				} 
				MixSnds[I].pos += MIX_LEN; 
			} 
		} 
	} 
    // clip after all the samples are collected from each sound in the list 
	for( I = 0; I < MIX_LEN; I ++ ) { 
		if (MixBuf[I] < 0) (byte)DMAptr[I] = 0; 
		else if ( MixBuf[I] > 255 ) (byte)DMAptr[I] = 255; 
		else (byte)DMAptr[I] = (byte)MixBuf[I]; 
    } 
 
#if UseQueue 
	while( QueueIdx ) { 
		wave *wav = MixQueue[--QueueIdx].Wav; 
		I = MixQueue[QueueIdx].I; 
 
		MixSnds[I].ptr = wav->ptr; 
		MixSnds[I].valid = 1; 
		MixSnds[I].repeat = MixQueue[QueueIdx].rep; 
		MixSnds[I].len = wav->len; 
		MixSnds[I].pos = 0; 
	} 
#endif 
 
    return 1; 
} 
 
; 
;   /*\ 
;---|*|----====< MakeZeroOffset >====---- 
;---|*| 
;---|*| Make the buffer address linear, with a zero offset, 
;---|*| not crossing a 64k boundary! (whew!). The true address pointer must 
;---|*| be pointing to a buffer of the length of len*2+16 
;---|*| 
;   \*/ 
char huge *MakeZeroOffset (addr,len) 
    char huge *addr; 
    long len; 
{ 
    long l; 
 
        _asm { 
            mov     ax,word ptr [addr+0] 
            mov     dx,word ptr [addr+2] 
 
            mov     bx,0xFFF0               // get the linear address 
            rol     dx,4 
            and     bx,dx 
            xor     dx,bx 
            add     ax,bx 
            adc     dx,0 
 
            and     ax,0xFFF0               // go to the next 16 byte boundary 
            add     ax,0x0010 
 
            mov     bx,dx                   // check to see if we wrap a 
            mov     cx,ax                   // 64k boundary 
            add     cx,word ptr [len+0] 
            adc     bx,word ptr [len+2]     // bx = dx, or bx = dx + 1 
 
            sub     bx,dx                   // bx = 0x0000 if same, else 0x0001 
            neg     bx                      // bx = 0x0000 if same, else 0xFFFF 
            sub     dx,bx                   // add 1 if it wrapped 
            not     bx                      // bx = 0xFFFF if same, else 0x0000 
            and     ax,bx                   // flush if wrapped, else save 
 
            ror     dx,4                    // make a seg:off out of the linear 
            add     dx,ax                   // address 
            sub     ax,ax 
        } 
} 
 
PRIVATE int PASCAL InitSound( void ) 
{ 
    int I; 
 
    if ( TotalSounds < 0 ) { 
#if VERBOSE_MODE 
        printf("Init sounds.\n"); 
#endif 
		TotalSounds = 0; 
		for( I = 0; I < MIX_LEN; I ++ ) SilentBuf[I] = 128; 
		for( I = 0; I < MIX_SNDS; I ++ ) { 
            MixSnds[I].ptr = 0; 
            MixSnds[I].valid = 0; 
            MixSnds[I].repeat = 0; 
            MixSnds[I].len = MixSnds[I].pos = 0; 
        } 
        if ((dmaptr = NewBuf(MIX_LEN*2+16)) == 0) { 
            printf ("Unable to allocate DMA buffer memory!\n"); 
            return 0; 
        } 
        dmaptr = MakeZeroOffset(dmaptr,MIX_LEN*2); 
        DMAptr = dmaptr; 
        return 1; 
    } else 
        return 0; 
} 
 
 
;   /*\ 
;---|*|----====< OurWAVECallback >====---- 
;---|*| 
;---|*| Block End callback. NOTE: No assumptions can be made about 
;---|*| the segment registers! (DS,ES,GS,FS) 
;---|*| 
;   \*/ 
void far pascal OurWAVECallback( han, fptr, len, filler ) 
    VESAHANDLE han; // device handle 
    void far *fptr; // buffer that just played 
    long len;       // length of completed transfer 
    long filler;    // reserved... 
{ 
 
    // setup our data segment 
        _asm { 
			push    ds 
            mov     ax,seg InWaveCallBack 
            mov     ds,ax 
        } 
 
		if ( InWaveCallBack == 0 ) { 
#if VERBOSE_MODE 
			_asm inc    [WaveCallBackOccured]; 
#endif 
			_asm inc    [InWaveCallBack]; 
 
			if ( TotalSounds == 0 ) { 
				SoundDone = 1; 
				MySoundPlaying = SoundPlaying = 0; 
			} else { 
				if ( DMAptr != dmaptr ) DMAptr = dmaptr; 
				else DMAptr = dmaptr + MIX_LEN; 
				MixSounds(); 
			} 
			_asm dec    [InWaveCallBack]; 
		} 
		_asm pop     ds; 
} 
 
; 
;   /*\ 
;---|*|----====< ProcessWAVHeader >====---- 
;---|*| 
;---|*| load the header from our WAV file format 
;---|*| 
;   \*/ 
PRIVATE int PASCAL ProcessWAVHeader(int fh,wave *wav) 
{ 
    int n; 
    RiffWave rw; 
 
    // eat the RIFF portion of the header 
		readblock ( fh, (void far *)&rw, sizeof (RiffWave) ); 
	// make sure its says RIFF 
		n  = rw.riff.name[0] - 'R'; 
		n += rw.riff.name[1] - 'I'; 
		n += rw.riff.name[2] - 'F'; 
		n += rw.riff.name[3] - 'F'; 
		if (n)  return(UNKNOWNFILETYPE); 
	// make sure it says WAVE 
		n  = rw.wave.name[0] - 'W'; 
		n += rw.wave.name[1] - 'A'; 
		n += rw.wave.name[2] - 'V'; 
		n += rw.wave.name[3] - 'E'; 
		if (n)  return(UNKNOWNFILETYPE); 
	// make sure it says 'fmt ' 
		n  = rw.wave.fmt.name[0] - 'f'; 
		n  = rw.wave.fmt.name[1] - 'm'; 
		n  = rw.wave.fmt.name[2] - 't'; 
		n  = rw.wave.fmt.name[3] - ' '; 
		if (n)  return(UNKNOWNFILETYPE); 
		wav->ch    = rw.wave.fmt.info.nChannels; 
		wav->sr    = rw.wave.fmt.info.nSamplesPerSec; 
		wav->sz    = rw.wave.fmt.info.nBitsPerSample; 
		wav->cp    = 0; 
	// make sure it says 'data' 
		n  = rw.wave.data.name[0] - 'd'; 
		n  = rw.wave.data.name[1] - 'a'; 
		n  = rw.wave.data.name[2] - 't'; 
		n  = rw.wave.data.name[3] - 'a'; 
		if (n)  return(UNKNOWNFILETYPE); 
		wav->len   = rw.wave.data.length; 
	// return OKAY 
		return(VALIDWAVE); 
} 
 
#if VERBOSE_MODE 
PRIVATE void PASCAL ReportWave( void ) 
{ 
    register char far *vidptr = (char far *)0xb8000140; 
    char sstr[80],*s; 
    static int cbcnt = 0; 
    // create the string 
        sprintf (sstr,"%5d %5d",cbcnt++, WaveCallBackOccured ); 
    // blast it out... 
        s = sstr; 
        while (*s) { 
            *vidptr++ = *s++; 
            *vidptr++ = 0x3F; 
        } 
} 
#endif 
 
;   /*\ 
;---|*| end of WAVE.C 
;   \*/