#include "strmini.h"
#include "mpinit.h"
#include <wingdi.h>
#include <mmreg.h>
#include "ksmedia.h"
#include "stdefs.h"
#include "mpvideo.h"
#include "mpaudio.h"
#include "copyprot.h"
#include "ls220.h"					  
#include "audio.h"
#include "video.h"
#include "dvhw32.h"

#ifdef WINDOWS2000

	#include "dsp.h"

#endif

#include "luxksprop.h"

#ifndef LS240
	#define USE_STC
#endif

BOOLEAN b_first_pcm_data = FALSE;
ULONG audiodecoutmode = KSAUDDECOUTMODE_STEREO_ANALOG;	// default to downmixed stereo output

extern BOOL fClkPause;
extern ULONGLONG LastSysTime;
extern ULONGLONG PauseTime;
extern ULONGLONG LastStamp;
extern ULONGLONG LastSys;
extern BOOLEAN fValid;
extern DWORD dwSTCinPause;
extern BOOLEAN fProgrammed;
extern BOOLEAN fStarted;
extern ULONGLONG StartSys;
extern KSDATAFORMAT Mpeg2ksdataformat;
extern KSDATAFORMAT LPCMksdataformat;
extern KSDATAFORMAT Mpeg2PESksdataformat;
extern DWORD vPts;
extern DWORD v_PTS;

extern BOOL	m_docopy;

extern WORD	width;
extern WORD	height;

extern BOOL m_Pal;	// Video Src


ULONG miniPortAudioStop (PHW_STREAM_REQUEST_BLOCK pSrb, PHW_DEVICE_EXTENSION pHwDevExt);
void AudioPacketStub(PHW_STREAM_OBJECT pstrm);

//Rate Change Functions
void InitAudioRate(PHW_DEVICE_EXTENSION phwdev);
void AudioRateChangeSetProp(PHW_STREAM_REQUEST_BLOCK pSrb);
void AudioRateChangeGetProp(PHW_STREAM_REQUEST_BLOCK pSrb);

void AudioLuxSetProp(PHW_STREAM_REQUEST_BLOCK pSrb);
void AudioLuxGetProp(PHW_STREAM_REQUEST_BLOCK pSrb);

void ProcessAudioFormat( PKSDATAFORMAT pfmt, PHW_DEVICE_EXTENSION pHwDevExt );
ULONGLONG ConvertVPTStoStrm(ULONGLONG vpts);

extern WORD GetWORD(unsigned char *p);
extern DWORD GetDWORD(unsigned char *p);
//Rate chnage global function
extern void SetRateChange(PHW_DEVICE_EXTENSION pHwDevExt, LONG strm);



////////////////////////////////////////////////////////////////////////////

VOID miniPortAudioGetProperty(PHW_STREAM_REQUEST_BLOCK pSrb)
{
PHW_DEVICE_EXTENSION phwdevext =
		((PHW_DEVICE_EXTENSION)pSrb->HwDeviceExtension);

	if(!pSrb)
    {
		#ifdef DBG
			DbgPrint("luxdvd ERROR : miniPortAudioGetProperty - invalid SRB !!!!\n");
		#endif
		return;
	}

	pSrb->Status = STATUS_SUCCESS;

	switch(pSrb->CommandData.PropertyInfo->PropertySetID)
	{
		case 1:
		{
			// this is a copy protection property go handle it there
			#ifdef DBG
				//DbgPrint("luxdvd : audio calling CopyProtGetProp\n");
			#endif
			CopyProtGetProp(pSrb);
			break;
		}//Audio Get CopyProtection Properties

		case 0:
		{
			// this is audio decoder output property, handle it
			pSrb->Status = STATUS_SUCCESS;

			switch(pSrb->CommandData.PropertyInfo->Property->Id)
			{
				case KSPROPERTY_AUDDECOUT_MODES:
		
					// enumerate the supported modes
					#ifdef DBG
						DbgPrint("luxdvd : miniPortAudioGetProperty - KSPROPERTY_AUDDECOUT_MODES\n");
					#endif
					*(PULONG)(pSrb->CommandData.PropertyInfo->PropertyInfo) =
							KSAUDDECOUTMODE_STEREO_ANALOG | KSAUDDECOUTMODE_SPDIFF;
		
					pSrb->ActualBytesTransferred = sizeof (ULONG);
					break;
		
				case KSPROPERTY_AUDDECOUT_CUR_MODE:
		
					#ifdef DBG
						DbgPrint("luxdvd : miniPortAudioGetProperty - KSPROPERTY_AUDDECOUT_CUR_MODE\n");
					#endif
					*(PULONG)(pSrb->CommandData.PropertyInfo->PropertyInfo) = audiodecoutmode;
					pSrb->ActualBytesTransferred = sizeof (ULONG);
					break;

				default:

					pSrb->Status = STATUS_NOT_IMPLEMENTED;
			}
			break;
		}//Audio Get Mode Properties


		case 2:
		{
			#ifdef DBG
				DbgPrint("luxdvd : audio calling AudioRateChangeGetProp()\n");
			#endif
			AudioRateChangeGetProp(pSrb);
			break; 
		}//Audio Get Rate Change Properties

		case 3:
			AudioLuxGetProp(pSrb);
			break;

		default:
			// invalid property
			pSrb->Status = STATUS_NOT_IMPLEMENTED;
			return;
	}

}

////////////////////////////////////////////////////////////////////////////

VOID miniPortAudioSetProperty(PHW_STREAM_REQUEST_BLOCK pSrb)
{
PHW_DEVICE_EXTENSION phwdevext =
		((PHW_DEVICE_EXTENSION)pSrb->HwDeviceExtension);

	if(!pSrb)
    {
		#ifdef DBG
			DbgPrint("luxdvd ERROR : miniPortAudioSetProperty - invalid SRB !!!!\n");
		#endif
		return;
	}

	pSrb->Status = STATUS_SUCCESS;

	switch(pSrb->CommandData.PropertyInfo->PropertySetID)
	{
		case 0:
		{
			switch(pSrb->CommandData.PropertyInfo->Property->Id)
			{
				case KSPROPERTY_AUDDECOUT_CUR_MODE:
	
					#ifdef DBG
						DbgPrint("luxdvd : miniPortAudioSetProperty - KSPROPERTY_AUDDECOUT_CUR_MODE ");
						switch(*(PULONG)(pSrb->CommandData.PropertyInfo->PropertyInfo))
						{
							case KSAUDDECOUTMODE_STEREO_ANALOG :
								DbgPrint("KSAUDDECOUTMODE_STEREO_ANALOG\n");
								break;
							case KSAUDDECOUTMODE_PCM_51 :
								DbgPrint("KSAUDDECOUTMODE_PCM_51\n");
								break;
							case KSAUDDECOUTMODE_SPDIFF :
								DbgPrint("KSAUDDECOUTMODE_SPDIFF\n");
								break;
						}
					#endif

					if (*(PULONG)(pSrb->CommandData.PropertyInfo->PropertyInfo) != audiodecoutmode)
					{
						if ((*(PULONG)(pSrb->CommandData.PropertyInfo->PropertyInfo)) &    	
							(!(KSAUDDECOUTMODE_STEREO_ANALOG | KSAUDDECOUTMODE_SPDIFF)))
						{
							break;
						}
	
						audiodecoutmode = *(PULONG)(pSrb->CommandData.PropertyInfo->PropertyInfo);
						return;
					}
					break;
	
			}
	
			pSrb->Status = STATUS_NOT_IMPLEMENTED;
			break;
		}

		case 1:
		{
			// this is a copy protection property
			#ifdef DBG
				//DbgPrint("luxdvd : audio calling CopyProtSetProp\n");
			#endif
			CopyProtSetProp(pSrb);
			break;
		}

		case 2:
		{
			#ifdef DBG
				DbgPrint("luxdvd : audio calling AudioRateChangeSetProp()\n");
			#endif
			AudioRateChangeSetProp(pSrb);
			break; 
		}//Audio Set Rate Change Properties

		case 3:
			AudioLuxSetProp(pSrb);
			break;

		default:

			// invalid property
			pSrb->Status = STATUS_NOT_IMPLEMENTED;
			return;
	}

}

////////////////////////////////////////////////////////////////////////////

VOID miniPortAudioSetState(PHW_STREAM_REQUEST_BLOCK pSrb)
{
PHW_DEVICE_EXTENSION phwdevext = ((PHW_DEVICE_EXTENSION)pSrb->HwDeviceExtension);

	#ifdef DBG
		DbgPrint("luxdvd : miniPortAudioSetState\n");
	#endif

	if(!pSrb)
    {
		#ifdef DBG
			DbgPrint("luxdvd ERROR : miniPortAudioSetState - invalid SRB !!!!\n");
		#endif
		return;
	}

	switch (pSrb->CommandData.StreamState)  
	{
		case KSSTATE_STOP:

			#ifdef DBG
				DbgPrint("luxdvd : miniPortAudioSetState - KSSTATE_STOP\n");
			#endif

			phwdevext->bAudioFF = FALSE;
            phwdevext->AudioDeviceExt.DeviceState = KSSTATE_STOP;
			InitAudioRate(phwdevext);
            break;


		case KSSTATE_PAUSE:

		    #ifdef DBG
				DbgPrint("luxdvd : miniPortAudioSetState - KSSTATE_PAUSE\n");
			#endif

			phwdevext->AudioDeviceExt.DeviceState = KSSTATE_PAUSE;
            break;

		case KSSTATE_RUN:

			#ifdef DBG
				DbgPrint("luxdvd : miniPortAudioSetState - KSSTATE_RUN\n");
			#endif

			phwdevext->bAudioFF = FALSE;
			phwdevext->AudioDeviceExt.DeviceState = KSSTATE_RUN;
			phwdevext->bAFlush = FALSE;
            break;

	}

	pSrb->Status = STATUS_SUCCESS;

}

////////////////////////////////////////////////////////////////////////////

VOID miniPortAudioGetState(PHW_STREAM_REQUEST_BLOCK pSrb)
{
PHW_DEVICE_EXTENSION phwdevext = ((PHW_DEVICE_EXTENSION)pSrb->HwDeviceExtension);

	#ifdef DBG
		DbgPrint("luxdvd : miniPortAudioGetState");
	#endif
	
	pSrb->CommandData.StreamState = phwdevext->AudioDeviceExt.DeviceState;
	pSrb->Status = STATUS_SUCCESS; 
}

////////////////////////////////////////////////////////////////////////////

void AudioPacketStub(PHW_STREAM_OBJECT pstrm)
{
	//AudioTimerCallBack(pstrm);
			
	StreamClassCallAtNewPriority(pstrm,
								pstrm->HwDeviceExtension,
								Dispatch,
								AudioTimerCallBack,
								pstrm);
	
}

////////////////////////////////////////////////////////////////////////////

VOID miniPortAudioPacket(PHW_STREAM_REQUEST_BLOCK pSrb)
{
PHW_DEVICE_EXTENSION pHwDevExt =
	 ((PHW_DEVICE_EXTENSION)pSrb->HwDeviceExtension);

PAUDIO_DEVICE_EXTENSION paudex = 
         &(((PHW_DEVICE_EXTENSION)pSrb->HwDeviceExtension)->AudioDeviceExt);

	if(!pSrb)
    {
		#ifdef DBG
			DbgPrint("luxdvd ERROR : miniPortAudioPacket - invalid SRB !!!!\n");
		#endif
		return;
	}

	
	 // set up for initial parsing of the scatter gather packet.
	paudex->cPacket = 0;
	paudex->cOffs = PACK_HEADER_SIZE;
    paudex->pPacket = pSrb->CommandData.DataBufferArray;

	if(paudex->pPacket->TypeSpecificFlags & KS_AM_UseNewCSSKey)
	{
		//Just return as it should be an empty packet
		#ifdef DBG
			DbgPrint("luxdvd : miniPortAudioPacket - received KS_AM_UseNewCSSKey flag\n");
		#endif
		
		pSrb->Status = STATUS_SUCCESS;
		paudex->pCurrentSRB = 0;
		StreamClassStreamNotification(ReadyForNextStreamDataRequest,pSrb->StreamObject);
		StreamClassStreamNotification(StreamRequestComplete,pSrb->StreamObject,pSrb);
        return;
		
	}

    paudex->pCurrentSRB = pSrb;

    //AudioPacketStub(pSrb->StreamObject);
	
	StreamClassCallAtNewPriority(pSrb->StreamObject,
								pSrb->StreamObject->HwDeviceExtension,
								Dispatch,
								AudioTimerCallBack,
								pSrb->StreamObject);
	
}

////////////////////////////////////////////////////////////////////////////

VOID AudioTimerCallBack(PHW_STREAM_OBJECT pstrm)
{
PHW_DEVICE_EXTENSION pdevext = pstrm->HwDeviceExtension;
PHW_STREAM_REQUEST_BLOCK pSrb;
ULONG	uSent=0;
PAUDIO_DEVICE_EXTENSION paudex = &(pdevext->AudioDeviceExt);
PVIDEO_DEVICE_EXTENSION	pvidex = &(pdevext->VideoDeviceExt);

	pSrb = paudex->pCurrentSRB;
		
	if (!pSrb)
	{
		#ifdef DBG
			DbgPrint("luxdvd ERROR : AudioTimerCallBack - received invalid SRB !!!!!!\n");
		#endif

		return;
	}

	if ( m_docopy )
	{
		pSrb->Status = STATUS_SUCCESS;
        mpstCommandComplete(pSrb);
		return;
	}

	do
	{
		if(paudex->pPacket->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_TIMEDISCONTINUITY)
		{
			#ifdef DBG
				DbgPrint("luxdvd : audio - KSSTREAM_HEADER_OPTIONSF_TIMEDISCONTINUITY\n");
			#endif

			if( pdevext->TimeDisCount++ >= 2)
			{
				#ifdef DBG
					DbgPrint("luxdvd : received all 3 Stream TimeDisContinuties !!!\n");
				#endif
				pdevext->TimeDisCount = 0;
				//After A, V, SP have all received TimeDis, we are fairly certain that
				//a menu will appear. Here we turn off menu workaround
//				LUXVideoMenu(0);
			}
		}

		if (paudex->pPacket->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_DATADISCONTINUITY)
		{
			//About to do Seek, dont send audio packets
			
			#ifdef DBG
				DbgPrint("luxdvd : audio - KSSTREAM_HEADER_OPTIONSF_DATADISCONTINUITY\n");
			#endif

			if ( pvidex->pCurrentSRB )
			{
				KeStallExecutionProcessor(1);
				StreamClassScheduleTimer(pstrm, 
							pstrm->HwDeviceExtension,
							50, 
							(PHW_PRIORITY_ROUTINE)AudioPacketStub, 
							pstrm);
				return;
			}
			else
				uSent = paudex->pPacket->DataUsed;	//dont send down audio data
		}

		if (paudex->pPacket->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_TYPECHANGED)			
		{
			#ifdef DBG
				DbgPrint("luxdvd : AudioTimerCallBack - KSSTREAM_HEADER_OPTIONSF_TYPECHANGED\n");
			#endif
			if(paudex->pPacket->DataUsed)
			{
				#ifdef DBG
					DbgPrint("luxdvd : AudioTimerCallback - KSSTREAM_HEADER_OPTIONSF_TYPECHANGED calling ProcessAudioFormat\n");
				#endif
				ProcessAudioFormat((PKSDATAFORMAT)paudex->pPacket->Data, pdevext);
				uSent = paudex->pPacket->DataUsed + 1;
			}
		}
		else
		{
			if( pdevext->bAudioFF == TRUE )
			{
				uSent = paudex->pPacket->DataUsed;	//dont send down audio data
			}
			else
			{	
				if(paudex->pPacket->DataUsed)
				{
					if(pdevext->bAFlush)
						uSent = paudex->pPacket->DataUsed;	//dont send down audio data
					else
						uSent = mpstAudioPacket(pSrb);
				}
				else
				{
					uSent=0;
				}
			}
		}

        paudex->cOffs += uSent;

		// check if we finished this packet.  If so, go on to the
		// next packet

        if (paudex->cOffs >= paudex->pPacket->DataUsed)
		{
			paudex->pPacket++;

			// reset the packet offset
			paudex->cOffs = PACK_HEADER_SIZE;
			paudex->cPacket++;

			// if we have finished all the packets, then we are done
			if (paudex->cPacket >= pSrb->NumberOfBuffers)           
			{
				pSrb->Status = STATUS_SUCCESS;
				pdevext->pCurSrb=0;
				StreamClassStreamNotification(ReadyForNextStreamDataRequest,
                                         pSrb->StreamObject);
				StreamClassStreamNotification(StreamRequestComplete,
                                         pSrb->StreamObject,
                                                 pSrb);
				paudex->pCurrentSRB = 0;
                return;
			}
		}

	} while (uSent);

	StreamClassScheduleTimer(pstrm, 
							pstrm->HwDeviceExtension,
							50, 
							(PHW_PRIORITY_ROUTINE)AudioPacketStub, 
							pstrm);
}

////////////////////////////////////////////////////////////////////////////

ULONG mpstAudioPacket(PHW_STREAM_REQUEST_BLOCK pSrb)
{
DWORD   aPts=0;
PAUDIO_DEVICE_EXTENSION paudex = &(((PHW_DEVICE_EXTENSION)pSrb->HwDeviceExtension)->AudioDeviceExt);
PUCHAR  p=NULL;
BOOL	b_scramble;
int		len, pes_len;
WORD	aoff=0;
PDWORD	pdw=NULL;

	if(!pSrb)
    {
		#ifdef DBG
			DbgPrint("luxdvd ERROR : mpstAudioPacket - invalid SRB !!!!\n");
		#endif
		return 0;
	}

    if(paudex->pPacket->OptionsFlags & KSSTREAM_HEADER_OPTIONSF_TYPECHANGED)
	{
		#ifdef DBG
			DbgPrint("luxdvd : audio - KSSTREAM_HEADER_OPTIONSF_TYPECHANGED\n");
		#endif
	}

	pdw = (PDWORD)((ULONG) paudex->pPacket->Data);

	if(*pdw == 0xBA010000)	//DVD PACK HEADER
	{
		p = (PUCHAR)((ULONG) paudex->pPacket->Data + PACK_HEADER_SIZE );
	}
	else	//MPEG2 PES Format
	{
		//MPEG2 PES AC3 seems to just have the audio start code
		p = (PUCHAR)((ULONG) paudex->pPacket->Data );
	}

	if ( ( p[3] >= 0xc0 ) && ( p[3] < 0xe0 ) ) // MPEG2 Audio
	{
		return LUXAudioMpegPacket((BYTE *)(((ULONG)paudex->pPacket->Data)),p+4,paudex->pPacket->DataUsed);
	}

	p += 4;

    len = (int)GetWORD(p);

	b_scramble = (BOOL)(p[2] & 0x30);
	pes_len = (int)p[4];
	len = (int)(len - 3 - pes_len);

	if( ((p[5] & 0xf0) == 0x20) && (pes_len > 0) )
    {
		aPts = GetWORD(&p[6]) >> 1;
        aPts = aPts << 14 | GetWORD(&p[8]) >> 2;
        aPts |= (DWORD) ( p[5] & 0xe )  << 28;
	}

	p += pes_len + 5;

	if( (*p & 0xf8) == 0x80 )	//AC3 DATA
	{
		aoff = GetWORD(p+2);	//get offset to next ac3 sync word

		if( LUXAudioSendData((BYTE *)(((ULONG)paudex->pPacket->Data)), 
								(BYTE *)(p+4),(len-4),aPts,b_scramble,aoff) )
		{
			return (paudex->pPacket->DataUsed);
		}
		else
			return 0;
	}
	else //PCM Data
	{
		aoff = 0;
	
		if(b_first_pcm_data)
		{
		DWORD pcmchar=0;
	
			b_first_pcm_data = FALSE;
		
			pcmchar = GetDWORD(p+4);
			pcmchar = (DWORD)(pcmchar << 24 | pcmchar >> 24 | ( pcmchar >> 8 ) & 0x0000ff00 | ( pcmchar << 8 ) & 0x00ff0000);

			#ifdef DBG
				DbgPrint("luxdvd : mpstAudioPacket - 1st pcm data packet, config word is 0x%lx\n",pcmchar);
			#endif

			LUXAudioSetConfig(pcmchar);
			LUXAudioPlay(1000);
		}

		if( LUXAudioSendData((BYTE *)(((ULONG)paudex->pPacket->Data)), 
								(BYTE *)(p+7),(len-7),aPts,b_scramble,aoff) )
		{
			return (paudex->pPacket->DataUsed);
		}
		else
			return 0;
	}
}

////////////////////////////////////////////////////////////////////////////

VOID STREAMAPI StreamClockRtn( IN PHW_TIME_CONTEXT TimeContext )
{
PHW_DEVICE_EXTENSION pHwDevExt = (PHW_DEVICE_EXTENSION)TimeContext->HwDeviceExtension;
ULONGLONG sysTime = GetSystemTime();
ULONG foo;

	if( TimeContext->Function != TIME_GET_STREAM_TIME ) 
	{
		return;
	}

	/*
	Indicates that the minidriver should return the current PTS being processed 
	by the hardware for the master clock in the Time field of the 
	HW_TIME_CONTEXT structure, in 100 ns units
	*/

	if(fClkPause) //set during Video KSSTATE_PAUSE
	{
		if(fProgrammed) //set during Video KSSTATE_RUN
		{
			#ifdef USE_STC
			foo = LUXVideoGet_STC();	//32bit STC value
			LastStamp = ConvertPTStoStrm( foo );	//convert to 100ns
			#else
//			LastStamp = ConvertVPTStoStrm((ULONGLONG)(vPts<<1));
			LastStamp = ConvertVPTStoStrm(((ULONGLONG)(v_PTS))<<1);
			#endif

			if( pHwDevExt->vid_speed > 1000 )//if( pHwDevExt->RunMode == PLAY_MODE_FAST ) 
			{
			REFERENCE_TIME tmp;

				//tmp = (REFERENCE_TIME)dwSTCinPause * 1000 / 9;
				tmp = (REFERENCE_TIME)ConvertPTStoStrm(dwSTCinPause);
				if( tmp > pHwDevExt->StartTime ) 
				{
					//LastStamp = (tmp - pHwDevExt->StartTime) * 10000/pHwDevExt->Rate + pHwDevExt->StartTime;
					LastStamp = ((tmp - pHwDevExt->StartTime) * 10000/pHwDevExt->Rate) + pHwDevExt->StartTime;
				}
			}

			LastSys = LastSysTime = sysTime;
			fValid = TRUE;
		}
		else 
		{
			LastStamp = 0;
			LastSys = LastSysTime = sysTime;
		}

		TimeContext->Time = LastStamp;
		TimeContext->SystemTime = sysTime;

		//DebugPrint(( DebugLevelTrace, "TOSDVD:Clk pause: 0x%x( 0x%s(100ns) )\r\n", ConvertStrmtoPTS(TimeContext->Time), DebugLLConvtoStr( TimeContext->Time, 16 ) ));
		return;
	}

	// update the clock 4 times a second, or once every 2500000 100 ns ticks
	
	if( TRUE || (sysTime - LastSysTime) > 2500000 ) 
	{
		if(fProgrammed)	//set during Video KSSTATE_RUN
		{
			#ifdef USE_STC
			foo = LUXVideoGet_STC();
			LastStamp = ConvertPTStoStrm( foo );
			#else
//			LastStamp = ConvertVPTStoStrm( (ULONGLONG)(vPts<<1) );
			LastStamp = ConvertVPTStoStrm(((ULONGLONG)(v_PTS))<<1 );
			#endif
			
			if(pHwDevExt->vid_speed > 1000)//if( pHwDevExt->RunMode == PLAY_MODE_FAST ) 
			{
			REFERENCE_TIME tmp;

				#ifdef USE_STC
				//tmp = (REFERENCE_TIME)(foo * 1000 / 9);	//convert to 100ns
				tmp = (REFERENCE_TIME)ConvertPTStoStrm(foo);	//convert to 100ns
				#else
				//tmp = (REFERENCE_TIME)((vPts<<1) * 1000 / 9);	//convert to 100ns
//				tmp = (REFERENCE_TIME)ConvertPTStoStrm((vPts<<1));	//convert to 100ns
				tmp = (REFERENCE_TIME)ConvertPTStoStrm(((ULONGLONG)(v_PTS))<<1);	//convert to 100ns
				#endif

				if( tmp > pHwDevExt->StartTime ) 
				{
					LastStamp = ((tmp - pHwDevExt->StartTime) * 10000/pHwDevExt->Rate) + pHwDevExt->StartTime;
				}
			}
		}
		else 
		{
			LastStamp = ( sysTime - StartSys );
		}

		LastSys = LastSysTime = sysTime;
		fValid = TRUE;
	}

	TimeContext->Time = LastStamp + ( sysTime - LastSysTime );
	TimeContext->SystemTime = sysTime;
	//DebugPrint(( DebugLevelTrace, "TOSDVD:Clk      : 0x%x( 0x%s(100ns) )\r\n", ConvertStrmtoPTS(TimeContext->Time), DebugLLConvtoStr( TimeContext->Time, 16 ) ));
	return;
}

////////////////////////////////////////////////////////////////////////////

ULONGLONG GetSystemTime()
{
	ULONGLONG ticks;
	ULONGLONG rate;

	ticks = (ULONGLONG)KeQueryPerformanceCounter((PLARGE_INTEGER)&rate).QuadPart;

	// convert from ticks to 100ns clock
	
	ticks = (ticks & 0xFFFFFFFF00000000) / rate * 10000000 +
			(ticks & 0xFFFFFFFF) * 10000000 / rate;

	return(ticks);

}

////////////////////////////////////////////////////////////////////////////

VOID STREAMAPI StreamTimeCB(IN PHW_TIME_CONTEXT tc)
{
	#ifdef DBG
		//DbgPrint("luxdvd : StreamTimeCB\n");
	#endif
	LastStamp = tc->Time;
	LastSys = tc->SystemTime;

	fValid = TRUE;
}

////////////////////////////////////////////////////////////////////////////

ULONG GetStreamPTS(PHW_STREAM_OBJECT strm)
{
ULONG foo;

	if (!hClk)
	{
		return(0);
	}

	StreamClassQueryMasterClock(strm, hClk, TIME_GET_STREAM_TIME , StreamTimeCB);
	if (fValid)
	{
		foo = ConvertStrmtoPTS(LastStamp + GetSystemTime() - LastSys);
		#ifdef DBG
			//DbgPrint("luxdvd : GetStreamPTS - STRM PTS=0x%lx, STC=0x%lx\n",foo,DVReadReg(0x218));
		#endif
		return(foo);
	}

	else
	{
		#ifdef DBG
			//DbgPrint("GetStreamPTS - STRM PTS: 0");
		#endif
		return(0);
	}
}


/////////////////////////////////////////////////////////////////////////
//
//			  Function : ConvertPTStoStrm
//			  Args : PTS
//			  Returns : 
//
//			  Purpose:
//				converts a PTS to a Stream class 100 NS clock
//				
//
//			  Last Modified 10.1.96 by JBS
//
/////////////////////////////////////////////////////////////////////////


ULONGLONG ConvertPTStoStrm(ULONG pts)
{
ULONGLONG strm;

	//LuxSonor STC is in units of 90kHz
	//To convert to 100ns units, *10^9 / (90*10^3 * 100)

	strm = (ULONGLONG)pts;
	strm = (strm * 1000) / 9;

	return (strm);

}

/////////////////////////////////////////////////////////////////////////

ULONGLONG ConvertVPTStoStrm(ULONGLONG vpts)
{
ULONGLONG strm;

	//LuxSonor STC is in units of 90kHz
	//To convert to 100ns units, *10^9 / (90*10^3 * 100)

	strm = (ULONGLONG)vpts;
	strm = (strm * 1000) / 9;

	return (strm);

}



/////////////////////////////////////////////////////////////////////////
//
//			  Function : ConvertStrmtoPTS
//			  Args : PTS
//			  Returns : 
//
//			  Purpose:
//				converts a stream class clock to a PTS
//				
//
//			  Last Modified 10.1.96 by JBS
//
/////////////////////////////////////////////////////////////////////////


ULONG ConvertStrmtoPTS(ULONGLONG strm)
{

ULONGLONG temp;
ULONG pts;

	// we may lose some bits here, but we're only using the 32bit PTS anyway
	temp = (strm * 9) / 1000;
	pts = (ULONG)temp;
	return (pts);

}

//////////////////////////////////////////////////////////////

/*
** ClockEvents ()
**
**     handle any time event mark events
**
** Arguments:
**
**
**
** Returns:
**
** Side Effects:
*/

void ClockEvents(PHW_DEVICE_EXTENSION pdevex)
{
PKSEVENT_ENTRY pEvent, pLast;
PMYTIME pTim;
LONGLONG MinIntTime;
LONGLONG strmTime;

	#ifdef DBG
		//DbgPrint("luxdvd : ClockEvents\n");
	#endif

	if (!pdevex || !pdevex->pstroAud)
	{										  	
		#ifdef DBG
			//DbgPrint("luxdvd ERROR : ClockEvents - invalid device extension !!!\n");
		#endif
		return;
	}

	//Get the current 100ns time
	strmTime = LastStamp + (GetSystemTime() - LastSys);

	// loop through all time_mark events

	pEvent = NULL;
	pLast = NULL;

	//receive notification that a presentation time position mark has been passed

	while( (pEvent = StreamClassGetNextEvent(	//retrieves the	1st event from event queue
				pdevex,
				pdevex->pstroAud,
				(GUID *)&KSEVENTSETID_Clock,
				KSEVENT_CLOCK_POSITION_MARK,	
				pLast)) != NULL )
	{
		#ifdef DBG
			//DbgPrint("luxdvd : ClockEvents - found KSEVENT_CLOCK_POSITION_MARK in event queue\n");
		#endif

		//Dont want to signal the event when the time is greater than the current time		
		//as you would be signalling it in the future (Bill's reply to logic below)
		if (((PKSEVENT_TIME_MARK)(pEvent +1))->MarkTime <= strmTime )
		{
			#ifdef DBG
				//DbgPrint("luxdvd : ClockEvents - KSEVENT_CLOCK_POSITION_MARK presentation tiem position mark has been passed\n");
			#endif

			// signal the event here
			StreamClassStreamNotification(SignalStreamEvent,pdevex->pstroAud,pEvent);

			//no longer need to discard the event after signalling it
		}

		pLast = pEvent;
	}

	// loop through all time_interval events
	pEvent = NULL;
	pLast = NULL;

	//receive notifications at a specified presentation time interval

	while( (pEvent = StreamClassGetNextEvent(  
                pdevex,                        
                pdevex->pstroAud,              
                (GUID *)&KSEVENTSETID_Clock,           
                KSEVENT_CLOCK_INTERVAL_MARK,   
                pLast)) != NULL )
	{
		#ifdef DBG
			//DbgPrint("luxdvd : ClockEvents - found KSEVENT_CLOCK_INTERVAL_MARK in event queue\n");
		#endif

		// check if this event has been used for this interval yet
		pTim = ((PMYTIME)(pEvent + 1));

		if(pTim && pTim->tim.Interval)
		{
			if(pTim->tim.TimeBase <= strmTime)
			{
				MinIntTime = (strmTime - pTim->tim.TimeBase) / pTim->tim.Interval;
				MinIntTime *= pTim->tim.Interval;
				MinIntTime +=  pTim->tim.TimeBase;
	
				if (MinIntTime > pTim->LastTime  )
				{
					#ifdef DBG
						//DbgPrint("luxdvd : ClockEvents - KSEVENT_CLOCK_INTERVAL_MARK - trigger event\n");
					#endif
					// signal the event here
					StreamClassStreamNotification(SignalStreamEvent,pdevex->pstroAud,pEvent);
					pTim->LastTime = strmTime;
				}
			}
		}
		else
		{
			//TRAP
		}
		pLast = pEvent;
		
	}
		
}


/*
** AudioEvent ()
**
**    receives notification for audio clock enable / disable events
**
** Arguments:
**
**
**
** Returns:
**
** Side Effects:
*/


STREAMAPI AudioEvent (PHW_EVENT_DESCRIPTOR pEvent)
{
PUCHAR pCopy = (PUCHAR)(pEvent->EventEntry +1);
PUCHAR pSrc = (PUCHAR)pEvent->EventData;
ULONG cCopy=0;
PMYTIME pmyt = (PMYTIME)pCopy;

	#ifdef DBG
		//DbgPrint("luxdvd : AudioEvent\n");
	#endif

	if (pEvent->Enable)
	{
		switch (pEvent->EventEntry->EventItem->EventId)
		{
			case KSEVENT_CLOCK_POSITION_MARK:
				//allows a client to receive notification that a presentation time position mark has been passed
				cCopy = sizeof (KSEVENT_TIME_MARK);
				break;

			case KSEVENT_CLOCK_INTERVAL_MARK:
				cCopy = sizeof (KSEVENT_TIME_INTERVAL);
				break;

			default:
				TRAP
				#ifdef DBG
					DbgPrint("luxdvd : AudioEvent - returning STATUS_NOT_IMPLEMENTED !!!\n");
				#endif
				return (STATUS_NOT_IMPLEMENTED);
		}

		if (pEvent->EventEntry->EventItem->DataInput != cCopy)
		{
			TRAP
			#ifdef DBG
				DbgPrint("luxdvd : AudioEvent - returning STATUS_INVALID_BUFFER_SIZE !!!\n");
			#endif
			return (STATUS_INVALID_BUFFER_SIZE);
		}

		//
		// copy the input buffer
		//

		for (;cCopy > 0; cCopy--)
		{
			*pCopy++ = *pSrc++;
		}
		
		if( pEvent->EventEntry->EventItem->EventId == KSEVENT_CLOCK_INTERVAL_MARK) 
		{
             pmyt->LastTime = 0;
        }
		
	}


	return (STATUS_SUCCESS);
}

////////////////////////////////////////////////////////////////////////////////////////////////

//Audio Rate Change Functions

void InitAudioRate(PHW_DEVICE_EXTENSION pHwDevExt)
{
	#ifdef DBG
		//DbgPrint("luxdvd : InitAudioRate\n");
	#endif
	pHwDevExt->AudioStartTime = 0;
	pHwDevExt->AudioInterceptTime = 0;
	pHwDevExt->AudioRate = 1 * 10000;
	pHwDevExt->AudioMaxFullRate = 1 * 10000;
}

////////////////////////////////////////////////////////////////////////////////////////////////

void AudioRateChangeSetProp(PHW_STREAM_REQUEST_BLOCK pSrb)
{
	#ifdef DBG
		//DbgPrint("luxdvd : AudioRateChangeSetProp\n");
	#endif

	switch( pSrb->CommandData.PropertyInfo->Property->Id ) 
	{
		case KS_AM_RATE_SimpleRateChange :
		{
		KS_AM_SimpleRateChange* pRateChange;
		PHW_DEVICE_EXTENSION pHwDevExt;
		REFERENCE_TIME NewStartTime;
		LONG NewRate;

			#ifdef DBG
				//DbgPrint("luxdvd :  AudioRateChangeSetProp - KS_AM_RATE_SimpleRateChange\n");
			#endif

			pRateChange = (KS_AM_SimpleRateChange*)pSrb->CommandData.PropertyInfo->PropertyInfo;
			pHwDevExt = (PHW_DEVICE_EXTENSION)pSrb->HwDeviceExtension;
			NewStartTime = pRateChange->StartTime;
			NewRate = ( pRateChange->Rate < 0 ) ? -pRateChange->Rate : pRateChange->Rate;
			#ifdef DBG
				//DbgPrint("luxdvd : Received Data - StartTime = 0x%08x\n",NewStartTime);
				//DbgPrint("luxdvd : Received Data - Rate = 0x%08x\n",NewRate);
			#endif
			//				pHwDevExt->AudioInterceptTime
			//					= (pHwDevExt->AudioInterceptTime - NewStartTime)
			//					* pHwDevExt->AudioRate
			//					/ NewRate
			//					+ NewStartTime;

			pHwDevExt->AudioRate = NewRate;

			if( NewRate == 10000 ) 
			{
				#ifdef DBG
					//DbgPrint("luxdvd : AUDIO rate set to NORMAL\n");
				#endif
				pHwDevExt->AudioInterceptTime = 0;
				pHwDevExt->AudioStartTime = 0;
			}
			else 
			{
				#ifdef DBG
					//DbgPrint("luxdvd : AUDIO rate set to DIFFERENT\n");
				#endif
				pHwDevExt->AudioInterceptTime = (ULONG)((-NewStartTime) * 10000 / NewRate + NewStartTime);
				pHwDevExt->AudioStartTime = NewStartTime;
			}

			SetRateChange( pHwDevExt, 0x02 );
			pSrb->Status = STATUS_SUCCESS;
			break;
		}

		case KS_AM_RATE_ExactRateChange :
			pSrb->Status = STATUS_NOT_IMPLEMENTED;
			break;

		case KS_AM_RATE_MaxFullDataRate :
			pSrb->Status = STATUS_NOT_IMPLEMENTED;
			break;

		case KS_AM_RATE_Step :
			pSrb->Status = STATUS_NOT_IMPLEMENTED;
			break;
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////

void AudioRateChangeGetProp(PHW_STREAM_REQUEST_BLOCK pSrb)
{
PHW_DEVICE_EXTENSION pHwDevExt = (PHW_DEVICE_EXTENSION)pSrb->HwDeviceExtension;

	#ifdef DBG
		//DbgPrint("luxdvd : AudioRateChangeGetProp\n");
	#endif

	switch( pSrb->CommandData.PropertyInfo->Property->Id ) 
	{
		case KS_AM_RATE_SimpleRateChange :
		{
		KS_AM_SimpleRateChange* pRateChange;

			#ifdef DBG
				//DbgPrint("luxdvd : AudioRateChangeGetProp - KS_AM_RATE_SimpleRateChange\n");
			#endif
			pSrb->ActualBytesTransferred = sizeof (KS_AM_RATE_SimpleRateChange);
			pRateChange = (KS_AM_SimpleRateChange*)pSrb->CommandData.PropertyInfo->PropertyInfo;
			pRateChange->StartTime = pHwDevExt->AudioStartTime;
			pRateChange->Rate = pHwDevExt->AudioRate;
			pSrb->Status = STATUS_SUCCESS;
			break;
		}

		case KS_AM_RATE_ExactRateChange :
			pSrb->Status = STATUS_NOT_IMPLEMENTED;
			break;

		case KS_AM_RATE_MaxFullDataRate :
		{
		KS_AM_MaxFullDataRate* pMaxRate;

			#ifdef DBG
				//DbgPrint("luxdvd : AudioRateChangeGetProp - KS_AM_RATE_MaxFullDataRate\n");
			#endif
			pSrb->ActualBytesTransferred = sizeof (KS_AM_RATE_MaxFullDataRate);
			pMaxRate = (KS_AM_MaxFullDataRate*)pSrb->CommandData.PropertyInfo->PropertyInfo;
			*pMaxRate = pHwDevExt->AudioMaxFullRate;
			pSrb->Status = STATUS_SUCCESS;
			break;
		}

		case KS_AM_RATE_Step :
			pSrb->Status = STATUS_NOT_IMPLEMENTED;
			break;
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////

void AudioQueryAccept(PHW_STREAM_REQUEST_BLOCK pSrb)
{
PKSDATAFORMAT pfmt = pSrb->CommandData.OpenFormat;

	#ifdef DBG
		DbgPrint("luxdvd : AudioQueryAccept\n");
	#endif

	if(!pSrb)
	{
		#ifdef DBG
			DbgPrint("luxdvd ERROR : AudioQueryAccept - invalid SRB !!!!\n");
		#endif
		return;
	}

	pSrb->Status = STATUS_SUCCESS;

}

////////////////////////////////////////////////////////////////////////////////////////////////
extern BYTE	TK[5];

void ProcessAudioFormat( PKSDATAFORMAT pfmt, PHW_DEVICE_EXTENSION pHwDevExt )
{
	#ifdef DBG
		DbgPrint("luxdvd : ProcessAudioFormat\n");
	#endif

	/*
	if (pfmt->FormatSize != sizeof(KSDATAFORMAT) + sizeof(WAVEFORMATEX))
	{
		TRAP
		#ifdef DBG
			DbgPrint("luxdvd ERROR : ProcessAudioFormat - invalid pfmt->FormatSize !!!\n");
		#endif
		return;
	}
	*/
	
	if( (IsEqualGUID2(&(pfmt->MajorFormat), &(Mpeg2ksdataformat.MajorFormat)) &&
		IsEqualGUID2(&(pfmt->SubFormat), &(Mpeg2ksdataformat.SubFormat))) ) 
	{
		// AC-3
		#ifdef DBG
			DbgPrint("luxdvd : ProcessAudioFormat - AC3\n");
		#endif

		if(pHwDevExt->AudioType != AUDIO_AC3)
		{
			//change back to AC3
			LUXAudioStop();
			LUXAudioStopDSP();

			LUXVideoStop();

			pHwDevExt->AudioType = AUDIO_AC3;

			LUXVideoCleanScreen();	//try not to show garbage whilst we reset chip

			DisableIT();
			LUXChngDSP(AUDIO_AC3,pHwDevExt->VideoPort);
			EnableIT();

			DspTitleKey(TK);	//force DSP setup Discramble register again

			LUXVideoSetVPM(1,pHwDevExt->VideoPort);

			LUXVideoPlay(1000);
			LUXAudioPlay(1000);
		}

	}
	else if( (IsEqualGUID2(&(pfmt->MajorFormat), &(LPCMksdataformat.MajorFormat)) &&
			IsEqualGUID2(&(pfmt->SubFormat), &(LPCMksdataformat.SubFormat))) ) 
	{
	WAVEFORMATEX * pblock = (WAVEFORMATEX *)((ULONG)pfmt + sizeof(KSDATAFORMAT) );

		// LPCM
		#ifdef DBG
			DbgPrint("luxdvd : ProcessAudioFormat - LPCM !!!!!!!!!!!!!!!!\n");
		#endif
		
		if(pHwDevExt->AudioType != AUDIO_PCM)
		{

			LUXAudioStop();
			LUXAudioStopDSP();

			LUXVideoStop();

			pHwDevExt->AudioType = AUDIO_PCM;

			LUXVideoCleanScreen();	//try not to show garbage whilst we reset chip

			DisableIT();
			LUXChngDSP(AUDIO_PCM,pHwDevExt->VideoPort);
			EnableIT();

			LUXVideoSetVPM(1,pHwDevExt->VideoPort);

			LUXVideoPlay(1000);
			LUXAudioPlay(1000);
			b_first_pcm_data = TRUE;
		}

		
		//We detect 1st pcm packet in mpstAudioPacket(), send config data
		//down to hw and we call LUXAudioPlay.

	}
	else if( (IsEqualGUID2(&(pfmt->MajorFormat), &(Mpeg2PESksdataformat.MajorFormat)) &&
		IsEqualGUID2(&(pfmt->SubFormat), &(Mpeg2PESksdataformat.SubFormat))) ) 
	{
		#ifdef DBG
			DbgPrint("luxdvd : ProcessAudioFormat - MPEG2 PES Audio !!!!!!!!!!!!!!!!\n");
		#endif
	}
	else 
	{
		#ifdef DBG
			DbgPrint("luxdvd :  ProcessAudioFormat - Unsupport audio type , Assume MPEG\n");
		#endif

		if ( pHwDevExt->AudioType != AUDIO_MPG )
		{
			LUXAudioStop();
			LUXAudioStopDSP();

			LUXVideoStop();		

			LUXVideoCleanScreen();	//try not to show garbage whilst we reset chip

			DisableIT();
			LUXChngDSP(AUDIO_MPG,pHwDevExt->VideoPort);
			EnableIT();

			DspTitleKey(TK);	//force DSP setup Discramble register again

			LUXVideoSetVPM(1,pHwDevExt->VideoPort);

			LUXVideoPlay(1000);
			LUXAudioPlay(1000);
		
			pHwDevExt->AudioType = AUDIO_MPG;
		}
	}

	m_Pal = FALSE;

}

////////////////////////////////////////////////////////////////////////////////////////////////

void AudioLuxSetProp(PHW_STREAM_REQUEST_BLOCK pSrb)
{
	#ifdef DBG
		//DbgPrint("luxdvd : AudioLuxSetProp\n");
	#endif

	switch( pSrb->CommandData.PropertyInfo->Property->Id ) 
	{
		case LUXSONOR_AM_PROPERTY_AUD_VOLUME :
		{
		LuxAudVolume* pVolume;

			#ifdef DBG
				//DbgPrint("luxdvd :  AudioLuxSetProp - SETVOLUME\n");
			#endif

			pVolume = (LuxAudVolume*)pSrb->CommandData.PropertyInfo->PropertyInfo;
			LUXAudioSetVolume(pVolume->volume);
			pSrb->Status = STATUS_SUCCESS;
			break;
		}

		case LUXSONOR_AM_PROPERTY_AUD_MUTE :
		{
		LuxMuteAudVolume* pMute;

			pMute = (LuxMuteAudVolume*)pSrb->CommandData.PropertyInfo->PropertyInfo;
			LUXAudioAudOnOff(pMute->mute);
			pSrb->Status = STATUS_SUCCESS;
			break;
		}

		case LUXSONOR_AM_PROPERTY_AUD_OUTPUTCHANNEL :
		{
		LuxSetAudChannel* pChannel;

			pChannel = (LuxSetAudChannel*)pSrb->CommandData.PropertyInfo->PropertyInfo;
			LUXAudioSetConfig(pChannel->channel);
			pSrb->Status = STATUS_SUCCESS;			
			break;
		}

		case LUXSONOR_AM_PROPERTY_AUD_KARAOKE :
		{
		LuxSetAudKaraokeMode* pMode;

			pMode = (LuxSetAudKaraokeMode*)pSrb->CommandData.PropertyInfo->PropertyInfo;
			LUXAudioSetKaraoke(pMode->Mode);
			pSrb->Status = STATUS_SUCCESS;			
			break;
		}

		default:
			pSrb->Status = STATUS_NOT_IMPLEMENTED;
			break;
	}
}

void AudioLuxGetProp(PHW_STREAM_REQUEST_BLOCK pSrb)
{
	pSrb->Status = STATUS_SUCCESS;

	switch( pSrb->CommandData.PropertyInfo->Property->Id ) 
	{
		case LUXSONOR_AM_PROPERTY_AUD_VOLUME :
		{
		LuxAudVolume* pVolume;

			pVolume = (LuxAudVolume*)pSrb->CommandData.PropertyInfo->PropertyInfo;
			pVolume->volume = LUXAudioGetVolume();
			pSrb->ActualBytesTransferred = sizeof(LuxAudVolume);
			break;
		}

		default:
			pSrb->Status = STATUS_NOT_IMPLEMENTED;
			break;
	}
}