/*
// ================================================================
// MPEG.c MMX VERSION
// Ubi Sound Studios
// 18/03/1997
// Guillaume Michel
// ================================================================
*/

#include <math.h>
//#include <crtdbg.h>

#include "SNDinc.h"

#if defined(_DLL_COMPILATION_MODE) 
#include "snddll.h"
#endif  

#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

// fonction de 'déquantisation' MMX
extern void __cdecl MMX_MPEGDeq_(short * P0,short * P1, long l1, long l2, long l3, long l4, long l5);
#define MMX_MPEGDeq	MMX_MPEGDeq_
// fonction de filtrage MMX
extern void __cdecl MMX_MPEGFilter_(short * P0,short * P1);
#define MMX_MPEGFilter	MMX_MPEGFilter_
// fonction de transformation en cosinus discrets en MMX
extern void __cdecl MMX_MPEGDct_(void * P0,void * P1,void * P2, void* P3, void* P4, void *P5);
#define MMX_MPEGDct	MMX_MPEGDct_
// fonction de lecture dur flux de bits en MMX
void __cdecl getbits_(long l, void * P0, void* P2);
#define getbits	getbits_
#define GETBITS(A,B,C) getbits(A,B,C) 
// fonction de 'mix' MMX
extern void __cdecl MMX_MPEGAdd_(short * P0,short * P1);
#define MMX_MPEGAdd	MMX_MPEGAdd_
// fonction de 'mix' MMX SURROUND
extern void __cdecl MMX_MPEGAddSurround_(short * P0,short * P1);
#define MMX_MPEGAddSurround	MMX_MPEGAddSurround_
// fonction de 'mix' MMX SURROUND
extern void __cdecl MMX_MPEGSubSurround_(short * P0);
#define MMX_MPEGSubSurround	MMX_MPEGSubSurround_


// ======= MPEG ENGINE ======================================
// Voices array... 
SND_tdstMPEGVoice		MPEGvoices[(kNumVoice+1)*2];

// VOICE THEME PART 0
// VOICE THEME	PART 1
// VOICE ONESHOT 
// VOICE THEME PART 0 SURROUND
// VOICE THEME PART 1 SURROUND
// VOICE ONESHOT SURROUND

static short					*pwSynthesisBufferR;	// buffer pour la synthèse
static short					*pwSynthesisBufferL;	// idem
static short					*pwSwapFraction;		// endroit où on va faire l'addition des voix

static short					*pwOutBuffer;		// Buffer de sortie stéréo 44kHz 16 bits
static short					*pwOutBufferEnd;	// Fin du buffer
static short					*pwOutBufferEnd2;	// Fin du buffer (variable pour thèmes)
static short					*pwOutBufferPos;	// position d'écriture dans le buffer

static int						iOffset[2];			// Offset dans le buffer de synthèse (doit être conservé d'une frame à l'autre)

static int						iOutOffset[2];		// Offset dans le buffer de sortie (remis à 0 à chaque frame)

//!!!variable exportée vers le module ASM
int						loffset;			// Offset partagé avec la routine assembleur MMX
SND_tdstMPEGVoice		*pSwapVoice[2];		// Pointeur sur les deux fichiers MPEG à décoder pour un thème

static SndBool					bInitDone = FALSE;	// Indicateur INIT effectuée

// matrices pour la fast DCT (algorithme de LEE)
static short					lTemp[kNumChannel<<1];	//Vecteur temporaire pour la fast DCT
static short					m1[64]; // matrice 8*8 réarrangée 
static short					m2[64];	// matrice 8*8 réarrangée
static short					m3[16][16];	// matrice 16*16

//!!!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;

extern SndBool bCallBackOneShotDone;

//short globalBuffer[36*44100*2];
//short *pBigInBuffer = globalBuffer;

//-------------------------------------------------------------------------
// 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,k,l;
	short		*pwTmp;
	sb_alloc	*pAllocTable;
	short		*pwMat;
	double		PI64 = 4.*atan(1.);
	double		dSum,dTmp;

	short	n1[8][8];
	double	a[16][16];
	double	b[16][16];
	double	c[16][16];
	double	d[16][16];

	// MPEG Signal Processing INITS

	PI64 = PI64 / 64.;

	// Matrice 8*8 PAIRE pour l'algorithme de LEE
	for (i = 0; i < 8; i++) {
		for (j = 0; j<8; j++) {
			n1[i][j] = (short)(-16384.*cos(4.*PI64*(double)i*(double)(2.*j+1.)));
		}
	}
	// Réarrangement de la matrice dans un vecteur (spécial MMX)
	pwMat = m1;
	for (i = 0; i < 8; i+=2) {
		for (j = 0; j < 8; j+=2) {
			*pwMat++ = n1[i][j];
			*pwMat++ = n1[i][j+1];
			*pwMat++ = n1[i+1][j];
			*pwMat++ = n1[i+1][j+1];
		}
	}

	// Matrice 16*16 IMPAIRE pour l'algorithme de LEE
	// Matrice de base
	for (i = 0; i < 16; i++) {
		for ( j = 0; j<16; j++) {
			c[i][j] = cos(2.*PI64*(double)i*(double)(2.*j+1.));
		}
	}

	// Création des deux matrices de transformations
	for (i = 0; i < 16; i++) {
		for (j = 0; j < 16; j++) {
			if (i == j) {
				b[i][j] = 1./(2.*cos(PI64*(double)(2.*i+1.)));
				a[i][j] = 1.;
			} else {
				b[i][j] = 0.;
				
				if (j==(i+1)) {
					a[i][j] = 1.;
				} else 	a[i][j] = 0.;
			}
		}
	}
	
	// Multiplication de la matrice de base par les deux matrices de transformations
	for (i = 0; i < 16; i++) {
		for (j = 0; j < 16; j++) {
			dSum = 0;
			for (k = 0; k < 16; k++)
				dSum += (c[i][k]*b[k][j]);
			d[i][j] = dSum;
		}
	}

	for (i = 0; i < 16; i++) {
		for (j = 0; j <16; j++) {
			dSum = 0;
			for (k = 0; k < 16; k++)
				dSum += (a[i][k]*d[k][j]);
			m3[i][j] = (short)(-16384.*dSum);
		}
	}

	// Matrice 8*8 IMPAIRE pour l'algorithme de LEE
	// Matrice de base
	for (i = 0; i < 8; i++) {
		for ( j = 0; j<8; j++) {
			c[i][j] = cos(4.*PI64*(double)i*(double)(2.*j+1.));
		}
	}

	// Matrices de transformations
	for (i = 0; i < 8; i++) {
		for (j = 0; j < 8; j++) {
			if (i == j) {
				b[i][j] = 1./(2.*cos(2.*PI64*(double)(2.*i+1.)));
				a[i][j] = 1.;
			} else {
				b[i][j] = 0.;
				
				if (j==(i+1)) {
					a[i][j] = 1.;
				} else 	a[i][j] = 0.;
			}
		}
	}

	// Multiplication des deux matrices 
	for (i = 0; i < 8; i++) {
		for (j = 0; j < 8; j++) {
			dSum = 0;
			for (k = 0; k < 8; k++)
				dSum += (c[i][k]*b[k][j]);
			d[i][j] = dSum;
		}
	}

	for (i = 0; i < 8; i++) {
		for (j = 0; j <8; j++) {
			dSum = 0;
			for (k = 0; k < 8; k++)
				dSum += (a[i][k]*d[k][j]);
			n1[i][j] = (short)(-16384.*dSum);
		}
	}

	// Réarrangement de la matrice dans un vecteur (spécial MMX)
	pwMat = m2;
	for (i = 0; i < 8; i+=2) {
		for (j = 0; j < 8; j+=2) {
			*pwMat++ = n1[i][j];
			*pwMat++ = n1[i][j+1];
			*pwMat++ = n1[i+1][j];
			*pwMat++ = n1[i+1][j+1];
		}
	}

	// Création de petits tableaux 
	for (i = 0; i < kSubBandLimit; i++){
		for (j = 0; j < 16; j++) {
			pAllocTable = &AllocationTable[i][j];
			l = 0;
			while(((unsigned int)(1L<<l)) < (pAllocTable->steps)) l++;
			dTmpDiviseur[i][j] = 1./(double) (1L<<(l-1));
		}
	}
	
	// modification de la table des puissances de 2
	for (i= 0;i < kNumChannel; i++) {
		for (j=0; j < 15; j++) {
			ucPowStep[i][j]--;
		}
	}

	// Normalisation de la fenêtre de synthèse pour l'utilisation du MMX
	for (i = 0; i < kAnalysisSize; i++) {
		dSynthesisWindow[i] *= 16384.;
	}

	// Réarrangement de la fenêtre de synthèse pour l'algorithme de filtrage MMX
	pwTmp = wSynthesisWindow;
	for (j = 0; j < 8; j++)
		for (i = 0; i < 32; i+=2) {
			*pwTmp++ = (short)dSynthesisWindow[(64*j)+i];
			*pwTmp++ = (short)dSynthesisWindow[(64*j)+i+32];
			*pwTmp++ = (short)dSynthesisWindow[(64*j)+i+1];
			*pwTmp++ = (short)dSynthesisWindow[(64*j)+i+33];
		}

	// Normalisation des coefficients de déquantification pour l'algorithme MMX
	for (i = 0; i < kNumScaleFactor; i++) {
		dMultipleSyn[i] =dMultiple[i] * 32768.; 
	}


	// Tableau de conversion volume MIDI -> dB
	ucVolume[0] = kNumScaleFactor;
	for (i = 1; i < 128; i++) {
		dTmp = ((double)i)/128.;
		dTmp = -20.*log10(dTmp);
		dTmp /= 2;
		ucVolume[i] = (unsigned char)dTmp;
	}
}

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

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

	pwSynthesisBufferL = (short*) SND_fn_pvMallocSnd(2*kFFTSize*sizeof(short));
	pwSynthesisBufferR = pwSynthesisBufferL + kFFTSize;

	if (pwSynthesisBufferL == NULL) {
		SND_fn_bDesInitMPEG();
		return FALSE;
	}

	memset(pwSynthesisBufferL,0,2*kFFTSize*sizeof(short));

	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;

	// FIN 

	// MPEG Engine INITS
	for (i = 0; i < (2*(kNumVoice+1)); i++) {
		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 {
				// DOLBY SURROUND VOICES
				// no need to do memory allocations for bitstreams
				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 
/*	FILE* pFile =  fopen("out.raw","wb");
	if (pFile) {
		fwrite(globalBuffer,36*44100*2,1,pFile);
		fclose(pFile);
	}
*/	

	if (pwSynthesisBufferL) {
		SND_fn_vFreeSnd(pwSynthesisBufferL);
		pwSynthesisBufferL = NULL;
		pwSynthesisBufferR = 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(18*kSubBandLimit*sizeof(double));
	pVoice->pdScaleFactor = pVoice->pdDiviseur + 6*kSubBandLimit;
	pVoice->pdSumFactor = pVoice->pdScaleFactor + 6*kSubBandLimit;
	pVoice->pwSample = (short*) SND_fn_pvMallocSnd(6*(kSubBandLimit+12*kNumChannel)*sizeof(short));
	pVoice->pwFraction = pVoice->pwSample + 6*kSubBandLimit;
	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;
#if M_FRED_DECREAU
		iFadeVolume>>=16;
#else
		iFadeVolume /= pVoice->rDuration;
#endif
		snd_assert( iFadeVolume <= 127 );
		volidx=ucVolume[iFadeVolume];
//		_RPT3( _CRT_WARN, "Fade %d Vol Cur %u Index %d\n", pVoice->iFadeVolume, volidx, iFadeVolume );
	}
	else volidx = pVoice->ucVolumeIndex;

	for (i = 0; i < kSubBandLimit; i++) {
		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 = dMultipleSyn[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;
	short			*pwMixStereo;
	short			*pwSample;
	short			*pwMix= pVoice->pwFraction+iIndex*3*2*kNumChannel;

	double dFract;

	l = pVoice->ucNbChannel;
	pucBitAlloc = pVoice->pucBitAlloc;
	pwSample = pVoice->pwSample;

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

	for(i = 0; i < kSubBandLimit; i++) {
		for (j = 0; j < l; j++) {
			
			pwMixStereo = (j == 0) ? pwMix : pwMix+kNumChannel;

			if (ba = *pucBitAlloc++) {
				ba--;
				x = ucPowStep[i][ba];
				//MMX_MPEGDeq(pwSample,pwMixStereo,(long) *pdDiviseur,(long) *pdSum,(long) *pdScaleFactor,x,l);
				for (k = 0; k < 3; k++) {
					dFract = (double)((pwSample[k]) & ((1<<x)-1));
					dFract *= (*pdDiviseur);
					dFract += (*pdSum);

					if (!((((pwSample[k]) >> x) & 1) == 1)) {
						dFract -= (*pdScaleFactor);
					}

					*(pwMixStereo+(k<<6)) = (short)dFract;	
					if(l == 1) {
						*(pwMixStereo+(k<<6)+32) = (short)dFract;
					}
					


				}
			}

			pdDiviseur += 3;
			pdSum += 3;
			pdScaleFactor += 3;
			pwSample += 3;

			if (j == (l-1)) pwMix++;

		}
	}
}

// 
//	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) {
	SND_tdstMPEGVoice* pVoiceTheme = pSwapVoice[0];
	SND_tdstMPEGVoice* pVoiceOneShot = &MPEGvoices[2];
	short *pwTmp1 = pVoiceTheme->pwFraction;
	short *pwTmp2 = pVoiceOneShot->pwFraction;

	pwTmp1 += (3*2*kNumChannel*iFrame0);
	pwTmp1 += (2*kNumChannel*iIndex0);
	pwTmp2 += (3*2*kNumChannel*iFrame1);
	pwTmp2 += (2*kNumChannel*iIndex1);

	MMX_MPEGAdd(pwTmp1,pwTmp2);
	pwSwapFraction = pwTmp1;
}

// 
//	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) {
	SND_tdstMPEGVoice* pVoiceTheme = pSwapVoice[0];
	SND_tdstMPEGVoice* pVoiceThemeSurround = pVoiceTheme + (kNumVoice+1);
	short *pwTmp1 = pVoiceTheme->pwFraction;
	short *pwTmp2 = pVoiceThemeSurround->pwFraction;

	pwTmp1 += (3*2*kNumChannel*iFrame0);
	pwTmp1 += (2*kNumChannel*iIndex0);
	pwTmp2 += (3*2*kNumChannel*iFrame0);
	pwTmp2 += (2*kNumChannel*iIndex0);

	MMX_MPEGAddSurround(pwTmp1,pwTmp2);
	pwSwapFraction = pwTmp1;
}

// 
//	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) {
	SND_tdstMPEGVoice* pVoiceOneShot = &MPEGvoices[2];
	SND_tdstMPEGVoice* pVoiceOneShotSurround = pVoiceOneShot + (kNumVoice+1);
	short *pwTmp1 = pVoiceOneShot->pwFraction;
	short *pwTmp2 = pVoiceOneShotSurround->pwFraction;

	pwTmp1 += (3*2*kNumChannel*iFrame0);
	pwTmp1 += (2*kNumChannel*iIndex0);
	pwTmp2 += (3*2*kNumChannel*iFrame0);
	pwTmp2 += (2*kNumChannel*iIndex0);

	MMX_MPEGAddSurround(pwTmp1,pwTmp2);
	pwSwapFraction = pwTmp1;
}

// 
//	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) {
	SND_tdstMPEGVoice* pVoiceTheme = pSwapVoice[0];
	short *pwTmp1 = pVoiceTheme->pwFraction;

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

	MMX_MPEGSubSurround(pwTmp1);
	pwSwapFraction = pwTmp1;
}

// 
//	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) {
	SND_tdstMPEGVoice* pVoiceOneShot = &MPEGvoices[2];
	short *pwTmp1 = pVoiceOneShot->pwFraction;

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

	MMX_MPEGSubSurround(pwTmp1);
	pwSwapFraction = pwTmp1;
}

//-------------------------------------------------------------------------
// 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->pwFraction,0,6*12*kNumChannel*sizeof(short));
			SND_fn_bFinishRead(pVoiceTheme);
			iMiniFrameTheme = 0;
			iIndexTheme = 0;

			if (pVoiceTheme->bIsFullSurround) {
				// il faut décoder deux frames surround
				SND_fn_bRead(pVoiceTheme->pAssociatedVoice);
				memset(pVoiceTheme->pAssociatedVoice->pwFraction,0,6*12*kNumChannel*sizeof(short));
				SND_fn_bFinishRead(pVoiceTheme->pAssociatedVoice);
			}
		}
	}
	
	// Voice One Shot
	if (iMiniFrameSample == 12 && iIndexSample == 3) {
		if (SND_fn_bRead(pVoiceOneShot)) {
			memset(pVoiceOneShot->pwFraction,0,6*12*kNumChannel*sizeof(short));
			SND_fn_bFinishRead(pVoiceOneShot);
			iMiniFrameSample = 0;
			iIndexSample = 0;
			if (pVoiceOneShot->bIsFullSurround) {
				SND_fn_bRead(pVoiceOneShot->pAssociatedVoice);
				memset(pVoiceOneShot->pAssociatedVoice->pwFraction,0,6*12*kNumChannel*sizeof(short));
				SND_fn_bFinishRead(pVoiceOneShot->pAssociatedVoice);
			}
		}
	}

	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)) {
				// 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) 
					pwSwapFraction = pVoiceTheme->pwFraction + (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->pwFraction,0,2*kNumChannel*sizeof(short));
						pwSwapFraction = pVoiceOneShot->pwFraction;
					} else {
						if (!pVoiceOneShot->bIsSurround) 
						pwSwapFraction = pVoiceOneShot->pwFraction + (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/*+480*/)) {
					// 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->pwFraction,0,6*12*kNumChannel*sizeof(short));
							SND_fn_bFinishRead(pVoiceTheme);
							if (pVoiceTheme->bIsFullSurround) {
								// deux frames...
								SND_fn_bRead(pVoiceTheme->pAssociatedVoice);
								memset(pVoiceTheme->pAssociatedVoice->pwFraction,0,6*12*kNumChannel*sizeof(short));
								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->pwFraction,0,6*12*kNumChannel*sizeof(short));
								SND_fn_bFinishRead(pVoiceOneShot);
								if (pVoiceOneShot->bIsFullSurround) {
									// deux frames pour le vrai surround....
									SND_fn_bRead(pVoiceOneShot->pAssociatedVoice);
									// on ne verifie pas que la lecture est correcte
									// on suppose que c'est ok...
									memset(pVoiceOneShot->pAssociatedVoice->pwFraction,0,6*12*kNumChannel*sizeof(short));
									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)
{

	short	*pwBuffOffsetPtr;
	short	*pwOut; 
	short	*pwCurrentFract = pwSwapFraction;

	pwCurrentFract += (kNumChannel*iChannel);
	loffset = iOffset[iChannel] = ( iOffset[iChannel] - (kNumChannel<<1)) & 0x3ff;
	pwBuffOffsetPtr = (iChannel == 0) ? pwSynthesisBufferL : pwSynthesisBufferR;
	pwOut = pwBaseOut + iOutOffset[iChannel];

	// synthesis...
	// FAST DCT
	MMX_MPEGDct(pwCurrentFract,lTemp,m1,m2,m3,pwBuffOffsetPtr + loffset);
	// FILTRAGE
	MMX_MPEGFilter(pwBuffOffsetPtr,pwOut);
	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(pwSynthesisBufferL,0,2*kFFTSize*sizeof(short));
//	memset(pwOutBuffer,0,(2*kFrameSize)*sizeof(short));
//	pwOutBufferPos = pwOutBufferEnd = pwOutBufferEnd2 = pwOutBuffer + 2*kFrameSize;
}


#define SND_fn_bTestSnd_Processor() SND_fn_bTestSnd_MMX()
#define C_PROCESSOR_WANTED "MMX or compatible"

#include "mpeg.cxx"
   