/*
// ================================================================
// MPEG.cxx
// Ubi Sound Studios
// 24/03/1997
// Guillaume Michel
// ================================================================
*/


//added by stephane RONSE on 04/29/98
// must be set FALSE each time a new sound begin
// will be set to TRUE each time silence beginn(i.e. no more sound are played) and so the SynhtesisBuffer is reinitilized
SndBool bSynthesisBufferCleaned=TRUE;


//-------------------------------------------------------------------------
// Description : Init MPEG Engine
//-------------------------------------------------------------------------
// MUST BE DONE ONCE AT THE BEGINING OF THE PROGRAM
//-------------------------------------------------------------------------
// Input  : None
// Output : SndBool
//-------------------------------------------------------------------------
// Creation date : 18/03/97              Author : Guillaume Michel
//-------------------------------------------------------------------------
// Modifications :
// Modification Date :              Author :
//-------------------------------------------------------------------------

SndBool SND_fn_bInitMPEG()
{
    if (!SND_fn_bTestSnd_Processor())
    {
        SND_fn_vDisplayError(E_uwSndBadHostConfiguration,C_PROCESSOR_WANTED);
        return C_INIT_FAILED;
    }

#ifdef _PROFILE_MFC_
	QueryPerformanceFrequency(&t0);
	iTotalTime = iNumTime = 0;
#endif

	SND_fn_vInitSignalProcessing();
	return (bInitDone = SND_fn_bInitMPEGMemory());

}

SndBool SND_fn_bInitDone()
{
	return bInitDone;
}

//-------------------------------------------------------------------------
// Description : free memory for decrompression...
//-------------------------------------------------------------------------
// DO ALL DESALLOC (CALLED ONCE AT THE END OF THE APPLICATION)
//-------------------------------------------------------------------------
// Input  : voice pointer and number of channel
// Output : SndBool
//-------------------------------------------------------------------------
// Creation date : 18/03/97              Author : Guillaume Michel
//-------------------------------------------------------------------------
// Modifications :
// Modification Date :              Author :
//-------------------------------------------------------------------------

SndBool SND_fn_bFreeMPEGMemory(SND_tdstMPEGVoice* pVoice)
{
	snd_assert(pVoice);
	if (pVoice->pucBitAlloc) {
		SND_fn_vFreeSnd(pVoice->pucBitAlloc);
		pVoice->pucBitAlloc = NULL;
		pVoice->pucScaleFactorPattern = NULL;
		pVoice->pucScaleFactor = NULL;
	}
	if (pVoice->pdDiviseur) {
		SND_fn_vFreeSnd(pVoice->pdDiviseur);
		pVoice->pdDiviseur = NULL;
		pVoice->pdScaleFactor = NULL;
		pVoice->pdSumFactor = NULL;
	}
	if (pVoice->pwSample) {
		SND_fn_vFreeSnd(pVoice->pwSample);
		pVoice->pwSample = NULL;
	}
	pVoice->pfnCallBack = NULL;
	return TRUE;
}

//-------------------------------------------------------------------------
// Description : Init MPEG  Engine before using it
//-------------------------------------------------------------------------
// 
//-------------------------------------------------------------------------
// Input  : None
// Output : None
//-------------------------------------------------------------------------
// Creation date : 24/03/97              Author : Guillaume Michel
//-------------------------------------------------------------------------
// Modifications :
// Modification Date :              Author :
//-------------------------------------------------------------------------

SndBool SND_fn_bStartMPEGEngine()
{
	int i;
	SND_tdstMPEGVoice*	pVoice;

	// MPEG Engine INITS
	for (i = 0; i < (kNumVoice+1); i++) {
		pVoice = &MPEGvoices[i];
		pVoice->bIsFree = TRUE;
		pVoice->bIsMuted = FALSE;
		pVoice->bIsPlaying = FALSE;
		
		pVoice->bIsFading = FALSE;
		pVoice->iFadeVolume = 0;
		pVoice->iFadeIncrement = 0;

		
		pVoice->bIsPaused = FALSE;
		pVoice->bIsOneShot = FALSE;
		pVoice->bIsSurround = FALSE;
		pVoice->bIsFullSurround = FALSE;
		pVoice->bIsFinishing = FALSE;
		pVoice->ulNumSampleBeforeEnd = 0;

		SND_fn_bInitBitStream(&(pVoice->bitstream));
		pVoice->pfnCallBack = NULL;
	}
	
	pSwapVoice[0] = &MPEGvoices[0];	// playing voice
	pSwapVoice[1] = &MPEGvoices[1];	// waiting voice
	iOffset[0] = iOffset[1] = 64;
	bSwitchPart = FALSE;

	iMiniFrameTheme=12;
	iIndexTheme=3;
	iMiniFrameSample=12;
	iIndexSample=3;

	return TRUE;
}


//-------------------------------------------------------------------------
// Description : Open a new voice from File
//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------
// Input  : filename and number of channel
// Output : voice pointer
//-------------------------------------------------------------------------
// Creation date : 18/03/97              Author : Guillaume Michel
//-------------------------------------------------------------------------
// Modifications :
// Modification Date :              Author :
//-------------------------------------------------------------------------

SND_tdstMPEGVoice* SND_fn_pAllocateMPEGVoiceFile(char* pFileName, unsigned long ulOffset)
{	
	int i;
	unsigned long param;
	SND_td_pfn_vSoundCallback cb;
	SND_tdstMPEGVoice* pCurrentVoice;

	if (pSwapVoice[0]->bIsFree) {
		i = 0; // La voie principale est libre: on démarre dessus
	} else {
		// on écrase la voie secondaire (impératif thème)
		SND_fn_bFreeMPEGVoice(pSwapVoice[1]);
		i = 1;
	}
	pCurrentVoice = pSwapVoice[i];
	cb = pCurrentVoice->pfnCallBack;
	param = pCurrentVoice->ulParam;

	if (SND_fn_bOpenBitStreamFile(pCurrentVoice->pRealBitStream,pFileName,ulOffset)) {
		// This voice will be used...
		pCurrentVoice->bIsFree = FALSE;	
		//pCurrentVoice->ucVolume = 127;
		//pCurrentVoice->ucVolumeIndex = 0;
		pCurrentVoice->bIsPaused = pCurrentVoice->bIsMuted = FALSE;	

		pCurrentVoice->ulPos = 0;
		pCurrentVoice->ulFinalSize = pCurrentVoice->pRealBitStream->ulFinalSize ;

		pCurrentVoice->bIsFullSurround = (pCurrentVoice->pRealBitStream->ulDolbyMark == 'SUR2');
		pCurrentVoice->bIsSurround =	((pCurrentVoice->pRealBitStream->ulDolbyMark == 'SUR2') || 
										(pCurrentVoice->pRealBitStream->ulDolbyMark == 'SUR1'));

		if (pCurrentVoice->bIsSurround) {
			pCurrentVoice->pAssociatedVoice = pCurrentVoice + (kNumVoice+1);
			pCurrentVoice->pAssociatedVoice->bIsPlaying = TRUE;
			//pCurrentVoice->pAssociatedVoice->ucVolume = 127;
			//pCurrentVoice->pAssociatedVoice->ucVolumeIndex = 0;
			pCurrentVoice->pAssociatedVoice->bIsMuted = FALSE;	
		} else {
			pCurrentVoice->pAssociatedVoice = NULL;
		}

		pCurrentVoice->pfnCallBack = cb;
		pCurrentVoice->ulParam = param;
		pCurrentVoice->bIsPlaying = TRUE;
		pCurrentVoice->bIsFinishing = FALSE;
		pCurrentVoice->ulNumSampleBeforeEnd = 0;

		pCurrentVoice->bIsFading = FALSE;
		pCurrentVoice->iFadeVolume = 0;
		pCurrentVoice->iFadeIncrement = 0;

		return pCurrentVoice;
	} else return NULL;

}

//-------------------------------------------------------------------------
// Description : Open a new voice from memory
//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------
// Input  : buffer pointer, buffer size, voice pointer and number of channel
// Output : voice pointer
//-------------------------------------------------------------------------
// Creation date : 18/03/97              Author : Guillaume Michel
//-------------------------------------------------------------------------
// Modifications :
// Modification Date :              Author :
//-------------------------------------------------------------------------

SND_tdstMPEGVoice* SND_fn_pAllocateMPEGVoiceMem(unsigned char* pBuffer, unsigned long ulSize)
{
	int i;
	unsigned long param;
	SND_td_pfn_vSoundCallback cb;

	SND_tdstMPEGVoice* pCurrentVoice;

	if (pSwapVoice[0]->bIsFree) {
		i = 0; 
	} else {
		// on écrase la voie secondaire (impératif thème)
		SND_fn_bFreeMPEGVoice(pSwapVoice[1]);
		i = 1;
	}
	pCurrentVoice = pSwapVoice[i];
	cb = pCurrentVoice->pfnCallBack;
	param = pCurrentVoice->ulParam;

	if (SND_fn_bOpenBitStreamMem(pCurrentVoice->pRealBitStream,pBuffer,ulSize)) {
		// This voice will be used...
		pCurrentVoice->bIsFree = FALSE;	
		//pCurrentVoice->ucVolume = 127;
		//pCurrentVoice->ucVolumeIndex = 0;
		pCurrentVoice->bIsPaused = pCurrentVoice->bIsMuted = FALSE;	

		pCurrentVoice->ulPos = 0;
		pCurrentVoice->ulFinalSize = pCurrentVoice->pRealBitStream->ulFinalSize ;
		pCurrentVoice->bIsFullSurround = (pCurrentVoice->pRealBitStream->ulDolbyMark == 'SUR2');
		pCurrentVoice->bIsSurround =	(pCurrentVoice->pRealBitStream->ulDolbyMark == 'SUR2') || 
										(pCurrentVoice->pRealBitStream->ulDolbyMark == 'SUR1');

		if (pCurrentVoice->bIsSurround) {
			pCurrentVoice->pAssociatedVoice = pCurrentVoice + (kNumVoice+1);
			pCurrentVoice->pAssociatedVoice->bIsPlaying = TRUE;
			//pCurrentVoice->pAssociatedVoice->ucVolume = 127;
			//pCurrentVoice->pAssociatedVoice->ucVolumeIndex = 0;
			pCurrentVoice->pAssociatedVoice->bIsMuted = FALSE;	
		} else {
			pCurrentVoice->pAssociatedVoice = NULL;
		}
		pCurrentVoice->pfnCallBack = cb;
		pCurrentVoice->ulParam = param;
		pCurrentVoice->bIsPlaying = TRUE;
		pCurrentVoice->bIsFinishing = FALSE;
		pCurrentVoice->ulNumSampleBeforeEnd = 0;

		pCurrentVoice->bIsFading = FALSE;
		pCurrentVoice->iFadeVolume = 0;
		pCurrentVoice->iFadeIncrement = 0;

		return pCurrentVoice;
	} else return NULL;

}

//-------------------------------------------------------------------------
// Description : Open a new voice from File
//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------
// Input  : filename and number of channel
// Output : voice pointer
//-------------------------------------------------------------------------
// Creation date : 18/03/97              Author : Guillaume Michel
//-------------------------------------------------------------------------
// Modifications :
// Modification Date :              Author :
//-------------------------------------------------------------------------

SND_tdstMPEGVoice* SND_fn_pSimpleAllocateMPEGVoiceFile(char* pFileName, unsigned long ulOffset)
{	
	SND_tdstMPEGVoice* pCurrentVoice;

	pCurrentVoice = pSwapVoice[0];
	if (!pCurrentVoice->bIsFree) {
		return NULL;
	}


	//added by stepahen RONSE on 04/29/98
	bSynthesisBufferCleaned=FALSE;
	
	if (SND_fn_bOpenBitStreamFile(pCurrentVoice->pRealBitStream,pFileName, ulOffset)) {
		// This voice will be used...
		pCurrentVoice->bIsFree = FALSE;	
		pCurrentVoice->bIsPlaying = TRUE;
		//pCurrentVoice->ucVolume = 127;
		//pCurrentVoice->ucVolumeIndex = 0;
		pCurrentVoice->bIsPaused = pCurrentVoice->bIsMuted = FALSE;	

		pCurrentVoice->bIsFullSurround = (pCurrentVoice->pRealBitStream->ulDolbyMark == 'SUR2');
		pCurrentVoice->bIsSurround =	(pCurrentVoice->pRealBitStream->ulDolbyMark == 'SUR2') || 
										(pCurrentVoice->pRealBitStream->ulDolbyMark == 'SUR1');

		if (pCurrentVoice->bIsSurround) {
			pCurrentVoice->pAssociatedVoice = pCurrentVoice + (kNumVoice+1);
			pCurrentVoice->pAssociatedVoice->bIsPlaying = TRUE;
			//pCurrentVoice->pAssociatedVoice->ucVolume = 127;
			//pCurrentVoice->pAssociatedVoice->ucVolumeIndex = 0;
			pCurrentVoice->pAssociatedVoice->bIsMuted = FALSE;	
		} else {
			pCurrentVoice->pAssociatedVoice = NULL;
		}
		pCurrentVoice->ulPos = 0;
		pCurrentVoice->ulFinalSize = pCurrentVoice->pRealBitStream->ulFinalSize  ;
		pCurrentVoice->bIsFinishing = FALSE;
		pCurrentVoice->ulNumSampleBeforeEnd = 0;

		pCurrentVoice->bIsFading = FALSE;
		pCurrentVoice->iFadeVolume = 0;
		pCurrentVoice->iFadeIncrement = 0;

		return pCurrentVoice;
	} else return NULL;

}

//-------------------------------------------------------------------------
// Description : Open a new voice from memory
//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------
// Input  : buffer pointer, buffer size, voice pointer and number of channel
// Output : voice pointer
//-------------------------------------------------------------------------
// Creation date : 18/03/97              Author : Guillaume Michel
//-------------------------------------------------------------------------
// Modifications :
// Modification Date :              Author :
//-------------------------------------------------------------------------

SND_tdstMPEGVoice* SND_fn_pSimpleAllocateMPEGVoiceMem(unsigned char* pBuffer, unsigned long ulSize)
{
	SND_tdstMPEGVoice* pCurrentVoice;

	pCurrentVoice = pSwapVoice[0];
	if (!pCurrentVoice->bIsFree) {
		return NULL;
	}

	//added by stepahen RONSE on 04/29/98
	bSynthesisBufferCleaned=FALSE;
	
	if (SND_fn_bOpenBitStreamMem(pCurrentVoice->pRealBitStream,pBuffer,ulSize)) {
		// This voice will be used...
		pCurrentVoice->bIsFree = FALSE;	
		pCurrentVoice->bIsPlaying = TRUE;
		//pCurrentVoice->ucVolume = 127;
		//pCurrentVoice->ucVolumeIndex = 0;
		pCurrentVoice->bIsPaused = pCurrentVoice->bIsMuted = FALSE;	
		pCurrentVoice->bIsFullSurround = (pCurrentVoice->pRealBitStream->ulDolbyMark == 'SUR2');
		pCurrentVoice->bIsSurround =	(pCurrentVoice->pRealBitStream->ulDolbyMark == 'SUR2') || 
										(pCurrentVoice->pRealBitStream->ulDolbyMark == 'SUR1');

		if (pCurrentVoice->bIsSurround) {
			pCurrentVoice->pAssociatedVoice = pCurrentVoice + (kNumVoice+1);
			pCurrentVoice->pAssociatedVoice->bIsPlaying = TRUE;
			//pCurrentVoice->pAssociatedVoice->ucVolume = 127;
			//pCurrentVoice->pAssociatedVoice->ucVolumeIndex = 0;
			pCurrentVoice->pAssociatedVoice->bIsMuted = FALSE;	
		} else {
			pCurrentVoice->pAssociatedVoice = NULL;
		}
		pCurrentVoice->ulPos = 0;
		pCurrentVoice->ulFinalSize = pCurrentVoice->pRealBitStream->ulFinalSize ;
		pCurrentVoice->bIsFinishing = FALSE;
		pCurrentVoice->ulNumSampleBeforeEnd = 0;

		pCurrentVoice->bIsFading = FALSE;
		pCurrentVoice->iFadeVolume = 0;
		pCurrentVoice->iFadeIncrement = 0;

		return pCurrentVoice;
	} else return NULL;

}

//-------------------------------------------------------------------------
// Description : Open a new voice from File
//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------
// Input  : filename and number of channel
// Output : voice pointer
//-------------------------------------------------------------------------
// Creation date : 18/03/97              Author : Guillaume Michel
//-------------------------------------------------------------------------
// Modifications :
// Modification Date :              Author :
//-------------------------------------------------------------------------

SND_tdstMPEGVoice* SND_fn_pSampleAllocateMPEGVoiceFile(char* pFileName, unsigned long ulOffset)
{	   
	SND_tdstMPEGVoice* pCurrentVoice;

	pCurrentVoice = &(MPEGvoices[2]);
	if (!pCurrentVoice->bIsFree) {
		SND_fn_vDisplayErrorEx(E_uwSndTooManySamples,"Too many MPEG streaming voices",SNDERR_DISPLAY_IF_WANTED);
		return NULL;
	}

	//added by stepahen RONSE on 04/29/98
	bSynthesisBufferCleaned=FALSE;
	
	if (SND_fn_bOpenBitStreamFile(pCurrentVoice->pRealBitStream,pFileName,ulOffset)) {
		// This voice will be used...
		pCurrentVoice->bIsFree = FALSE;	
		pCurrentVoice->ucVolume = 127;
		pCurrentVoice->ucCurrentVolume = 127;
		pCurrentVoice->ucVolumeIndex = 0;
		pCurrentVoice->bIsPaused = pCurrentVoice->bIsMuted = FALSE;	
		pCurrentVoice->bIsOneShot = TRUE;
		pCurrentVoice->bIsFullSurround = (pCurrentVoice->pRealBitStream->ulDolbyMark == 'SUR2');
		pCurrentVoice->bIsSurround =	(pCurrentVoice->pRealBitStream->ulDolbyMark == 'SUR2') || 
										(pCurrentVoice->pRealBitStream->ulDolbyMark == 'SUR1');

		if (pCurrentVoice->bIsSurround) {
			pCurrentVoice->pAssociatedVoice = pCurrentVoice + (kNumVoice+1);
			pCurrentVoice->pAssociatedVoice->bIsPlaying = TRUE;
			pCurrentVoice->pAssociatedVoice->ucVolume = 127;
			pCurrentVoice->pAssociatedVoice->ucCurrentVolume = 127;
			pCurrentVoice->pAssociatedVoice->ucVolumeIndex = 0;
			pCurrentVoice->pAssociatedVoice->bIsMuted = FALSE;	
		} else {
			pCurrentVoice->pAssociatedVoice = NULL;
		}
		pCurrentVoice->ulPos = 0;
		pCurrentVoice->ulFinalSize = pCurrentVoice->pRealBitStream->ulFinalSize;
		pCurrentVoice->bIsPlaying = TRUE;
		pCurrentVoice->bIsFinishing = FALSE;
		pCurrentVoice->ulNumSampleBeforeEnd = 0;

		pCurrentVoice->bIsFading = FALSE;
		pCurrentVoice->iFadeVolume = 0;
		pCurrentVoice->iFadeIncrement = 0;

		return pCurrentVoice;
	} else return NULL;

}

//-------------------------------------------------------------------------
// Description : Open a new voice from memory
//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------
// Input  : buffer pointer, buffer size, voice pointer and number of channel
// Output : voice pointer
//-------------------------------------------------------------------------
// Creation date : 18/03/97              Author : Guillaume Michel
//-------------------------------------------------------------------------
// Modifications :
// Modification Date :              Author :
//-------------------------------------------------------------------------

SND_tdstMPEGVoice* SND_fn_pSampleAllocateMPEGVoiceMem(unsigned char* pBuffer, unsigned long ulSize)
{
	SND_tdstMPEGVoice* pCurrentVoice;

	pCurrentVoice = &(MPEGvoices[2]);
	if (!pCurrentVoice->bIsFree) {
		return NULL;
	}

	//added by stepahen RONSE on 04/29/98
	bSynthesisBufferCleaned=FALSE;
	
	if (SND_fn_bOpenBitStreamMem(pCurrentVoice->pRealBitStream,pBuffer,ulSize)) {
		// This voice will be used...
		pCurrentVoice->bIsFree = FALSE;	
		pCurrentVoice->bIsPlaying = TRUE;
		pCurrentVoice->ucVolume = 127;
		pCurrentVoice->ucCurrentVolume = 127;
		pCurrentVoice->ucVolumeIndex = 0;
		pCurrentVoice->bIsPaused = pCurrentVoice->bIsMuted = FALSE;	
		pCurrentVoice->bIsOneShot = TRUE;
		pCurrentVoice->bIsFullSurround = (pCurrentVoice->pRealBitStream->ulDolbyMark == 'SUR2');
		pCurrentVoice->bIsSurround =	(pCurrentVoice->pRealBitStream->ulDolbyMark == 'SUR2') || 
										(pCurrentVoice->pRealBitStream->ulDolbyMark == 'SUR1');

		if (pCurrentVoice->bIsSurround) {
			pCurrentVoice->pAssociatedVoice = pCurrentVoice + (kNumVoice+1);
			pCurrentVoice->pAssociatedVoice->bIsPlaying = TRUE;
			pCurrentVoice->pAssociatedVoice->ucVolume = 127;
			pCurrentVoice->pAssociatedVoice->ucCurrentVolume = 127;
			pCurrentVoice->pAssociatedVoice->ucVolumeIndex = 0;
			pCurrentVoice->pAssociatedVoice->bIsMuted = FALSE;	
		} else {
			pCurrentVoice->pAssociatedVoice = NULL;
		}

		pCurrentVoice->ulPos = 0;
		pCurrentVoice->ulFinalSize = pCurrentVoice->pRealBitStream->ulFinalSize;
		pCurrentVoice->bIsFinishing = FALSE;
		pCurrentVoice->ulNumSampleBeforeEnd = 0;

		pCurrentVoice->bIsFading = FALSE;
		pCurrentVoice->iFadeVolume = 0;
		pCurrentVoice->iFadeIncrement = 0;

		return pCurrentVoice;
	} else return NULL;

}

//-------------------------------------------------------------------------
// Description : close a voice 
//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------
// Input  : voice pointer
// Output : SndBool
//-------------------------------------------------------------------------
// Creation date : 18/03/97              Author : Guillaume Michel
//-------------------------------------------------------------------------
// Modifications :
// Modification Date :              Author :
//-------------------------------------------------------------------------

SndBool SND_fn_bFreeMPEGVoice(SND_tdstMPEGVoice* pVoice)
{
	snd_assert(pVoice);
	snd_assert(pVoice->pRealBitStream == &pVoice->bitstream);
	
	pVoice->bIsPlaying = FALSE;
	pVoice->bIsPaused = FALSE;

	SND_fn_bCloseBitStream(pVoice->pRealBitStream);
	//pVoice->ucVolume = 0;
	//pVoice->ucVolumeIndex = 0;
	pVoice->bIsMuted = FALSE;	
	pVoice->ulPos = 0;
	pVoice->ulFinalSize = 0;
	pVoice->bIsOneShot = FALSE;
	pVoice->bIsSurround = FALSE;
	pVoice->pAssociatedVoice = NULL;
	pVoice->bIsFinishing = FALSE;
	pVoice->ulNumSampleBeforeEnd = 0;

	// DO NOT CLEAR CALLBACK AND PARAM FIELDS !!!

	if (pVoice == &MPEGvoices[2]) {
		// one shot voices
		iMiniFrameSample=12;
		iIndexSample=3;
	}

	pVoice->bIsFree = TRUE;	

	return TRUE;
}

//-------------------------------------------------------------------------
// Description : read datas from the bit stream (Part I)
//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------
// Input  : voice pointer
// Output : SndBool
//-------------------------------------------------------------------------
// Creation date : 18/03/97              Author : Guillaume Michel
//-------------------------------------------------------------------------
// Modifications :
// Modification Date :              Author :
//-------------------------------------------------------------------------

SndBool SND_fn_bReadPartI(SND_tdstMPEGVoice* pVoice)
{
	int					i,j,k,l,m;
	unsigned char		*pucBitAlloc;
	unsigned char		*pucScaleFactorPattern;
	unsigned char		*pucScaleFactor;
	SND_tdstBitStream	*pbitstream;
	unsigned short		uwMode, uwExtMode, uwTmp;
	unsigned char		ucTmp;

	snd_assert(pVoice);
	
	pbitstream = pVoice->pRealBitStream;
	if(SND_fn_bSeekSynch(pbitstream)) {

		// We have found a synch word -> there is at least one frame 
		GETBITS(4,&uwMode,pbitstream);

		uwExtMode = uwMode & 0x3,
		uwMode >>= 2;	// Tell us if we are in stereo mode, joint mode or mono mode

		k = pVoice->ucNbChannel = (uwMode == kMono) ? 1 : 2;
		l = pVoice->ucJointBound = (uwMode != kJointStereo) ? kSubBandLimit : iJointMode[uwExtMode];

		// First READ bits allocations...
		// before Joint boundary there are two bits allocation values
		pucBitAlloc = pVoice->pucBitAlloc;
		for (i = 0; i < l; i++) {
			m = iNumBitPerChannel[i];
			for (j = 0; j < k; j++) {
				GETBITS((unsigned char)m,&uwTmp,pbitstream);
				*pucBitAlloc++ = (unsigned char) uwTmp;
			}
		}
		
		// after Joint boundary the same bit allocation value is used for left and right
		for (i = l; i < kSubBandLimit; i++) {
				GETBITS((unsigned char)iNumBitPerChannel[i],&uwTmp,pbitstream);
				*pucBitAlloc++ = ucTmp = (unsigned char) uwTmp;
				*pucBitAlloc++ = ucTmp;
		}
	
		// Now we can read scales factors...
		pucBitAlloc = pVoice->pucBitAlloc;
		pucScaleFactorPattern = pVoice->pucScaleFactorPattern;
		for (i = 0; i < kSubBandLimit; i++) {
			for(j = 0; j < k; j++) {
				if(*pucBitAlloc++) {
					GETBITS(2,&uwTmp,pbitstream);
					*pucScaleFactorPattern = (unsigned char) uwTmp;
				}
				
				pucScaleFactorPattern++;
			}
		}

		// Then we can read scale factor index
		pucBitAlloc = pVoice->pucBitAlloc;
		pucScaleFactorPattern = pVoice->pucScaleFactorPattern;
		pucScaleFactor = pVoice->pucScaleFactor;
		for (i = 0; i < kSubBandLimit; i++) {
			for (j = 0; j < k; j++) {
				if(*pucBitAlloc++) {
					switch(*pucScaleFactorPattern++) {
					case 0: // three different scale factors
						GETBITS(6,&uwTmp,pbitstream);
						*pucScaleFactor++ = (unsigned char) uwTmp;
						GETBITS(6,&uwTmp,pbitstream);
						*pucScaleFactor++ = (unsigned char) uwTmp;
						GETBITS(6,&uwTmp,pbitstream);
						*pucScaleFactor++ = (unsigned char) uwTmp;
						break;
					case 1:// two
						GETBITS(6,&uwTmp,pbitstream);
						*pucScaleFactor++ = ucTmp = (unsigned char) uwTmp;
						*pucScaleFactor++ = ucTmp;
						GETBITS(6,&uwTmp,pbitstream);
						*pucScaleFactor++ = (unsigned char) uwTmp;
						break;
					case 3:// two
						GETBITS(6,&uwTmp,pbitstream);
						*pucScaleFactor++ = (unsigned char) uwTmp;
						GETBITS(6,&uwTmp,pbitstream);
						*pucScaleFactor++ = ucTmp = (unsigned char) uwTmp;
						*pucScaleFactor++ = ucTmp;
						break;
					case 2:// one scale factor...
						GETBITS(6,&uwTmp,pbitstream);
						*pucScaleFactor++ = ucTmp = (unsigned char) uwTmp;
						*pucScaleFactor++ = ucTmp;
						*pucScaleFactor++ = ucTmp;
						break;
					default:
						snd_assert(0); // should not happen
						break;
					}
				} else {
					pucScaleFactorPattern++;
					pucScaleFactor += 3;
				}
			}
		}

		return TRUE;
	} else return FALSE; // No more synch word -> EOF

}


//-------------------------------------------------------------------------
// Description : read datas from the bit stream (Part II)
//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------
// Input  : voice pointer
// Output : SndBool
//-------------------------------------------------------------------------
// Creation date : 19/03/97              Author : Guillaume Michel
//-------------------------------------------------------------------------
// Modifications :
// Modification Date :              Author :
//-------------------------------------------------------------------------

void SND_fn_vReadPartII(SND_tdstMPEGVoice* pVoice)
{
	int					i, j, jointboundary, k, ba, l, m;
	unsigned short		wTmp;
	short				*pwSample;
	short				*pwOldSample;
	unsigned char		*pucBitAlloc,ucl;
	sb_alloc			*pAllocTable;
	sb_alloc			*pAllocTable2;
	SND_tdstBitStream	*pBitStream;


	jointboundary = pVoice->ucJointBound;
	k = pVoice->ucNbChannel;
	pBitStream = pVoice->pRealBitStream;
	pwSample = pVoice->pwSample;

	pucBitAlloc = pVoice->pucBitAlloc;
	for (i = 0; i < kSubBandLimit; i++) {
		pAllocTable2 = &AllocationTable[i][0];
		for (j = 0; j < ((i < jointboundary) ? k : 1); j++) {
			if ( ba = (*pucBitAlloc++)) {
				// There are samples to read
				ba--;
				pAllocTable = pAllocTable2 + ba;
				l = pAllocTable->bits;
				ucl = (unsigned char) l;

				if (pAllocTable->group == 3) {
					// No Grouping...
					GETBITS(ucl,(unsigned short*)(pwSample++),pBitStream);
					GETBITS(ucl,(unsigned short*)(pwSample++),pBitStream);
					GETBITS(ucl,(unsigned short*)(pwSample++),pBitStream);


				} else {
					// Grouping...
					m = pAllocTable->steps;
					GETBITS(ucl,&wTmp,pBitStream);
					
					// degroup...
#if defined(__WATCOMC__) && (__WATCOMC__<1100)
					*pwSample++ = wTmp % m;
					wTmp /= m;
					*pwSample++ = wTmp % m;
					wTmp /= m;
					*pwSample++ = wTmp;
#else
					_asm {
						xor eax,eax
						mov esi, pwSample
						xor edx, edx
						mov ax, wTmp
						div m
						mov word ptr [esi], dx

						xor edx, edx
						div m
						mov word ptr [esi + 2], dx
						mov word ptr [esi + 4], ax
						add esi, 6
						mov pwSample,esi
					}
#endif //Watcom					

				}
			} else {
				*pwSample++ = 0;
				*pwSample++ = 0;
				*pwSample++ = 0;
			}
		}
		if( (k == 2) && (i >= jointboundary)) {
			pwOldSample = pwSample - 3; // last three sample read
			*pwSample++ = *pwOldSample++;
			*pwSample++ = *pwOldSample++;
			*pwSample++ = *pwOldSample++;
			pucBitAlloc++;
		}
	}

}

SndBool SND_fn_bRead(SND_tdstMPEGVoice* pVoice)
{
	if ((pVoice->bIsPlaying)&&(!pVoice->bIsPaused)) {
		if (SND_fn_bReadPartI(pVoice)) {
			SND_fn_vEvaluateSCF(pVoice);
		} else {
			pVoice->bIsPlaying = FALSE;
			
			if (pVoice == &MPEGvoices[2]) {
				// voix offs
				pVoice->bIsFinishing = TRUE;
				pVoice->ulNumSampleBeforeEnd = 32*kFrameSize;
			}
		}
	}


	return pVoice->bIsPlaying;
}
SndBool SND_fn_bFinishRead(SND_tdstMPEGVoice* pVoice)
{
	int j;
	if ((pVoice->bIsPlaying)&&(!pVoice->bIsPaused)) {
		for(j =0; j < 12; j++) {
			SND_fn_vReadPartII(pVoice);
			if (!((pVoice->ucVolume==0) || pVoice->bIsMuted))
				SND_fn_vDequantizeSample(pVoice,j);
		}
	}
	return TRUE;
}

SndBool	SND_fn_bSwitchPart() 
{
	unsigned long	ulPar;
	SndBool bPartIsFading;
	SND_td_pfn_vSoundCallback pcb;
	SND_tdstMPEGVoice* pOldVoice = pSwapVoice[0];
	SND_tdstMPEGVoice* pNewVoice = pSwapVoice[1];
	bPartIsFading=pOldVoice->bIsFading;
	pcb = pOldVoice->pfnCallBack;
	ulPar = pOldVoice->ulParam;
	pOldVoice->bIsPlaying = FALSE;
	// TRY WITH NEXT ONE...
	if (pSwapVoice[1]->bIsPlaying)
	{
//	OutputDebugString("Switch part called, ");
		// WE MUST TRY VOICE TWO
		// swap the voices...
		pSwapVoice[0] = pNewVoice;
		pSwapVoice[1] = pOldVoice;
		if (bPartIsFading)
		{// copy fade params from previous voice to new one:
			pNewVoice->bIsFading      = TRUE;
			pNewVoice->iFadeVolume    = pOldVoice->iFadeVolume;
			pNewVoice->iFadeIncrement = pOldVoice->iFadeIncrement;
		}
		// We either launch a transition because of a forecoming transition with fade,
		//	in which case we disable the callback, or we call the callback as usual:
		if (SND_g_uFadeRes.pstPtr!=NULL)
		{
//			OutputDebugString("Calling set transition \n");
			SND_fn_bSetNextTransitionMPEG(0,SND_g_uFadeRes);
			SND_g_uFadeRes.pstPtr=NULL;
		}
		else if (pcb)
		{
//			OutputDebugString("Calling callback \n");
			pcb(ulPar);
		}
		else
			pcb=pcb;
		
		return TRUE;
	} else
	{
		return FALSE;
	}
}

//-------------------------------------------------------------------------
// Description: do the mix to fill the client buffer
//				in the case of SINGLE CHANNEL, if one voice can't fill the client buffer
//				we try with the next voice...
//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------
// Input  : buffer pointer and size in samples
// Output : SndBool
//-------------------------------------------------------------------------
// Creation date : 20/03/97              Author : Guillaume Michel
//-------------------------------------------------------------------------
// Modifications :
// Modification Date :              Author :
//-------------------------------------------------------------------------

SndBool SND_fn_bFillBufferTwice(short* pwBuffer, unsigned long ulSize)
{
	unsigned long		ulNbSample;
	int					iResultDecoder = 0;
	int					iMaxFrameToDo;
	SndBool				bVoiceEnded;//TRUE si une voie s'est arretée pendant le mixage->SWAP?

	while ( ulSize ) {
		//on regarde si il y a des résidus à caser...
		/*
		 * Currently we get previous remain decompress data size
		 */
		ulNbSample = pwOutBufferEnd2 - pwOutBufferPos;
		ulNbSample >>= 1;
		if (ulNbSample) {
			// il y a encore des restes
			if (ulSize <= ulNbSample) {
				// il y en a assez...
				memcpy(pwBuffer,pwOutBufferPos,(ulSize<<1)*sizeof(short));
				pwOutBufferPos += (ulSize<<1);
				return TRUE;
			} else {
				memcpy(pwBuffer,pwOutBufferPos,(ulNbSample<<1)*sizeof(short));
				pwOutBufferPos = pwOutBufferEnd2;
				pwBuffer += (ulNbSample<<1);
				ulSize -= ulNbSample;
			}
		}

		if (ulSize < kNumChannel) {
			// on doit décoder dans le buffer temporaire
			bVoiceEnded=SND_fn_bGetOneFrame(pwOutBuffer,1,&iResultDecoder);
			if (bVoiceEnded || (iResultDecoder != kNumChannel)) {
				pwOutBufferEnd2 = pwOutBuffer +(iResultDecoder<<1);
				if (bSwitchPart) {
					// On swapp: il y a donc quelque chose dans le buffer temporiare
					if(SND_fn_bSwitchPart()) {
						// On peut swapper de part en part
						if (ulSize >= (unsigned long)iResultDecoder) {
							// on n'a pas décodé assez...
							memcpy(pwBuffer,pwOutBuffer,(iResultDecoder<<1)*sizeof(short));
							pwOutBufferPos = pwOutBufferEnd2;
							ulSize -= iResultDecoder;
							pwBuffer += (iResultDecoder<<1);
						} else {
							// on a décodé assez...
							memcpy(pwBuffer,pwOutBuffer,(ulSize<<1)*sizeof(short));
							pwOutBufferPos = pwOutBuffer + (ulSize<<1);
							return TRUE;
						}

					} else {
						// On ne peut pas swapper
						if (ulSize >= (unsigned long)iResultDecoder) {
							// on n'a pas décodé assez...
							memcpy(pwBuffer,pwOutBuffer,(iResultDecoder<<1)*sizeof(short));
							pwOutBufferPos = pwOutBufferEnd2;
							ulSize -= iResultDecoder;
							memset(pwBuffer+(iResultDecoder<<1),0,(ulSize<<1)*sizeof(short));
						} else {
							// on a décodé assez...
							memcpy(pwBuffer,pwOutBuffer,(ulSize<<1)*sizeof(short));
							pwOutBufferPos = pwOutBuffer + (ulSize<<1);
							ulSize = 0;
						}

						//added by stephane RONSe on 04/29/98
						if (!bSynthesisBufferCleaned)
						{
							fn_vCleanSynthesisBuffer();
							bSynthesisBufferCleaned=TRUE;
						}

						return FALSE;
					}
				} else {
					// Pas de switch -> arrêt normal -> pas de décodage
					pwOutBufferPos = pwOutBufferEnd2;
					memset(pwBuffer,0, (ulSize<<1)*sizeof(short));

					//added by stephane RONSe on 04/29/98
					if (!bSynthesisBufferCleaned)
					{
						fn_vCleanSynthesisBuffer();
						bSynthesisBufferCleaned=TRUE;
					}

					return FALSE;
				}
			} else {
				// tout est ok... on continu
				memcpy(pwBuffer,pwOutBuffer,(ulSize<<1)*sizeof(short));
				pwBuffer += (ulSize<<1);
				pwOutBufferPos = pwOutBuffer + (ulSize<<1);
				pwOutBufferEnd2 = pwOutBuffer +(kNumChannel<<1);
				ulSize = 0; 
				// normalement cela doit être terminé...
			}
		} else {
			iMaxFrameToDo = ulSize>>5;

			// on peut décoder directement au bon endroit
			bVoiceEnded=SND_fn_bGetOneFrame(pwBuffer,iMaxFrameToDo,&iResultDecoder);
			if (bVoiceEnded || (iResultDecoder != (iMaxFrameToDo<<5))) {
				if (bSwitchPart) {
					if(SND_fn_bSwitchPart()) {
						pwBuffer += (iResultDecoder<<1);
						ulSize -= iResultDecoder;
					} else {
						memset(pwBuffer+(iResultDecoder<<1),0, ((ulSize-iResultDecoder)<<1)*sizeof(short));

						//added by stephane RONSe on 04/29/98
						if (!bSynthesisBufferCleaned)
						{
							fn_vCleanSynthesisBuffer();
							bSynthesisBufferCleaned=TRUE;
						}

						return FALSE;
					}
				} else {
					memset(pwBuffer,0, (ulSize<<1)*sizeof(short));

					//added by stephane RONSe on 04/29/98
					if (!bSynthesisBufferCleaned)
					{
						fn_vCleanSynthesisBuffer();
						bSynthesisBufferCleaned=TRUE;
					}

					return FALSE;
				}
			} else {
				// tout est ok... on continu
				ulSize -= iResultDecoder;
				pwBuffer += (iResultDecoder<<1);
			}
		}
	}
	return TRUE;

}

void WINAPI SND_fn_vRefreshMPEG(long idBuffer,unsigned long idSample, unsigned long nbSample, void* pvBuffer)
{
	bInMPEGDecomp = TRUE; //MUTEX
	SND_fn_bFillBufferTwice((short*)pvBuffer,nbSample);
	
	//memcpy(pBigInBuffer,pvBuffer,nbSample*2*2);
	//pBigInBuffer+=2*nbSample;

	bInMPEGDecomp = FALSE; // MUTEX
}

void SND_fnvStopAllVoicesMPEGTheme()
{
	// Stop the two voices associated with theme player...
	// both voices are stopped...
	pSwapVoice[1]->bIsPlaying = pSwapVoice[0]->bIsPlaying = FALSE;
	pSwapVoice[1]->bIsPaused = pSwapVoice[0]->bIsPaused = FALSE;

	// wait for end of the current frame decompression 
	// this function and GetOneFrame MUST BE IN TWO DIFFERENT THREADS !!!
	while(bInMPEGDecomp){
		Sleep(30);
	}

	iMiniFrameTheme=12;
	iIndexTheme=3;

	SND_fn_bFreeMPEGVoice(pSwapVoice[0]);
	SND_fn_bFreeMPEGVoice(pSwapVoice[1]);
}
