///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Winnov L.P., 1997.  All rights reserved
// hal.c: CAsrc class hal methods implementation.
//
// Modified : C++ -> C
///////////////////////////////////////////////////////////////////////////////

#include "common.h"
#include "time.h"
#include "io.h"
#include "asrc.h"
#include "hal.h"

#ifdef WINKERNEL
#else
#include <mmsystem.h>
#endif

#define		XTAL_UNKNOWN		0
#define		XTAL_8K			1
#define		XTAL_11K		2
#define		XTAL_SPAM		3	/* both xtals */
       
#undef PUBLIC
#undef PRIVATE
#define PUBLIC
#define PRIVATE static

       
//////////////////////////////////////////////////////////////////////////////

UINT nMapVol [256] = {
//      AVOID BEST  FAIR  BEST  POOR  BEST  FAIR  BEST  AVOID BEST  FAIR  BEST  POOR  BEST  FAIR  BEST
//      0x00  0x01  0x02  0x03  0x04  0x05  0x06  0x07  0x08  0x09  0x0A  0x0B  0x0C  0x0D  0x0E  0x0F,
/* 0 */ 0x00, 0x01, 0x01, 0x03, 0x03, 0x05, 0x05, 0x07, 0x07, 0x09, 0x09, 0x0B, 0x0B, 0x0D, 0x0D, 0x0F,
/* 1 */ 0x11, 0x11, 0x11, 0x13, 0x13, 0x15, 0x15, 0x17, 0x17, 0x19, 0x19, 0x1B, 0x1B, 0x1D, 0x1D, 0x1F,
/* 2 */ 0x21, 0x21, 0x21, 0x23, 0x23, 0x25, 0x25, 0x27, 0x27, 0x29, 0x29, 0x2B, 0x2B, 0x2D, 0x2D, 0x2F,
/* 3 */ 0x31, 0x31, 0x31, 0x33, 0x33, 0x35, 0x35, 0x37, 0x37, 0x39, 0x39, 0x3B, 0x3B, 0x3D, 0x3D, 0x3F,
/* 4 */ 0x41, 0x41, 0x41, 0x43, 0x43, 0x45, 0x45, 0x47, 0x47, 0x49, 0x49, 0x4B, 0x4B, 0x4D, 0x4D, 0x4F,
/* 5 */ 0x51, 0x51, 0x51, 0x53, 0x53, 0x55, 0x55, 0x57, 0x57, 0x59, 0x59, 0x5B, 0x5B, 0x5D, 0x5D, 0x5F,
/* 6 */ 0x61, 0x61, 0x61, 0x63, 0x63, 0x65, 0x65, 0x67, 0x67, 0x69, 0x69, 0x6B, 0x6B, 0x6D, 0x6D, 0x6F,
/* 7 */ 0x71, 0x71, 0x71, 0x73, 0x73, 0x75, 0x75, 0x77, 0x77, 0x79, 0x79, 0x7B, 0x7B, 0x7D, 0x7D, 0x7F,
/* 8 */ 0x81, 0x81, 0x81, 0x83, 0x83, 0x85, 0x85, 0x87, 0x87, 0x89, 0x89, 0x8B, 0x8B, 0x8D, 0x8D, 0x8F,
/* 9 */ 0x91, 0x91, 0x91, 0x93, 0x93, 0x95, 0x95, 0x97, 0x97, 0x99, 0x99, 0x9B, 0x9B, 0x9D, 0x9D, 0x9F,
/* A */ 0xA1, 0xA1, 0xA1, 0xA3, 0xA3, 0xA5, 0xA5, 0xA7, 0xA7, 0xA9, 0xA9, 0xAB, 0xAB, 0xAD, 0xAD, 0xAF,
/* B */ 0xB1, 0xB1, 0xB1, 0xB3, 0xB3, 0xB5, 0xB5, 0xB7, 0xB7, 0xB9, 0xB9, 0xBB, 0xBB, 0xBD, 0xBD, 0xBF,
/* C */ 0xC1, 0xC1, 0xC1, 0xC3, 0xC3, 0xC5, 0xC5, 0xC7, 0xC7, 0xC9, 0xC9, 0xCB, 0xCB, 0xCD, 0xCD, 0xCF,
/* D */ 0xD1, 0xD1, 0xD1, 0xD3, 0xD3, 0xD5, 0xD5, 0xD7, 0xD7, 0xD9, 0xD9, 0xDB, 0xDB, 0xDD, 0xDD, 0xDF,
/* E */ 0xE1, 0xE1, 0xE1, 0xE3, 0xE3, 0xE5, 0xE5, 0xE7, 0xE7, 0xE9, 0xE9, 0xEB, 0xEB, 0xED, 0xED, 0xEF,
/* F */ 0xF1, 0xF1, 0xF1, 0xF3, 0xF3, 0xF5, 0xF5, 0xF7, 0xF7, 0xF9, 0xF9, 0xFB, 0xFB, 0xFD, 0xFD, 0xFF};

UINT nLogVol[256] = {
0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x02,0x02,0x02,0x02,0x03,0x03,0x03,0x03,
0x04,0x04,0x04,0x05,0x05,0x05,0x06,0x06,0x06,0x07,0x07,0x07,0x08,0x08,0x08,0x08,
0x09,0x09,0x0a,0x0a,0x0b,0x0b,0x0c,0x0c,0x0d,0x0d,0x0e,0x0e,0x0f,0x0f,0x10,0x10,
0x11,0x11,0x12,0x13,0x13,0x14,0x15,0x15,0x16,0x17,0x17,0x18,0x19,0x19,0x1a,0x1b,
0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,

0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,
0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,
0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,
0x5c,0x5d,0x5e,0x5f,0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,
0x6c,0x6d,0x6e,0x6f,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,
0x7c,0x7d,0x7e,0x7f,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,
0x8c,0x8d,0x8e,0x8f,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,
0x9c,0x9d,0x9e,0x9f,0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,

0xac,0xad,0xae,0xaf,0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,
0xbc,0xbd,0xbe,0xbf,0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc8,0xca,0xcc,0xce,0xd0,
0xd2,0xd5,0xd8,0xdb,0xde,0xe1,0xe4,0xe7,0xea,0xed,0xf0,0xf3,0xf6,0xf9,0xfc,0xff
};

UINT nInverseLog[256] = {
0x03,0x07,0x0b,0x0f,0x12,0x15,0x18,0x1b,0x1f,0x21,0x23,0x25,0x27,0x29,0x2b,0x2d,
0x2f,0x31,0x32,0x34,0x35,0x37,0x38,0x3a,0x3b,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,
0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0x53,
0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,0x60,0x61,0x62,0x63,
0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,0x70,0x71,0x72,0x73,
0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,0x80,0x81,0x82,0x83,
0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,0x90,0x91,0x92,0x93,
0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,0xa0,0xa1,0xa2,0xa3,
0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,0xb0,0xb1,0xb2,0xb3,
0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,0xc0,0xc1,0xc2,0xc3,
0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,0xd0,0xd1,0xd2,0xd3,
0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,0xe0,0xe1,0xe2,0xe3,
0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xee,0xef,0xef,0xf0,0xf0,
0xf1,0xf1,0xf2,0xf2,0xf3,0xf3,0xf4,0xf4,0xf4,0xf5,0xf5,0xf5,0xf6,0xf6,0xf6,0xf7,
0xf7,0xf7,0xf8,0xf8,0xf8,0xf9,0xf9,0xf9,0xfa,0xfa,0xfa,0xfb,0xfb,0xfb,0xfc,0xfc,
0xfc,0xfd,0xfd,0xfd,0xfd,0xfe,0xfe,0xfe,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff,0xff};

///////////////////////////////////////////////////////////////////////////////

PUBLIC void halConstructor (PHAL pHal, PSHARED pShared, PVOID pServiceParm, UINT nBoard, SETMUTECALLBACK pSetSpeakerMute, SETMUTECALLBACK pSetWaveOutMute, DWORD dwInstance)
{
    pHal->m_pService = (PSERVICE)pServiceParm;
    pHal->m_nBoard = nBoard;
    pHal->m_pSetSpeakerMute = pSetSpeakerMute;
    pHal->m_pSetWaveOutMute = pSetWaveOutMute;
    pHal->m_dwInstance = dwInstance;
    pHal->m_pShared = pShared;
    pHal->m_fIsWimp = 2;  // not initialized
}

PUBLIC void halDestructor (PHAL pHal)
{
}
      
PUBLIC UINT halInstanceSize(PHAL pService)
{
	return sizeof(HAL);
}

///////////////////////////////////////////////////////////////////////////////

PRIVATE void Local_Sleep (DWORD dwDelay)
{
#ifdef WINKERNEL
	LARGE_INTEGER liTime;

	liTime.HighPart = 0;
	liTime.LowPart = 10000L * dwDelay;
	KeDelayExecutionThread (
		KernelMode,	//IN KPROCESSOR_MODE  WaitMode,
		FALSE,		//IN BOOLEAN  Alertable,
		&liTime);	//IN PLARGE_INTEGER  Interval
#else
#ifdef WIN32
    Sleep (dwDelay);
#else
    DWORD dwStartTime = timeGetTime ();
    while ((timeGetTime () - dwStartTime)
< dwDelay)
    ;
#endif
#endif
}

///////////////////////////////////////////////////////////////////////////////

PRIVATE UINT halSetTransitionSpeed (PHAL pHal, UINT nTransitionSpeed)
{
    UINT nOldSpeed = sharedGetTransitionSpeed (pHal->m_pShared);
    sharedSetTransitionSpeed (pHal->m_pShared, nTransitionSpeed);
    return nOldSpeed;
}

///////////////////////////////////////////////////////////////////////////////

PUBLIC UINT halReadReg (PHAL pHal, UINT nReg)
{
    return serviceReadReg (pHal->m_pService, nReg);
}

PUBLIC void halWriteReg (PHAL pHal, UINT nReg, UINT nVal)
{
    serviceWriteReg (pHal->m_pService, nReg, nVal);
}

///////////////////////////////////////////////////////////////////////////////

PUBLIC BOOL halIsVideoTuner (PHAL pHal, UINT nVsrc)
{
    return serviceIsVideoTuner (pHal->m_pService, nVsrc);
}

PUBLIC BOOL halIsVideoCamera (PHAL pHal, UINT nVsrc)
{
    return serviceIsVideoCamera (pHal->m_pService, nVsrc);
}

PUBLIC BOOL halIsWimp (PHAL pHal)
{
    if (pHal->m_fIsWimp > 1)
    {
    	halSetPower (pHal, TRUE);
        switch (halReadReg (pHal, HW_PORT_MMA) & HW_FULLID)
        {
	case HW_WAVI1:
	case HW_WAVI95:
	case HW_WAVI97:
	    pHal->m_fIsWimp = 0;
	    break;
	case HW_WIMP:
	default:
	    pHal->m_fIsWimp = 1;
	    break;
	}
    }    	
    return pHal->m_fIsWimp;
}

PRIVATE BOOL halIsVideum2 (PHAL pHal)
{
    if ((sharedGetType (pHal->m_pShared) == TYPE_VIDEUM &&
         sharedGetMinorType (pHal->m_pShared) == MINORTYPE_V2WINNOV)
     || sharedGetType (pHal->m_pShared) == TYPE_VIDEUM10)
    	return TRUE;
    else
    	return FALSE;
}

PRIVATE BOOL halIsWaveOutMono (PHAL pHal)
{
    return halIsVideum2 (pHal);
}

PRIVATE BOOL halIsMonitorOutMono (PHAL pHal)
{
    return halIsVideum2 (pHal);
}

PRIVATE BOOL halIsAuxOutMono (PHAL pHal)
{
    return halIsVideum2 (pHal);
}

PUBLIC BOOL halIs4218 (PHAL pHal)
{
    return (sharedGetHwFlags (pHal->m_pShared) & HWFLAGS_CS4218)?TRUE:FALSE;
}

PUBLIC BOOL halIsWimpCam1 (PHAL pHal)
{
    return (sharedGetType (pHal->m_pShared) == TYPE_WIMPCAM1);
}

PUBLIC BOOL halIsVideoOnly (PHAL pHal)
{
    return (sharedGetMinorType (pHal->m_pShared) == MINORTYPE_VO);
}

PRIVATE BOOL halIsHotshot (PHAL pHal)
{
    return (sharedGetType (pHal->m_pShared) == TYPE_HOTSHOT);
}

PRIVATE BOOL halIsOdin (PHAL pHal)
{
    return (sharedGetType (pHal->m_pShared) == TYPE_ODIN2);
}

PUBLIC BOOL halIsVideum1 (PHAL pHal)
{
    if ((sharedGetType (pHal->m_pShared) == TYPE_VIDEUM)
     && (sharedGetMinorType (pHal->m_pShared) < MINORTYPE_V2WINNOV))
    	return TRUE;
    else
    	return FALSE;
}

///////////////////////////////////////////////////////////////////////////////
// nAtten: Output Attenuation (0 = max volume, 255 = min volume)

///////////////////////////////////////////////////////////////////////////////

PUBLIC DWORD halGetVolume (PHAL pHal, UINT nVolumeID)
{
    switch (nVolumeID)
    {
case HAL_MASTER_VOLUME: 	return (DWORD)sharedGetMasterVolume (pHal->m_pShared);
case HAL_MASTER_BALANCE:	return (DWORD)sharedGetMasterBalance (pHal->m_pShared);
case HAL_WAVEOUT_VOLUME:	return (DWORD)sharedGetWaveOutVolume (pHal->m_pShared);
case HAL_AUX_VOLUME:		return (DWORD)sharedGetAuxVolume (pHal->m_pShared);
case HAL_MONITOR_VOLUME:    	return (DWORD)sharedGetMonitorVolume (pHal->m_pShared);
case HAL_LEFTVOLUME:		return (DWORD)sharedGetLeftVolume (pHal->m_pShared);
case HAL_RIGHTVOLUME:		return (DWORD)sharedGetRightVolume (pHal->m_pShared);
    }
    return 0;    
}

///////////////////////////////////////////////////////////////////////////////
// audio xtal configuration

PUBLIC UINT halGetXtalConfiguration (PHAL pHal)
{
    BOOL bAceState;
    BOOL bMuteState;
    UINT nFreq;
    
    bMuteState = halGetMuteOut (pHal);
    halSetMuteOut (pHal, TRUE);
    bAceState = halGetACE (pHal);
    halSetACE (pHal, TRUE);			// must be running to profile
    nFreq = (UINT)halGetSampleRate (pHal);

#if 0	// some obsolete code for groping the audio xtal configuration
    if (pBoard->nXtal == XTAL_UNKNOWN)
    	ASCOInit (pBoard);    
    if (pBoard->nXtal == XTAL_11K)
    {	// try 8K (SPAM)
	DWORD dwElapsed;
    	pBoard->nXtal = XTAL_SPAM;		// assume std xtal
    	mixerSetFreq (pBoard, 8000);		// set to 8Khz
	dwElapsed = ASMAUDIOTIME ((WORD)pBoard->nBoard);	// time an audio buffer in .8 us increments
    	dwElapsed = (dwElapsed * 838L) / 1000L;	// convert to uS
	if (TryFreq (dwElapsed, 8000L))
    	    pBoard->nXtal = XTAL_SPAM;		// spam board, xtal is selectable
    	else
    	    pBoard->nXtal = XTAL_11K;		// 11Khz xtal, not selectable    	
    }
#endif
    
    // set the xtal type from the eeprom configuration (1 means xtal not present)
    sharedSetXtal (pHal->m_pShared, (sharedGetHwFlags (pHal->m_pShared) & HWFLAGS_XTAL_8K_NOT_PRESENT)  ? 0:XTAL_8K);
    sharedSetXtal (pHal->m_pShared, sharedGetXtal (pHal->m_pShared) | ((sharedGetHwFlags (pHal->m_pShared) & HWFLAGS_XTAL_11K_NOT_PRESENT) ? 0:XTAL_11K));
        	
    if (nFreq) halSetSampleRate (pHal, nFreq);
    halSetMuteOut (pHal, bMuteState);
    return sharedGetXtal (pHal->m_pShared);    
}

PUBLIC void halSetXtalConfiguration (PHAL pHal, UINT nVal)
{
    sharedSetXtal (pHal->m_pShared, nVal);
}

///////////////////////////////////////////////////////////////////////////////

PRIVATE void halSetSampleControl (PHAL pHal, UINT nSampleControl)
{
    UINT old;
    
    old = halReadReg (pHal, HW_PORT_FREQ);
    halWriteReg (pHal, HW_PORT_FREQ, (nSampleControl & HW_FREQFIELD) | (old & ~HW_FREQFIELD));
}

///////////////////////////////////////////////////////////////////////////////

#define XTAL_NONE	0	/* current xtal select is not known */
#define XTAL_DEFAULT	1	/* the one and only xtal, select is nop */
#define XTAL_MM		2	/* select the multimedia xtal */
#define XTAL_COM	3	/* select the communications xtal */

PRIVATE void halSetXtalSelect (PHAL pHal, UINT nXtalSel, UINT nSampleControl)
{
    UINT nVal;	//nVal2,nVal3;
#ifdef MUTW_WHILE_CHANGING    
    BOOL fWaveOutMute, fAuxMute;
#endif    
    UINT nCurrentXtalSel;
#ifdef CACHE_SAMPLE_RATE    
    UINT nCurrentSampleControl;
#endif
    
    switch (sharedGetType (pHal->m_pShared))
    {
	case TYPE_MOVIEMAN:
	case TYPE_SPAM2:
	default:
	    return;	// not supported
	case TYPE_ODIN2:
	case TYPE_HOTSHOT:
	case TYPE_ALARIS:
	case TYPE_VIDEUM:
	case TYPE_VIDEUM10:
	    // IMIBIT is the crystal select (even for W97)
	    nVal = halReadReg (pHal, HW_PORT_CTL);
	    nCurrentXtalSel = (nVal & HW_IMIBIT)?(UINT)0:(UINT)1;
	    break;
	case TYPE_WIMPCAM1:
	    // AUDXSEL in MMA selects the crystal
	    nVal = halReadReg (pHal, HW_PORT_MMA);
	    nCurrentXtalSel = (nVal & HW_AUDXSEL)?(UINT)1:(UINT)0;
	    break;
    }
    
#ifdef CACHE_SAMPLE_RATE    
    nCurrentSampleControl = halReadReg (pHal, HW_PORT_FREQ) & HW_FREQFIELD;
            
    if ((nCurrentXtalSel == nXtalSel)			// same xtal
     && (nCurrentSampleControl == nSampleControl))	// same sample control
    	return;						// no change (avoids clicks)
#endif
    	
    sharedSetXtalSelect (pHal->m_pShared, nXtalSel);
    sharedSetSampleControl (pHal->m_pShared, nSampleControl);
    
    switch (nXtalSel)
    {
	default:
	case XTAL_NONE:
	case XTAL_DEFAULT:
	case XTAL_MM:
	    nXtalSel = 0;
	    break;
	case XTAL_COM:
     	    nXtalSel = 1;
	    break;
    }
        
    switch (sharedGetType (pHal->m_pShared))
    {
	case TYPE_ODIN2:
	case TYPE_HOTSHOT:
	case TYPE_ALARIS:
	case TYPE_VIDEUM:
	case TYPE_VIDEUM10:
	    // IMIBIT is the crystal select (even for W97)
	    nVal = halReadReg (pHal, HW_PORT_CTL);
		
	    if (((nVal & HW_IMIBIT)?(UINT)0:(UINT)1) == nXtalSel)
		halSetSampleControl (pHal, nSampleControl);		// change clock while ASCO is reset
	    else
	    {   
#ifdef MUTE_WHILE_CHANGING	    
		fWaveOutMute = halGetWaveOutMute (pHal);
		fAuxMute = halGetAuxMute (pHal);
		halSetWaveOutMute (pHal, TRUE);
		halSetAuxMute (pHal, TRUE);
		
		nVal = nVal | HW_IMCBIT;			// hold ASCO in reset
		halWriteReg (pHal, HW_PORT_CTL, nVal);		// reset
#endif	
		            
		nVal = nVal & ~HW_IMIBIT;			// select xtal
		if (!nXtalSel) 
		    nVal |= HW_IMIBIT;                  	// "
		halWriteReg (pHal, HW_PORT_CTL, nVal);		// reset and select xtal
		halSetSampleControl (pHal, nSampleControl);	// change clock while ASCO is reset
			
#ifdef MUTE_WHILE_CHANGING		
		// delay to extend reset, allow xtal to start oscillating
		hack Local_Sleep (10);
		// drop reset    
		nVal = nVal & ~HW_IMCBIT;			// turn off ASCO reset
		halWriteReg (pHal, HW_PORT_CTL, nVal);		// just select xtal
	        serviceClearAudioBuffer (pHal->m_pService);
	        
		halSetWaveOutMute (pHal, fWaveOutMute);
		halSetAuxMute (pHal, fAuxMute);
#endif		
	    }
	    break;
	case TYPE_MOVIEMAN:
	case TYPE_SPAM2:
	    halSetSampleControl (pHal, nSampleControl);
	    nVal = halReadAttnReg (pHal);
	    nVal = nVal & ~HW_AFSBIT;
	    if (nXtalSel) 
		nVal |= HW_AFSBIT;
	    halWriteAttnReg (pHal, nVal);
	    break;         
	case TYPE_WIMPCAM1:
#ifdef CACHE_SAMPLE_RATE    
	    nVal = halReadReg (pHal, HW_PORT_MMA);
		
	    if (((nVal & HW_AUDXSEL)?(UINT)1:(UINT)0) == nXtalSel)
		halSetSampleControl (pHal, nSampleControl);		// change clock while ASCO is reset
	    else
#endif
	    {
#ifdef MUTE_WHILE_CHANGING
		fWaveOutMute = halGetWaveOutMute (pHal);
		fAuxMute = halGetAuxMute (pHal);
		halSetWaveOutMute (pHal, TRUE);
		halSetAuxMute (pHal, TRUE);
#endif			
		serviceClearAudioBuffer (pHal->m_pService);
		serviceWimpCamSampleRateChangeBegin (pHal->m_pService);

		nVal = nVal & ~HW_AUDXSEL;			// select xtal
		if (nXtalSel) 
		    nVal |= HW_AUDXSEL;                	// "
		halWriteReg (pHal, HW_PORT_MMA, nVal);			// reset and select xtal
		halSetSampleControl (pHal, nSampleControl);		// change clock while ASCO is reset
		// delay to extend reset, allow xtal to start oscillating
		Local_Sleep (10);
		
		serviceWimpCamSampleRateChangeEnd (pHal->m_pService, nVal);

	    	serviceClearAudioBuffer (pHal->m_pService);
#ifdef MUTE_WHILE_CHANGING
		halSetWaveOutMute (pHal, fWaveOutMute);
		halSetAuxMute (pHal, fAuxMute);
#endif
	    }
	    break;
	default:
	    break;	
    }    
}

//////////////////////////////////////////////////////////////////////////////
// wave input source selection
// sources have been assigned as follows: 
//	Source		MovMan	HotShot	Odin-2
// 0: ASRC_ZEYE		0		-
// 1: ASRC_MIC		1       1	1
// 2: ASRC_TUNER	2
// 3: ASRC_LINE         3       3	0
// 4: ASRC_CD           4       4	3
// 5: not used          5
// 6: not used          6
// 7: not used          7
// source select bits are defined as follows:
// S2: GAIN reg ISL:ISR (inverted, set in tandem)
// S1: ATTN ASS2.1
// S0: ATTN ASS2.0
//
// software tracks the gain associated with each source

#define NAUDIOSOURCES	8
//   Videum				    mic MXC lin AUX
//					eye mic tun lin cda tl1 tl2 tl3
//   MovieMan				0   1   2   3   4   5   6   7
UINT OdinAudioSource [NAUDIOSOURCES] = {5,  6,  0,  4,  7,  1,  2,  3};
UINT ShotAudioSource [NAUDIOSOURCES] = {4,  0,  4,  3,  4,  4,  4,  4};
UINT Vid2AudioSource [NAUDIOSOURCES] = {1,  2,  1,  0,  3,  1,  6,  7};
UINT WCamAudioSource [NAUDIOSOURCES] = {7,  0,  7,  4,  7,  7,  7,  7}; // lin index=camera mic, mic index=headset mic

PUBLIC void halSetSource (PHAL pHal, UINT nOldLogicalSource, UINT nLogicalSource)
{
    UINT nnew, old;
    BOOL bMute;
    UINT nSourceSelect;

    sharedSetSource (pHal->m_pShared, nLogicalSource);
        
    switch (sharedGetType (pHal->m_pShared))
    {	// translate source select parameter
case TYPE_ODIN2:    
    	nSourceSelect = OdinAudioSource [nLogicalSource];
  	break;
case TYPE_MOVIEMAN:
case TYPE_SPAM2:
	nSourceSelect = nLogicalSource;
	break;
case TYPE_ALARIS:	// if nLogicalSource, set the VID2 bit (B02)
    	old = halReadAttnReg (pHal);
	old &= ~HW_ASS2FIELD_MIC;
	if (nLogicalSource) old |= HW_ASS2FIELD_MIC;
	halWriteAttnReg (pHal, old);

	// select AIN1R/L
	old = halReadGainReg (pHal);
	old &= ~(HW_ISLBIT | HW_ISRBIT);
	halWriteGainReg (pHal, old);
	return;
case TYPE_WIMPCAM1:	// source select is done by ASCO
	nSourceSelect = WCamAudioSource [nLogicalSource];
	break;	
default:
case TYPE_HOTSHOT:
case TYPE_VIDEUM:
case TYPE_VIDEUM10:
	if (halIsVideum2 (pHal))
	    nSourceSelect = Vid2AudioSource [nLogicalSource];
	else
	    nSourceSelect = ShotAudioSource [nLogicalSource];
	break;  	
    }

    // the output mixer goes nuts when changing from Mic to another source
    // mute the monitor path while changing from Mic
    if (serviceIsMic (pHal->m_pService, nOldLogicalSource))
    {
        UINT nPrev = halSetTransitionSpeed (pHal, TRANSITION_FAST);
    	bMute = halGetMonitorMute (pHal);
    	halSetMonitorMute (pHal, TRUE);
    	halSetTransitionSpeed (pHal, nPrev);
    }

    // if source is CD, use ISL/ISR to select
    // S2 is selected via ISL/ISR
    old = halReadGainReg (pHal);
    if (nSourceSelect & 4) old &= ~(HW_ISLBIT | HW_ISRBIT);
    else	           old |=  (HW_ISLBIT | HW_ISRBIT);

    halWriteGainReg (pHal, old);

    // source S1:S2 is set via GAIN reg
    old = halReadAttnReg (pHal);
    nnew = ((nSourceSelect
<<2) & HW_ASS2FIELD) | (old & ~HW_ASS2FIELD);
    halWriteAttnReg (pHal, nnew /* & ATTN_MASK */);

    // restore state of monitor mute
    if (serviceIsMic (pHal->m_pService, nOldLogicalSource))
    {
    	UINT nPrev;
    	Local_Sleep (200);
    	nPrev = halSetTransitionSpeed (pHal, TRANSITION_NORMAL);
    	halSetMonitorMute (pHal, bMute);
    	halSetTransitionSpeed (pHal, nPrev);
    }
    
    // Select between Camera and Tuner on MXC
    if (serviceIsCamera (pHal->m_pService, nLogicalSource))
    	serviceSetMxcMux (pHal->m_pService, 1);
    if (serviceIsTuner (pHal->m_pService, nLogicalSource))
    	serviceSetMxcMux (pHal->m_pService, 0);
    	
    // reflect new source select to monitor volume
    halUpdateVolume (pHal);    
}

PUBLIC UINT halGetSource (PHAL pHal)
{
    return sharedGetSource (pHal->m_pShared);
}

PUBLIC UINT halGetDefaultSource (PHAL pHal)
{
    return serviceGetDefaultSource (pHal->m_pService, sharedGetType (pHal->m_pShared));
}

// Codec abstraction for 4218/4216 and WIMP

BYTE bReverse [256] = {
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, 
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 
0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, 
0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, 
0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 
0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, 
0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, 
0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 
0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, 
0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, 
0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 
0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, 
0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, 
0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 
0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, 
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF};

PRIVATE void Local_XlatBuffer (LPBYTE lpBuf, UINT nSize)
{
	UINT i;
	BYTE b;

	for (i = 0; (i
< nSize); i+=2)
	{
		b = bReverse [*lpBuf];
		*lpBuf = bReverse [*(lpBuf+1)];
		*(lpBuf+1) = b;
		lpBuf += 2;
	}
}

#pragma optimize("",off)

PRIVATE DWORD halXlatBuffer (PHAL pHal, LPVOID lpBuf, UINT nSize)
{
    if (!halIsWimp (pHal)) return 0;

    // reverse the bit order of the audio sample data
#ifdef WIN32
#ifdef ix86
    _asm {    
    mov	edi,lpBuf		; edi->sample buffer
    mov	ecx,nSize		; loop count
    shr	ecx,1			; is left + right samples
    mov	ebx,0			; zero extend index
next_word:    
    mov	bl,[edi]		; ls byte of left channel
    mov	ah,bReverse[ebx]	; reverse ls byte order
    mov	bl,[edi]+1		; ms byte of left channel
    mov	al,bReverse[ebx]	; reverse ms byte order
    mov	[edi],ax		; save reverse left sample
    add	edi,2			; next word
    loop next_word
    }
#else	//!ix86
	Local_XlatBuffer (lpBuf, nSize);
#endif	//!ix86
#else	//!WIN32
    _asm {    
    les	di,lpBuf		; es:di->sample buffer
    mov	cx,nSize		; loop count
    shr	cx,1			; is left + right samples
    mov	bh,0			; zero extend index
next_word:    
    mov	bl,es:[di]		; ls byte of left channel
    mov	ah,bReverse[bx]		; reverse ls byte order
    mov	bl,es:[di]+1		; ms byte of left channel
    mov	al,bReverse[bx]		; reverse ms byte order
    mov	es:[di],ax		; save reverse left sample
    add	di,2			; next word
    loop next_word
    }
#endif

    return 0;        
}

#pragma optimize("",on)

PUBLIC DWORD halXlatReadBuffer (PHAL pHal, LPVOID lpBuf, UINT nSize)
{
    return halXlatBuffer (pHal, lpBuf, nSize);
}

PUBLIC DWORD halXlatWriteBuffer (PHAL pHal, LPVOID lpBuf, UINT nSize)
{
    return halXlatBuffer (pHal, lpBuf, nSize);
}

PRIVATE void halASCOWriteReg (PHAL pHal, UINT nReg, UINT nVal)
{
    if (halIsWimp (pHal))
    	nVal = (((UINT)bReverse [nVal & 0xff])
<< 8) |
    	        ((UINT)bReverse [(nVal >> 8) & 0xff]);
    halWriteReg (pHal, nReg, nVal);
}

PRIVATE UINT halASCOReadReg (PHAL pHal, UINT nReg)
{
    UINT nVal;
    
    nVal = halReadReg (pHal, nReg);
    if (halIsWimp (pHal))
    	nVal = (((UINT)bReverse [nVal & 0xff])
<< 8) |
    	        ((UINT)bReverse [(nVal >> 8) & 0xff]);
    return nVal;
}

PUBLIC void halWriteAttnReg (PHAL pHal, UINT nVal)
{
    halASCOWriteReg (pHal, HW_PORT_ATTN, nVal);
    sharedSetAttnReg (pHal->m_pShared, nVal);
}

PUBLIC UINT halReadAttnReg (PHAL pHal)
{
    return sharedGetAttnReg (pHal->m_pShared);	//ASCOReadReg (HW_PORT_ATTN);
}

PUBLIC void halWriteGainReg (PHAL pHal, UINT nVal)
{
    halASCOWriteReg (pHal, HW_PORT_GAIN, nVal);
    sharedSetGainReg (pHal->m_pShared, nVal);
}

PUBLIC UINT halReadGainReg (PHAL pHal)
{
    return sharedGetGainReg (pHal->m_pShared);	//ASCOReadReg (HW_PORT_GAIN);
}

//////////////////////////////////////////////////////////////////////////////

PUBLIC void halSetRightGain (PHAL pHal, UINT nGain)
{
    UINT nVal;

    nGain = nGain>>4;				// scale from 8 bits to 4    
    nVal = halReadGainReg (pHal);			// takes care of bit reversal
    nVal = ((nVal & ~HW_GAINRTFIELD) 
          | (nGain & HW_GAINRTFIELD)) & 0x7ff;
    halWriteGainReg (pHal, nVal);			// takes care of bit reversal
}

PUBLIC UINT halGetRightGain (PHAL pHal)
{
    UINT nGain;

    nGain = halReadGainReg (pHal);			// takes care of bit reversal
    nGain &= HW_GAINRTFIELD;
    nGain = nGain
<<4;				// scale from 4 bits to 8
    return nGain;
}

PUBLIC void halSetLeftGain (PHAL pHal, UINT nGain)
{
    UINT nVal;
    
    nGain = nGain>>4;				// scale gain to 4 bits    
    nVal = halReadGainReg (pHal);        		// takes care of bit reversal
    nVal = ((nVal & ~HW_GAINLFFIELD) 
         | ((nGain<<4) & HW_GAINLFFIELD)) & 0x7ff;
    halWriteGainReg (pHal, nVal);			// takes care of bit reversal
}

PUBLIC UINT halGetLeftGain (PHAL pHal)
{
    UINT nGain;
    
    nGain = halReadGainReg (pHal);			// takes care of bit reversal
    nGain = (nGain & HW_GAINLFFIELD) >> 4;
    nGain = nGain<<4;				// scale from 4 bits to 8
    return nGain;
}

PUBLIC void halSetRightAttenuation (PHAL pHal, UINT nAtten)
{
    UINT nVal;

    nVal = halReadAttnReg (pHal);
    if (halIs4218 (pHal))
    {	// 4218
    	nAtten >>= 3;	// attenuation is 5 bits
    	nVal = (nVal & ~HW_4218_ATTRTFIELD) | ((nAtten<<4) & HW_4218_ATTRTFIELD);
    }
    else
    {	// 4216 or ASCO
    	nAtten >>= 4;	// attenuation is 4 bits
    	nVal = ((nVal & ~HW_ATTRTFIELD) | ((nAtten<<4) & HW_ATTRTFIELD)) & ATTN_MASK;
    }
    halWriteAttnReg (pHal, nVal);
}

PUBLIC void halSetLeftAttenuation (PHAL pHal, UINT nAtten)
{
    UINT nVal;

    nVal = halReadAttnReg (pHal);
    if (halIs4218 (pHal))
    {	// 4218
    	nAtten >>= 3; 	// attenuation is 5 bits
    	nVal = (nVal & ~HW_4218_ATTLFFIELD) | ((nAtten<<9) & HW_4218_ATTLFFIELD);
    }
    else
    {	// 4216 or ASCO
    	nAtten >>= 4;	// attenuation is 4 bits
    	nVal = ((nVal & ~HW_ATTLFFIELD) | ((nAtten<<8) & HW_ATTLFFIELD)) & ATTN_MASK;
    }
    halWriteAttnReg (pHal, nVal);
}

//////////////////////////////////////////////////////////////////////////////
///////////////////////// WAVI PCM Mixer Control /////////////////////////////
//////////////////////////////////////////////////////////////////////////////

// MovieMan
UINT MManMixerPort [NVOLUME_CONTROLS] = 
{HW_PORT_FREQ, HW_PORT_FREQ, HW_PORT_AUDMIX2, HW_PORT_AUDMIX2, HW_PORT_AUDMIX1, HW_PORT_AUDMIX1};
UINT MManMixerMask [NVOLUME_CONTROLS] = 
{HW_AOVFIELD, HW_AOVFIELD, HW_MIXLFIELD, HW_MIXRFIELD, HW_MIXLFIELD, HW_MIXRFIELD};
UINT MManMixerShift [NVOLUME_CONTROLS] =
{8, 8, 0, 8, 0, 8};
UINT MManMixerAddr [NVOLUME_CONTROLS] =
{HAL_AOV, HAL_AOV, HAL_MIX2L, HAL_MIX2R, HAL_MIX1L, HAL_MIX1R};

// ODIN does not have FM, so the FM controls are used for ASCO
UINT OdinMixerPort [NVOLUME_CONTROLS] =
{HW_PORT_AUDMIX2, HW_PORT_AUDMIX2, HW_PORT_FREQ, HW_PORT_FREQ, HW_PORT_AUDMIX1, HW_PORT_AUDMIX1};
UINT OdinMixerMask [NVOLUME_CONTROLS] =
{HW_MIXLFIELD, HW_MIXRFIELD, HW_AOVFIELD, HW_AOVFIELD, HW_MIXLFIELD, HW_MIXRFIELD};
UINT OdinMixerShift [NVOLUME_CONTROLS] =
{0, 8, 8, 8, 0, 8};
UINT OdinMixerAddr [NVOLUME_CONTROLS] =
{HAL_MIX2L, HAL_MIX2R, HAL_AOV, HAL_AOV, HAL_MIX1L, HAL_MIX1R};

// HotShot
UINT ShotMixerPort [NVOLUME_CONTROLS] =
{HW_PORT_AUDMIX1, HW_PORT_AUDMIX1, HW_PORT_FREQ, HW_PORT_FREQ, HW_PORT_AUDMIX2, HW_PORT_AUDMIX2};
UINT ShotMixerMask [NVOLUME_CONTROLS] =
{HW_MIXLFIELD, HW_MIXRFIELD, HW_AOVFIELD, HW_AOVFIELD, HW_MIXLFIELD, HW_MIXRFIELD};
UINT ShotMixerShift [NVOLUME_CONTROLS] =
{0, 8, 8, 8, 0, 8};
UINT ShotMixerAddr [NVOLUME_CONTROLS] = 
{HAL_MIX1L, HAL_MIX1R, HAL_AOV, HAL_AOV, HAL_MIX2L, HAL_MIX2R};

// Videum spin 1
UINT Vdm1MixerPort [NVOLUME_CONTROLS] =
{HW_PORT_AUDMIX2, HW_PORT_AUDMIX2, HW_PORT_FREQ, HW_PORT_FREQ, HW_PORT_AUDMIX1, HW_PORT_AUDMIX1};
UINT Vdm1MixerMask [NVOLUME_CONTROLS] =
{HW_MIXRFIELD, HW_MIXLFIELD, HW_AOVFIELD, HW_AOVFIELD, HW_MIXRFIELD, HW_MIXLFIELD};
UINT Vdm1MixerShift [NVOLUME_CONTROLS] =
{8, 0, 8, 8, 8, 0};
UINT Vdm1MixerAddr [NVOLUME_CONTROLS] = 
{HAL_MIX2L, HAL_MIX2R, HAL_AOV, HAL_AOV, HAL_MIX1L, HAL_MIX1R};

// Videum spin 2
UINT Vdm2MixerPort [NVOLUME_CONTROLS] =
{HW_PORT_AUDMIX2, HW_PORT_AUDMIX2, HW_PORT_FREQ, HW_PORT_FREQ, HW_PORT_AUDMIX1, HW_PORT_AUDMIX1};
UINT Vdm2MixerMask [NVOLUME_CONTROLS] =
{HW_MIXLFIELD, HW_MIXRFIELD, HW_AOVFIELD, HW_AOVFIELD, HW_MIXRFIELD, HW_MIXLFIELD};
UINT Vdm2MixerShift [NVOLUME_CONTROLS] =
{0, 8, 8, 8, 8, 0};
UINT Vdm2MixerAddr [NVOLUME_CONTROLS] = 
{HAL_MIX2L, HAL_MIX2R, HAL_AOV, HAL_AOV, HAL_MIX1L, HAL_MIX1R};

// Alaris QuickVideo
UINT AlrsMixerPort [NVOLUME_CONTROLS] =
{HW_PORT_AUDMIX2, HW_PORT_AUDMIX2, HW_PORT_AUDMIX1, HW_PORT_AUDMIX1, HW_PORT_FREQ, HW_PORT_FREQ};
UINT AlrsMixerMask [NVOLUME_CONTROLS] =
{HW_MIXLFIELD, HW_MIXRFIELD, HW_MIXLFIELD, HW_MIXRFIELD, HW_AOVFIELD, HW_AOVFIELD};
UINT AlrsMixerShift [NVOLUME_CONTROLS] =
{0, 8, 0, 8, 8, 8};
UINT AlrsMixerAddr [NVOLUME_CONTROLS] = 
{HAL_MIX2L, HAL_MIX2R, HAL_MIX1L, HAL_MIX1R, HAL_AOV, HAL_AOV};

#define MAX_VOLUME	255

PRIVATE UINT halGetMixerAddr (PHAL pHal, UINT nMixerID)
{
    UINT nMixerAddr;
    
    if (nMixerID >= NVOLUME_CONTROLS) return 0;
    
    switch (sharedGetType (pHal->m_pShared))
    {
case TYPE_MOVIEMAN:
case TYPE_SPAM2:
	nMixerAddr = MManMixerAddr[nMixerID];
	break;
case TYPE_ODIN2:
	nMixerAddr = OdinMixerAddr[nMixerID];
	break;
case TYPE_HOTSHOT:
	nMixerAddr = ShotMixerAddr[nMixerID];
	break;
case TYPE_ALARIS:
	nMixerAddr = AlrsMixerAddr[nMixerID];
	break;	
case TYPE_VIDEUM:
case TYPE_VIDEUM10:
default:
	if (halIsVideum2 (pHal))
	    nMixerAddr = Vdm2MixerAddr[nMixerID];
	else
	    nMixerAddr = Vdm1MixerAddr[nMixerID];
	break;
    }
    return nMixerAddr;
}

PUBLIC UINT halGetMixer (PHAL pHal, UINT nMixerID)
{
    UINT nMixerAddr;

    nMixerAddr = halGetMixerAddr (pHal, nMixerID);    
#if 0
{
char buf[128];
wsprintf (buf, "\nhalGetMixer: nMixerID=%x, nMixerMixerAddr=%x, nMixerShadow=%x", nMixerID, nMixerAddr, sharedGetMixerShadow (pHal->m_pShared, nMixerAddr));
OutputDebugString (buf);
}            
#endif
    return sharedGetMixerShadow (pHal->m_pShared, nMixerAddr);
}

PRIVATE void halWriteMixerField (PHAL pHal, BOOL bGoFast, UINT nMixerPort, UINT nMixerMask, UINT nMixerShift, UINT nVal)
{
    UINT nData;
    UINT nPrev, nCurr, nFinal;
    int nIncr;
    
#if 0
{
char buf[128];
wsprintf (buf, "\nhalWriteMixerField: nMixerPort=%x, nMixerMask=%x, nMixerShift=%x, nVal=%x", nMixerPort, nMixerMask, nMixerShift, nVal);
OutputDebugString (buf);
}            
#endif

    nData = halReadReg (pHal, nMixerPort);
    nCurr = ((nData & nMixerMask) >> nMixerShift) & 255;
    nPrev = nCurr;
    nFinal = nMapVol [nLogVol[nVal & 255]];
    if (nFinal > nCurr)
    	nIncr = 1;
    else
    	nIncr = -1;
    
    while (nCurr != nFinal)
    {
		if (sharedGetType (pHal->m_pShared) == TYPE_MOVIEMAN)
			nCurr = nFinal;				// ramping feature not supported
		else
			nCurr += nIncr;				// do ramping
		if (nMapVol [nPrev] != nMapVol [nCurr])
		{
   			nData = (nData & ~nMixerMask) | ((nCurr
<< nMixerShift) & nMixerMask);
			halWriteReg (pHal, nMixerPort, nData);
			nPrev = nCurr;
		
			// delay at each step while ramping to minimize noise generated
			serviceDelayStep (pHal->m_pService, bGoFast);
		}
    }
}

PRIVATE void halVideum2WriteMonitor (PHAL pHal, UINT nVol)
{	        	    	    
    halWriteMixerField (pHal, 
        TRUE, 
        Vdm2MixerPort[HAL_MIXER_MONITOR], 
        Vdm2MixerMask[HAL_MIXER_MONITOR], 
        Vdm2MixerShift[HAL_MIXER_MONITOR], 
        nVol);
}

PRIVATE void halVideum2WriteAux (PHAL pHal, UINT nVol)
{
    halWriteMixerField (pHal, 
        TRUE,
        Vdm2MixerPort[HAL_MIXER_AUX], 
        Vdm2MixerMask[HAL_MIXER_AUX], 
        Vdm2MixerShift[HAL_MIXER_AUX], 
        nVol);
}

// On Videum 2, the Monitor and Aux volumes are written in tandem.
PRIVATE void halVideum2WriteMonitorAndAux (PHAL pHal, UINT nMon, UINT nAux)
{
    DWORD dwVol;
    UINT nMonVol, nAuxVol;

    // scale the monitor gain, based on the currently selected input's gain        
    dwVol = max (halGetLeftGain (pHal), halGetRightGain (pHal));	
    dwVol >>= 4;			// scale to 4 bits
    dwVol = min (dwVol, 15);
    nMon = min (nMon, 255);
    dwVol = dwVol * (DWORD)nMon;	// change with input gain	// 12 bit
    dwVol = (dwVol * 144L) / 100L;	// inflate some			// 13 bit
    dwVol = min (dwVol, 0xfff);		// saturate to 12 bits
    dwVol = dwVol / 16;			// scale to 8 bits
    nMonVol = (UINT)dwVol;			// move to 8 bit holder
    nMonVol = min (nMonVol ,255);	// saturate (redundant)
    
    // Aux volume is just the input
    nAuxVol = nAux;
     
    if (halGetMonitorMute (pHal))
    	if (halGetAuxMute (pHal))
    	{   // both muted
    	    halVideum2WriteAux (pHal, 0);
    	    halVideum2WriteMonitor (pHal, 0);
    	}
    	else
    	{
    	    halVideum2WriteAux (pHal, nAuxVol); 	// Monitor muted, but aux is not, only update Aux
    	    halVideum2WriteMonitor (pHal, 0);
    	}
    else
        if (halGetAuxMute (pHal))
        {
            halVideum2WriteAux (pHal, 0);
            halVideum2WriteMonitor (pHal, nMonVol); // Monitor not muted, but aux is, only update monitor
        }
	else
	{	// neither aux or monitor is muted, can write both
	    if ((serviceIsAux (pHal->m_pService, sharedGetSource (pHal->m_pShared)))
	     && nMonVol && nAuxVol)		// skip if due to mute in progress...
	    {	// aux is active on both aux and monitor channels, combine into monitor channel only
	    	nMonVol = nMonVol + nAuxVol;	// quasi-logical "or" of volumes
    		nMonVol = min (nMonVol, 255);	// saturate
    		halVideum2WriteAux (pHal, 0);
    		halVideum2WriteMonitor (pHal, nMonVol);
	    }
	    else
	    {	// aux is only active on the aux channel, and is not on the monitor channel
	    	halVideum2WriteAux (pHal, nAuxVol);
	    	halVideum2WriteMonitor (pHal, nMonVol);
	    }
	}
}

PUBLIC void halSetMixer (PHAL pHal, UINT nMixerID, UINT nVal)
{
    UINT nMixerPort;
    UINT nMixerMask;
    UINT nMixerShift;
    UINT nMixerAddr;
    
    if (nMixerID >= NVOLUME_CONTROLS) return;
    
    switch (sharedGetType (pHal->m_pShared))
    {
case TYPE_MOVIEMAN:
case TYPE_SPAM2:
	nMixerPort = MManMixerPort[nMixerID];
	nMixerMask = MManMixerMask[nMixerID];
	nMixerShift = MManMixerShift[nMixerID];
	nMixerAddr = MManMixerAddr[nMixerID];
	break;
case TYPE_ODIN2:
	if ((nMixerID == HAL_MIXER_LEFT_MONITOR)
	 || (nMixerID == HAL_MIXER_RIGHT_MONITOR))
	    return;	// mixer control not supported by hardware
	nMixerPort = OdinMixerPort[nMixerID];
	nMixerMask = OdinMixerMask[nMixerID];
	nMixerShift = OdinMixerShift[nMixerID];
	nMixerAddr = OdinMixerAddr[nMixerID];
	break;
case TYPE_HOTSHOT:
	if ((nMixerID == HAL_MIXER_LEFT_MONITOR)
	 || (nMixerID == HAL_MIXER_RIGHT_MONITOR))
	    return;	// mixer control not supported by hardware
	nMixerPort = ShotMixerPort[nMixerID];
	nMixerMask = ShotMixerMask[nMixerID];
	nMixerShift = ShotMixerShift[nMixerID];
	nMixerAddr = ShotMixerAddr[nMixerID];
	break;
case TYPE_ALARIS:	
	nMixerPort = AlrsMixerPort[nMixerID];
	nMixerMask = AlrsMixerMask[nMixerID];
	nMixerShift = AlrsMixerShift[nMixerID];
	nMixerAddr = AlrsMixerAddr[nMixerID];
	break;
case TYPE_VIDEUM:
case TYPE_VIDEUM10:
default:
	if (halIsVideum2 (pHal))
	{
	    nMixerPort = Vdm2MixerPort[nMixerID];
	    nMixerMask = Vdm2MixerMask[nMixerID];
	    nMixerShift = Vdm2MixerShift[nMixerID];
	    nMixerAddr = Vdm2MixerAddr[nMixerID];
	}
	else
	{
	    if ((nMixerID == HAL_MIXER_LEFT_MONITOR)
	     || (nMixerID == HAL_MIXER_RIGHT_MONITOR))
	    	return;	// mixer control not supported by hardware
	    nMixerPort = Vdm1MixerPort[nMixerID];
	    nMixerMask = Vdm1MixerMask[nMixerID];
	    nMixerShift = Vdm1MixerShift[nMixerID];
	    nMixerAddr = Vdm1MixerAddr[nMixerID];
	}
	break;
    }
    
    nVal = min (nVal, MAX_VOLUME);
    sharedSetMixerShadow (pHal->m_pShared, nMixerAddr, nVal);

    // check individual volume mute controls
    if (sharedGetType (pHal->m_pShared) == TYPE_ALARIS)
    switch (nMixerID)
    {
// logical mixer settings for Alaris
case HAL_LEFT_VOLUME:
case HAL_RIGHT_VOLUME:
	if (halGetMuteOut (pHal))
	    nVal = 0;	// do not modify output state while muted
case HAL_LINE1_VOLUME:
case HAL_MIC_VOLUME:
case HAL_LINE2_VOLUME:
	break;	// not implemented
    }
    else
    if (halIsVideum2 (pHal))
    {
    	switch (nMixerID)
    	{
case HAL_MIXER_LEFT_SPEAKER:
case HAL_MIXER_RIGHT_SPEAKER:
	    if (halGetMuteOut (pHal))
	    	nVal = 0;
	    break;
case HAL_MIXER_WAVEOUT:
	    if (halGetWaveOutMute (pHal))
	        nVal = 0;
	    break;
case HAL_MIXER_MONITOR:
case HAL_MIXER_AUX:
	    halVideum2WriteMonitorAndAux (pHal, 
	        sharedGetMixerShadow (pHal->m_pShared, Vdm2MixerAddr[HAL_MIXER_MONITOR]), 
	        sharedGetMixerShadow (pHal->m_pShared, Vdm2MixerAddr[HAL_MIXER_AUX]));
	    return;
        }
    }
    else
    switch (nMixerID)
    {
case HAL_MIXER_LEFT_WAVEOUT:
case HAL_MIXER_RIGHT_WAVEOUT:
	if (halGetMuteOut (pHal)
	 || halGetWaveOutMute (pHal))
	    nVal = 0;
	break;
case HAL_MIXER_LEFT_MONITOR:
case HAL_MIXER_RIGHT_MONITOR:
	if (halGetMuteOut (pHal)
	 || halGetMonitorMute (pHal))
	    nVal = 0;
	break;
case HAL_MIXER_LEFT_AUX:
case HAL_MIXER_RIGHT_AUX:
	if (halGetMuteOut (pHal)
	 || halGetAuxMute (pHal))
	    nVal = 0;
	break;
    }
        
    halWriteMixerField (pHal, FALSE, nMixerPort, nMixerMask, nMixerShift, nVal);
}

///////////////////////////////////////////////////////////////////////////////
//////////////////////////////// VOLUME ///////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

PRIVATE void halStandardSetVolume (PHAL pHal)
{
    UINT nBal, nVol;
    UINT nLeft, nRight;
    
    nBal = 255 - sharedGetMasterBalance (pHal->m_pShared);
    nVol = sharedGetMasterVolume (pHal->m_pShared)/2;
    if (nBal > 127)
    {
	nLeft = nVol;
	nRight = (nVol * (255 - nBal)) / 128;
    }
    else
    {
	nRight = nVol;
	nLeft = (nVol * (nBal)) / 128;
    }
    halSetRightAttenuation (pHal, 0);
    halSetLeftAttenuation (pHal, 0);
    halSetMixer (pHal, HAL_MIXER_LEFT_WAVEOUT, nLeft * sharedGetWaveOutVolume (pHal->m_pShared) /128);
    halSetMixer (pHal, HAL_MIXER_RIGHT_WAVEOUT, nRight * sharedGetWaveOutVolume (pHal->m_pShared) / 128);
    halSetMixer (pHal, HAL_MIXER_LEFT_MONITOR, nLeft * sharedGetMonitorVolume (pHal->m_pShared) / 128);
    halSetMixer (pHal, HAL_MIXER_RIGHT_MONITOR, nRight * sharedGetMonitorVolume (pHal->m_pShared) / 128);
    halSetMixer (pHal, HAL_MIXER_LEFT_AUX, nLeft * sharedGetAuxVolume (pHal->m_pShared)/ 128);
    halSetMixer (pHal, HAL_MIXER_RIGHT_AUX, nRight * sharedGetAuxVolume (pHal->m_pShared)/ 128);
}

PRIVATE void halVideum1SetVolume (PHAL pHal)
{
    halSetRightAttenuation (pHal, 0);
    halSetLeftAttenuation (pHal, 0);
    halSetMixer (pHal, HAL_MIXER_LEFT_WAVEOUT, (UINT)((DWORD)sharedGetLeftVolume (pHal->m_pShared) * (DWORD)sharedGetWaveOutVolume (pHal->m_pShared) / 256));
    halSetMixer (pHal, HAL_MIXER_RIGHT_WAVEOUT,(UINT)((DWORD)sharedGetRightVolume (pHal->m_pShared) * (DWORD)sharedGetWaveOutVolume (pHal->m_pShared) / 256));
    halSetMixer (pHal, HAL_MIXER_LEFT_MONITOR, (UINT)((DWORD)sharedGetLeftVolume (pHal->m_pShared) * (DWORD)sharedGetMonitorVolume (pHal->m_pShared) / 256));
    halSetMixer (pHal, HAL_MIXER_RIGHT_MONITOR,(UINT)((DWORD)sharedGetRightVolume (pHal->m_pShared) * (DWORD)sharedGetMonitorVolume (pHal->m_pShared) / 256));
    halSetMixer (pHal, HAL_MIXER_LEFT_AUX,     (UINT)((DWORD)sharedGetLeftVolume (pHal->m_pShared) * (DWORD)sharedGetAuxVolume (pHal->m_pShared)/ 256));
    halSetMixer (pHal, HAL_MIXER_RIGHT_AUX,    (UINT)((DWORD)sharedGetRightVolume (pHal->m_pShared) * (DWORD)sharedGetAuxVolume (pHal->m_pShared)/ 256));
}

PRIVATE void halVideum2SetVolume (PHAL pHal)
{
    halSetRightAttenuation (pHal, 0);
    halSetLeftAttenuation (pHal, 0);
    halSetMixer (pHal, HAL_MIXER_WAVEOUT, sharedGetWaveOutVolume (pHal->m_pShared));
    halSetMixer (pHal, HAL_MIXER_LEFT_SPEAKER, sharedGetLeftVolume (pHal->m_pShared));
    halSetMixer (pHal, HAL_MIXER_RIGHT_SPEAKER, sharedGetRightVolume (pHal->m_pShared));
    halSetMixer (pHal, HAL_MIXER_AUX, sharedGetAuxVolume (pHal->m_pShared));
    halSetMixer (pHal, HAL_MIXER_MONITOR, sharedGetMonitorVolume(pHal->m_pShared));	// must be after AUX
}

// invoked when input gain or source is changed.
// be careful not to modify muted mixers

PUBLIC void halUpdateVolume (PHAL pHal)
{
    if (halIsVideum2 (pHal))
    	halVideum2SetVolume (pHal);
}

PRIVATE void halMovieManSetVolume (PHAL pHal)
{       
    UINT nBal, nVol;
    UINT nLeft, nRight;
    
    nBal = 255 - sharedGetMasterBalance (pHal->m_pShared);
    nVol = sharedGetMasterVolume (pHal->m_pShared)/2;
    if (nBal > 127)
    {
	nLeft = 255;
	nRight = (255 * (255 - nBal)) / 128;
    }
    else
    {
	nRight = 255;
	nLeft = (255 * (nBal)) / 128;
    }
    
    halSetRightAttenuation (pHal, 255 - nRight);
    halSetLeftAttenuation (pHal, 255 - nLeft);
    halSetMixer (pHal, HAL_MIXER_LEFT_WAVEOUT, nVol * sharedGetWaveOutVolume (pHal->m_pShared) /128);

    nBal = 255 - sharedGetMasterBalance (pHal->m_pShared);
    nVol = sharedGetMasterVolume (pHal->m_pShared)/2;
    if (nBal > 127)
    {
	nLeft = nVol;
	nRight = (nVol * (255 - nBal)) / 128;
    }
    else
    {
	nRight = nVol;
	nLeft = (nVol * (nBal)) / 128;
    }
    
    halSetMixer (pHal, HAL_MIXER_LEFT_MONITOR, nLeft * sharedGetMonitorVolume (pHal->m_pShared) / 128);
    halSetMixer (pHal, HAL_MIXER_RIGHT_MONITOR, nRight * sharedGetMonitorVolume (pHal->m_pShared) / 128);
    halSetMixer (pHal, HAL_MIXER_LEFT_AUX, nLeft * sharedGetAuxVolume (pHal->m_pShared)/ 128);
    halSetMixer (pHal, HAL_MIXER_RIGHT_AUX, nRight * sharedGetAuxVolume (pHal->m_pShared)/ 128);
}

PRIVATE void halAlarisSetVolume (PHAL pHal)
{
    UINT nBal, nVol;
    UINT nLeft, nRight;
    UINT nScale;
    UINT nCurrent;
    
    nBal = 255 - sharedGetMasterBalance (pHal->m_pShared);
    nVol = sharedGetMasterVolume (pHal->m_pShared)/2;
    if (nBal > 127)
    {
	nLeft = nVol;
	nRight = (nVol * (255 - nBal)) / 128;
    }
    else
    {
	nRight = nVol;
	nLeft = (nVol * (nBal)) / 128;
    }
    
    // scale the volume by the currently selected output volume
    nCurrent = (halReadAttnReg (pHal) & HW_ASS2FIELD_VID2)?1:0;
    if (nCurrent == 0)
    	nScale = sharedGetMonitorVolume (pHal->m_pShared);
    else
    	nScale = sharedGetWaveOutVolume (pHal->m_pShared);
    	
    nLeft = (nLeft * nScale) / 128;
    nRight = (nRight * nScale) / 128;
    
    halSetRightAttenuation (pHal, 0);
    halSetLeftAttenuation (pHal, 0);
    halSetMixer (pHal, HAL_MIXER_LEFT_WAVEOUT, nLeft);
    halSetMixer (pHal, HAL_MIXER_RIGHT_WAVEOUT, nRight);
}

PRIVATE void halWimpCam1SetVolume (PHAL pHal)
{
    halSetRightAttenuation (pHal, 255 - (UINT)(((DWORD)sharedGetRightVolume (pHal->m_pShared) * (DWORD)sharedGetWaveOutVolume (pHal->m_pShared)) / 255));
    halSetLeftAttenuation  (pHal, 255 - (UINT)(((DWORD)sharedGetLeftVolume  (pHal->m_pShared) * (DWORD)sharedGetWaveOutVolume (pHal->m_pShared)) / 255));
}

#define HAL_MASTER_VOLUME	0
#define HAL_MASTER_BALANCE	1
#define HAL_WAVEOUT_VOLUME	2
#define HAL_AUX_VOLUME		3
#define HAL_MONITOR_VOLUME	4
#define HAL_LEFTVOLUME		5
#define HAL_RIGHTVOLUME		6
#define NHALVOLUMES		(HAL_RIGHTVOLUME + 1)

PUBLIC void halSetVolume (PHAL pHal, UINT nVolumeID, UINT nVal)
{
    if (nVolumeID >= NHALVOLUMES) 
    	return;	// bad volume id
            
    switch (sharedGetType (pHal->m_pShared))
    {
case TYPE_MOVIEMAN:
	switch (nVolumeID)
	{
    case HAL_MASTER_VOLUME:
    	    sharedSetMasterVolume (pHal->m_pShared, nVal);
    	    halMovieManSetVolume (pHal);
    	    break;
    case HAL_MASTER_BALANCE:
    	    sharedSetMasterBalance (pHal->m_pShared, nVal);
    	    halMovieManSetVolume (pHal);
    	    break;
    case HAL_WAVEOUT_VOLUME:
    	    sharedSetWaveOutVolume (pHal->m_pShared, nVal);
    	    halMovieManSetVolume (pHal);
    	    break;
    case HAL_AUX_VOLUME:
    	    sharedSetAuxVolume (pHal->m_pShared, nVal);
    	    halMovieManSetVolume (pHal);
    	    break;
    case HAL_MONITOR_VOLUME:
    	    sharedSetMonitorVolume (pHal->m_pShared, nVal);
    	    halMovieManSetVolume (pHal);
    	    break;
	}
	break;
case TYPE_SPAM2:
case TYPE_ODIN2:
case TYPE_HOTSHOT:
case TYPE_VIDEUM:
case TYPE_VIDEUM10:
	if (halIsVideum2 (pHal))
	switch (nVolumeID)
	{
    case HAL_LEFTVOLUME:
    	    sharedSetLeftVolume (pHal->m_pShared, nVal);
    	    halVideum2SetVolume (pHal);
    	    break;
    case HAL_RIGHTVOLUME:    	    
    	    sharedSetRightVolume (pHal->m_pShared, nVal);
    	    halVideum2SetVolume (pHal);
    	    break;
    case HAL_WAVEOUT_VOLUME:
    	    sharedSetWaveOutVolume (pHal->m_pShared, nVal);
    	    halVideum2SetVolume (pHal);
    	    break;
    case HAL_AUX_VOLUME:
    	    sharedSetAuxVolume (pHal->m_pShared, nVal);
    	    halVideum2SetVolume (pHal);
    	    break;
    case HAL_MONITOR_VOLUME:
    	    sharedSetMonitorVolume (pHal->m_pShared, nVal);
    	    halVideum2SetVolume (pHal);
    	    break;
	}
	else
	switch (nVolumeID)
	{
#if 0	
    case HAL_MASTER_VOLUME:
    	    sharedSetMasterVolume (pHal->m_pShared, nVal);
    	    halVideum1SetVolume (pHal);
    	    break;
    case HAL_MASTER_BALANCE:
    	    sharedSetMasterBalance (pHal->m_pShared, nVal);
    	    halVideum1SetVolume (pHal);
    	    break;
#endif    	    
    case HAL_LEFTVOLUME:
    	    sharedSetLeftVolume (pHal->m_pShared, nVal);
    	    halVideum1SetVolume (pHal);
    	    break;
    case HAL_RIGHTVOLUME:    	    
    	    sharedSetRightVolume (pHal->m_pShared, nVal);
    	    halVideum1SetVolume (pHal);
    	    break;
    case HAL_WAVEOUT_VOLUME:
    	    sharedSetWaveOutVolume (pHal->m_pShared, nVal);
    	    halVideum1SetVolume (pHal);
    	    break;
    case HAL_AUX_VOLUME:
    	    sharedSetAuxVolume (pHal->m_pShared, nVal);
    	    halVideum1SetVolume (pHal);
    	    break;
    case HAL_MONITOR_VOLUME:
    	    sharedSetMonitorVolume (pHal->m_pShared, nVal);
    	    halVideum1SetVolume (pHal);
    	    break;               
	}
	break;
case TYPE_ALARIS:
	switch (nVolumeID)
	{
    case HAL_MASTER_VOLUME:
    	    sharedSetMasterVolume (pHal->m_pShared, nVal);
    	    halAlarisSetVolume (pHal);
    	    break;
    case HAL_MASTER_BALANCE:
    	    sharedSetMasterBalance (pHal->m_pShared, nVal);
    	    halAlarisSetVolume (pHal);
    	    break;
    case HAL_WAVEOUT_VOLUME:
    	    sharedSetWaveOutVolume (pHal->m_pShared, nVal);
    	    halAlarisSetVolume (pHal);
    	    break;
    case HAL_MONITOR_VOLUME:
    	    sharedSetMonitorVolume (pHal->m_pShared, nVal);
    	    halAlarisSetVolume (pHal);
    	    break;
	}	
	break;
case TYPE_WIMPCAM1:
	switch (nVolumeID)
	{
    case HAL_LEFTVOLUME:
    	    sharedSetLeftVolume (pHal->m_pShared, nVal);
    	    //sharedSetWaveOutVolume (pHal->m_pShared, max (sharedGetLeftVolume (pHal->m_pShared), sharedGetRightVolume (pHal->m_pShared)));
    	    halWimpCam1SetVolume (pHal);
    	    break;
    case HAL_RIGHTVOLUME:    	    
    	    sharedSetRightVolume (pHal->m_pShared, nVal);
    	    //sharedSetWaveOutVolume (pHal->m_pShared, max (sharedGetLeftVolume (pHal->m_pShared), sharedGetRightVolume (pHal->m_pShared)));
    	    halWimpCam1SetVolume (pHal);
    	    break;
    case HAL_WAVEOUT_VOLUME:	// map wave volume to speaker volume
    	    //sharedSetLeftVolume (pHal->m_pShared, nVal);
    	    //sharedSetRightVolume (pHal->m_pShared, nVal);
    	    sharedSetWaveOutVolume (pHal->m_pShared, nVal);
    	    halWimpCam1SetVolume (pHal);
	}
	break;	
default:	// undefined board type
	break;
    }
}

///////////////////////////////////////////////////////////////////////////////
/////////////////////////////// MUTE Output ///////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

PRIVATE void halMuteMixer (PHAL pHal, UINT nMixerID)
{
    UINT nVolume;

    nVolume = halGetMixer (pHal, nMixerID);
    halSetMixer (pHal, nMixerID, 0);
    sharedSetMixerShadow (pHal->m_pShared, halGetMixerAddr (pHal, nMixerID), nVolume);
}

PRIVATE void halUnmuteMixer (PHAL pHal, UINT nMixerID)
{
    halSetMixer (pHal, nMixerID, sharedGetMixerShadow (pHal->m_pShared, halGetMixerAddr (pHal, nMixerID)));
}

PRIVATE void halMuteSpeaker (PHAL pHal)
{
    halMuteMixer (pHal, HAL_MIXER_LEFT_SPEAKER);
    halMuteMixer (pHal, HAL_MIXER_RIGHT_SPEAKER);
}

PRIVATE void halMuteWaveOut (PHAL pHal)
{
    pHal->m_pSetWaveOutMute (pHal->m_dwInstance, TRUE);	// Mute ASCO

    if (halIsWaveOutMono (pHal))
	halMuteMixer (pHal, HAL_MIXER_WAVEOUT);
    else
    {
    	halMuteMixer (pHal, HAL_MIXER_LEFT_WAVEOUT);
    	halMuteMixer (pHal, HAL_MIXER_RIGHT_WAVEOUT);
    }
}

PRIVATE void halMuteMonitor (PHAL pHal)
{
    if (halIsMonitorOutMono (pHal))
    	halMuteMixer (pHal, HAL_MIXER_MONITOR);
    else
    {
    	halMuteMixer (pHal, HAL_MIXER_LEFT_MONITOR);
    	halMuteMixer (pHal, HAL_MIXER_RIGHT_MONITOR);
    }
}

PRIVATE void halMuteAux (PHAL pHal)
{
    if (halIsAuxOutMono (pHal))
    	halMuteMixer (pHal, HAL_MIXER_AUX);
    else
    {
    	halMuteMixer (pHal, HAL_MIXER_LEFT_AUX);
    	halMuteMixer (pHal, HAL_MIXER_RIGHT_AUX);
    }
}

PRIVATE void halUnmuteSpeaker (PHAL pHal)
{
    halUnmuteMixer (pHal, HAL_MIXER_LEFT_SPEAKER);
    halUnmuteMixer (pHal, HAL_MIXER_RIGHT_SPEAKER);
}

PRIVATE void halUnmuteWaveOut (PHAL pHal)
{        
    pHal->m_pSetWaveOutMute (pHal->m_dwInstance, FALSE);	// unmute ASCO

    if (halIsWaveOutMono (pHal))
    	halUnmuteMixer (pHal, HAL_MIXER_WAVEOUT);
    else
    {
    	halUnmuteMixer (pHal, HAL_MIXER_LEFT_WAVEOUT);
    	halUnmuteMixer (pHal, HAL_MIXER_RIGHT_WAVEOUT);
    }
}

PRIVATE void halUnmuteMonitor (PHAL pHal)
{
    if (halIsMonitorOutMono (pHal))
	halUnmuteMixer (pHal, HAL_MIXER_MONITOR);
    else
    {
    	halUnmuteMixer (pHal, HAL_MIXER_LEFT_MONITOR);
    	halUnmuteMixer (pHal, HAL_MIXER_RIGHT_MONITOR);
    }
}

PRIVATE void halUnmuteAux (PHAL pHal)
{
    if (halIsAuxOutMono (pHal))
	halUnmuteMixer (pHal, HAL_MIXER_AUX);
    else
    {
    	halUnmuteMixer (pHal, HAL_MIXER_LEFT_AUX);
    	halUnmuteMixer (pHal, HAL_MIXER_RIGHT_AUX);
    }
}

// master output volume
PRIVATE void halMuteOut (PHAL pHal)
{
    if (halIsVideum2 (pHal))
    	halMuteSpeaker (pHal);
    else
    if (halIsWimpCam1 (pHal))
    	pHal->m_pSetSpeakerMute (pHal->m_dwInstance, TRUE);
    else
    if (sharedGetType (pHal->m_pShared) == TYPE_ALARIS)
    	halMuteWaveOut (pHal);		// only mute the output volume controls, bypass shadow update
    else
    {
	if (!sharedGetWaveOutMute (pHal->m_pShared))
	    halMuteWaveOut (pHal);
	if (!sharedGetAuxMute (pHal->m_pShared))		// must do aux before monitor...
	    halMuteAux (pHal);
	if (!sharedGetMonitorMute (pHal->m_pShared))
	    halMuteMonitor (pHal);
    }
}

// master output volume
PRIVATE void halUnmuteOut (PHAL pHal)
{
    if (halIsVideum2 (pHal))
    	halUnmuteSpeaker (pHal);
    else
    if (halIsWimpCam1 (pHal))
    	pHal->m_pSetSpeakerMute (pHal->m_dwInstance, FALSE);
    else
    if (sharedGetType (pHal->m_pShared) == TYPE_ALARIS)
        halUnmuteWaveOut (pHal);	// only mute the output volume controls, bypass shadow update
    else
    {
	if (!sharedGetWaveOutMute (pHal->m_pShared))
	    halUnmuteWaveOut (pHal);
	if (!sharedGetAuxMute (pHal->m_pShared))		// must do aux before monitor
	    halUnmuteAux (pHal);
	if (!sharedGetMonitorMute (pHal->m_pShared))
	    halUnmuteMonitor (pHal);
    }
}

PUBLIC void halSetMuteOut (PHAL pHal, BOOL fMute)
{
//    if ((fMute?TRUE:FALSE) == pHal->m_fMuteOut)
//	return;	// no change

    if (fMute)
    {	// mute output
	// set mixer outputs to 0
	halMuteOut (pHal);
	sharedSetSpeakerMute (pHal->m_pShared, TRUE);
    }
    else
    {	// umute out
	sharedSetSpeakerMute (pHal->m_pShared, FALSE);
    	halUnmuteOut (pHal);
    }
}

PUBLIC BOOL halGetMuteOut (PHAL pHal)
{
    return sharedGetSpeakerMute (pHal->m_pShared);
}

PUBLIC void halSetAuxMute (PHAL pHal, BOOL fMute)
{
//    if ((fMute?TRUE:FALSE) == pHal->m_fAuxMute) 
//    	return;	// no change
    if (fMute)
    {
	halMuteAux (pHal);
    	sharedSetAuxMute (pHal->m_pShared, TRUE);
    }
    else
    {
        sharedSetAuxMute (pHal->m_pShared, FALSE);
	halUnmuteAux (pHal);
    }
}

PUBLIC BOOL halGetAuxMute (PHAL pHal)
{
    return sharedGetAuxMute (pHal->m_pShared);
}

PUBLIC void halSetWaveOutMute (PHAL pHal, BOOL fMute)
{
//    if ((fMute?TRUE:FALSE) == pHal->m_fWaveOutMute) return;	// no change
    if (fMute)
    {
		halMuteWaveOut (pHal);
    	sharedSetWaveOutMute (pHal->m_pShared, TRUE);
    }
    else
    {
    	sharedSetWaveOutMute (pHal->m_pShared, FALSE);
    	halUnmuteWaveOut (pHal);
    }
}

PUBLIC BOOL halGetWaveOutMute (PHAL pHal)
{
    return sharedGetWaveOutMute (pHal->m_pShared);
}

PUBLIC void halSetMonitorMute (PHAL pHal, BOOL fMute)
{
//    if ((fMute?TRUE:FALSE) == pHal->m_fMonitorMute) return;	// no change
    if (fMute)
    {
	halMuteMonitor (pHal);
    sharedSetMonitorMute (pHal->m_pShared, TRUE);
    }
    else
    {
    sharedSetMonitorMute (pHal->m_pShared, FALSE);
	halUnmuteMonitor (pHal);
    }
}

PUBLIC BOOL halGetMonitorMute (PHAL pHal)
{
    return sharedGetMonitorMute (pHal->m_pShared);
}

PRIVATE void halDelayMilliseconds (PHAL pHal, DWORD dwDelay)
{
	Local_Sleep (dwDelay);
}

#define SAMPLE_ERR	(((12000L-11025L)/(12000L/1024L))/2L)
PRIVATE BOOL halTryFreq (PHAL pHal, DWORD dwMicroseconds, DWORD dwSampleRate)
{
    DWORD dwSamples;
    
    dwSamples = dwMicroseconds / (1000000L / dwSampleRate);	// us/us/sample = samples
    if ((dwSamples > (1024L - SAMPLE_ERR)) && (dwSamples < (1024L + SAMPLE_ERR)))
    	return TRUE;	// match
    else
    	return FALSE;	// not a match
}

PRIVATE void halASCOReset (PHAL pHal)
{
    UINT nVal;

    if ((!halIsHotshot (pHal)) && (!halIsOdin (pHal))) return;
		
    nVal = halReadReg (pHal, HW_PORT_CTL);

    // drop reset    
    nVal = nVal & ~HW_IMCBIT;			// turn off ASCO reset
    halWriteReg (pHal, HW_PORT_CTL, nVal);			// 
    halDelayMilliseconds (pHal, 100L);

    nVal = nVal | HW_IMCBIT;			// turn on ASCO reset
    halWriteReg (pHal, HW_PORT_CTL, nVal);			// 
    halDelayMilliseconds (pHal, 200L);	

    // drop reset    
    nVal = nVal & ~HW_IMCBIT;			// turn off ASCO reset
    halWriteReg (pHal, HW_PORT_CTL, nVal);			// 
    halDelayMilliseconds (pHal, 100L);
}

PRIVATE void halInitHotshot (PHAL pHal)
{
#if 0	// not supported
    DWORD dwElapsed;
    DWORD dwStart = halGetTime (pHal);
    BOOL bAudioEnabled;             

#if 0    
    if (halIsHWPresent(pHal, pBoard->dwHWStatus)==FALSE)		// do not do if HW not available!
    {            
	pBoard->nXtal = XTAL_UNKNOWN;		
	return 0;	
    }    
#endif
    
    // Audio ATTN
    halWriteAttnReg (pHal, halReadAttnReg (pHal) & HW_ASS2FIELD);
    // Audio GAIN
    halWriteGainReg (pHal, halReadGainReg (pHal) | HW_MUTBIT);
    // Audio FREQ
    halWriteReg (pHal, HW_PORT_FREQ, 
    	halReadReg (pHal, HW_PORT_FREQ) | HW_AMSBIT | HW_FREQFIELD_44100);
    halASCOReset (pHal);
    // determine xtal type
    halSetMuteOut (pHal, TRUE);
    bAudioEnabled = halGetACE (pHal);
    halSetACE (pHal, TRUE);				// must be running to profile
    sharedSetXtal (pHal->m_pShared, XTAL_11K);				// assume std xtal
    halSetSampleRate (pHal, 11025);

    sharedSetXtal (pHal->m_pShared, XTAL_UNKNOWN);
    while ((sharedGetXtal (pHal->m_pShared) == XTAL_UNKNOWN) && ((halGetTime (pHal) - dwStart) < 10000L))
    {
    	// try 11K (SPAM)
#ifdef HOTSHOT_PORTED	//!!! not ported    	
    	dwElapsed = ASMAUDIOTIME ((WORD)pBoard->nBoard);	// time an audio buffer in .8 us increments
#else
	dwElapsed = 0;	// keep compiler happy
#endif    	
    	dwElapsed = (dwElapsed * 838L) / 1000L;	// convert to uS
    	if (halTryFreq (pHal, dwElapsed, 11025L))
   	    sharedSetXtal (pHal->m_pShared, XTAL_11K);
    	else
    	if (halTryFreq (pHal, dwElapsed, 12000L))
    	    sharedSetXtal (pHal->m_pShared, XTAL_8K);
        else
        {
    	    sharedSetXtal (pHal->m_pShared, XTAL_UNKNOWN);		// don't know what to do
    	    halASCOReset (pHal);
    	}
    }
    	
#if 0        
    {
    	    char buf[256];
    	    wsprintf (buf, "Unknown xtal type, 8/11 sample rate=%ld", (DWORD)((1024L * 1000000L)/dwElapsed));
    	    MessageBox (0, buf, "Debug", 0);
    }
#endif    	    

#if 0	// not ported    
    //!!!hack for alaris if (!bAudioEnabled) IDisableAudio (pBoard);
    halSaveSetting (pHal, NLS_ASCOINITTIME, 
    	max (halGetSetting (pHal, NLS_ASCOINITTIME, 0),
    	(UINT)(halGetTime (pHal) - dwStart));
#endif    	
#endif	// not supported
}

PUBLIC void halInit (PHAL pHal)
{
    if (halIsVideoOnly (pHal)) return;

    if (halIsHotshot (pHal))
    	halInitHotshot (pHal);
}

PUBLIC UINT halSetSampleRate (PHAL pHal, UINT nVal)
{
    UINT nSampleControl;
    UINT nXtalSel;
    UINT nActualRate;
    UINT nSampleRate = nVal;

    switch (sharedGetXtal (pHal->m_pShared))
    {
case XTAL_11K:	// standard xtal is 22.5792 Mhz
    	if (nSampleRate <= 7350)
    	{
    	    nActualRate = 7350;
	    nSampleControl = HW_FREQFIELD_07350;
	}
     	else
#if ALL_ASCO_FREQ
	if (nSampleRate <= 8019)	// 8018.18 doesn't work 6/17/94 HLG
	{
	    nActualRate = 8019;
	    nSampleControl = 10;
	}
	else
#endif     	
        if (nSampleRate <= 8820)
        {
            nActualRate = 8820;
	    nSampleControl = HW_FREQFIELD_08820;
	}
        else
        if (nSampleRate <= 11025)
        {
            nActualRate = 11025;
	    nSampleControl = HW_FREQFIELD_11025;
	}
        else
        if (nSampleRate <= 14700)
        {
            nActualRate = 14700;
	    nSampleControl = HW_FREQFIELD_14700;
	}
        else
        if (nSampleRate <= 22050)
        {
            nActualRate = 22050;
	    nSampleControl = HW_FREQFIELD_22050;
	}
        else
        if (nSampleRate <= 29400)
        {
            nActualRate = 29400;
	    nSampleControl = HW_FREQFIELD_29400;
	}
        else
        {
            nActualRate = 44100;
	    nSampleControl = HW_FREQFIELD_44100;
	}
	nXtalSel = XTAL_MM;	// sb XTAL_DEFAULT for MovieMan
	break;
case XTAL_8K:	// videoconf xtal is 24.576 Mhz
    	if (nSampleRate <= 8000)
    	{
    	    nActualRate = 8000;
	    nSampleControl = HW_FREQFIELD_07350;
	}
     	else
        if (nSampleRate <= 9600)
        {
            nActualRate = 9600;
	    nSampleControl = HW_FREQFIELD_08820;
	}
        else
        if (nSampleRate <= 12000)
        {
            nActualRate = 12000;
	    nSampleControl = HW_FREQFIELD_11025;
	}
        else
        if (nSampleRate <= 16000)
        {
            nActualRate = 16000;
	    nSampleControl = HW_FREQFIELD_14700;
	}
        else
        if (nSampleRate <= 24000)
        {
            nActualRate = 24000;
	    nSampleControl = HW_FREQFIELD_22050;
	}
        else
        if (nSampleRate <= 32000)
        {
            nActualRate = 32000;
	    nSampleControl = HW_FREQFIELD_29400;
	}
        else
        {
            nActualRate = 48000;
	    nSampleControl = HW_FREQFIELD_44100;	// 48000    
	}
	nXtalSel = XTAL_COM;	// sb XTAL_DEFAULT for MovieMan
	break;
case XTAL_SPAM:
    	if (nSampleRate <= 7350)
    	{
    	    nXtalSel = XTAL_MM;
    	    nActualRate = 7350;
	    nSampleControl = HW_FREQFIELD_07350;
	}
     	else
    	if (nSampleRate <= 8000)
    	{
    	    nXtalSel = XTAL_COM;
    	    nActualRate = 8000;
	    nSampleControl = HW_FREQFIELD_07350;
	}
     	else
        if (nSampleRate <= 8820)
        {
            nXtalSel = XTAL_MM;
            nActualRate = 8820;
	    nSampleControl = HW_FREQFIELD_08820;
	}
        else
        if (nSampleRate <= 9600)
        {
            nXtalSel = XTAL_COM;
            nActualRate = 9600;
	    nSampleControl = HW_FREQFIELD_08820;
	}
        else
        if (nSampleRate <= 11025)
        {
            nXtalSel = XTAL_MM;
            nActualRate = 11025;
	    nSampleControl = HW_FREQFIELD_11025;
	}
        else
        if (nSampleRate <= 12000)
        {
            nXtalSel = XTAL_COM;
            nActualRate = 12000;
	    nSampleControl = HW_FREQFIELD_11025;
	}
        else
        if (nSampleRate <= 14700)
        {
            nXtalSel = XTAL_MM;
            nActualRate = 14700;
	    nSampleControl = HW_FREQFIELD_14700;
	}
        else
        if (nSampleRate <= 16000)
        {
            nXtalSel = XTAL_COM;
            nActualRate = 16000;
	    nSampleControl = HW_FREQFIELD_14700;
	}
        else
        if (nSampleRate <= 22050)
        {
            nXtalSel = XTAL_MM;
            nActualRate = 22050;
	    nSampleControl = HW_FREQFIELD_22050;
	}
        else
        if (nSampleRate <= 24000)
        {
            nXtalSel = XTAL_COM;
            nActualRate = 24000;
	    nSampleControl = HW_FREQFIELD_22050;
	}
        else
        if (nSampleRate <= 29400)
        {
            nXtalSel = XTAL_MM;
            nActualRate = 29400;
	    nSampleControl = HW_FREQFIELD_29400;
	}
        else
        if (nSampleRate <= 32000)
        {
            nXtalSel = XTAL_COM;
            nActualRate = 32000;
	    nSampleControl = HW_FREQFIELD_29400;
	}
	else
	if (nSampleRate <= 44100)
	{
	    nXtalSel = XTAL_MM;
	    nActualRate = 44100;
	    nSampleControl = HW_FREQFIELD_44100;
	}
        else
        {
            nXtalSel = XTAL_COM;
            nActualRate = 48000;
	    nSampleControl = HW_FREQFIELD_44100;	// 48000    
	}
	break;
default:
	return 0;	
    }

    halSetXtalSelect (pHal, nXtalSel, nSampleControl);    

    sharedSetCurrentSampleRate (pHal->m_pShared, nSampleRate);
    sharedSetActualSampleRate (pHal->m_pShared, nActualRate);
    serviceSampleRateChange (pHal->m_pService, sharedGetXtalSelect (pHal->m_pShared), sharedGetActualSampleRate (pHal->m_pShared));
    return nActualRate;
}

PUBLIC UINT halGetSampleRate (PHAL pHal)
{
   return sharedGetCurrentSampleRate (pHal->m_pShared);
}

PUBLIC UINT halGetDefaultSampleRate (PHAL pHal)
{
    if (sharedGetHwFlags (pHal->m_pShared) & HWFLAGS_XTAL_8K_NOT_PRESENT)
    	return 11025;
    else
    if (sharedGetHwFlags (pHal->m_pShared) & HWFLAGS_XTAL_11K_NOT_PRESENT)
	return 8000;
    else
    	return 11025;    
}

PUBLIC void halSetPower (PHAL pHal, BOOL fState)
{
    serviceSetPower (pHal->m_pService, fState);
}

PUBLIC void halSetACE (PHAL pHal, BOOL fACE)
{
    UINT nOld;
    
    nOld = halReadReg (pHal, HW_PORT_FREQ);
    if (fACE)
    	halWriteReg (pHal, (UINT)HW_PORT_FREQ, nOld | HW_ACEBIT);
    else
	halWriteReg (pHal, HW_PORT_FREQ, nOld & ~HW_ACEBIT);

    sharedSetACE (pHal->m_pShared, fACE);
}

PUBLIC BOOL halGetACE (PHAL pHal)
{
    return sharedGetACE (pHal->m_pShared);
}

///////////////////////////////////////////////////////////////////////////////
// Initialize invariant and configuration dependent regs
// returns: Board Type

PUBLIC UINT halStartupBegin (PHAL pHal)
{
    pHal->m_fIsWimp = 2;  // not initialized
    serviceGetEEPROM (pHal->m_pService, sharedGetEeprom (pHal->m_pShared));

	if (halIsVideoOnly (pHal))
		return 0;

    // Configure hardware
    if (halIs4218 (pHal))
    {	// use CS4218 audio codec, instead of CS4216
       	halWriteReg (pHal, HW_PORT_MMA, halReadReg (pHal, HW_PORT_MMA) | HW_CS4218BIT);
    }

    if (sharedGetHwFlags (pHal->m_pShared) & HWFLAGS_AUDXSEL)
    {	// use internal audio xtal selection, (W97 ONLY)
    	// note: this bit in the MMA does different things for W97 vs. WIMP
    	// on W97, it selects between external (legacy) and internal xtal select,
    	// and IMIBIT continues to do the xtal select.
    	// on WIMP, AUDXSEL is the xtal select (replaces IMIBIT).
       	halWriteReg (pHal, HW_PORT_MMA, halReadReg (pHal, HW_PORT_MMA) | HW_AUDXSEL_INT97);
    }

    // init ASCO Regs
    halWriteGainReg (pHal, halReadGainReg (pHal));	// do this first to set the ASCO Mute
    halWriteAttnReg (pHal, halReadAttnReg (pHal));
    
    // init WAVI PWM Regs so the shadow values are correct
    halWriteReg (pHal, HW_PORT_AUDMIX1, halReadReg (pHal, HW_PORT_AUDMIX1));
    halWriteReg (pHal, HW_PORT_AUDMIX2, halReadReg (pHal, HW_PORT_AUDMIX2));
    halWriteReg (pHal, HW_PORT_FREQ, halReadReg (pHal, HW_PORT_FREQ));
    
    halSetACE (pHal, halGetACE (pHal));	// restore or initialize
    
    return sharedGetType (pHal->m_pShared);
}    

PUBLIC void halStartupEnd (PHAL pHal)
{
    UINT nSampleRate;
    
    sharedSetXtalSelect (pHal->m_pShared, (UINT)-1);	// invalidate current selection
    halSetXtalConfiguration (pHal, halGetXtalConfiguration (pHal));
    // make sure the ASCO gets reset at hot insertion time
    // If it doesn't it will be confused until the wave device is activate,
    // and the mixer level indictor will have DC in it.
    Local_Sleep (1000);	// delay to allow DC to stabalize
    nSampleRate = halGetSampleRate (pHal);	// preserve the sample rate
    halSetSampleRate (pHal, 11025);
    halSetSampleRate (pHal, 8000);
    halSetSampleRate (pHal, nSampleRate);	// restore the sample rate
}

PUBLIC void halShutdown (PHAL pHal)
{
    halSetACE (pHal, FALSE);
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////// End of File /////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////