/***************************************************************************
*   NAME:  WAVEFIX.C $Revision: 1.28 $
**  COPYRIGHT:
**  "Copyright (c) 1994,1995 by e-Tek Labs"
**
**       "This software is furnished under a license and may be used,
**       copied, or disclosed only in accordance with the terms of such
**       license and with the inclusion of the above copyright notice.
**       This software or any other copies thereof may not be provided or
**       otherwise made available to any other person. No title to and
**       ownership of the software is hereby transfered."
****************************************************************************
* $Log: wavefix.c $
* Revision 1.28  1996/01/17 14:31:04  unknown
* 
* Revision 1.27  1995/11/15 09:23:22  sdsmith
* Removed GetPreferredBufferSize
* Revision 1.26  1995/11/10 15:00:37  sdsmith
* Changes for Win95 compatability
* Revision 1.25  1995/10/31 15:31:54  mks
* Correct versions now
* Revision 1.24  1995/10/30 14:12:04  teckert
* Corrected error in wodOpen.  Wasn't freeing hardware in all cases where
* the device could not be allocated.
* Revision 1.23  1995/10/27 15:38:26  teckert
* Fixed more ACT failures
* Revision 1.22  1995/10/13 09:04:07  teckert
* Fixed a bug that caused the hardware to stay allocated to the windows system
* virtual machine if a wave client was opened with a wave client already
* running.
* Revision 1.21  1995/07/27 18:04:35  sdsmith
* Fixed buffering problems for .AVI file playback
* Revision 1.20  1995/07/13 16:49:55  sdsmith
* Let's try to go with the buffer as specified
* Revision 1.19  1995/06/05 15:01:55  teckert
* Added Low-Priority input mode support
* Revision 1.18  1995/05/29 17:32:52  sdsmith
* Fixed buffer sizing code
* Revision 1.17  1995/05/26 15:14:24  teckert
* Fixed digital record position bug
* Revision 1.16  1995/05/25 02:41:34  sdsmith
* Changed GetPreferredBufSize routine 
* Revision 1.15  1995/05/23 14:26:00  wweigert
* Added BuffsFromKernel count
* Revision 1.14  1995/05/09 17:22:45  teckert
* Added bandwidth sensitive DMA buffer size adjustment
* Revision 1.12  1995/05/07 19:22:49  teckert
* Wave-in and wave-out block each other when sharing a DMA channel
* Revision 1.11  1995/05/08 07:14:07  sdsmith
* Switched to use CODEC for playback
* Revision 1.10  1995/05/04 16:37:46  teckert
* Added checks for 0-length wave buffers
* Revision 1.9  1995/05/03 14:31:04  teckert
* Revision 1.8  1995/04/20 03:54:20  teckert
* Fixed GPF fault on wodClose
* Revision 1.7  1995/04/20 00:54:21  teckert
* Changed wave client allocation to global
* Revision 1.6  1995/04/18 14:10:32  sdsmith
* Changed widGetPos to call the proper position routine for recording.
* Revision 1.5  1995/04/04 15:39:36  teckert
* Fixed hardware acquisition code
* Revision 1.4  1995/03/30 16:40:15  teckert
* Updated arbitration support
* Revision 1.3  1995/03/17 17:01:13  teckert
* Added HWAllocate and HWFree calls to wave clients
* Revision 1.2  1995/03/01 16:58:49  unknown
* Added file header
* Revision 1.1  1995/02/23 15:14:59  sdsmith
* Initial revision
***************************************************************************/
#include <windows.h>
#include <mmsystem.h>
#include <mmddk.h>
#include <iw.h>
#include <globals.h>
#include <os.h>
#include "wave.h"
#include "interwav.h"
#include <viwd.h>

unsigned long gfpBuffer;          // iw_buffer address

extern DWORD DMA_Buf_Phys;
extern DWORD DMA_Buf_Linr;

int nInSendBuffers = 0;
int nInBufferCount = 0;
int nOutBufferCount = 0;
int nBuffsFromClient = 0;
int nBuffsToKernel = 0;
int nBuffsFromKernel = 0;
int nBuffsDone = 0;
LPWAVECLIENT ClientList[MAXCLIENTS+1] = {0};
WAVEINCLIENT RecordClientOne,RecordClientTwo;
struct iw_dma_buff dma_buf_one;
unsigned long buf_one_size;
struct iw_dma_buff dma_buf_two;
unsigned long buf_two_size;
DWORD dwVolume = 0xFFFFFFFFul;                     
int KernelCallback(int,int,unsigned char far* far*,unsigned long far*);
int RecordCallback(int,int,unsigned char far* far*,unsigned long far*);


/****************************************************************************

FUNCTION DEFINITION:
wodUnInit - uninitialized the wave out device

DESCRIPTION:
Uninitializes the wave out section by re-enabling the translation for the DMA 
channel.

RETURNS: void
*/
void FAR PASCAL wodUnInit(void)
{                             
	return;
}


/****************************************************************************

FUNCTION DEFINITION:
waveCallback - sends the specified callback message to the client

DESCRIPTION:
Using the client information stored in the WAVECLIENT structure the wave's
callback function is called (or the appropriate message is posted) with the
specified parameters.

RETURNS: void
*/                                            
void FAR PASCAL waveCallback(
	LPWAVECLIENT pClient,   // pointer to the WAVECLIENT structure
	WORD msg,               // the specific callback message (i.e. WOM_DONE)
	DWORD dw1)              // message specific data
{

	// invoke the callback function, if it exists.  dwFlags contains
	// wave driver specific flags in the LOWORD and generic driver
	// flags in the HIWORD

	if (pClient->dwCallback)
	DriverCallback(pClient->dwCallback, // user's callback DWORD
			   HIWORD(pClient->dwFlags),// callback flags
			   pClient->hWave,          // handle to the wave device
			   msg,                     // the message
			   pClient->dwInstance,     // user's instance data
			   dw1,                     // first DWORD
			   0L);                     // second DWORD
}

/****************************************************************************

FUNCTION DEFINITION:
wodOpen - handles the WODM_OPEN message

DESCRIPTION:
Allocates the client structure, initializes it and reserves neccesary resources

RETURNS: MMSYSERR_NOERROR on success or an appropriate mmsystem error message
*/
DWORD FAR PASCAL wodOpen(
	LPWAVEOPENDESC lpDesc,  // a pointer to the WAVEOPENDESC structure passed
				// to the WODM_OPEN call
	DWORD dwFlags,          // the client flags to be stored and passed back
				// with callback messages
	int far *pnClient,      // a pointer to an integer that is to receive the
				// clients index in the client array
	int nCodec)             // a flag to indicate 
{
	unsigned long nPrefBufSize;
	LPWAVECLIENT pClient=NULL;
	HGLOBAL hGlbl;
	int i;
				 
	if((hGlbl = GlobalAlloc(GMEM_FIXED|GHND,sizeof(WAVECLIENT)))!=NULL){
		pClient = (LPWAVECLIENT)GlobalLock(hGlbl);
	}
	if(!pClient){
		return MMSYSERR_NOMEM;
	}
	pClient->nType = IW_TYPE_PRELOAD;
	if(nCodec){
	    pClient->nUseCodec = 1;
	    if(ClientList[MAXCLIENTS] || ((pClient->wHWAH = HWAllocate(IWAR_CODECPLAY))==-1)){
		GlobalUnlock(hGlbl);
		GlobalFree(hGlbl);
		return MMSYSERR_ALLOCATED;
	    }
	    if(wIWDriverFlags&IWDF_SHARINGDMABUF){
		// Check record clients      
		if(RecordClientOne.nStatus & WIS_OPEN){
		    if(RecordClientOne.nStatus & WIS_LOWPRIORITY && !(RecordClientTwo.nStatus & WIS_OPEN)){
		    	SuspendLPClient(&RecordClientOne,WIS_LPBYWAVEOUT);
		    }else{
		    	HWFree(pClient->wHWAH);
			GlobalUnlock(hGlbl);
			GlobalFree(hGlbl);
			return MMSYSERR_ALLOCATED;
		    }
		}else if(RecordClientTwo.nStatus & WIS_OPEN){
		    if(RecordClientTwo.nStatus & WIS_LOWPRIORITY){
		    	SuspendLPClient(&RecordClientTwo,WIS_LPBYWAVEOUT);
		    }else{
		    	HWFree(pClient->wHWAH);
			GlobalUnlock(hGlbl);
			GlobalFree(hGlbl);
			return MMSYSERR_ALLOCATED;
		    }
		}
	    }
	    i = MAXCLIENTS;
	    pClient->nIndex = MAXCLIENTS;
	    ClientList[MAXCLIENTS] = pClient;
	}else{
		pClient->nUseCodec = 0;
		for(i=0;i<MAXCLIENTS;i++){
			if(!ClientList[i]){
				pClient->nIndex = i;
				ClientList[i] = pClient;
				break;
			}
		}
		if(i==MAXCLIENTS){
			GlobalUnlock(hGlbl);
			GlobalFree(hGlbl);
			return MMSYSERR_ALLOCATED;
		}
		if ((pClient->wHWAH = HWAllocate(IWAR_SOMEVOICES|IWAR_SOMEMEMORY))==-1) {
			GlobalUnlock(hGlbl);
			GlobalFree(hGlbl);
			ClientList[i] = NULL;
			return MMSYSERR_ALLOCATED;
		}    
	}
	*pnClient = i;                     
	if(lpDesc->lpFormat->nChannels == 2)
		pClient->nType |= IW_TYPE_STEREO;
	if(((LPPCMWAVEFORMAT)lpDesc->lpFormat)->wBitsPerSample == 8){
		pClient->nType |= IW_TYPE_8BIT;
		pClient->nType |= IW_TYPE_INVERT_MSB;
	}
		
	pClient->nStatus     = WOS_UNDERRUN;
	pClient->dwCallback  = lpDesc->dwCallback;
	pClient->dwInstance  = lpDesc->dwInstance;       
	pClient->hWave       = lpDesc->hWave;
	pClient->dwFlags     = dwFlags;
	pClient->nFrequency  = lpDesc->lpFormat->nSamplesPerSec;   
	pClient->dwVolume    = dwVolume;
	pClient->dwBytesFromClient = 0;
	pClient->dwLastPosition = 0;
	pClient->nPan        = 64;
	wodSetVolume(pClient);
	pClient->gfpBuffer   = gfpBuffer;
	pClient->nVoice = iw_codec_play_digital(//0,
					  NULL,
					  0l,
					  //pClient->gfpBuffer,
					  pClient->wVolume,
					  pClient->nPan,
					  pClient->nFrequency,
					  (unsigned char)pClient->nType,
					  (struct iw_dma_buff RFAR *)&dma_buf_two,
					  KernelCallback);
	if(pClient->nVoice == -1){
		ClientList[i] = NULL;
		HWFree(pClient->wHWAH);
		GlobalUnlock(hGlbl);
		GlobalFree(hGlbl);
		return MMSYSERR_NOMEM;
	}
	return MMSYSERR_NOERROR;
}

		
/****************************************************************************

FUNCTION DEFINITION:
wodClose - handles the WODM_CLOSE message.

DESCRIPTION:
Stops the wave output, releases the headers, issues the WOM_CLOSE callback message
and frees the resources.

RETURNS: MMSYSERR_NOERROR on success, WAVERR_STILLPLAYING if the output is still
going.
*/                                                            
DWORD FAR PASCAL wodClose(
	LPWAVECLIENT pClient)           // a pointer to the WAVECLIENT structure
{
	HGLOBAL hGlbl;
	
	if(pClient->lpQueue)
		return WAVERR_STILLPLAYING;

	if(pClient->nVoice != -1)
		iw_codec_stop_digital(pClient->nVoice);

	waveCallback(pClient, WOM_CLOSE, 0L);
	
	ClientList[pClient->nIndex] = NULL;
	if(wIWDriverFlags&IWDF_SHARINGDMABUF){
	    ResumeLPClient(WIS_LPBYWAVEOUT);
	}
	HWFree(pClient->wHWAH);
	hGlbl = GlobalHandle(HIWORD(pClient));
	GlobalUnlock(hGlbl);
	GlobalFree(hGlbl);
	
	return MMSYSERR_NOERROR;
}


/****************************************************************************

FUNCTION DEFINITION:
wodWrite - handles the WODM_WRITE message

DESCRIPTION:
After the header is added to the To-Play Queue the status of the transfer is
checked. If there was an underflow any available buffers are sent to the Kernel.

RETURNS: mmsystem error code
*/ 
										 
DWORD FAR PASCAL wodWrite(
	LPWAVECLIENT pClient,       // a pointer to the WAVECLIENT structure
	LPWAVEHDR lpHdr)            // a pointer to the wave header
{
	nBuffsFromClient++;
	/* store the pointer to the WAVECLIENT structure in the wavehdr */
	lpHdr->reserved = (DWORD)(LPSTR)pClient;
	
	/* add the buffer to our queue */
	lpHdr->dwFlags |= WHDR_INQUEUE;
	lpHdr->dwFlags &= ~WHDR_DONE; 
	
	if(pClient->nStatus & WOS_WFEOZIL){
		if(lpHdr->dwFlags & WHDR_ENDLOOP)
			pClient->nStatus &= ~WOS_WFEOZIL;
		if(!pClient->lpQueue){
			ReturnHeader(lpHdr);
			return MMSYSERR_NOERROR;
		}else
			AddToQueue(pClient,lpHdr);
	}else{
		AddToQueue(pClient, lpHdr);
	}
	
	OS_PUSH_DISABLE();
#ifdef INTSONOFFTRACE
	OutputDebugStr("Ints OFF\r\n");
#endif
	if(!NextPlayPointer(pClient)){
		pClient->lpPlayNext = lpHdr;
		CheckPlayLoop(pClient);
	}                          
#ifdef INTSONOFFTRACE
	OutputDebugStr("Ints ON\r\n");
#endif
	OS_POPF();
	
	if(pClient->nVoice == -1 && !(pClient->nStatus & WOS_WRAPPERPAUSED)){
		pClient->nVoice = iw_codec_play_digital(//0,
							NULL,
							0l,
							//pClient->gfpBuffer,
							pClient->wVolume,
							pClient->nPan,
							pClient->nFrequency,
							(unsigned char)pClient->nType,
							(struct iw_dma_buff RFAR *)&dma_buf_two,
							KernelCallback);
	}
	if(pClient->nVoice != -1){
		SendBuffers(pClient);
		if(!(pClient->nStatus & (WOS_KERNELPLAYING | WOS_WRAPPERPAUSED))){
			iw_codec_start_digital(pClient->nVoice);
			pClient->nStatus |= WOS_KERNELPLAYING;
		}
	}
	return MMSYSERR_NOERROR;
}


/****************************************************************************

FUNCTION DEFINITION:
wodPause - response function for the WODM_PAUSE message

DESCRIPTION:
Pauses the wave output if started, otherwise just modifies status

RETURNS: mmsystem error code
*/ 
DWORD FAR PASCAL wodPause(
	LPWAVECLIENT pClient)       // a pointer to the WAVECLIENT structure
{
	pClient->nStatus |= WOS_WRAPPERPAUSED;

	OS_PUSH_DISABLE();
#ifdef INTSONOFFTRACE
	OutputDebugStr("Ints OFF\r\n");
#endif

	if(pClient->nStatus & WOS_KERNELPLAYING){
		iw_codec_pause_digital(pClient->nVoice);
		pClient->nStatus |= WOS_KERNELPAUSED;
	}

#ifdef INTSONOFFTRACE
	OutputDebugStr("Ints ON\r\n");
#endif
	OS_POPF();

	return MMSYSERR_NOERROR;
}


/****************************************************************************

FUNCTION DEFINITION:
wodRestart - response function for the WODM_RESUME message

DESCRIPTION:
If wave ouput had been started ouput is resumed otherwise output is started.
If WODM_PAUSE had never been received no action is taken.

RETURNS: MMSYSERR code
*/ 
DWORD FAR PASCAL wodRestart(
	LPWAVECLIENT pClient)       // a pointer to the WAVECLIENT structure
{                           
	if(pClient->nStatus & WOS_KERNELPAUSED){
		iw_codec_restart_digital(pClient->nVoice);
	}else{
		if(pClient->nVoice == -1)
			pClient->nVoice = iw_codec_play_digital(//0,
							  NULL,
							  0l,
							  //pClient->gfpBuffer,
							  pClient->wVolume,
							  pClient->nPan,
							  pClient->nFrequency,
							  (unsigned char)pClient->nType,
							  (struct iw_dma_buff RFAR *)&dma_buf_two,
							  KernelCallback);
		if(NextPlayPointer(pClient))
			SendBuffers(pClient);
		iw_codec_start_digital(pClient->nVoice);
		pClient->nStatus |= WOS_KERNELPLAYING;
	}       
	pClient->nStatus &= ~(WOS_KERNELPAUSED | WOS_WRAPPERPAUSED);

	return MMSYSERR_NOERROR;
}


/****************************************************************************

FUNCTION DEFINITION:
wodReset - response function for the WODM_RESET message

DESCRIPTION:
Resets the client's state by stopping any output, releasing any unplayed 
headers, and resetting the position count.

RETURNS: MMSYSERR code
*/ 
DWORD FAR PASCAL wodReset(
	LPWAVECLIENT pClient)           // a pointer to the WAVECLIENT structure
{   
LPWAVEHDR lpHdr;
	
	OS_PUSH_DISABLE();
#ifdef INTSONOFFTRACE
	OutputDebugStr("Ints OFF\r\n");
#endif

	if(pClient->nVoice != -1)
		iw_codec_stop_digital(pClient->nVoice);      // voice should be -1 before call returns

#ifdef INTSONOFFTRACE
	OutputDebugStr("Ints ON\r\n");
#endif
	OS_POPF();
	
	pClient->nLocks = 0;
	while(pClient->lpQueue){                    // return any buffer in to-play Q
		lpHdr = pClient->lpQueue;
		pClient->lpQueue = pClient->lpQueue->lpNext;
		ReturnHeader(lpHdr);
	}
	pClient->nStatus = WOS_UNDERRUN;
	pClient->nDoneHeaders = 0;
	pClient->dwByteCount = 0;
	pClient->dwBytesBeforeDone = 0;
	pClient->lpPlayNext = NULL;  
	pClient->lpPlayLoopTop = NULL;
	pClient->dwPlayLoopCount = 0l;
	pClient->lpDoneNext = NULL;  
	pClient->dwDoneLoopCount = 0l;
	pClient->dwBytesFromClient = 0;
	pClient->dwLastPosition = 0;
	pClient->dwVolume = dwVolume;
	wodSetVolume(pClient);
	return MMSYSERR_NOERROR;
}


/****************************************************************************

FUNCTION DEFINITION:
wodGetPos - response function for the WODM_GETPOS message

DESCRIPTION:
Determines the number of bytes or samples played since last WODM_RESET or since
the device was opened and returns this information to the client                

RETURNS: MMSYSERR result
*/ 
DWORD FAR PASCAL wodGetPos(
	LPWAVECLIENT pClient,       // a pointer to the WAVECLIENT structure
	LPMMTIME lpMMT,             // a pointer to the MMTIME structure
	WORD wSize)                 // the size of the structure pointed to by lpMMT
{
DWORD dwCurrentSample;
MMTIME MMT;

	dwCurrentSample = pClient->dwBytesBeforeDone;
	MMT.wType = lpMMT->wType;

	OS_PUSH_DISABLE();
#ifdef INTSONOFFTRACE
	OutputDebugStr("Ints OFF\r\n");
#endif

	if(pClient->nStatus & WOS_KERNELPLAYING){
		dwCurrentSample += iw_codec_digital_position(pClient->nVoice);
	}

#ifdef INTSONOFFTRACE
	OutputDebugStr("Ints ON\r\n");
#endif
	OS_POPF();

	if (dwCurrentSample > pClient->dwLastPosition)
	   pClient->dwLastPosition = dwCurrentSample;
	else
	   dwCurrentSample = pClient->dwLastPosition;

	if (MMT.wType == TIME_BYTES)
		MMT.u.cb = dwCurrentSample;
	else{                          // default is samples -
		MMT.wType = TIME_SAMPLES;
		MMT.u.sample = dwCurrentSample;

		if(pClient->nType & IW_TYPE_STEREO)
			MMT.u.sample >>= 1;

		if(!(pClient->nType & IW_TYPE_8BIT))
			MMT.u.sample >>= 1;
	}
	
	_fmemcpy(lpMMT,(void far *)&MMT,min(wSize,sizeof(MMT)));
	return MMSYSERR_NOERROR;
}


/****************************************************************************

FUNCTION DEFINITION:
wodSetVolume - response function for the WODM_SETVOLUME message

DESCRIPTION:
Uses the clients dwVolume field (which has been set to the requested value before
this function is called) to set the nVolume and nPan fields and, if output is 
going, sends a volume and pan request to the Kernel.

RETURNS: void
*/ 
void FAR PASCAL wodSetVolume(
	LPWAVECLIENT pClient)           // a pointer to the WAVECLIENT structure
{
int nLeft = 0;
WORD wMore,wLess;           
int i;

	pClient->wVolume = max(HIWORD(pClient->dwVolume),LOWORD(pClient->dwVolume));     
	if(HIWORD(pClient->dwVolume)==0)
		pClient->nPan = 0;
	else if(LOWORD(pClient->dwVolume)==0)
		pClient->nPan = 127;
	else if(LOWORD(pClient->dwVolume)==HIWORD(pClient->dwVolume))
		pClient->nPan = 64;
	else{                                      // do a "cheap" log base-2 of the ratio
						       // to get a pan value
		if(HIWORD(pClient->dwVolume)>LOWORD(pClient->dwVolume)){     
			nLeft = 1;
			wMore = HIWORD(pClient->dwVolume);
			wLess = LOWORD(pClient->dwVolume);
		}else{
			wMore = LOWORD(pClient->dwVolume);
			wLess = HIWORD(pClient->dwVolume);
		}
		wLess = (DWORD)wLess << 16 / wMore;
		pClient->nPan = 64;
		while(!(wLess & 0x8000)){
			wLess *= 2;
			pClient->nPan -= 8;
		}
		if(!nLeft)
			pClient->nPan = 127 - pClient->nPan;
		}
	pClient->wVolume >>= 9;

	OS_PUSH_DISABLE();
#ifdef INTSONOFFTRACE
	OutputDebugStr("Ints OFF\r\n");
#endif

	if(pClient->nVoice != -1){                     
		iw_codec_dig_set_vol((int)pClient->wVolume);
		iw_codec_dig_set_pan(pClient->nPan);
	}

#ifdef INTSONOFFTRACE
	OutputDebugStr("Ints ON\r\n");
#endif
	OS_POPF();
}
 

void FAR PASCAL UngetBuffer(
	LPWAVECLIENT pClient,           // pointer to the WAVECLIENT structure
	LPWAVEHDR lpHdr)                // pointer to the WAVEHDR structure
{
	pClient->lpUnGotBuf = lpHdr;
	pClient->nStatus |= WOS_UNGOTBUFAVAIL;
}
 
/****************************************************************************

FUNCTION DEFINITION:
SendBuffers - sends buffers to the Kernel to be played, if needed

DESCRIPTION:
If the Kernel currently needs data to play (i.e. the underrun flag is set) then
as many buffers are sent to the Kernel as it will accept.  Because this function
does not remove the buffer from the ToPlay queue until the Kernel responds 
positively to the iw_play_next_buffer() call a flag must be set to keep the 
same buffer from being sent to the Kernel in response to a IW_DIG_MORE_DATA callback
message

RETURNS: void
*/
void FAR PASCAL SendBuffers(
	LPWAVECLIENT pClient)               // the WAVECLIENT structure
{
int nMode;
LPWAVEHDR lpHdr;

	OS_PUSH_DISABLE();
#ifdef INTSONOFFTRACE
	OutputDebugStr("Ints OFF\r\n");
#endif
	nInSendBuffers = 1;
	if(pClient->nVoice == -1){
	    pClient->nStatus &= ~WOS_UNDERRUN;
	    nInSendBuffers = 0;
#ifdef INTSONOFFTRACE
	    OutputDebugStr("Ints ON\r\n");
#endif
	    OS_POPF();
	    return;
	}        
	lpHdr = NextPlayPointer(pClient);
	if(!lpHdr || !(pClient->nStatus & WOS_UNDERRUN)){
	    nInSendBuffers = 0;
#ifdef INTSONOFFTRACE
	    OutputDebugStr("Ints ON\r\n");
#endif
	    OS_POPF();
	    return;         
	}   
	nBuffsToKernel++;
	AdvancePlayPointer(pClient);
	CheckPlayLoop(pClient);
	pClient->dwByteCount += lpHdr->dwBufferLength;
	// if the Kernel will not accept the buffer return it to the q
	pClient->nStatus &= ~WOS_UNDERRUN;
	if( iw_codec_play_next_buffer(pClient->nVoice,lpHdr->lpData,lpHdr->dwBufferLength)!=0 ){
	    nBuffsToKernel--;
	    UngetBuffer(pClient,lpHdr);
	    pClient->dwByteCount -= lpHdr->dwBufferLength;
	}
	nInSendBuffers = 0;
#ifdef INTSONOFFTRACE
	OutputDebugStr("Ints ON\r\n");
#endif
	OS_POPF();
	ReturnBuffers(pClient,0);           // For every blocked IW_DIG_BUFFER_DONE message
					    // Try to send a buffer to the client
}


/****************************************************************************

FUNCTION DEFINITION:
AddToQueue - adds a header to the ToPlay queue

DESCRIPTION:
Takes a wave header and adds it to the end of the Queue.  It then checks the loop status of
the done queue to see if its loop status should be checked.

RETURNS: void
*/
void FAR PASCAL AddToQueue(
	LPWAVECLIENT pClient,           // pointer to the WAVECLIENT structure
	LPWAVEHDR lpHdr)                // pointer to the WAVEHDR structure
{
LPWAVEHDR pTail;
int nNewQueue = 0;
		
	lpHdr->lpNext = NULL;
	pClient->dwBytesFromClient += lpHdr->dwBufferLength;
	OS_PUSH_DISABLE();
#ifdef INTSONOFFTRACE
	OutputDebugStr("Ints OFF\r\n");
#endif
	
	pTail = pClient->lpQueue;
	if(pTail){
		while(pTail->lpNext)
			pTail = pTail->lpNext;
		pTail->lpNext = lpHdr;
		if(pClient->dwDoneLoopCount > 1 && !pClient->lpDoneNext){
			pClient->lpDoneNext = lpHdr;
		}
	}else{
		pClient->lpQueue = lpHdr;
		nNewQueue = 1;
	}                         
		
#ifdef INTSONOFFTRACE
	OutputDebugStr("Ints ON\r\n");
#endif
	OS_POPF();
	
	if(nNewQueue)
		CheckDoneLoop(pClient);
}


/****************************************************************************

FUNCTION DEFINITION:
BreakLoop - breaks out of currently executing loop

DESCRIPTION:

RETURNS: void
*/
void FAR PASCAL BreakLoop(
	LPWAVECLIENT pClient)           // a pointer to the client structure
{
	int nNumSkippedLoops;
	LPWAVEHDR lpHdr;
								 
	// First set it up so that the play pointer will skip additional loops.
	nNumSkippedLoops = pClient->dwPlayLoopCount - 1;
	pClient->dwPlayLoopCount = 1;

	// That was easy, now set it up so that the done pointer will skip the
	// same number of loops.
	
	OS_PUSH_DISABLE();
#ifdef INTSONOFFTRACE
	OutputDebugStr("Ints OFF\r\n");
#endif
	
	// If the done pointer is currently in that loop decrement the current loop count
	if( pClient->lpQueue == pClient->lpPlayLoopTop ){            
		pClient->dwDoneLoopCount -= nNumSkippedLoops;
		if(pClient->dwDoneLoopCount == 1){
			for(lpHdr = pClient->lpQueue;lpHdr != pClient->lpDoneNext;lpHdr = pClient->lpQueue){
				pClient->lpQueue = pClient->lpQueue->lpNext;
				ReturnHeader(lpHdr);
			}
			pClient->lpDoneNext = NULL;
		}
	}else{  // otherwise decrement the count in the wave header
		pClient->lpPlayLoopTop->dwLoops -= nNumSkippedLoops;
	}

#ifdef INTSONOFFTRACE
	OutputDebugStr("Ints ON\r\n");
#endif
	OS_POPF();
}


/****************************************************************************

FUNCTION DEFINITION:
AdvancePlayPointer - removes a buffer from the ToPlay queue

DESCRIPTION:
Advances the PlayNext pointer.

RETURNS: void
*/
void FAR PASCAL AdvancePlayPointer(
	LPWAVECLIENT pClient)           // a pointer to the client structure
{
	if(pClient->nStatus & WOS_UNGOTBUFAVAIL){
		pClient->nStatus &= ~WOS_UNGOTBUFAVAIL;
		return;
	}
	if(pClient->dwPlayLoopCount && (pClient->lpPlayNext->dwFlags & WHDR_ENDLOOP)){
		if(pClient->nStatus & WOS_BREAKLOOP){
			if(pClient->dwPlayLoopCount > 1){
				BreakLoop(pClient);
			}
			pClient->nStatus &= ~WOS_BREAKLOOP;
		}
	}
	
	if(pClient->dwPlayLoopCount && (pClient->lpPlayNext->dwFlags & WHDR_ENDLOOP) && --pClient->dwPlayLoopCount)
		pClient->lpPlayNext = pClient->lpPlayLoopTop;
	else
		pClient->lpPlayNext = pClient->lpPlayNext->lpNext;
}


/****************************************************************************

FUNCTION DEFINITION:
CheckPlayLoop - checks the loop status of the play pointer

DESCRIPTION:
This function keeps the looping state in order, it is called any time the play
pointer is advanced or initialized

RETURNS: void
*/
void FAR PASCAL CheckPlayLoop(
	LPWAVECLIENT pClient)           // a pointer to the client structure
{                                                            
	if(pClient->dwPlayLoopCount)    // already looping
		return;               
		
	if(!pClient->lpPlayNext)        // no buffers to play
		return;                        
		
	while(1){                       // skip any zero iteration loops
		if(!(pClient->lpPlayNext) || 
		   !(pClient->lpPlayNext->dwFlags & WHDR_BEGINLOOP) || 
		   pClient->lpPlayNext->dwLoops)
			break;
		while(pClient->lpPlayNext && !(pClient->lpPlayNext->dwFlags & WHDR_ENDLOOP))
			pClient->lpPlayNext = pClient->lpPlayNext->lpNext;
		if(!pClient->lpPlayNext){
			pClient->nStatus |= WOS_WFEOZIL;
			return;
		}else{
			pClient->lpPlayNext = pClient->lpPlayNext->lpNext;
		}
	}
	if(pClient->lpPlayNext){
		if(pClient->lpPlayNext->dwFlags & WHDR_BEGINLOOP){
			pClient->lpPlayLoopTop = pClient->lpPlayNext;
			pClient->dwPlayLoopCount = pClient->lpPlayNext->dwLoops;
		}else{
			pClient->lpPlayLoopTop = NULL;
		}
	}
}


/****************************************************************************

FUNCTION DEFINITION:
AdvanceDonePointer - removes a buffer from the queue

DESCRIPTION:
Advances the DoneNext pointer.

RETURNS: 1 if a buffer was removed from the queue, 0 otherwise
*/
int FAR PASCAL AdvanceDonePointer(
	LPWAVECLIENT pClient)           // a pointer to the client structure
{

	if(pClient->dwDoneLoopCount > 1){
		if(pClient->lpDoneNext->dwFlags & WHDR_ENDLOOP){
			if(--pClient->dwDoneLoopCount == 1)
				pClient->lpDoneNext = NULL;
			else
				pClient->lpDoneNext = pClient->lpQueue;
		}else{
			pClient->lpDoneNext = pClient->lpDoneNext->lpNext;
		}
		return 0;
	}
	if(pClient->dwDoneLoopCount == 1 && pClient->lpQueue->dwFlags & WHDR_ENDLOOP)
		pClient->dwDoneLoopCount = 0;
	if (pClient->lpQueue) {
	    pClient->lpQueue = pClient->lpQueue->lpNext;
	    return 1;
	}
	else return 0;
}


/****************************************************************************

FUNCTION DEFINITION:
CheckDoneLoop - checks the loop status of the done pointer

DESCRIPTION:
This function keeps the looping state in order, it is called any time the done
pointer is advanced or initialized

RETURNS: void
*/
void FAR PASCAL CheckDoneLoop(
	LPWAVECLIENT pClient)           // a pointer to the client structure
{       
	LPWAVEHDR lpHdr;                                                     
	int nTrulyAmazed = 0;
					   
	if(pClient->dwDoneLoopCount)    // already looping
		return;               
		
	if(!pClient->lpQueue)           // no buffers to play
		return;                        
		
	while(1){                       // skip any zero iteration loops
		if(!(pClient->lpQueue->dwFlags & WHDR_BEGINLOOP) || pClient->lpQueue->dwLoops)
			break;
		// We have a zero iteration loop!!  I am truly amazed.
		nTrulyAmazed = 1;
		while(pClient->lpQueue && !(pClient->lpQueue->dwFlags & WHDR_ENDLOOP)){
			lpHdr = pClient->lpQueue;
			pClient->lpQueue = pClient->lpQueue->lpNext;
			ReturnHeader(lpHdr);
		}
		if(pClient->lpQueue){
			lpHdr = pClient->lpQueue;
			pClient->lpQueue = pClient->lpQueue->lpNext;
			ReturnHeader(lpHdr);
		}else{
			return;
		}
	}
	
	if(pClient->lpQueue->dwFlags & WHDR_BEGINLOOP){
		pClient->lpDoneNext = pClient->lpQueue;
		pClient->dwDoneLoopCount = pClient->lpQueue->dwLoops;
	}else{
		pClient->lpDoneNext = NULL;
	}
}


/****************************************************************************

FUNCTION DEFINITION:
ReturnHeader - returns the header to the client when done playing

DESCRIPTION:
Modifies the flags of the header and sends the header back to the client with
a WOM_DONE message 

RETURNS: void
*/
void FAR PASCAL ReturnHeader(
	LPWAVEHDR lpHdr)            // pointer to the WAVEHDR to be returned
{
LPWAVECLIENT pClient;        

	nBuffsDone++;
	pClient = (LPWAVECLIENT) lpHdr->reserved;

	// set the 'done' bit
	lpHdr->dwFlags |= WHDR_DONE;
	lpHdr->dwFlags &= ~WHDR_INQUEUE;

	waveCallback(pClient, WOM_DONE, (DWORD)lpHdr);        
}


/****************************************************************************

FUNCTION DEFINITION:
ReturnBuffers - returns finished buffers to the client

DESCRIPTION:
Returns the requested number of wave headers to the client.

RETURNS: void
*/                                          
void FAR PASCAL ReturnBuffers(
	LPWAVECLIENT pClient,       // pointer to the WAVECLIENT structure
	int nCount)                 // number of headers to be returned for this request
{        
LPWAVEHDR lpHdr;
int nLocks;

	OS_PUSH_DISABLE();
#ifdef INTSONOFFTRACE
	OutputDebugStr("Ints OFF\r\n");
#endif
	nLocks = pClient->nLocks;           
	pClient->nLocks++;       
#ifdef INTSONOFFTRACE
	OutputDebugStr("Ints ON\r\n");
#endif
	OS_POPF();
			
	pClient->nDoneHeaders += nCount;
	if(nLocks){                         // if blocked decrease locks and leave
		pClient->nLocks--;
		return;
	}      

	OS_PUSH_DISABLE();                  // we need this CritEnter here because the end
#ifdef INTSONOFFTRACE                   // loop condition must be checked with ints off
	OutputDebugStr("Ints OFF\r\n");
#endif  

	while(pClient->nDoneHeaders){       // CritLeave at the top of the do loop becuse we                                            
#ifdef INTSONOFFTRACE                   // need to check our completion condition in a
		OutputDebugStr("Ints ON\r\n");  // critical section
#endif
		OS_POPF();                      
		
		lpHdr = NextDonePointer(pClient);
		if(AdvanceDonePointer(pClient)){
			ReturnHeader(lpHdr);
		}
		CheckDoneLoop(pClient);
		pClient->nDoneHeaders--;

		OS_PUSH_DISABLE();              // Check the next condition in a critical section
#ifdef INTSONOFFTRACE
		OutputDebugStr("Ints OFF\r\n");
#endif
	}

	pClient->nLocks--;
#ifdef INTSONOFFTRACE
	OutputDebugStr("Ints ON\r\n");
#endif
	OS_POPF();
}


/****************************************************************************

FUNCTION DEFINITION:
GetClient - finds the client that is using the specified voice

DESCRIPTION:
Finds the active client structure assocated with the digital channel playing
through the specified voice and returns a pointer to it.

RETURNS: a pointer to the WAVECLIENT structure on success NULL on failure
*/
LPWAVECLIENT GetClient(
	int nVoice)             // the voice being used for wave out
{   
int i;

	for(i=0;i<MAXCLIENTS+1;i++)
		if(ClientList[i] && ClientList[i]->nVoice == nVoice)
			return ClientList[i];
	
	return NULL;
}
			 
	
/****************************************************************************

FUNCTION DEFINITION:
KernelCallback - callback function used by the Kernel

DESCRIPTION:
This callback function is called for three reasons:
	IW_DIG_BUFFER_DONE - when the Kernel has finished playing a buffer
	IW_DIG_MORE_DATA - when the Kernel needs more data
	IW_DIG_DONE - when the ouput is complete and the voice has been released
	
RETURNS: int value depending on the reason:
	IW_DIG_BUFFER_DONE - return value is not used (returns 0)
	IW_DIG_MORE_DATA - returns 1 if another buffer was available, 0 otherwise
	IW_DIG_DONE - return value is not used (returns 0)
*/
int KernelCallback(
	int reason,                     // reason for callback
	int voice,                      // identifies the client
	unsigned char far * far *buf,   // pointer to buffer pointer to be modified
	unsigned long far *size)        // pointer to buffer size to be modified
{
int i;
LPWAVECLIENT pClient;                              
LPWAVEHDR lpHdr;

	pClient = GetClient(voice);
	if(!pClient){
		/* We may still be in the call to iw_play_digital so just return */
		return IW_DIG_DONE;
	}
	
	switch(reason){
		case IW_DIG_DONE:
			pClient->nVoice = -1;
			pClient->nStatus &= ~(WOS_KERNELPAUSED|WOS_KERNELPLAYING);
			pClient->dwBytesBeforeDone += pClient->dwByteCount;
			pClient->dwByteCount = 0;
			break;                  
		case IW_DIG_MORE_DATA:                     // return 1 if there is more data to play, 0 otherwise
			OS_PUSH_DISABLE();
#ifdef INTSONOFFTRACE
			OutputDebugStr("Ints OFF\r\n");
#endif
			lpHdr = NextPlayPointer(pClient);
			if(lpHdr){
				AdvancePlayPointer(pClient);
				CheckPlayLoop(pClient);
			}
#ifdef INTSONOFFTRACE
			OutputDebugStr("Ints ON\r\n");
#endif
			OS_POPF();
			if(!lpHdr){
				pClient->nStatus |= WOS_UNDERRUN;
				*buf = NULL;
				*size = 0l;
				return IW_DIG_BUFFER_DONE;
			}
			nBuffsToKernel++;
			*buf = lpHdr->lpData;
			*size = lpHdr->dwBufferLength;
			pClient->dwByteCount += lpHdr->dwBufferLength;
			return IW_DIG_MORE_DATA;
		case IW_DIG_BUFFER_DONE:                
			nBuffsFromKernel++;
			ReturnBuffers(pClient, 1);
			break;
	}
    return 0;
}


/****************************************************************************

FUNCTION DEFINITION:
waveInCallback -sends the specified callback message to the client

DESCRIPTION:
Using the client information stored in the WAVEINCLIENT structure the wave's
callback function is called (or the appropriate message is posted) with the
specified parameters.

RETURNS: void
*/                                            
void FAR PASCAL waveInCallback(
	LPWAVEINCLIENT pClient,     // pointer to the WAVECLIENT structure
	WORD msg,                   // the specific callback message (i.e. WOM_DONE)
	DWORD dw1)                  // message specific data
{
	// invoke the callback function, if it exists.  dwFlags contains
	// wave driver specific flags in the LOWORD and generic driver
	// flags in the HIWORD

	// DON'T switch stacks in DriverCallback--we already did at the
	// beginning of our ISR (using StackEnter).  No need to burn another
	// stack, we should have plenty of room for the callback.  Also,
	// we may not have been called from an ISR.  In that case, we know
	// that we are on an app's stack, and this should be ok.

	if (pClient->dwCallback)
	DriverCallback(pClient->dwCallback, // user's callback DWORD
			   HIWORD(pClient->dwFlags),// callback flags
			   pClient->hWave,          // handle to the wave device
			   msg,                     // the message
			   pClient->dwInstance,     // user's instance data
			   dw1,                     // first DWORD
			   0L);                     // second DWORD
}


/****************************************************************************

FUNCTION DEFINITION:
widOpen - response function for the WIDM_OPEN message

DESCRIPTION:
Handles the WIDM_OPEN call by initializing the client structure.  The kernel's
recording will be started when the WIDM_START message has been received.

RETURNS: mmsystem error code
*/
DWORD FAR PASCAL widOpen(
	LPWAVEOPENDESC lpDesc,      // a pointer to the WAVEOPENDESC structure
	DWORD dwFlags,              // the clients flags
	LPDWORD lpUser)             // a pointer to the clients handle
{
	LPWAVEINCLIENT pClient;
	                                                     
	if ( (wIWDriverFlags & IWDF_SHARINGDMABUF) && ClientList[MAXCLIENTS] ){
		return MMSYSERR_ALLOCATED;
	}
	
	if(RecordClientOne.nStatus & WIS_OPEN){
	    if(RecordClientOne.nStatus & WIS_LOWPRIORITY && !(RecordClientTwo.nStatus & WIS_OPEN)){
	    	SuspendLPClient(&RecordClientOne,WIS_LPBYWAVEIN);
	    	pClient = &RecordClientTwo;
	    	*lpUser = 2l;
	    }else{
		return MMSYSERR_ALLOCATED;
	    }
	}else if(RecordClientTwo.nStatus & WIS_OPEN){
	    if(RecordClientTwo.nStatus & WIS_LOWPRIORITY){
	    	SuspendLPClient(&RecordClientTwo,WIS_LPBYWAVEIN);
	    	pClient = &RecordClientOne;
	    	*lpUser = 1l;
	    }else{
		return MMSYSERR_ALLOCATED;
	    }
	}else{
	    pClient = &RecordClientOne;
	    *lpUser = 1l;
	}
	
	if ((pClient->wHWAH = HWAllocate(IWAR_CODECREC))==-1) {
		return MMSYSERR_ALLOCATED;
	}
	pClient->nStatus  = WIS_OPEN;
	pClient->nVoice = -1;
	pClient->dwCallback = lpDesc->dwCallback;
	pClient->dwInstance = lpDesc->dwInstance;
	pClient->dwFlags = dwFlags;
	pClient->hWave = lpDesc->hWave;
	pClient->nFrequency = lpDesc->lpFormat->nSamplesPerSec;
	pClient->dwBytesFromClient = 0;
	pClient->dwLastPosition = 0;
	if(lpDesc->lpFormat->nChannels == 2)
		pClient->nType |= IW_TYPE_STEREO;
	if(((LPPCMWAVEFORMAT)lpDesc->lpFormat)->wBitsPerSample == 8){
		pClient->nType |= IW_TYPE_8BIT;
		pClient->nType |= IW_TYPE_INVERT_MSB;
	}
	waveInCallback(pClient,WIM_OPEN,0l);
	return MMSYSERR_NOERROR;
}


/****************************************************************************

FUNCTION DEFINITION:
widClose - response function for the WIDM_CLOSE message

DESCRIPTION:
Handles the WIDM_CLOSE message.  Stops the wave output, issues the WIM_CLOSE
callback message and frees the resources.

RETURNS: mmsystem error code
*/                                                            
DWORD FAR PASCAL widClose(
	LPWAVEINCLIENT pClient)     // a pointer to the WAVEINCLIENT structure
{                               
	if(pClient->lpBufferQueue)
	    return WAVERR_STILLPLAYING;
	if(pClient->nVoice != -1){
	    //if(pClient->nStatus & WIS_OVERFLOW){
		//widStop(pClient);
	    //}else{
		return WAVERR_STILLPLAYING;
	    //}
	}
	HWFree(pClient->wHWAH);
	waveInCallback(pClient,WIM_CLOSE,0l);
	_fmemset(pClient,0,sizeof(WAVEINCLIENT));

	return MMSYSERR_NOERROR;
}


/****************************************************************************

FUNCTION DEFINITION:
widAddBuffer - response function for the WIDM_ADDBUFFER message

DESCRIPTION:
Adds a buffer to the queue of buffers waiting to be filled with recorded data.
If recording had stopped due to insufficient buffers then recording is started
with the new buffer.

RETURNS: mmsystem error code
*/                                                            
DWORD FAR PASCAL widAddBuffer(
	LPWAVEINCLIENT pClient,         // a pointer to the WAVEINCLIENT structure
	LPWAVEHDR lpHdr)                // a pointer to the WAVEHDR structure that
									// is to be added to the record queue
{
LPWAVEHDR lpTail;
DWORD dwResult = MMSYSERR_NOERROR;
DWORD org_size;

	OS_PUSH_DISABLE();
	// If the system had been started without a buffer or ran out of buffers we need to
	// start/restart the input
	nInBufferCount++;
	lpHdr->lpNext = NULL;
	lpHdr->dwFlags &= ~WHDR_DONE;
	lpHdr->dwFlags |= WHDR_INQUEUE;
	lpHdr->dwBytesRecorded = 0l;
	pClient->dwBytesFromClient += lpHdr->dwBufferLength;
	if(pClient->nVoice == -1 && pClient->nStatus & WIS_OVERFLOW && !(pClient->nStatus & WIS_LPSUSPENDED)){
		if(lpHdr->dwBufferLength == 0){
			ReturnRecordBuffer(pClient,lpHdr);
			return dwResult;
		}
		pClient->lpRecording = lpHdr;
		pClient->nVoice = iw_codec_record_digital(lpHdr->lpData,      // buffer
						lpHdr->dwBufferLength,// size
						pClient->nFrequency,// frequency
						(unsigned char)pClient->nType,     // type
						(struct iw_dma_buff RFAR *)&dma_buf_one,
						RecordCallback      // callback
						);
		if(pClient->nVoice == IW_NO_MORE_VOICES)
			dwResult == MMSYSERR_ALLOCATED;
	}else{
	// Otherwise just add the buffer to the queue
		if(pClient->lpBufferQueue){    
			lpTail = pClient->lpBufferQueue;
			while(lpTail->lpNext)   
				lpTail = lpTail->lpNext;
			lpTail->lpNext = lpHdr;
		}else
			pClient->lpBufferQueue = lpHdr;
	}
	OS_POPF();
	return dwResult;
}


/****************************************************************************

FUNCTION DEFINITION:
widStart - response function for the WIDM_START message

DESCRIPTION:
Starts or restarts the recording

RETURNS: mmsystem error code
*/                                                            
DWORD FAR PASCAL widStart(
	LPWAVEINCLIENT pClient)     // a pointer to the WAVECLIENT structure
{                                   
DWORD dwResult= MMSYSERR_NOERROR; 
LPWAVEHDR lpHdr;
DWORD org_size;

// If we were already running return no error reguardless of whether or not we have
// buffers
	if(pClient->nVoice == -1){
// If we have no buffers don't bother the kernel, just set the flags that will start us
// going when we have a buffer to fill
		if(pClient->lpBufferQueue == NULL){
			pClient->nStatus |= WIS_OVERFLOW;
		}else{
// Otherwise start the recording
			lpHdr = GetNextRecordBuffer(pClient);
			pClient->lpRecording = lpHdr;
			pClient->nVoice = iw_codec_record_digital(lpHdr->lpData,      // buffer
						lpHdr->dwBufferLength,// size
						pClient->nFrequency,// frequency
						(unsigned char)pClient->nType,     // type
										// dma buffer
						(struct iw_dma_buff RFAR *)&dma_buf_one,
						RecordCallback      // callback
						);
			nOutBufferCount++;                                              
			if(pClient->nVoice == IW_NO_MORE_VOICES)
				dwResult == MMSYSERR_ALLOCATED;
		}
	}
	if(dwResult==MMSYSERR_NOERROR){
	    pClient->nStatus |= WIS_STARTED;
	}
	return dwResult;
}


/****************************************************************************

FUNCTION DEFINITION:
widStop - response function for the WIDM_STOP message

DESCRIPTION:
Stops the recording

RETURNS: mmsystem error code
*/                                                            
DWORD FAR PASCAL widStop(
	LPWAVEINCLIENT pClient)     // a pointer to the WAVECLIENT structure
{                                          
	OS_PUSH_DISABLE();
	// if we are actually running we need to pause the recording
	if(pClient->nVoice != -1){
		pClient->nStatus |= WIS_STOPPING;
		iw_codec_stop_record(pClient->nVoice);
		pClient->nStatus &= ~WIS_STOPPING;
	}
	OS_POPF();
	
	// if this is a Low-Priority client and it's suspended and there is
	// a partially filled buffer in lpHoldBuf, return this buffer
	if(pClient->nStatus & WIS_LPSUSPENDED && pClient->lpHoldBuf && pClient->lpHoldBuf->dwBytesRecorded){
		ReturnRecordBuffer(pClient,pClient->lpHoldBuf);
	}
	pClient->nStatus &= ~WIS_OVERFLOW;
	pClient->nStatus &= ~WIS_STARTED;

	return MMSYSERR_NOERROR;
}


/****************************************************************************

FUNCTION DEFINITION:
widReset - response function for the WIDM_RESET message

DESCRIPTION:
Stops the recording and gets the current position during the IW_DIG_DONE callback,
when the WIS_FINISHED flag is set by the callback any remaining buffers are 
returned to the application.

RETURNS: mmsystem error code
*/                                                            
DWORD FAR PASCAL widReset(
	LPWAVEINCLIENT pClient)     // a pointer to the WAVECLIENT structure
{                  
LPWAVEHDR pHdr;

	widStop(pClient);              
	
	while(pClient->lpBufferQueue){
		pHdr = pClient->lpBufferQueue;                             
		pClient->lpBufferQueue = pClient->lpBufferQueue->lpNext;
		ReturnRecordBuffer(pClient, pHdr);
	}                      
		
	pClient->dwByteCount = 0;
	pClient->dwBytesFromBefore = 0;
	pClient->dwLastPosition = 0;
	pClient->dwBytesFromClient = 0;
	
	return MMSYSERR_NOERROR;
}


/****************************************************************************

FUNCTION DEFINITION:
widGetPos - response function for the WIDM_GETPOS message

DESCRIPTION:
Determines the number of bytes or samples played since last WODM_RESET or since
the device was opened and returns this information to the client.

RETURNS: mmsystem error code
*/ 
DWORD FAR PASCAL widGetPos(
	LPWAVEINCLIENT pClient,     // a pointer to the WAVECLIENT structure
	LPMMTIME lpMMT,             // a pointer to the MMTIME structure
	WORD wSize)                 // the size of the structure pointed to by lpMMT
{
DWORD dwResult;
DWORD dwByteCount;
	
	if (wSize < sizeof(MMTIME))
		return MMSYSERR_ERROR;

	dwByteCount = pClient->dwBytesFromBefore;
	if(pClient->nVoice != -1)
		dwByteCount += iw_codec_record_position(pClient->nVoice);
		
	if (dwByteCount > pClient->dwBytesFromClient) dwByteCount = pClient->dwBytesFromClient;
	if (dwByteCount > pClient->dwLastPosition)
	    pClient->dwLastPosition = dwByteCount;
	else
	    dwByteCount = pClient->dwLastPosition;
	// If neccesary change this to a sample count
	if (lpMMT->wType == TIME_BYTES){
		lpMMT->u.cb = dwByteCount;
	}else{
	// default is samples
	// Adjust count for stereo and 16 bit data 
		lpMMT->wType = TIME_SAMPLES;
		lpMMT->u.sample = dwByteCount;

		if (pClient->nType & IW_TYPE_STEREO)
		    lpMMT->u.sample >>=1;

		if (!(pClient->nType & IW_TYPE_8BIT))
		    lpMMT->u.sample >>=1;
	}

	return MMSYSERR_NOERROR;
}


/****************************************************************************

FUNCTION DEFINITION:
GetNextRecordBuffer - gets the next record buffer from the queue

DESCRIPTION:
Gets the next buffer from the record queue and returns it so that the kernel
can fill it, it also makes it the current lpRecording buffer.

RETURNS: a pointer to the WAVEHDR structure that is next in line to receive data
*/
LPWAVEHDR FAR PASCAL GetNextRecordBuffer(
	LPWAVEINCLIENT pClient)         // a pointer to the WAVEINCLIENT structure
{
LPWAVEHDR pHdr=NULL;
	
	if(pClient->lpBufferQueue){
		pHdr = pClient->lpBufferQueue;
		pClient->lpBufferQueue = pHdr->lpNext;
	}                                     
	return pHdr;
}


/****************************************************************************

FUNCTION DEFINITION:
ReturnRecordBuffer - returns a buffer to the client

DESCRIPTION:
Returns the buffer to the client with a WIM_DATA callback message after modifying
its flags accordingly.

RETURNS: void
*/
void FAR PASCAL ReturnRecordBuffer(
	LPWAVEINCLIENT pClient,     // a pointer to the WAVEINCLIENT
	LPWAVEHDR pHdr)             // a pointer to the WAVEHDR to be returned
{ 
	pHdr->dwFlags |= WHDR_DONE;
	pHdr->dwFlags &= ~WHDR_INQUEUE;
	pClient->dwByteCount += pHdr->dwBytesRecorded;
	waveInCallback(pClient,WIM_DATA,(DWORD)pHdr);
	nInBufferCount--;
}


/****************************************************************************

FUNCTION DEFINITION:
RecordCallback - Kernel callback function for recording

DESCRIPTION:
This callback function is called for three reasons:
	IW_DIG_BUFFER_DONE - when the Kernel has finished recording a buffer
	IW_DIG_MORE_DATA - when the Kernel needs another buffer
	IW_DIG_DONE - when the input is complete and the voice has been released

RETURNS: int value depending on the reason:
	IW_DIG_BUFFER_DONE - return value is not used (returns 0)
	IW_DIG_MORE_DATA - returns 1 if another buffer was available, 0 otherwise
	IW_DIG_DONE - return value is not used (returns 0)
*/
int RecordCallback(
	int reason,                     // reason for callback
	int voice,                      // identifies the client
	unsigned char far * far *buf,   // pointer to buffer pointer to be modified
	unsigned long far *size)        // pointer to buffer size to be modified
{                  
LPWAVEINCLIENT pClient;
LPWAVEHDR pHdr; 
DWORD dwPosition;

	if(RecordClientOne.nVoice == voice)
		pClient = &RecordClientOne;
	else if(RecordClientTwo.nVoice == voice)
		pClient = &RecordClientTwo;
	else
		pClient = NULL;
		
	if(!pClient){
		/* We may still be in the call to iw_play_digital so just return */
		return IW_DIG_DONE;
	}
	
	switch(reason){
		case IW_DIG_DONE:
			if(pClient->nStatus & WIS_STOPPING){	// if we were stopping the output then we have a
								// partial, otherwise we just filled the last
				dwPosition = iw_codec_record_position(pClient->nVoice);
				if(pClient->lpHoldBuf){
					pClient->lpHoldBuf->dwBytesRecorded = dwPosition - pClient->dwByteCount;
					if(!(pClient->nStatus & WIS_LPSUSPENDING)){
					    ReturnRecordBuffer(pClient,pClient->lpHoldBuf);
					    pClient->lpHoldBuf = NULL;
					}
				}
			}
			pClient->dwBytesFromBefore += pClient->dwByteCount;
			pClient->dwByteCount = 0;
			pClient->nVoice = -1;
			break;                  
		case IW_DIG_MORE_DATA:                     // return 1 if there is more data to play, 0 otherwise
			pHdr = GetNextRecordBuffer(pClient);
			while(pHdr && pHdr->dwBufferLength == 0){
				ReturnRecordBuffer(pClient,pHdr);
				pHdr = GetNextRecordBuffer(pClient);
			}
			if(pHdr){
				pClient->lpRecording = pHdr;
				*buf = pHdr->lpData;
				*size = pHdr->dwBufferLength;
				nOutBufferCount++;
				return IW_DIG_MORE_DATA;
			}
			pClient->nStatus |= WIS_OVERFLOW;
			return IW_DIG_DONE;
		case IW_DIG_BUFFER_DONE:
			// If we're stopping we need to hold the last buffer until we process the IW_DIG_DONE message
			nOutBufferCount--;                                                   
			// Assume the buffer was full, if it wasn't we'll fix this in IW_DIG_DONE
			pClient->lpRecording->dwBytesRecorded = pClient->lpRecording->dwBufferLength;
			if(pClient->nStatus & WIS_STOPPING){
				//      If there is a current "on-deck" buffer return it to the client
				if(pClient->lpHoldBuf)                       
					ReturnRecordBuffer(pClient,pClient->lpHoldBuf);
				//      Save the current buffer as the new "on-deck" buffer
				pClient->lpHoldBuf = pClient->lpRecording;          
			}else
			// Otherwise return the current buffer to the client
				ReturnRecordBuffer(pClient,pClient->lpRecording);
			pClient->lpRecording = NULL;
			break;
	}
    return IW_DIG_BUFFER_DONE;
}

