/***************************************************************************
*   NAME:  WAVEIN.C $Revision: 1.6 $
**  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: wavein.c $
* Revision 1.6  1995/10/24 11:26:38  sdsmith
* Fixes for Windows 95 compatability tests
* Revision 1.5  1995/09/05 15:55:32  teckert
* Added PnP style device capabilities structure.
* Revision 1.4  1995/06/05 15:02:13  teckert
* Added Low-Priority input mode support
* Revision 1.3  1995/05/03 14:22:46  teckert
* Revision 1.2  1995/03/01 16:58:54  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 "interwav.h"
#include "wave.h"
#include "sndsys.h"

//#define MM_GRAVIS               34
//#define MM_ULTRASND_WAVEIN      14  // wPID's are left up to the vendor
//#define DRIVER_VERSION  0x0700      // Max and Ver 3.8 release version
extern int nEnabled;
int RecordCallback(int,int,unsigned char far* far*,unsigned long far*); 
extern int nOutBufferCount;


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

FUNCTION DEFINITION:
widGetDevCaps - reports the capabilities of the wave input device

DESCRIPTION:
The wc structure is filled with the capabilities information of the device
and is copied into the provided buffer.

RETURNS: void
*/
static void NEAR PASCAL widGetDevCaps(
	LPBYTE  lpCaps,         // Pointer to a structure to receive the information
	WORD    wSize)          // Size of the structure pointed to by lpCaps
{
	WAVEINCAPS wc;

	wc.wMid = MM_ETEK;
	wc.wPid = MM_INTERWAVE_WAVEIN;
	wc.vDriverVersion = DRIVER_VERSION;
	LoadString(ghModule, STR_IWAVE_WAVEIN, (LPSTR)&(wc.szPname), sizeof(wc.szPname));
		wc.dwFormats =  WAVE_FORMAT_1M08 | WAVE_FORMAT_1S08 |
						WAVE_FORMAT_1M16 | WAVE_FORMAT_1S16 |
						WAVE_FORMAT_2M08 | WAVE_FORMAT_2S08 |
						WAVE_FORMAT_2M16 | WAVE_FORMAT_2S16 |
						WAVE_FORMAT_4M08 | WAVE_FORMAT_4S08 |
						WAVE_FORMAT_4M16 | WAVE_FORMAT_4S16;
	wc.wChannels = 2;

	_fmemcpy(lpCaps,(void far *)&wc,min(wSize,sizeof(wc)));
}


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

FUNCTION DEFINITION:
widValidFormat - determines if the format is one supported by the driver

DESCRIPTION:
The format is checked to see if it is supported by the hardware.

RETURNS: 1 if the format is supported, 0 otherwise
*/
int FAR PASCAL widValidFormat(
	LPWAVEFORMAT lpFmt)     // a pointer to the wave format structure
{        
	if(lpFmt->wFormatTag != WAVE_FORMAT_PCM)
	return 0;
	
	if(lpFmt->nChannels < 1 || lpFmt->nChannels > 2)
	return 0;
	
	if(lpFmt->nSamplesPerSec < 2402 || lpFmt->nSamplesPerSec > 48000)
	return 0;
		
	if(((LPPCMWAVEFORMAT)lpFmt)->wBitsPerSample != 8 &&
	   ((LPPCMWAVEFORMAT)lpFmt)->wBitsPerSample != 16)
		return 0;
		
	if(lpFmt->nAvgBytesPerSec != lpFmt->nChannels * ((LPPCMWAVEFORMAT)lpFmt)->wBitsPerSample / 8 *
							 lpFmt->nSamplesPerSec)
	return 0;
	
	return 1;
}


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

FUNCTION DEFINITION:
widMessage - message entry point for wave input device

DESCRIPTION:
This function conforms to the standard wave input driver message proc (widMessage),
which is documented in mmddk.d.

RETURNS: doubleword whose meaning depends upon the message, usually it is an
mmsystem error code.
*/
DWORD FAR PASCAL _loadds widMessage(
	WORD id,                // identifies the device
	WORD msg,               // specifies the message
	DWORD dwUser,           // identifies the client
	DWORD dwParam1,         // message specific parameter one
	DWORD dwParam2)         // message specific parameter two
{
LPWAVEINCLIENT pInClient;       // pointer to client information structure
DWORD dwResult;

	if (!nEnabled) {
#ifdef DEBUG
	OutputDebugString("widMessage called while disabled\r\n");
#endif
	if (msg == WIDM_GETNUMDEVS)
		return 0L;
	else
		return MMSYSERR_NOTENABLED;
	}

	// this driver only supports one device
	if (id != 0) {               
#ifdef DEBUG
	OutputDebugString("widMessage called with bad device ID\r\n");
#endif
	return MMSYSERR_BADDEVICEID;
	}
                  
        if(dwUser == 1){
		pInClient = &RecordClientOne;
        }else if(dwUser == 2){
		pInClient = &RecordClientTwo;
	}else{
		pInClient = NULL;
	}
	
	switch (msg) {

	case WIDM_GETNUMDEVS:
#ifdef DEBUG
		OutputDebugString("WIDM_GETNUMDEVS\r\n");
#endif
		return 1L;

	case WIDM_GETDEVCAPS:
#ifdef DEBUG
		OutputDebugString("WIDM_GETDEVCAPS\r\n");
#endif
		if(wIWDriverFlags & IWDF_WINDOWS95){
		    widGetDevCaps(((MDEVICECAPSEX FAR *)dwParam1)->pCaps, (WORD)((MDEVICECAPSEX FAR *)dwParam1)->cbSize);
		}else{
		    widGetDevCaps((LPBYTE)dwParam1, (WORD)dwParam2);
		}
		return 0L;

	case WIDM_OPEN:
#ifdef DEBUG
		OutputDebugString("WIDM_OPEN\r\n");
#endif  
		if(!widValidFormat((LPWAVEFORMAT)((LPWAVEOPENDESC)dwParam1)->lpFormat))
			return WAVERR_BADFORMAT;
			
		// did they just want format information?
		if (dwParam2 & WAVE_FORMAT_QUERY)
			return MMSYSERR_NOERROR;
			
		dwResult = widOpen((LPWAVEOPENDESC)dwParam1,dwParam2,(LPDWORD)dwUser);
		if (dwResult == MMSYSERR_NOERROR) mxdLineChange(1,MM_INTERWAVE_WAVEIN);
						
		return dwResult;

	case WIDM_CLOSE:                                 
#ifdef DEBUG
		OutputDebugString("WIDM_CLOSE\r\n");
#endif
		dwResult = MMSYSERR_INVALPARAM;
		
		if(pInClient->nStatus & WIS_OPEN) {
			dwResult = widClose(pInClient);
			if (dwResult == MMSYSERR_NOERROR) mxdLineChange(0,MM_INTERWAVE_WAVEIN);
		}
			
		return dwResult;

	case WIDM_ADDBUFFER:
#ifdef DEBUG
		OutputDebugString("WIDM_ADDBUFFER\r\n");
#endif
		if(!((LPWAVEHDR)dwParam1)->dwFlags & WHDR_PREPARED)
			return WAVERR_UNPREPARED;
			
		if(((LPWAVEHDR)dwParam1)->dwFlags & WHDR_INQUEUE)
			return WAVERR_STILLPLAYING;
			
		((LPWAVEHDR)dwParam1)->reserved = (DWORD)dwUser;

		dwResult = widAddBuffer(pInClient, (LPWAVEHDR)dwParam1);
		return MMSYSERR_NOERROR;

	case WIDM_START:
#ifdef DEBUG
		OutputDebugString("WIDM_START\r\n");
#endif              
		dwResult = widStart(pInClient);
		return dwResult;

	case WIDM_STOP:
#ifdef DEBUG
		OutputDebugString("WIDM_STOP\r\n");
#endif
		dwResult = widStop(pInClient);
		return dwResult;

	case WIDM_RESET:
#ifdef DEBUG
		OutputDebugString("WIDM_RESET\r\n");
#endif           
		dwResult = widReset(pInClient);
		return 0L;

	case WIDM_GETPOS:
#ifdef DEBUG
		OutputDebugString("WIDM_GETPOS\r\n");
#endif
		return widGetPos(pInClient, (LPMMTIME)dwParam1, (WORD)dwParam2);

	case WIDM_LOWPRIORITY:
#ifdef DEBUG
		OutputDebugString("WIDM_LOWPRIORITY\r\n");
#endif
		if(!(pInClient->nStatus & WIS_OPEN))
		    return MMSYSERR_INVALPARAM;        
		    
		// Check the other client for low-priority access
	        if(id == 1){
		    if(RecordClientTwo.nStatus & WIS_OPEN && RecordClientTwo.nStatus & WIS_LOWPRIORITY)
		    	return MMSYSERR_ALLOCATED;
	        }else{
		    if(RecordClientOne.nStatus & WIS_OPEN && RecordClientOne.nStatus & WIS_LOWPRIORITY)
		    	return MMSYSERR_ALLOCATED;
		}
		
		pInClient->nStatus |= WIS_LOWPRIORITY;
		  
	default:
		return MMSYSERR_NOTSUPPORTED;
	}

	// should never get here...
	return MMSYSERR_NOTSUPPORTED;
}

/****************************************************************************
Low-Priority Wave In Support Functions
*/

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

FUNCTION DEFINITION:
SuspendLPClient

DESCRIPTION:
RETURNS:
*/   
int SuspendLPClient(
	LPWAVEINCLIENT pClient, 	// a pointer to the WAVEINCLIENT
	int nByWhom)			// indicates wave-in or wave-out
{
    if(!(pClient->nStatus & WIS_LOWPRIORITY))return 0;
    if(nByWhom!=WIS_LPBYWAVEIN && nByWhom!=WIS_LPBYWAVEOUT)return 0;
    
    pClient->nStatus |= nByWhom;
    if(!(pClient->nStatus & WIS_LPSUSPENDED)){
	OS_PUSH_DISABLE();
	// if we are actually running we need to pause the recording
	if(pClient->nVoice != -1){
	    pClient->nStatus |= WIS_STOPPING;
	    pClient->nStatus |= WIS_LPSUSPENDING;
	    iw_codec_stop_record(pClient->nVoice);
	    pClient->nStatus &= ~WIS_STOPPING;
	    pClient->nStatus &= ~WIS_LPSUSPENDING;
	    pClient->nStatus |= WIS_LPSUSPENDED;
	}
	OS_POPF();

	pClient->nStatus &= ~WIS_OVERFLOW;
    }
    return 1;
}

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

FUNCTION DEFINITION:
ResumeLPClient

DESCRIPTION:
RETURNS:
*/
int ResumeLPClient(
	int nByWhom)			// indicates wave-in or wave-out
{
    LPWAVEINCLIENT pClient;
    
    if(nByWhom!=WIS_LPBYWAVEIN && nByWhom!=WIS_LPBYWAVEOUT)return 0;

    if(RecordClientOne.nStatus & WIS_LOWPRIORITY)
        pClient = &RecordClientOne;
    else if(RecordClientOne.nStatus & WIS_LOWPRIORITY)
        pClient = &RecordClientOne;
    else
    	return 1;

    pClient->nStatus &= ~nByWhom;
    if(pClient->nStatus & WIS_LPSUSPENDED){
    	if(!(pClient->nStatus & WIS_LPBYWAVEIN) && !(pClient->nStatus & WIS_LPBYWAVEOUT)){
    	    if(pClient->nStatus & WIS_STARTED){
    	    	// If there is a partially filled buffer then we need to restart the recording
    	    	// here, otherwise just call widStart
    	    	if(pClient->lpHoldBuf){
		    pClient->nVoice = iw_codec_record_digital((char far *)pClient->lpHoldBuf->lpData + 
		    				pClient->lpHoldBuf->dwBytesRecorded,// offset buffer
						pClient->lpHoldBuf->dwBufferLength - 
						pClient->lpHoldBuf->dwBytesRecorded,// 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)
		        return 0;
    	        }else{
    	            if(widStart(pClient)==IW_NO_MORE_VOICES)
    	                return 0;
    	        }
    	    }
    	    pClient->nStatus &= ~WIS_LPSUSPENDED;    	    
    	}
    }
    return 1;
}
