/***************************************************************************
*   NAME:  WAVEOUT.C $Revision: 1.13 $
**  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: waveout.c $
* Revision 1.13  1995/11/06 13:23:35  sdsmith
* Version Fixup
* Revision 1.12  1995/10/31 15:00:00  mks
* Added #ifndef for WAVECAPS_SAMPLEACCURATE
* Revision 1.11  1995/10/27 15:38:42  teckert
* Fixed more ACT failures
* Revision 1.10  1995/10/24 11:26:39  sdsmith
* Fixes for Windows 95 compatability tests
* Revision 1.9  1995/09/19 12:34:31  teckert
* Fixed bug: GPF when opening multiple wave clients.
* Revision 1.8  1995/09/05 15:55:31  teckert
* Added PnP style device capabilities structure.
* Revision 1.7  1995/08/14 16:23:27  sdsmith
* Restored save of dwVolume in "no client" case
* Revision 1.6  1995/05/03 14:23:21  teckert
* Revision 1.5  1995/04/20 05:57:16  teckert
* Bug Fixes
* Revision 1.4  1995/04/20 03:54:36  teckert
* Fixed GPF fault on wodClose
* Revision 1.3  1995/04/19 23:34:37  teckert
* Revision 1.2  1995/03/01 16:58:58  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 <string.h>
#include "iw.h"
#include "os.h"
#include "interwav.h"
#include "wave.h"
#include "sndsys.h"

extern int nEnabled;
extern int nCodecVersion;
extern DWORD dwVolume;
#define CODEC_FLAG 1
//#define CODEC_FLAG 0

#ifndef WAVECAPS_SAMPLEACCURATE
#define WAVECAPS_SAMPLEACCURATE 0x0020
#endif

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

FUNCTION DEFINITION:
wodGetDevCaps - returns the capabilities of the wave out device

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

RETURNS: void
*/
void PASCAL wodGetDevCaps(
	LPBYTE lpCaps,          // pointer to the structure that is to receive the info
	WORD wSize)             // size of the structure pointed to by lpCaps
{
WAVEOUTCAPS wc;

	wc.wMid = MM_ETEK;
	wc.wPid = MM_INTERWAVE_WAVEOUT;
	wc.vDriverVersion = DRIVER_VERSION;
	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;
        wc.dwSupport = WAVECAPS_VOLUME | WAVECAPS_LRVOLUME | WAVECAPS_SAMPLEACCURATE;
	LoadString(ghModule, STR_IWAVE_WAVEOUT, (LPSTR)&(wc.szPname), sizeof(wc.szPname));
	_fmemcpy(lpCaps, &wc, min(wSize, sizeof(wc)));
}


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

FUNCTION DEFINITION:
wodValidFormat - 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 wodValidFormat(
	LPWAVEFORMAT lpFmt,             // points to the wave format structure
	int nCodec)                     // indicates CODEC wave output if true
{        
	DWORD nBytesPerSec;
	
	if(lpFmt->wFormatTag != WAVE_FORMAT_PCM)
	return 0;
	
	if(lpFmt->nSamplesPerSec < 4000 || lpFmt->nSamplesPerSec > (nCodec?48000:44100))
	return 0;

	nBytesPerSec = lpFmt->nSamplesPerSec;
	
	if(lpFmt->nChannels < 1 || lpFmt->nChannels > 2)
	return 0;
						       
	if(lpFmt->nChannels == 2)
		nBytesPerSec *= 2;
		
	if(((LPPCMWAVEFORMAT)lpFmt)->wBitsPerSample != 8 &&
	   ((LPPCMWAVEFORMAT)lpFmt)->wBitsPerSample != 16)
		return 0;
		
	if(((LPPCMWAVEFORMAT)lpFmt)->wBitsPerSample == 16)
		nBytesPerSec *= 2;

#ifdef STRICTWAVE               
	if(lpFmt->nAvgBytesPerSec != nBytesPerSec)
	return 0;
#endif
	
	return 1;
}



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

FUNCTION DEFINITION:                                                      
wodMessage - message entry point for the wave output device

DESCRIPTION:
This function conforms to the standard Wave output driver message proc (wodMessage),
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 wodMessage(
	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
{
const WAVEFORMAT FAR *lpFmt;    /* pointer to passed format */
LPWAVECLIENT pOutClient;        /* pointer to client information structure */
int nClient;
DWORD dwResult;

	if (!nEnabled){
#ifdef DEBUG
		OutputDebugString("wodMessage called while disabled\r\n");
#endif
		if (msg == WODM_GETNUMDEVS)
			return 0L;
		else
			return MMSYSERR_NOTENABLED;
	}
	
#ifdef DEBUG
		// this driver supports one device
		if (id > 0) {
			OutputDebugString("invalid wave device id\r\n");
			return MMSYSERR_BADDEVICEID;
		}
#endif
	
		pOutClient = (LPWAVECLIENT)dwUser;
		
		switch (msg) {
	
			case WODM_GETNUMDEVS:
#ifdef DEBUG
				OutputDebugString("WODM_GETNUMDEVS\r\n");
#endif
				return 1L;
		
			case WODM_GETDEVCAPS:
#ifdef DEBUG
				OutputDebugString("WODM_GETDEVCAPS\r\n");
#endif                                
				if(wIWDriverFlags & IWDF_WINDOWS95){
				    wodGetDevCaps(((MDEVICECAPSEX FAR *)dwParam1)->pCaps, (WORD)((MDEVICECAPSEX FAR *)dwParam1)->cbSize);
				}else{
				    wodGetDevCaps((LPBYTE)dwParam1, (WORD)dwParam2);
				}
				return MMSYSERR_NOERROR;
		
			case WODM_OPEN:
#ifdef DEBUG
				OutputDebugString("WODM_OPEN\r\n");
#endif
				if(!wodValidFormat((LPWAVEFORMAT)(((LPWAVEOPENDESC)dwParam1)->lpFormat),CODEC_FLAG ))
					return WAVERR_BADFORMAT;
					
				/* did they just want format information? */
				if (dwParam2 & WAVE_FORMAT_QUERY)
					return MMSYSERR_NOERROR;

				dwResult = wodOpen((LPWAVEOPENDESC) dwParam1, dwParam2, (int far *)&nClient, 
									CODEC_FLAG);
				
				if(dwResult == MMSYSERR_NOERROR)
					{
                                        pOutClient = ClientList[nClient];
                                        *((LPWAVECLIENT far *)dwUser) = pOutClient;
					waveCallback(pOutClient, WOM_OPEN, 0L); // sent client his OPEN callback message
					mxdLineChange(1,MM_INTERWAVE_WAVEOUT);
					}
	
				return dwResult;
	
			case WODM_CLOSE:
#ifdef DEBUG
				OutputDebugString("WODM_CLOSE\r\n");
#endif
				dwResult = wodClose(pOutClient);
				if (dwResult == MMSYSERR_NOERROR) mxdLineChange(0,MM_INTERWAVE_WAVEOUT);

				return dwResult;
	
			case WODM_WRITE:
#ifdef DEBUG
				OutputDebugString("WODM_WRITE\r\n");
#endif
				/* check if it's been prepared */
				if (!(((LPWAVEHDR)dwParam1)->dwFlags & WHDR_PREPARED))
					return WAVERR_UNPREPARED;
	
				/* if it is already in our Q, then we cannot do this */
				if ( ((LPWAVEHDR)dwParam1)->dwFlags & WHDR_INQUEUE )
					return WAVERR_STILLPLAYING;
	
				return wodWrite(pOutClient, (LPWAVEHDR)dwParam1);
	
			case WODM_PAUSE:
#ifdef DEBUG
				OutputDebugString("WODM_PAUSE\r\n");
#endif
				if(pOutClient->nStatus & WOS_WRAPPERPAUSED)
					return MMSYSERR_NOERROR;

				return wodPause(pOutClient);
	
			case WODM_RESTART:
#ifdef DEBUG
				OutputDebugString("WODM_RESTART\r\n");
#endif
				if(!(pOutClient->nStatus & WOS_WRAPPERPAUSED))
					return MMSYSERR_NOERROR;

				return wodRestart(pOutClient);
	
			case WODM_RESET:
#ifdef DEBUG
				OutputDebugString("WODM_RESET\r\n");
#endif                                                       
				return wodReset(pOutClient);

			case WODM_BREAKLOOP:
#ifdef DEBUG
				OutputDebugString("WODM_BREAKLOOP\r\n");
#endif
				pOutClient->nStatus |= WOS_BREAKLOOP;
				
				return MMSYSERR_NOERROR;
	
			case WODM_GETPOS:
#ifdef DEBUG
				OutputDebugString("WODM_GETPOS\r\n");
#endif
				return wodGetPos(pOutClient, (LPMMTIME)dwParam1, (WORD)dwParam2);
	
			case WODM_GETVOLUME:
#ifdef DEBUG
				OutputDebugString("WODM_GETVOLUME\r\n");
#endif
				if(pOutClient)
					*((LPDWORD)dwParam1) = pOutClient->dwVolume;
				else
					*((LPDWORD)dwParam1) = dwVolume;
				return MMSYSERR_NOERROR;
	
			case WODM_SETVOLUME:
#ifdef DEBUG
				OutputDebugString("WODM_SETVOLUME\r\n");
#endif
				if(pOutClient){
					pOutClient->dwVolume = dwParam1;
					wodSetVolume(pOutClient);
				}
				else{
				    iw_dig_master_vol(LOWORD(dwParam1)>>9);
                                    dwVolume = dwParam1;
//                                      for(nClient = 0;nClient<=MAXCLIENTS;nClient++)
//                                              if(ClientList[nClient]){
//                                                      ClientList[nClient]->dwVolume = dwVolume;
//                                                      wodSetVolume(ClientList[nClient]);
//                                              }
				}
				return MMSYSERR_NOERROR;
			}
	
	return MMSYSERR_NOTSUPPORTED;
}

