/*
// ================================================================
// MPEG.cpp PENTIUM VERSION...
// Ubi Sound Studios
// 18/03/1997
// Guillaume Michel
// ================================================================
*/

#include <math.h>

#include "SNDinc.h"

#include "mpeg.hxx"
#include "sndmpeg.h"
#include "mpeg.h"

//#define _PROFILE_MFC_

#ifdef _PROFILE_MFC_
static LARGE_INTEGER t1,t2,t0;
static int iTotalTime, iNumTime;
#endif

static double	*pdTmp1Fract;
static double	*pdTmp2Fract;
static double	dSynthesisMatrix[kNumChannel][kNumChannel>>1];

// ======= MPEG ENGINE ======================================
// Voices array... 
SND_tdstMPEGVoice		MPEGvoices[2*(kNumVoice+1)];
// VOICE THEME PART 0
// VOICE THEME	PART 1
// VOICE ONESHOT 
// VOICE THEME PART 0 SURROUND
// VOICE THEME PART 1 SURROUND
// VOICE ONESHOT SURROUND

//!!!variable exportée vers le module ASM
SND_tdstMPEGVoice		*pSwapVoice[2];

static double					*pdSwapFraction;
static double					*pdSynthesisBufferR;
static double					*pdSynthesisBufferL;
static short					*pwOutBuffer;
static short					*pwOutBufferEnd;
static short					*pwOutBufferEnd2;
static short					*pwOutBufferPos;

	
static int						iOffset[2];
static int						iTmpOffset[16];
static int						iOutOffset[2];

static SndBool					bInitDone = FALSE;
static double					dTmp[kNumChannel];

//!!!variable exportée vers le module ASM
SndBool bInMPEGDecomp = FALSE;	// Mutex 

static SndBool bSwitchPart = FALSE;	// Passage d'une part à une autre

static int	iMiniFrameTheme;
static int	iIndexTheme;
static int	iMiniFrameSample;
static int	iIndexSample;

#define GETBITS(A,B,C) SND_fn_bGetNBits(C,B,A)

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

void SND_fn_vInitSignalProcessing() 
{
	int			i,j,l;
	sb_alloc*	pAllocTable;
	double		PI64 = 4.*atan(1.);
	double		dTmp;
	
	PI64 = PI64 / 64.;

	// MPEG Signal Processing INITS
	/*
	 * Structure definition:
	 *
	 * typedef struct {
	 * unsigned int    steps;
	 * unsigned int    bits;
	 * unsigned int    group;
	 * unsigned int    quant;
	 * } sb_alloc
	 *
	 * kSubBandLimit = 27
	 */
	for (i = 0; i < kSubBandLimit; i++){
		for (j = 0; j < 16; j++) {
			pAllocTable = &AllocationTable[i][j];
			l = 0;
			/*
			 * For each step we look for the lowest power of 2
			 * immediatly bigger than the step( X(n) )
			 */
			while(((unsigned int)(1L<<l)) < (pAllocTable->steps)) l++;
			dTmpDiviseur[i][j] = 1./(double) (1L<<(l-1));
		}
	}
	for (i = 0; i < kAnalysisSize; i++) {
		dSynthesisWindow[i] *= 32768.;
	}
	/*
	 * Calculate all coefficients employed in the DCT transform
	 * Use the LEE algorithm
	 * Negative signe is added in order to optimize decodage
	 */
	for (i = 0 ; i < (kNumChannel>>1); i++){
		for (j = 0; j < kNumChannel ; j++) {
			dSynthesisMatrix[j][i] = -cos((PI64*((double)((2*i+1)*(/*16+*/j)))));
		}
	}
	/*
	 * Volume management
	 * kNumScaleFactor = 64
	 */
	ucVolume[0] = kNumScaleFactor;
	for (i = 1; i < 128; i++) {
		dTmp = ((double)i)/128.;
		dTmp = -20.*log10(dTmp);
		dTmp /= 2;
		ucVolume[i] = (int)dTmp;
	}
}

//-------------------------------------------------------------------------
// Description : Init MPEG  Engine's memory 
//-------------------------------------------------------------------------
// 
//-------------------------------------------------------------------------
// Input  : None
// Output : None
//-------------------------------------------------------------------------
// Creation date : 24/03/97              Author : Guillaume Michel
//-------------------------------------------------------------------------
// Modifications :
// Modification Date :              Author :
//-------------------------------------------------------------------------

BOOL SND_fn_bInitMPEGMemory()
{
	int i;
	SND_tdstMPEGVoice*	pVoice;

	/*
	 * Allocate two buffers (R & L) for FFT storage, and
	 * two buffers of 16 double
	 * kFFTSize = 1024
	 */
	pdSynthesisBufferL = (double*) SND_fn_pvMallocSnd(2*(kFFTSize+16)*sizeof(double));
	pdSynthesisBufferR = pdSynthesisBufferL + kFFTSize;
	pdTmp1Fract = pdSynthesisBufferR + kFFTSize;
	pdTmp2Fract = pdTmp1Fract + 16;
	/*
	 * Alloaction failed
	 */
	if (pdSynthesisBufferL == NULL) {
		SND_fn_bDesInitMPEG();
		return FALSE;
	}
	/*
	 * Allocate two buffers for integer FFT storage
	 */
	pwOutBuffer = (short*) SND_fn_pvMallocSnd(2*kFrameSize*sizeof(short));
	if (pwOutBuffer == NULL) {
		SND_fn_bDesInitMPEG();
		return FALSE;
	}

	memset(pwOutBuffer,0,2*kFrameSize*sizeof(short));
	pwOutBufferPos = pwOutBufferEnd = pwOutBufferEnd2 = pwOutBuffer + 2*kFrameSize;

	// MPEG Engine INITS
	for (i = 0; i < (2*(kNumVoice+1)); i++) {
		/*
		 * Initialise all MPEG specific voices
		 * This part allocate memory & put all fields to "0"
		 */
		pVoice = &MPEGvoices[i];
		if (SND_fn_bAllocateMPEGMemory(pVoice)) {
			if (i < (kNumVoice+1)) {
				// REAL VOICES
				pVoice->pRealBitStream = &(pVoice->bitstream);
				SND_fn_bInitBitStream(&(pVoice->bitstream));
				if(!SND_fn_bAllocateBitStream(&(pVoice->bitstream))) {
					SND_fn_bDesInitMPEG();
					return FALSE;
				}
			} else {
				// SURROUND VOICES
				pVoice->pRealBitStream = &MPEGvoices[i - (kNumVoice+1)].bitstream;
			}
		} else {
			// not enough memory for voices
			SND_fn_bDesInitMPEG();
			return FALSE;
		}
	}

	return TRUE;
}

//-------------------------------------------------------------------------
// Description : DesInit MPEG  Engine's memory 
//-------------------------------------------------------------------------
// 
//-------------------------------------------------------------------------
// Input  : None
// Output : None
//-------------------------------------------------------------------------
// Creation date : 24/03/97              Author : Guillaume Michel
//-------------------------------------------------------------------------
// Modifications :
// Modification Date :              Author :
//-------------------------------------------------------------------------

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

#ifdef _PROFILE_MFC_
	FILE* pFile =  fopen("report.txt","at");
	if (pFile) {
		fprintf(pFile,"%f %d\n",(32.*(double)iTotalTime)/((double)t0.LowPart)/((double)iNumTime),iNumTime);
		fclose(pFile);
	}
#endif
	if (pdSynthesisBufferL) {
		SND_fn_vFreeSnd(pdSynthesisBufferL);
		pdSynthesisBufferL = NULL;
	}
	if (pwOutBuffer) {
		SND_fn_vFreeSnd(pwOutBuffer);
		pwOutBuffer = NULL;
	}

	// MPEG Engine INITS
	for (i = 0; i < (2*(kNumVoice+1)); i++) {
		pVoice = &MPEGvoices[i];
		if (i < (kNumVoice+1)) SND_fn_bFreeBitStream(&(pVoice->bitstream));
		SND_fn_bFreeMPEGMemory(pVoice);
	}
	

	return TRUE;
}

//-------------------------------------------------------------------------
// Description : Allocate memory for decrompression...
//-------------------------------------------------------------------------
// CALLED ONCED AT THE BEGINING OF THE APPLICATION
//-------------------------------------------------------------------------
// Input  : voice pointer
// Output : SndBool
//-------------------------------------------------------------------------
// Creation date : 18/03/97              Author : Guillaume Michel
//-------------------------------------------------------------------------
// Modifications :
// Modification Date :              Author :
//-------------------------------------------------------------------------

SndBool SND_fn_bAllocateMPEGMemory(SND_tdstMPEGVoice* pVoice)
{
	snd_assert(pVoice);
	/*
	 * 
	 */
	pVoice->pucBitAlloc = (unsigned char*) SND_fn_pvMallocSnd(10*kSubBandLimit);
	pVoice->pucScaleFactorPattern = pVoice->pucBitAlloc + 2*kSubBandLimit;
	pVoice->pucScaleFactor = pVoice->pucScaleFactorPattern + 2*kSubBandLimit;
	/*
	 * 
	 */
	pVoice->pdDiviseur = (double*) SND_fn_pvMallocSnd(6*(3*kSubBandLimit+12*kNumChannel)*sizeof(double));
	pVoice->pdScaleFactor = pVoice->pdDiviseur + 6*kSubBandLimit;
	pVoice->pdSumFactor = pVoice->pdScaleFactor + 6*kSubBandLimit;
	pVoice->pdFraction = pVoice->pdSumFactor + 6*kSubBandLimit;
	/*
	 * 
	 */
	pVoice->pwSample = (short*) SND_fn_pvMallocSnd(6*kSubBandLimit*sizeof(short));
	pVoice->pfnCallBack = NULL;
	if (pVoice->pucBitAlloc && pVoice->pdDiviseur && pVoice->pwSample)
		return TRUE;

	return FALSE;
}


//-------------------------------------------------------------------------
// Description : from datas read in part I , gets the scale factors
//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------
// Input  : voice pointer
// Output : void
//-------------------------------------------------------------------------
// Creation date : 18/03/97              Author : Guillaume Michel
//-------------------------------------------------------------------------
// Modifications :
// Modification Date :              Author :
//-------------------------------------------------------------------------

void SND_fn_vEvaluateSCF(SND_tdstMPEGVoice* pVoice)
{
	int				i,j,k,l,q,iFadeVolume;
	double			dDiviseur, dScaleFactor, dTmp1, dTmp2;
	unsigned char	*pucBitAlloc;
	unsigned char	*pucScaleFactor;
	double			*pdScaleFactor;
	double			*pdSumFactor;
	double			*pdDiviseur;

	unsigned char ba,scfi,volidx; // bit allocation and scale factor index
	
	snd_assert(pVoice);

	l = pVoice->ucNbChannel;
	pucBitAlloc = pVoice->pucBitAlloc;
	pucScaleFactor = pVoice->pucScaleFactor;
	pdScaleFactor = pVoice->pdScaleFactor;
	pdSumFactor = pVoice->pdSumFactor;
	pdDiviseur = pVoice->pdDiviseur;
	if (pVoice->bIsFading)
	{
		iFadeVolume=pVoice->ucCurrentVolume;
		iFadeVolume*=pVoice->iFadeVolume;
		/*
		 * "ucVolume" is an array of 127 values defined in "sndmpeg.c"
		 * It contain volume attenuation parameters expressed in dB.
		 * These volume expressed a deceasing curve from ( 0, 64 ) to ( 128, 0 )
		 */
#if M_FRED_DECREAU
		iFadeVolume>>=16;
#else
		iFadeVolume /= pVoice->rDuration;
#endif
		snd_assert( iFadeVolume <= 127 );
		volidx = ucVolume[iFadeVolume];
	}
	else volidx = pVoice->ucVolumeIndex;

	/*
	 * Operation done on every polyphase sub band
	 */
	for (i = 0; i < kSubBandLimit; i++) {
		/*
		 * Stereo or Mono
		 */
		for(j = 0; j < l; j++) {
			if (ba = *pucBitAlloc++) {		
				// USEFULL...
				ba--;
				dDiviseur = dTmpDiviseur[i][ba];
				q = AllocationTable[i][ba].quant;
				dTmp1 = gs_cd[q];
				dTmp2 = gs_c[q];

				for(k = 0; k < 3; k++) {
					scfi = 	(*pucScaleFactor++)+1;
					
					// volume modification...
					scfi +=volidx;
					if (scfi >= kNumScaleFactor) scfi = kNumScaleFactor - 1;
					dScaleFactor = dMultiple[scfi];
					*pdSumFactor++ = dScaleFactor * dTmp1;
					dScaleFactor *= dTmp2;
					
					*pdScaleFactor++ =dScaleFactor;
					*pdDiviseur++ = dDiviseur*dScaleFactor;
				}
			} else {
				// NOT USEFULL...
				pucScaleFactor+=3;
				pdScaleFactor+=3;
				pdSumFactor+=3;
				pdDiviseur+=3;
			}
		}
	}

}

//-------------------------------------------------------------------------
// Description : dequantize datas (Part II)
//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------
// Input  : voice pointer and index of subframe
// Output : void
//-------------------------------------------------------------------------
// Creation date : 19/03/97              Author : Guillaume Michel
//-------------------------------------------------------------------------
// Modifications :
// Modification Date :              Author :
//-------------------------------------------------------------------------

void SND_fn_vDequantizeSample(SND_tdstMPEGVoice* pVoice, int iIndex)
{
	int				i,j,k,l,ba,x;
	unsigned char	*pucBitAlloc;
	double			*pdDiviseur;
	double			*pdSum;
	double			*pdScaleFactor;
	double			*pdMix;
	double			*pdMixBase;
	double			*pdOldMix;
	short			*pwSample;

	double			dDiviseur;
	double			dSum;
	double			dScaleFactor;
	double			dFract;

	l = pVoice->ucNbChannel;
	pucBitAlloc = pVoice->pucBitAlloc;
	pwSample = pVoice->pwSample;
	pdMixBase = pVoice->pdFraction + iIndex*3*2*kNumChannel;

	iIndex >>= 2;
	
	pdDiviseur = pVoice->pdDiviseur+iIndex;
	pdSum = pVoice->pdSumFactor+iIndex;
	pdScaleFactor = pVoice->pdScaleFactor+iIndex;

	for(i = 0; i < kSubBandLimit; i++) {
		pdMix = pdMixBase + i;
		for (j = 0; j < l; j++) {
			
			dDiviseur = *pdDiviseur;
			dSum = *pdSum;
			dScaleFactor = *pdScaleFactor;

			pdDiviseur += 3;
			pdSum += 3;
			pdScaleFactor += 3;
			
			if (ba = *pucBitAlloc++) {
				ba--;
				x = ucPowStep[i][ba];
				
				for (k = 0; k < 3; k++) {
					dFract = (double)((*pwSample) & ((1<<(x-1))-1));
					dFract *= dDiviseur;
					dFract += dSum;

					if (!((((*pwSample) >> (x-1)) & 1) == 1)) {
						dFract -= dScaleFactor;
					}

					pwSample++;
					pdOldMix = pdMix;
					*(pdMix+(k<<6)) = dFract;	
					if(l == 1) {
						pdOldMix += 32;
						*(pdOldMix+(k<<6)) = dFract;
					}
				}

				if ( l == 2) pdMix +=32;

			} else {
				pwSample += 3;
				if ( l == 2) pdMix +=32;
			}
		}
	}
}

// 
//	voice 0 L0	L1	L2	L3... L31	R0	R1	R2	R3... R31
//	voice 1 L0	L1	L2	L3... L31	R0	R1	R2	R3... R31

//  sum		+	+	+	+		+	+	+	+	+		+

void SND_fn_vAddFraction(int iIndex0, int iFrame0, int iIndex1, int iFrame1) {
	int i;
	SND_tdstMPEGVoice* pVoiceTheme = pSwapVoice[0];
	SND_tdstMPEGVoice* pVoiceOneShot = &MPEGvoices[2];
	double *pdTmp1 = pVoiceTheme->pdFraction;
	double *pdTmp2 = pVoiceOneShot->pdFraction;
	double *pdTmpOut;

	pdTmp1 += (3*2*kNumChannel*iFrame0);
	pdTmp1 += (2*kNumChannel*iIndex0);
	pdTmp2 += (3*2*kNumChannel*iFrame1);
	pdTmp2 += (2*kNumChannel*iIndex1);

	pdSwapFraction = pdTmpOut = pdTmp1;
	for (i = 0; i < 2*kNumChannel;i++) {
		*pdTmpOut++ = ((*pdTmp1++)+(*pdTmp2++));
	}
}

// 
//	voice 0 L0	L1	L2	L3... L31	R0	R1	R2	R3... R31
//	voice 1 S0	S1	S2	S3... S31	S0	S1	S2	S3... S31

//  sum		+	+	+	+		+	-	-	-	-		-

void SND_fn_vAddFractionThemeSurround(int iIndex0, int iFrame0) {
	int i;
	SND_tdstMPEGVoice* pVoiceTheme = pSwapVoice[0];
	SND_tdstMPEGVoice* pVoiceThemeSurround = pVoiceTheme + (kNumVoice+1);
	double *pdTmp1 = pVoiceTheme->pdFraction;
	double *pdTmp2 = pVoiceThemeSurround->pdFraction;
	double *pdTmpOut;

	pdTmp1 += (3*2*kNumChannel*iFrame0);
	pdTmp1 += (2*kNumChannel*iIndex0);
	pdTmp2 += (3*2*kNumChannel*iFrame0);
	pdTmp2 += (2*kNumChannel*iIndex0);

	pdSwapFraction = pdTmpOut = pdTmp1;
	for (i = 0; i < kNumChannel;i++) {
		*pdTmpOut++ = ((*pdTmp1++)+(*pdTmp2++));
	}
	for (i = 0; i < kNumChannel;i++) {
		*pdTmpOut++ = ((*pdTmp1++)-(*pdTmp2++));
	}
}

// 
//	voice 0 L0	L1	L2	L3... L31	R0	R1	R2	R3... R31
//	voice 1 S0	S1	S2	S3... S31	S0	S1	S2	S3... S31

//  sum		+	+	+	+		+	-	-	-	-		-

void SND_fn_vAddFractionOneShotSurround(int iIndex0, int iFrame0) {
	int i;
	SND_tdstMPEGVoice* pVoiceOneShot = &MPEGvoices[2];
	SND_tdstMPEGVoice* pVoiceOneShotSurround = pVoiceOneShot + (kNumVoice+1);
	double *pdTmp1 = pVoiceOneShot->pdFraction;
	double *pdTmp2 = pVoiceOneShotSurround->pdFraction;
	double *pdTmpOut;

	pdTmp1 += (3*2*kNumChannel*iFrame0);
	pdTmp1 += (2*kNumChannel*iIndex0);
	pdTmp2 += (3*2*kNumChannel*iFrame0);
	pdTmp2 += (2*kNumChannel*iIndex0);

	pdSwapFraction = pdTmpOut = pdTmp1;
	for (i = 0; i < kNumChannel;i++) {
		*pdTmpOut++ = ((*pdTmp1++)+(*pdTmp2++));
	}
	for (i = 0; i < kNumChannel;i++) {
		*pdTmpOut++ = ((*pdTmp1++)-(*pdTmp2++));
	}
}

// 
//	voice 0 0	0	0	0...	0	0	0	0	0...	0
//	voice 1 S0	S1	S2	S3... S31	S0	S1	S2	S3... S31

//  sum		+	+	+	+		+	-	-	-	-		-

void SND_fn_vSubFractionThemeSurround(int iIndex0, int iFrame0) {
	int i;
	SND_tdstMPEGVoice* pVoiceTheme = pSwapVoice[0];
	double *pdTmp1 = pVoiceTheme->pdFraction;
	double *pdTmpOut;

	pdTmp1 += (3*2*kNumChannel*iFrame0);
	pdTmp1 += (2*kNumChannel*iIndex0);

	pdSwapFraction = pdTmpOut = pdTmp1;
	pdTmp1 += kNumChannel;

	for (i = 0; i < kNumChannel;i++) {
		*pdTmpOut++ = -(*pdTmp1++);
	}
}

// 
//	voice 0 0	0	0	0...	0	0	0	0	0...	0
//	voice 1 S0	S1	S2	S3... S31	S0	S1	S2	S3... S31

//  sum		+	+	+	+		+	-	-	-	-		-

void SND_fn_vSubFractionOneShotSurround(int iIndex0, int iFrame0) {
	int i;
	SND_tdstMPEGVoice* pVoiceOneShot = &MPEGvoices[2];
	double *pdTmp1 = pVoiceOneShot->pdFraction;
	double *pdTmpOut;

	pdTmp1 += (3*2*kNumChannel*iFrame0);
	pdTmp1 += (2*kNumChannel*iIndex0);

	pdSwapFraction = pdTmpOut = pdTmp1;
	pdTmp1 += kNumChannel;

	for (i = 0; i < kNumChannel;i++) {
		*pdTmpOut++ = -(*pdTmp1++);
	}
}

//-------------------------------------------------------------------------
// Description : do the mix of all voices and generate one frame of sound
// i.e. 1152 samples
//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------
// Input  : output buffer (stereo 16 bits)
// Output : SndBool
//-------------------------------------------------------------------------
// Creation date : 19/03/97              Author : Guillaume Michel
//-------------------------------------------------------------------------
// Modifications :
// Modification Date :              Author :
//-------------------------------------------------------------------------

// MONO CHANNEL AVAILABLE...
// When voice 0 is finished voice 1 start...

//int SND_fn_iGetOneFrame(short* pOut, int iMiniFrametoDo)
SndBool SND_fn_bGetOneFrame(short* pOut, int iMiniFrametoDo,int* piSamplesDone)
{
	int k;
	SND_tdstMPEGVoice* pVoiceTheme = pSwapVoice[0];
	SND_tdstMPEGVoice* pVoiceOneShot = &MPEGvoices[2];

#ifdef _PROFILE_MFC_
	// PERF EVALUATION
	QueryPerformanceCounter(&t1);
#endif

	iOutOffset[0] = 0;
	iOutOffset[1] = 1;

	// Update fade volume:
	if ((pVoiceTheme->bIsPlaying)&&(pVoiceTheme->bIsFading))
	{
		pVoiceTheme->iFadeVolume+=iMiniFrametoDo*pVoiceTheme->iFadeIncrement;
		if( pVoiceTheme->pAssociatedVoice )
			pVoiceTheme->pAssociatedVoice->iFadeVolume+=iMiniFrametoDo*pVoiceTheme->pAssociatedVoice->iFadeIncrement;
		// test change of fade status:
		if ((pVoiceTheme->iFadeIncrement<0)&&(pVoiceTheme->iFadeVolume<=0))
		{ // end of fade out: start fade in:
//			pVoiceTheme->iFadeIncrement=-pVoiceTheme->iFadeIncrement;
			pVoiceTheme->iFadeVolume=0;
			pVoiceTheme->iFadeIncrement=0;
			pVoiceTheme->bIsFading=FALSE; // fade in disabled
			if( pVoiceTheme->pAssociatedVoice ) {
				pVoiceTheme->pAssociatedVoice->iFadeVolume    = 0;
				pVoiceTheme->pAssociatedVoice->iFadeIncrement = 0;
				pVoiceTheme->pAssociatedVoice->bIsFading      = FALSE;
			}
//			OutputDebugString(" fade: starting fade in\n");
		}
#ifdef M_FRED_DECREAU
		else if ((pVoiceTheme->iFadeIncrement>0)&&(pVoiceTheme->iFadeVolume>=(1<<16)))
#else
		else if ( ( pVoiceTheme->iFadeIncrement > 0 ) &&( pVoiceTheme->iFadeVolume >= pVoiceTheme->rDuration ) )
#endif
		{ // end of fade in -> end of fade
			pVoiceTheme->iFadeIncrement=0;
			pVoiceTheme->iFadeVolume=0;
			pVoiceTheme->bIsFading=FALSE;
			if( pVoiceTheme->pAssociatedVoice )
				pVoiceTheme->pAssociatedVoice->bIsFading=FALSE;
//			OutputDebugString(" fade: end of fade in\n");
		}
	}
	// Voice Theme
	if (iMiniFrameTheme == 12 && iIndexTheme == 3) {
		if (SND_fn_bRead(pVoiceTheme)) {
			memset(pVoiceTheme->pdFraction,0,6*12*kNumChannel*sizeof(double));
			SND_fn_bFinishRead(pVoiceTheme);

			if (pVoiceTheme->bIsFullSurround) {
				// il faut décoder une frame pour la voix surround
				SND_fn_bRead(pVoiceTheme->pAssociatedVoice);
				memset(pVoiceTheme->pAssociatedVoice->pdFraction,0,6*12*kNumChannel*sizeof(double));
				SND_fn_bFinishRead(pVoiceTheme->pAssociatedVoice);
			}

			iMiniFrameTheme = 0;
			iIndexTheme = 0;
		}
	}
	
	// Voice One Shot
	if (iMiniFrameSample == 12 && iIndexSample == 3) {
		if (SND_fn_bRead(pVoiceOneShot)) {
			memset(pVoiceOneShot->pdFraction,0,6*12*kNumChannel*sizeof(double));
			SND_fn_bFinishRead(pVoiceOneShot);
			if (pVoiceOneShot->bIsFullSurround) {
				// il faut décoder une frame pour la voix surround
				SND_fn_bRead(pVoiceOneShot->pAssociatedVoice);
				memset(pVoiceOneShot->pAssociatedVoice->pdFraction,0,6*12*kNumChannel*sizeof(double));
				SND_fn_bFinishRead(pVoiceOneShot->pAssociatedVoice);
			}
			iMiniFrameSample = 0;
			iIndexSample = 0;
		}
	}

	k = 0;
	if (!pVoiceTheme->bIsPlaying && !pVoiceOneShot->bIsPlaying && !pVoiceOneShot->bIsFinishing)
	{
		*piSamplesDone=0;
		return TRUE;
	}

	while(k < iMiniFrametoDo) {
		if (pVoiceTheme->bIsPlaying || pVoiceOneShot->bIsPlaying || pVoiceOneShot->bIsFinishing ) 
		{
//			if ((pVoiceTheme->ulPos >= /*480*/0	)||(!pVoiceTheme->bIsPlaying)) //test inutile:always TRUE
			{
				// Les 480 premiers samples d'une voie thème sont à éliminer -> blanc retard du filtre


				if (pVoiceTheme->bIsPlaying && pVoiceOneShot->bIsPlaying) {
					
					if (pVoiceTheme->bIsSurround) {
						if (pVoiceTheme->bIsFullSurround)
						SND_fn_vAddFractionThemeSurround(iIndexTheme,iMiniFrameTheme);
						else
						SND_fn_vSubFractionThemeSurround(iIndexTheme,iMiniFrameTheme);
					}

					if (pVoiceOneShot->bIsSurround) {
						if (pVoiceOneShot->bIsFullSurround)
						SND_fn_vAddFractionOneShotSurround(iIndexSample,iMiniFrameSample);
						else
						SND_fn_vSubFractionOneShotSurround(iIndexSample,iMiniFrameSample);
					}
					
					SND_fn_vAddFraction(iIndexTheme,iMiniFrameTheme,iIndexSample,iMiniFrameSample);
				
				}
				else if (pVoiceTheme->bIsPlaying) {
					if (!pVoiceTheme->bIsSurround) 
					pdSwapFraction = pVoiceTheme->pdFraction + (3*2*kNumChannel*iMiniFrameTheme) + (2*kNumChannel*iIndexTheme);
					else {
						if (pVoiceTheme->bIsFullSurround)
						SND_fn_vAddFractionThemeSurround(iIndexTheme,iMiniFrameTheme);
						else
						SND_fn_vSubFractionThemeSurround(iIndexTheme,iMiniFrameTheme);
					}
				}
				else {
					if (pVoiceOneShot->bIsFinishing) {
						memset(pVoiceOneShot->pdFraction,0,2*kNumChannel*sizeof(double));
						pdSwapFraction = pVoiceOneShot->pdFraction;
					} else {
						if (!pVoiceOneShot->bIsSurround) 
						pdSwapFraction = pVoiceOneShot->pdFraction + (3*2*kNumChannel*iMiniFrameSample) + (2*kNumChannel*iIndexSample);
						else { 
							if (pVoiceOneShot->bIsFullSurround)
							SND_fn_vAddFractionOneShotSurround(iIndexSample,iMiniFrameSample);
							else
							SND_fn_vSubFractionOneShotSurround(iIndexSample,iMiniFrameSample);
						}
					}
				}

				SND_fn_vSynthesis(pOut,0);	// stereo L
				SND_fn_vSynthesis(pOut,1);	// stereo R
			
				k++;

			}

			if ((pVoiceTheme->bIsPlaying)&&(!pVoiceTheme->bIsPaused)) {
				pVoiceTheme->ulPos += kNumChannel;
				if (pVoiceTheme->ulPos >= pVoiceTheme->ulFinalSize) {
					// La part va bientôt se terminer...
					// on stoppe le décodage maintenant
					iMiniFrameTheme = 12;
					iIndexTheme = 3;
					bSwitchPart = TRUE;
					pVoiceTheme->bIsPlaying = FALSE;

					*piSamplesDone=k*32;
					return TRUE;
				}
			}

			if ((pVoiceTheme->bIsPlaying)&&(!pVoiceTheme->bIsPaused)) {
				iIndexTheme++;
				if (iIndexTheme >= 3) {
					iIndexTheme = 0 ;
					iMiniFrameTheme++;
					if (iMiniFrameTheme >= 12 ) {
						if (SND_fn_bRead(pVoiceTheme)) {
							memset(pVoiceTheme->pdFraction,0,6*12*kNumChannel*sizeof(double));
							SND_fn_bFinishRead(pVoiceTheme);
							if (pVoiceTheme->bIsFullSurround) {
								// il faut décoder une frame pour la voix surround
								SND_fn_bRead(pVoiceTheme->pAssociatedVoice);
								memset(pVoiceTheme->pAssociatedVoice->pdFraction,0,6*12*kNumChannel*sizeof(double));
								SND_fn_bFinishRead(pVoiceTheme->pAssociatedVoice);
							}
							iMiniFrameTheme = 0;
							iIndexTheme = 0;
						}
					}
				}
			}
			if ((pVoiceOneShot->bIsPlaying)&&(!pVoiceOneShot->bIsPaused)) {
				pVoiceOneShot->ulPos += kNumChannel;
				if (pVoiceOneShot->ulPos >= pVoiceOneShot->ulFinalSize) {
					pVoiceOneShot->bIsPlaying = FALSE;
					pVoiceOneShot->bIsFinishing = TRUE;
					pVoiceOneShot->ulNumSampleBeforeEnd = 32*kFrameSize;
				} else {
					iIndexSample++;
					if (iIndexSample >= 3) {
						iIndexSample = 0;
						iMiniFrameSample++;
						if (iMiniFrameSample >= 12 ) {
							if (SND_fn_bRead(pVoiceOneShot)) {
								memset(pVoiceOneShot->pdFraction,0,6*12*kNumChannel*sizeof(double));
								SND_fn_bFinishRead(pVoiceOneShot);
								if (pVoiceOneShot->bIsFullSurround) {
									// il faut décoder une frame pour la voix surround
									SND_fn_bRead(pVoiceOneShot->pAssociatedVoice);
									memset(pVoiceOneShot->pAssociatedVoice->pdFraction,0,6*12*kNumChannel*sizeof(double));
									SND_fn_bFinishRead(pVoiceOneShot->pAssociatedVoice);
								}
								iMiniFrameSample = 0;
								iIndexSample = 0;
							} 
						}
					}
				}
			} else {
				if (pVoiceOneShot->bIsFinishing) {
					pVoiceOneShot->ulNumSampleBeforeEnd-= kNumChannel;
					pVoiceOneShot->ulPos += kNumChannel;
					if (pVoiceOneShot->ulNumSampleBeforeEnd == 0) {
						pVoiceOneShot->bIsFinishing = FALSE;
						SND_fn_bFreeMPEGVoice(pVoiceOneShot);  
						if (pVoiceOneShot->pfnCallBack) {
							start_callbackMPEG(pVoiceOneShot);
						}
						pVoiceOneShot->pfnCallBack = NULL;
					}
				}
			}

		} else {
			*piSamplesDone=k*32;
			return FALSE;
		}
	}

#ifdef _PROFILE_MFC_
	// PERF EVALUATION
	QueryPerformanceCounter(&t2);
	if (t2.LowPart > t1.LowPart) {
		iTotalTime += (t2.LowPart - t1.LowPart);
		iNumTime+=k;
	}
#endif

	*piSamplesDone=k*32;
	return FALSE;
}


//-------------------------------------------------------------------------
// Description : synthesis the output datas
//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------
// Input  : voice pointer , index of subframe and channel number
// Output : void
//-------------------------------------------------------------------------
// Creation date : 19/03/97              Author : Guillaume Michel
//-------------------------------------------------------------------------
// Modifications :
// Modification Date :              Author :
//-------------------------------------------------------------------------

void SND_fn_vSynthesis(short* pwBaseOut, int iChannel)
{
	int		i,k,offset;
	double	*pdBuffOffsetPtr;
	double	*pdCurrentFract = pdSwapFraction;
	double	*pdLastFract;
	double	*pdTmp1;
	double	*pdTmp2;
	double	*pdSynthMat;
	short	*pwOut;
	double	dSum;
//	double	*pdOut;

	pdCurrentFract += (kNumChannel*iChannel);
	pdLastFract = pdCurrentFract + (kNumChannel-1);

	offset = iOffset[iChannel] = ( iOffset[iChannel] - (kNumChannel<<1)) & 0x3ff;
	pdBuffOffsetPtr = (iChannel == 0) ? pdSynthesisBufferL : pdSynthesisBufferR;
	pdBuffOffsetPtr += offset;

	pdTmp1 = pdTmp1Fract;
	pdTmp2 = pdTmp2Fract;

	// preparation...
	for (k = 0; k < (kNumChannel>>1); k++)  {
		iTmpOffset[k] = wIndexMap[k] + offset;
		*pdTmp1++ = *pdCurrentFract + *pdLastFract;
		*pdTmp2++ = (*pdCurrentFract) - (*pdLastFract);
		pdCurrentFract ++;
		pdLastFract --;

	}

	// synthesis...
	pdSynthMat = (double*)dSynthesisMatrix;
	pdLastFract = dTmp;

	for (i = 0; i < kNumChannel; i++) {
		dSum = 0.;
		if (i % 2) pdCurrentFract = pdTmp1Fract;
		else pdCurrentFract = pdTmp2Fract;

		for (k = 0; k < (kNumChannel>>1); k++) {
			dSum += ((*pdCurrentFract++)*(*pdSynthMat++));
		}
		*pdLastFract++ = dSum;
	}

	for (i = 0; i < 16; i++) {
		pdBuffOffsetPtr[i] = -dTmp[i+16];
		pdBuffOffsetPtr[i+17] = dTmp[31-i];
		pdBuffOffsetPtr[i+32] = dTmp[16-i];
		pdBuffOffsetPtr[i+48] = dTmp[i];
	}
	pdBuffOffsetPtr[16] = 0;

	// filtering...
	pwOut = pwBaseOut + iOutOffset[iChannel];
	pdBuffOffsetPtr = (iChannel == 0) ? pdSynthesisBufferL : pdSynthesisBufferR;

	for (k =0; k < kNumChannel; k++) {
		dSum = dSynthesisWindow[k] * (pdBuffOffsetPtr[( k + iTmpOffset[0] ) & 0x3ff]);
		for (i = 32; i < (kNumChannel<<4); i+=32) {
			dSum += dSynthesisWindow[k+i] * (pdBuffOffsetPtr[( k + i + iTmpOffset[i>>5] ) & 0x3ff]);
		}
		*pwOut = (short)dSum;
		pwOut += 2;
	}

	iOutOffset[iChannel] += (kNumChannel<<1);

}

//#Added on 04/28/98 by Stephane RONSE (Antibug)
//Clean SynhtesisBuffer berfore starting a new theme to avoid Squizz
void fn_vCleanSynthesisBuffer()
{
	memset(pdSynthesisBufferL,0xCD,2*(kFFTSize+16)*sizeof(double));
}

#define SND_fn_bTestSnd_Processor() SND_fn_bTestSnd_Pentium()
#define C_PROCESSOR_WANTED "Pentium or compatible"

#include "mpeg.cxx"

