#include <windows.h>
#include <process.h>
#include "sndwave.hxx"

#define REVERBOUT                      	1
#define FLANGEOUT                      	2
#define READY_TO_INC_FLANGE_OFFSET     	200
#define MASQUE                          65534
#define NB_DECAL						            8   
#define NB_BITS_MUL_VOL					        1
#define INC_SAMPLE_MONO                 2
#define INC_SAMPLE_STEREO               4
#define NBVOIES                       	2
#define REVERBHISTOSIZE                	2*8192
#define FLANGEHISTOSIZE                 2*8192
#define SURROUNDSIZE					          44000
#define FLANGEMAX                       FLANGEHISTOSIZE/2
#define FLANGEMIN                       FLANGEHISTOSIZE/16
#define FLANGEINC                       FLANGEHISTOSIZE/2048
#define NB_COEFS						            32


DWORD outptrR_histo=0;
DWORD outptrF_histo=0;
DWORD ptrR_histo[NBVOIES];
DWORD ptrE_histo[NBVOIES];
DWORD ptrF_histo[NBVOIES];
DWORD ptrF_delai[NBVOIES];

DWORD adr_histo=0;
DWORD compte_sample=0;
DWORD change_flange_inc=READY_TO_INC_FLANGE_OFFSET;
DWORD flange_offset=FLANGEMIN;
DWORD inc_delai=FLANGEINC;

DWORD HistoReverb[REVERBHISTOSIZE/2];
DWORD OutHistoReverb[REVERBHISTOSIZE/2];
DWORD OutHistoFlange[FLANGEHISTOSIZE/2];
DWORD HistoFlange[FLANGEHISTOSIZE/2];

DWORD BufferOut[44000];

//; Buffer de stockage de la voie surround, de taille SURROUNDSIZE+NB_COEFS*2 octets
DWORD BufferSurround[(SURROUNDSIZE/4+NB_COEFS/2)];

//; pointeur courant d'ecriture dans le buffer de surround
DWORD ptr_surround=0;
DWORD mmS[8];
DWORD Dataout_ptr=0;

#ifndef DISABLE_WAVE

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

//___________________________________________________________________________________________________________________
//	Flags effets

BOOL gReverbOn;
BOOL gFlangeOn;

//___________________________________________________________________________________________________________________
//	Macro d'appel de fonction genere_buffer, en fonction du flag SURROUND, qui dénote la possibilité physique 
//	de faire du mixage en dolby surround


#ifdef SURROUND
#define genere_buffer(x,y)      if (dolby_actif){MMX_genere_buffer_dolby((x),(y));} else { MMX_genere_buffer((x),(y));}
#else
#define genere_buffer(x,y)      MMX_genere_buffer((x),(y))
#endif


#define RajParamHard(x)
#define genere_part(x,y,z)      if (dolby_actif){MMX_genere_buffer_low_latency_dolby((void *)(x),(y),(BUFFER_DISPO*)(z));   }       else    {MMX_genere_buffer_low_latency((void *)(x),(y),(BUFFER_DISPO*)(z));}



//___________________________________________________________________________________________________________________
// Macro made to call mmx mixing assembly functions
// function_name : function to call
// function parameters for prototype :
// P1 : buffer_input : input sound buffer
// P2 : buffer_output : output sound mix buffer
// P3 : iNbBytesToProvide : number of sound bytes to provide in output
// P4 : type_effect : if you want to add an effect on the mix
// P5 dolby : 
// P5 or P6 dolby : add_mem : flag to say if output mem should be overwrite or not, true = mix, false = overwrite

#define FUNCTION_PROTOTYPE_MMX_2P(function_name)\
void  function_name##_(long,long);
#define FUNCTION_PROTOTYPE_MMX_3P(function_name)\
void __cdecl function_name##_(long,long,long);
#define FUNCTION_PROTOTYPE_MMX_4P(function_name)\
void __cdecl function_name##_(long,long,long,long);
#define FUNCTION_PROTOTYPE_MMX_5P(function_name)\
void __cdecl function_name##_(long,long,long,long,long);
#define FUNCTION_PROTOTYPE_MMX_6P(function_name)\
void __cdecl function_name##_(long,long,long,long,long,long);

/* Mono Mix Functions */

FUNCTION_PROTOTYPE_MMX_5P(MixVolPan);                        /* Mix mono with known constant pitch of 1 (dest freq = src freq) */ 
FUNCTION_PROTOTYPE_MMX_5P(MixVolPan_f_vers_2f);              /* Mix mono with known constant pitch of 1/2 (dest freq = 2 x src freq) */ 
FUNCTION_PROTOTYPE_MMX_5P(MixVolPan_2f_vers_f);              /* Mix mono with known constant pitch of 2 (dest freq = 1/2 x src freq) */  
FUNCTION_PROTOTYPE_MMX_5P(MixVolPanEcho);                    /* Mix mono with known constant pitch of 1 (dest freq = src freq) + echo */ 
FUNCTION_PROTOTYPE_MMX_5P(MixVolPanPitch);                   /* Mix mono with unknown constant floating pitch */ 
FUNCTION_PROTOTYPE_MMX_5P(MixVolPanPitch1);                  /* Mix mono with unknown constant floating pitch */ 
FUNCTION_PROTOTYPE_MMX_5P(MixVolPanPitchEcho);               /* Mix mono with unknown constant floating pitch + echo */ 
FUNCTION_PROTOTYPE_MMX_5P(MixVolPanPitchReverb);             /* Mix mono with unknown constant floating pitch + reverb */ 
FUNCTION_PROTOTYPE_MMX_5P(MixVolPanPitchFlange);             /* Mix mono with unknown constant floating pitch + flange */ 

/* Stereo Mix Functions */

FUNCTION_PROTOTYPE_MMX_5P(MixVolPanStereo_f_vers_2f);        /* Mix stereo with known constant pitch of 1/2 (dest freq = 2 x src freq) */ 
FUNCTION_PROTOTYPE_MMX_5P(MixVolPanStereo_2f_vers_f);        /* Mix stereo with known constant pitch of 2 (dest freq = 2 x src freq) */ 
FUNCTION_PROTOTYPE_MMX_5P(MixVolPanStereo);                  /* Mix stereo with known constant pitch of 1 (dest freq = 2 x src freq) */ 
FUNCTION_PROTOTYPE_MMX_5P(MixVolPanPitchStereo);             /* Mix stereo with unknown constant floating pitch */ 

/* Mono Mix Functions with dolby */

FUNCTION_PROTOTYPE_MMX_6P(MixVolPanDolby);
FUNCTION_PROTOTYPE_MMX_6P(MixVolPanPitchDolby1);
FUNCTION_PROTOTYPE_MMX_6P(MixVolPanPitchDolby);

FUNCTION_PROTOTYPE_MMX_3P(GlobalReverb);
FUNCTION_PROTOTYPE_MMX_3P(GlobalFlange);
FUNCTION_PROTOTYPE_MMX_3P(MMX_memset);

FUNCTION_PROTOTYPE_MMX_2P(MixBuffers);
FUNCTION_PROTOTYPE_MMX_2P(ResetBufferSurround);
FUNCTION_PROTOTYPE_MMX_4P(FIR_MMX);
FUNCTION_PROTOTYPE_MMX_5P(CONVOLUTION_MMX_STEREO);
//___________________________________________________________________________________________________________________
//	Constantes 

#define NB_COEFS_MAX 64
#define NSP_PI   3.1415

//___________________________________________________________________________________________________________________
//	Typedef

//#define MIX_WITH_QUERYPERFORMANCE
LARGE_INTEGER TempsDebut,TempsFin,TempsFrequence;
static double g_dDeltaTemps = 0,g_dDeltaTemps1 = 0;
static int g_iCount = 0,g_iCount1 = 0;
static int g_iReentrance = 0;
char g_csPerf[512];
double g_dFreq;

#ifdef MIX_WITH_QUERYPERFORMANCE
#pragma message ("Query performance active")
double ADD64(LARGE_INTEGER a,LARGE_INTEGER b){	return ((double)a.QuadPart + (double)b.QuadPart);}
double SUB64(LARGE_INTEGER a,LARGE_INTEGER b){	return ((double)a.QuadPart - (double)b.QuadPart);}
double MUL64(LARGE_INTEGER a,LARGE_INTEGER b){	return ((double)a.QuadPart * (double)b.QuadPart);}
double DIV64(LARGE_INTEGER a,LARGE_INTEGER b){	return ((double)a.QuadPart / (double)b.QuadPart);}


//#define FILTER_C

// --------------------------------------------------------------------------------------------------
// Macro used to call assembly mix functions with time evaluation
// function_name : function to call
// buffer_input : input sound buffer
// buffer_output : output sound mix buffer
// iNbBytesToProvide : number of sound bytes to provide in output
// add_mem : flag to say if output mem should be overwrite or not, true = mix, false = overwrite
// --------------------------------------------------------------------------------------------------

#define MIX_STEREO_PERFORMANCE(function_name,comment,buffer_input,buffer_output,iNbBytesToProvide,type_effet,add_mem)\
{ QueryPerformanceCounter(&TempsDebut);function_name##_((long)buffer_input,(long)buffer_output,(long)iNbBytesToProvide,type_effet,add_mem);\
	QueryPerformanceCounter(&TempsFin);g_iCount++;\
	if (g_iCount < 100)\
		g_dDeltaTemps += SUB64(TempsFin,TempsDebut);\
	else {\
		sprintf(g_csPerf,"\nMix stereo (%s) pour %d samples : %f µsec",comment,iNbBytesToProvide/4,(double)(((g_dDeltaTemps/g_iCount)*1000000.0))/g_dFreq);\
		OutputDebugString(g_csPerf);\
		g_dDeltaTemps = 0;g_iCount = 0;\
	}\
}

#define MIX_MONO_PERFORMANCE(function_name,comment,buffer_input,buffer_output,iNbBytesToProvide,type_effet,add_mem)\
{	QueryPerformanceCounter(&TempsDebut);function_name##_((long)buffer_input,(long)buffer_output,(long)iNbBytesToProvide,type_effet,add_mem);\
	QueryPerformanceCounter(&TempsFin);g_iCount++;\
	if (g_iCount < 100)\
		g_dDeltaTemps += SUB64(TempsFin,TempsDebut);\
	else {\
		sprintf(g_csPerf,"\nMix mono (%s) pour %d samples : %f µsec",comment,iNbBytesToProvide/4,(double)(((g_dDeltaTemps/g_iCount)*1000000.0))/g_dFreq);\
		OutputDebugString(g_csPerf);\
		g_dDeltaTemps = 0;g_iCount = 0;\
	}\
}

#define MIX_STEREO_DOLBY_PERFORMANCE(function_name,comment,buffer_input,buffer_output,iNbBytesToProvide,type_effet,ptr_surround,add_mem)\
{	QueryPerformanceCounter(&TempsDebut);function_name##_((long)buffer_input,(long)buffer_output,(long)iNbBytesToProvide,type_effet,ptr_surround,add_mem);\
	QueryPerformanceCounter(&TempsFin);g_iCount++;\
	if (g_iCount < 100)\
		g_dDeltaTemps += SUB64(TempsFin,TempsDebut);\
	else {\
		sprintf(g_csPerf,"\nMix stereo dolby (%s) pour %d samples : %f µsec",comment,iNbBytesToProvide/4,(double)(((g_dDeltaTemps/g_iCount)*1000000.0))/g_dFreq);\
		OutputDebugString(g_csPerf);\
		g_dDeltaTemps = 0;g_iCount = 0;\
	}\
}

#define MIX_MONO_DOLBY_PERFORMANCE(function_name,comment,buffer_input,buffer_output,iNbBytesToProvide,type_effet,ptr_surround,add_mem)\
{	QueryPerformanceCounter(&TempsDebut);\
  function_name##_((long)buffer_input,(long)buffer_output,(long)iNbBytesToProvide,type_effet,ptr_surround,add_mem);\
	QueryPerformanceCounter(&TempsFin);g_iCount++;\
	if (g_iCount < 100)\
		g_dDeltaTemps += SUB64(TempsFin,TempsDebut);\
	else {\
		sprintf(g_csPerf,"\nMix mono dolby (%s) pour %d samples : %f µsec",comment,iNbBytesToProvide/4,(double)(((g_dDeltaTemps/g_iCount)*1000000.0))/g_dFreq);\
		OutputDebugString(g_csPerf);\
		g_dDeltaTemps = 0;g_iCount = 0;\
	}\
}

#define CALL_GENERIC_FUNCTION_PERFORMANCE_2P(function_name,comment,P0,P1){\
	QueryPerformanceCounter(&TempsDebut);\
  function_name##_((long)P0,(long)P1);\
	QueryPerformanceCounter(&TempsFin);g_iCount1++;\
	if (g_iCount1 < 100)\
		g_dDeltaTemps1 += SUB64(TempsFin,TempsDebut);\
	else {\
		sprintf(g_csPerf,"\nGeneric function (%s) : %f µsec",comment,(double)(((g_dDeltaTemps1/g_iCount1)*1000000.0))/g_dFreq);\
		OutputDebugString(g_csPerf);\
		g_dDeltaTemps1 = 0;g_iCount1 = 0;\
	}\
}
#define CALL_GENERIC_FUNCTION_PERFORMANCE_3P(function_name,comment,P0,P1,P2){\
	QueryPerformanceCounter(&TempsDebut);\
  function_name##_((long)P0,(long)P1,(long)P2);\
	QueryPerformanceCounter(&TempsFin);g_iCount1++;\
	if (g_iCount1 < 100)\
		g_dDeltaTemps1 += SUB64(TempsFin,TempsDebut);\
	else {\
		sprintf(g_csPerf,"\nGeneric function (%s) : %f µsec",comment,(double)(((g_dDeltaTemps1/g_iCount1)*1000000.0))/g_dFreq);\
		OutputDebugString(g_csPerf);\
		g_dDeltaTemps1 = 0;g_iCount1 = 0;\
	}\
}
#define CALL_GENERIC_FUNCTION_PERFORMANCE_4P(function_name,comment,P0,P1,P2,P3){\
	QueryPerformanceCounter(&TempsDebut);\
  function_name##_((long)P0,(long)P1,(long)P2,(long)P3);\
	QueryPerformanceCounter(&TempsFin);g_iCount1++;\
	if (g_iCount1 < 100)\
		g_dDeltaTemps1 += SUB64(TempsFin,TempsDebut);\
	else {\
		sprintf(g_csPerf,"\nGeneric function (%s) : %f µsec",comment,(double)(((g_dDeltaTemps1/g_iCount1)*1000000.0))/g_dFreq);\
		OutputDebugString(g_csPerf);\
		g_dDeltaTemps1 = 0;g_iCount1 = 0;\
	}\
}
#define CALL_GENERIC_FUNCTION_PERFORMANCE_5P(function_name,comment,P0,P1,P2,P3,P4){\
	QueryPerformanceCounter(&TempsDebut);\
  function_name##_((long)P0,(long)P1,(long)P2,(long)P3,(long)P4);\
	QueryPerformanceCounter(&TempsFin);g_iCount1++;\
	if (g_iCount1 < 100)\
		g_dDeltaTemps1 += SUB64(TempsFin,TempsDebut);\
	else {\
		sprintf(g_csPerf,"\nGeneric function (%s) : %f µsec",comment,(double)(((g_dDeltaTemps1/g_iCount1)*1000000.0))/g_dFreq);\
		OutputDebugString(g_csPerf);\
		g_dDeltaTemps1 = 0;g_iCount1 = 0;\
	}\
}

#else

#define MIX_STEREO_PERFORMANCE(function_name,comment,buffer_input,buffer_output,iNbBytesToProvide,type_effet,add_mem)\
{function_name##_((long)buffer_input,(long)buffer_output,(long)iNbBytesToProvide,type_effet,add_mem);}

#define MIX_MONO_PERFORMANCE(function_name,comment,buffer_input,buffer_output,iNbBytesToProvide,type_effet,add_mem)\
{function_name##_((long)buffer_input,(long)buffer_output,(long)iNbBytesToProvide,type_effet,add_mem);}

#define MIX_STEREO_DOLBY_PERFORMANCE(function_name,comment,buffer_input,buffer_output,iNbBytesToProvide,type_effet,ptr_surround,add_mem)\
{function_name##_((long)buffer_input,(long)buffer_output,(long)iNbBytesToProvide,type_effet,ptr_surround,add_mem);}

#define MIX_MONO_DOLBY_PERFORMANCE(function_name,comment,buffer_input,buffer_output,iNbBytesToProvide,type_effet,ptr_surround,add_mem)\
{function_name##_((long)buffer_input,(long)buffer_output,(long)iNbBytesToProvide,type_effet,ptr_surround,add_mem);}

#define CALL_GENERIC_FUNCTION_PERFORMANCE_2P(function_name,comment,P0,P1){function_name##_((long)P0,(long)P1);}
#define CALL_GENERIC_FUNCTION_PERFORMANCE_3P(function_name,comment,P0,P1,P2){function_name##_((long)P0,(long)P1,(long)P2);}
#define CALL_GENERIC_FUNCTION_PERFORMANCE_4P(function_name,comment,P0,P1,P2,P3){function_name##_((long)P0,(long)P1,(long)P2,(long)P3);}
#define CALL_GENERIC_FUNCTION_PERFORMANCE_5P(function_name,comment,P0,P1,P2,P3,P4){function_name##_((long)P0,(long)P1,(long)P2,(long)P3,(long)P4);}

#endif

#define CALL_GENERIC_FUNCTION_2P(function_name,comment,P0,P1){function_name##_((long)P0,(long)P1);}
#define CALL_GENERIC_FUNCTION_3P(function_name,comment,P0,P1,P2){function_name##_((long)P0,(long)P1,(long)P2);}
#define CALL_GENERIC_FUNCTION_4P(function_name,comment,P0,P1,P2,P3){function_name##_((long)P0,(long)P1,(long)P2,(long)P3);}
#define CALL_GENERIC_FUNCTION_5P(function_name,comment,P0,P1,P2,P3,P4){function_name##_((long)P0,(long)P1,(long)P2,(long)P3,(long)P4);}

// Parametres de callback

typedef struct
{
	SND_td_pfn_vSoundCallback fct;
	DWORD param;
} callback_param;

typedef struct 
{
	float fLowFreq;
	float fHighFreq;
	int iType;
	int iNbCoefs;
	BOOL bCreated;
	short * psaCoefs;
}
tdst_FilterDef;

tdst_FilterDef	g_stLowPassFilter,g_stDolbyFilter;

//___________________________________________________________________________________________________________________
//	Variables

short g_saPreviousSampleBuffer[NB_COEFS_MAX*2];
short g_saTempSampleBuffer[2000];

//___________________________________________________________________________________________________________________
// fonction de preparation des coefficients d'un filtre, en fonction de son type
//___________________________________________________________________________________________________________________

void CreateFilter(tdst_FilterDef * pFilter,int OutputFreq)
{
#ifdef SURROUND
	// Get current values of active variables
	int  center = pFilter->iNbCoefs/2,i,k;
	double sum,facteur;
	float freq1 = pFilter->fLowFreq/((float)OutputFreq/2);
	float freq2 = pFilter->fHighFreq/((float)OutputFreq/2);
	//char csOutputText[20];

    /// Prepare signals mixture

    ////////////////////////////////////////// NSP. Tone
    /// Calculate "raw" values of the filter coefficients

    // Handbook for Digital Signal Processing. ed. S.K.Mitra, J,F.Kaiser
    // John Wiley & Sons. 1993. p.176.

    //double *FFirTab=(double *)GlobalAlloc(GPTR,FirLen*sizeof(double));

    double *FFirTab=(double *)SND_fn_pvMallocSnd(pFilter->iNbCoefs*sizeof(double));

   
    switch(pFilter->iType)
    {
    case LOWPASS:
            // Lowpass filter
            FFirTab[center] = freq2;
            for(i=1; i<center; i++) 
				FFirTab[center-i] = FFirTab[center+i] = sin(NSP_PI*freq2*i)/(NSP_PI*i);
            FFirTab[0] = sin(NSP_PI*freq2*center)/(NSP_PI*center);
            break;
    case HIGHPASS:
            // Highpass filter
            FFirTab[center] = 1.0f-freq1;
            for(i=1; i<center; i++)
				FFirTab[center-i] = FFirTab[center+i] = -sin(NSP_PI*freq1*i)/(NSP_PI*i);
            FFirTab[0] = -sin(NSP_PI*freq1*center)/(NSP_PI*center);
            break;
    case BANDPASS:
            // Bandpass filter
            FFirTab[center] = (freq2-freq1);
            for(i=1; i<center; i++)
				FFirTab[center+i] = FFirTab[center-i] = ((sin(NSP_PI*freq2*i)-sin(NSP_PI*freq1*i))/(NSP_PI*i));
            FFirTab[0] = ((sin(NSP_PI*freq2*center)-sin(NSP_PI*freq1*center))/(NSP_PI*center));
            break;
    case PHASE:
            FFirTab[center] = FFirTab[0] = 0.0f;
            for(i=1; i<center; i++)
            {
				if (i % 2)
					FFirTab[center-i] = -4.0f/(NSP_PI*i);
				else
					FFirTab[center-i] = 0.0f;
				FFirTab[center+i] = -FFirTab[center-i];
            }
            break;
    case BAND_AND_PHASE:
			for(i=-center; i<center; i++)
			{
				sum=0.0f;
				for (k=-128;k<128;k++)
				{
					if ((k-i) % 2)
					{
						if (k!=0)
							sum+=(-4.0f/(NSP_PI*(k-i)))*(((sin(NSP_PI*freq2*k)-sin(NSP_PI*freq1*k))/(NSP_PI*k)));
						else 
							sum+=(-4.0f/(NSP_PI*(k-i)))*((freq2-freq1));
					}
				}
				FFirTab[center+i] = sum;
			}
            break;
    default:
            FFirTab[0] = 1.0f;
            FFirTab[center] = 1.0f;
            for(i=1; i<center; i++) 
				FFirTab[center+i] = FFirTab[center-i] = 1.0f;
    //      FFirTab[center] = 1.0f;
            //FFirTab[2*center-1] = -1.0f;
    }
    // NORMALISATION:
    // 1)calcule la Norme L1 du filtre:
    sum=0;
    for (i=0; i<pFilter->iNbCoefs; i++) 
		  sum += fabs(FFirTab[i]);
    
    // 2) On doit avoir : Norme(Filtre)*Max(Sample:32000)<Max(Resultat de la convolution:65000*32000):
    facteur = 65535.0;// / sum;
//    facteur = 32768.0;// / sum;
    pFilter->psaCoefs = (short *)SND_fn_pvMallocSnd(pFilter->iNbCoefs*sizeof(short));
//	  OutputDebugString("\nFilter coef ");
    for(i=0; i<pFilter->iNbCoefs; i++) 
	  {
		  pFilter->psaCoefs[i]=(short)(FFirTab[i]*facteur);
//		  wsprintf(csOutputText," %d ",pFilter->psaCoefs[i]);
//		  OutputDebugString(csOutputText);
	  }

	pFilter->bCreated = TRUE;
    SND_fn_vFreeSnd(FFirTab);
#endif
}

void DestroyFilter(tdst_FilterDef Filter)
{
	SND_fn_vFreeSnd(Filter.psaCoefs);
	Filter.psaCoefs = NULL;
	Filter.bCreated = FALSE;
}

#ifdef FILTER_AFTER_INTERPOLATION
void Convolution_(long bi,long bo,long F,int nbSamplesToProvide)
{
  short * buffer_input = (short *)bi;
  short * buffer_output = (short *)bo;
  tdst_FilterDef * pFilter = (tdst_FilterDef *) F;
  
	int iIndexSample,iIndexCoef;
	int iLeftSample,iRightSample;
	int iSigmaCoef = 0;
	int iNbCoefs = pFilter->iNbCoefs;
	short * pFilterCoef = pFilter->psaCoefs;

	for (iIndexCoef = 0;iIndexCoef < iNbCoefs;iIndexCoef++)
		iSigmaCoef += pFilterCoef[iIndexCoef];

	for (iIndexSample = 0;iIndexSample < nbSamplesToProvide;iIndexSample++)
	{
		iLeftSample = iRightSample = 0;
		for (iIndexCoef = 0;iIndexCoef < iNbCoefs;iIndexCoef++)
		{
			if (iIndexCoef + iIndexSample < iNbCoefs)
			{
				iLeftSample += g_saPreviousSampleBuffer[2*(iIndexCoef+iIndexSample)] * pFilterCoef[iIndexCoef];
				iRightSample += g_saPreviousSampleBuffer[2*(iIndexCoef+iIndexSample)+1] * pFilterCoef[iIndexCoef];
			}
			else
			{
				iLeftSample += buffer_input[2*(iIndexSample-(iNbCoefs-iIndexCoef))] * pFilterCoef[iIndexCoef];
				iRightSample += buffer_input[2*(iIndexSample-(iNbCoefs-iIndexCoef))+1] * pFilterCoef[iIndexCoef];
			}
		}
		iLeftSample /= iSigmaCoef;
		buffer_output[2*iIndexSample] = (short)iLeftSample;
		iRightSample /= iSigmaCoef;
		buffer_output[2*iIndexSample+1] = (short)iRightSample;
	}
}

#endif

#ifdef INTERPOLATION_C
/*  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    
    void Interpolation_f_vers_2f(short * buffer_in,short * buffer_out,int nbSamplesToProvide)
    
    From one sample make two sample, making a linear interpolation, 
    output sample = (input sample 1 + input sample 2) / 2
    That for each voice, left and right. NbSamplesToProvide is the number of sample needed in output.

    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/
void Interpolation_f_vers_2f(short * buffer_in,short * buffer_out,int nbSamplesToProvide)
{
	int iIndexSampleIn,iIndexSampleOut;

	short sCurrentLeftSample,sCurrentRightSample;
	short sNextLeftSample1,sNextRightSample1;
	short sNextLeftSample2,sNextRightSample2;
	short sLeftDeltaNext1,sRightDeltaNext1;
	short sLeftDeltaNext2,sRightDeltaNext2;

#define OFFSET_INTERPOLATION	25

	for (iIndexSampleIn = 0,iIndexSampleOut = 0;iIndexSampleOut < nbSamplesToProvide;iIndexSampleIn+=2,iIndexSampleOut+=4)
	{
		sCurrentLeftSample = buffer_in[iIndexSampleIn];
		sCurrentRightSample = buffer_in[iIndexSampleIn+1];
		sNextLeftSample1 = iIndexSampleOut < nbSamplesToProvide-2 ? buffer_in[iIndexSampleIn+2] : sCurrentLeftSample;
		sNextRightSample1 = iIndexSampleOut < nbSamplesToProvide-2 ? buffer_in[iIndexSampleIn+3] : sCurrentRightSample;
		sNextLeftSample2 = iIndexSampleOut < nbSamplesToProvide-4 ? buffer_in[iIndexSampleIn+4] : sCurrentLeftSample;
		sNextRightSample2 = iIndexSampleOut < nbSamplesToProvide-4 ? buffer_in[iIndexSampleIn+5] : sCurrentRightSample;

		sLeftDeltaNext1 = sNextLeftSample1 - sCurrentLeftSample;
		sRightDeltaNext1 = sNextRightSample1 - sCurrentRightSample;
		sLeftDeltaNext2 = sNextLeftSample2 - sNextLeftSample1;
		sRightDeltaNext2 = sNextRightSample2 - sNextRightSample1;

		buffer_out[iIndexSampleOut] = sCurrentLeftSample;
		buffer_out[iIndexSampleOut+1] = sCurrentRightSample;

		if (sLeftDeltaNext1 > sLeftDeltaNext2)
		{
			buffer_out[iIndexSampleOut+2] = (sNextLeftSample1 + sCurrentLeftSample)/2 - OFFSET_INTERPOLATION;
		}
		else
		{
			buffer_out[iIndexSampleOut+2] = (sNextLeftSample1 + sCurrentLeftSample)/2 + OFFSET_INTERPOLATION;
		}
		if (sRightDeltaNext1 > sRightDeltaNext2)
		{
			buffer_out[iIndexSampleOut+3] = (sNextRightSample1 + sCurrentRightSample)/2 - OFFSET_INTERPOLATION;
		}
		else
		{
			buffer_out[iIndexSampleOut+3] = (sNextRightSample1 + sCurrentRightSample)/2 + OFFSET_INTERPOLATION;
		}
	}
}
#endif

#ifdef REVERB
/*  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    
    void InitReverb(void)
    void DesInitReverb(void)
    Create a buffer to save sample reverb historic.Free this buffer.

    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/
void InitReverb(void)
{
  DataBufferReverb=(void*)SND_fn_pvMallocSnd(2*taille_buffer+8);
  memset(DataBufferReverb,0,2*taille_buffer);
  DataBufferReverbMax=(void*)((int)DataBufferReverb+2*taille_buffer);
}

void DesInitReverb(void)
{
  SND_fn_vFreeSnd(DataBufferReverb);
}
#endif //REVERB 


//___________________________________________________________________________________________________________________
//	Fonctions de gestion des callback
//	thread lancée à chaque occurence de callback
//	int WINAPI start_callback_thread(DWORD *param)

#if defined(__WATCOMC__) && (__WATCOMC__<1100)
void start_callback_thread(callback_param *callback_par)
#else
void __cdecl start_callback_thread(callback_param *callback_par)
#endif
{
	(callback_par->fct)(callback_par->param);
	GlobalFree(callback_par);
}

//	fct appelée par genere en cas de Callback
//	*buffer_dispo dans esi
void __stdcall start_callback(BUFFER_DISPO *buffer)
{
	callback_param *callback_par;
	callback_par = GlobalAlloc(GMEM_FIXED,sizeof(callback_param));
	callback_par->fct = buffer->fct_callback;
	callback_par->param = buffer->param_callback;

#if defined(__WATCOMC__) && (__WATCOMC__<1100)
  _beginthread(start_callback_thread,NULL,0,(void*)callback_par);
#else
  _beginthread(start_callback_thread,0,(void*)callback_par);
#endif
}

#ifdef FILTER_AFTER_INTERPOLATION

/*  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    
    void FilterStereoBuffer(short * pInputBuffer,void * pOutputBuffer,tdst_Filter * pFilter,
                            unsigned long iNbBytesToProvide)
    
    Take a stereo buffer in input (which could not be the output buffer) apply the pFilter filter on the 
    input buffer and giev the result in the output buffer pOutputBuffer.

    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/
void FilterStereoBuffer(short * pInputBuffer,void * pOutputBuffer,tdst_FilterDef * pFilter,unsigned long ulNbBytesToProvide)
{
#ifdef FILTER_C
  short * pBufferTemp;
	if (pFilter->bCreated)
	{
		pBufferTemp	= (short *)pInputBuffer + (ulNbBytesToProvide/2) - pFilter->iNbCoefs*2;
		memcpy(g_saPreviousSampleBuffer,pBufferTemp,pFilter->iNbCoefs*4);
    CALL_GENERIC_FUNCTION_PERFORMANCE_4P(Convolution,"Convolution pour le filtrage en C",pInputBuffer,pOutputBuffer,(long)&pFilter,ulNbBytesToProvide/4);
  }
#else
	if (pFilter->bCreated)
    CALL_GENERIC_FUNCTION_PERFORMANCE_5P(CONVOLUTION_MMX_STEREO,"Convolution pour le filtrage en MMX",pInputBuffer,pOutputBuffer,pFilter->psaCoefs,pFilter->iNbCoefs,ulNbBytesToProvide);
#endif
}
#endif

/*  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    
    void MixStereoBuffer(BUFFER_DISPO * pBuffer,void * pOutputBuffer,unsigned long iNbBytesToProvide)
    
    Take a stereo sample struct in input, mix the stereo sample with the output buffer pOutputBuffer, this
    code write iNbBytesToProvide in the output buffer. This code is called for each stereo sound to be mix.
    If the pitch is known as specific value (as 0.5,1,2), a specific assembly routine is called, else the 
    generic pitching routine is called. A filtering work could be added to the sound before mixing it.

    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/
void MixStereoBuffer(BUFFER_DISPO * pBuffer,void * pOutputBuffer,unsigned long ulNbBytesToProvide)
{
  BOOL bCouldApplyFilter = g_stLowPassFilter.bCreated;

  if (!pBuffer->pitchable)
    switch (pBuffer->inc_sign)
    {
    case 0x80:// on mixe une source de frequence moitie de celle de restitution
#ifdef FILTER_AFTER_INTERPOLATION
	    MIX_STEREO_PERFORMANCE(MixVolPanStereo_f_vers_2f,"MixVolPanStereo_f_vers_2f (pitch 0.5) avec interpolation",pBuffer,g_saTempSampleBuffer,ulNbBytesToProvide,0,!bCouldApplyFilter);
      FilterStereoBuffer(g_saTempSampleBuffer,pOutputBuffer,&g_stLowPassFilter,ulNbBytesToProvide);
#else
	    MIX_STEREO_PERFORMANCE(MixVolPanStereo_f_vers_2f,"MixVolPanStereo_f_vers_2f (pitch 0.5) avec interpolation",pBuffer,pOutputBuffer,ulNbBytesToProvide,0,TRUE);
#endif
	    break;
  
    case 0x100:// on mixe une source et une restitution a la meme frequence
	    MIX_STEREO_PERFORMANCE(MixVolPanStereo,"MixVolPanStereo (pitch 1)",pBuffer,pOutputBuffer,ulNbBytesToProvide,0,TRUE);
      break;
  
    case 0x200:// on mixe une source de frequence double de celle de restitution
	    MIX_STEREO_PERFORMANCE(MixVolPanStereo_2f_vers_f,"MixVolPanStereo_2f_vers_f (pitch 2)",pBuffer,pOutputBuffer,ulNbBytesToProvide,0,TRUE);
      break;
  
    default:// on mixe une source de frequence non multiple de celle de restitution
  
      // si le pitch est superieur a 4 ou inferieur a 1/2 =, on filtre le resultat 
#ifdef FILTER_AFTER_INTERPOLATION
      if ((pBuffer->inc_sign > 0x400) || (pBuffer->inc_sign < 0x80))
      {
 		    MIX_STEREO_PERFORMANCE(MixVolPanPitchStereo,"MixVolPanPitchStereo (pitch quelconque)",pBuffer,g_saTempSampleBuffer,ulNbBytesToProvide,0,!bCouldApplyFilter);
        FilterStereoBuffer(g_saTempSampleBuffer,pOutputBuffer,&g_stLowPassFilter,ulNbBytesToProvide);
      }
      else
  		    MIX_STEREO_PERFORMANCE(MixVolPanPitchStereo,"MixVolPanPitchStereo (pitch quelconque)",pBuffer,pOutputBuffer,ulNbBytesToProvide,0,TRUE);
#else
	    MIX_STEREO_PERFORMANCE(MixVolPanPitchStereo,"MixVolPanPitchStereo (pitch quelconque)",pBuffer,pOutputBuffer,ulNbBytesToProvide,0,TRUE);
#endif
  
      break;
    }
  else
    MIX_STEREO_PERFORMANCE(MixVolPanPitchStereo,"MixVolPanPitchStereo (pitch quelconque)",pBuffer,pOutputBuffer,ulNbBytesToProvide,0,TRUE);
}

/*  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    
    void MixMonoBuffer(BUFFER_DISPO * pBuffer,void * pOutputBuffer,unsigned long iNbBytesToProvide)
    
    Take a mono sample struct in input, mix the mono sample with the output buffer pOutputBuffer, this
    code write iNbBytesToProvide in the output buffer. This code is called for each mono sound to be mix.
    If the pitch is known as specific value (as 0.5,1,2), a specific assembly routine is called, else the 
    generic pitching routine is called. A filtering work could be added to the sound before mixing it.

    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/
void MixMonoBuffer(BUFFER_DISPO * pBuffer,void * pOutputBuffer,unsigned long ulNbBytesToProvide)
{
  BOOL bCouldApplyFilter = g_stLowPassFilter.bCreated;

  if (!pBuffer->pitchable)
	  switch (pBuffer->inc_sign)
	  {
	  case 0x80:// on mixe une source de frequence moitie de celle de restitution
  #ifdef FILTER_AFTER_INTERPOLATION
      MIX_MONO_PERFORMANCE(MixVolPan_f_vers_2f,"MixVolPan_f_vers_2f (pitch 0.5) avec interpolation",pBuffer,g_saTempSampleBuffer,ulNbBytesToProvide,0,!bCouldApplyFilter);
		  FilterStereoBuffer(g_saTempSampleBuffer,(short *)pOutputBuffer,&g_stLowPassFilter,ulNbBytesToProvide);
  #else
		  MIX_MONO_PERFORMANCE(MixVolPan_f_vers_2f,"MixVolPan_f_vers_2f (pitch 0.5) avec interpolation",pBuffer,pOutputBuffer,ulNbBytesToProvide,0,TRUE);
  #endif
		  break;
	  case 0x100:// on mixe une source et une restitution a la meme frequence
		  MIX_MONO_PERFORMANCE(MixVolPan,"MixVolPan (pitch 1)",pBuffer,pOutputBuffer,ulNbBytesToProvide,0,TRUE);break;
	  case 0x200:// on mixe une source de frequence double de celle de restitution
		  MIX_MONO_PERFORMANCE(MixVolPan_2f_vers_f,"MixVolPan_2f_vers_f (pitch 2)",pBuffer,pOutputBuffer,ulNbBytesToProvide,0,TRUE);break;
	  default:// on mixe une source de frequence non multiple de celle de restitution
  #ifdef FILTER_AFTER_INTERPOLATION
      if ((pBuffer->inc_sign > 0x400) || (pBuffer->inc_sign < 0x80))
      {
    		  MIX_MONO_PERFORMANCE(MixVolPanPitch1,"MixVolPanPitch1 (pitch quelconque)",pBuffer,g_saTempSampleBuffer,ulNbBytesToProvide,0,!bCouldApplyFilter);
				  FilterStereoBuffer(g_saTempSampleBuffer,(short *)pOutputBuffer,&g_stLowPassFilter,ulNbBytesToProvide);
		  }
      else
    	  MIX_MONO_PERFORMANCE(MixVolPanPitch1,"MixVolPanPitch1 (pitch quelconque)",pBuffer,pOutputBuffer,ulNbBytesToProvide,0,TRUE);break;
  #else
		  MIX_MONO_PERFORMANCE(MixVolPanPitch1,"MixVolPanPitch1 (pitch quelconque)",pBuffer,pOutputBuffer,ulNbBytesToProvide,0,TRUE);break;
  #endif
	  }
  else
	  MIX_MONO_PERFORMANCE(MixVolPanPitch1,"MixVolPanPitch1 (pitch quelconque)",pBuffer,pOutputBuffer,ulNbBytesToProvide,0,TRUE);;
}

/*  @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

    void MMX_genere_buffer(void * pOutputBuffer,unsigned long ulNbBytesToProvide)
    
    Mix all sounds contained in the voices list, corresponding to the active sounds, hear or not hear.
  	pOutputBuffer is the output buffer and the function writes ulNbBytesToProvide bytes in that buffer.

    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/
void MMX_genere_buffer(void * pOutputBuffer,unsigned long iNbBytesToProvide)
{
  BUFFER_DISPO *buffer,B0;
  int nbVoiesActives=0,nbVoiesReverb=0,VolumeTotal=0,nbVoiesJouees=0,nbVoiesMixees=0;//int taille_sample = 0;
  BOOL anti_parasitage = FALSE;

// init de la fréquence processeur pour l'évaluation temps machine 
#ifdef MIX_WITH_QUERYPERFORMANCE
  QueryPerformanceFrequency(&TempsFrequence);
  g_dFreq = ADD64(TempsFrequence,TempsFrequence) / 2.0;
#endif

  NBVM = 0;
  buffer = buffer_dispo_min;

  // remise a zero du buffer de sortie avant mix
  CALL_GENERIC_FUNCTION_3P(MMX_memset,"MemSet du buffer de sortie",pOutputBuffer,0,iNbBytesToProvide);

// TEST DU NOMBRE D'OCTETS MULTIPLE DE SEIZE 
  if (iNbBytesToProvide % 16)  
	  nbVoiesReverb ++;

  B0 = *buffer;
// mix des n voies utiles

  do // # BCL0
  {
// Gestion des bruitages joues
    if (iNbBytesToProvide && buffer->is_playing && !buffer->paused) // # BCL1
    {
// Gestion des bruitages reellement mixes                                                                                          
      if (buffer->is_mixing)
      {
        VolumeTotal += buffer->vol;
        NBVM++;
// Le son est en train d'etre joue et doit etre mixe (volume suffisant)
        if (buffer->pitchable) // # BCL2
        {
          if (Sound_Global_Effect == LOCAL)  // # BCL3
          {
            switch  (buffer->dynamic_effects)
            {
              case 0x0:
                MIX_MONO_PERFORMANCE(MixVolPanPitch1,"MixVolPanPitch1 (pitch quelconque)",buffer,pOutputBuffer,iNbBytesToProvide,0,TRUE);
                nbVoiesActives++;
                break;
              case 0x1:
                MIX_MONO_PERFORMANCE(MixVolPanPitch1,"MixVolPanPitch1 (pitch quelconque)",buffer,pOutputBuffer,iNbBytesToProvide,1,TRUE);
                nbVoiesActives++;nbVoiesReverb=1;
                break;
              case 0x100:
                MIX_MONO_PERFORMANCE(MixVolPanPitchFlange,"MixVolPanPitchFlange",buffer,pOutputBuffer,iNbBytesToProvide,nbVoiesActives,TRUE);
                break;
            }
          }
          else
          {
			      if (buffer->stereo)
              MixStereoBuffer(buffer,pOutputBuffer,iNbBytesToProvide);
			      else
              MixMonoBuffer(buffer,pOutputBuffer,iNbBytesToProvide);
            nbVoiesActives++;
          } // # BCL3
        }
        else // # BCL2
        {
          if (Sound_Global_Effect == LOCAL)
          {
            switch  (buffer->dynamic_effects)
            {
              case 0x0:
                MIX_MONO_PERFORMANCE(MixVolPan,"MixVolPan (pitch 1)",buffer,pOutputBuffer,iNbBytesToProvide,0,TRUE);
                break;
              case 0x1:
                MIX_MONO_PERFORMANCE(MixVolPan,"MixVolPan (pitch 1)",buffer,pOutputBuffer,iNbBytesToProvide,0,TRUE);
                nbVoiesReverb=1;
                break;
              case 0x100:
                MIX_MONO_PERFORMANCE(MixVolPan,"MixVolPan (pitch 1)",buffer,pOutputBuffer,iNbBytesToProvide,0,TRUE);
                break;
            }
          }
          else
          {
			      if (buffer->stereo)
              MixStereoBuffer(buffer,pOutputBuffer,iNbBytesToProvide);
			      else
              MixMonoBuffer(buffer,pOutputBuffer,iNbBytesToProvide);
            nbVoiesActives++;
          }
        } // # BCL2
      } 
      else
      // Le son est en train d'etre joue et ne doit pas etre mixe (volume insuffisant)
      {
        buffer->pt_sign+=(iNbBytesToProvide/2)*buffer->inc_sign;

        if (buffer->pt_sign >= buffer->pt_max)
        if (buffer->must_looping)
          buffer->pt_sign=buffer->pt_loop;
        else
          buffer->is_playing=FALSE;
      }

	    //--le sample s'est-il terminé?
	    if ((!buffer->is_playing) && (buffer->waiting_callback))
	    {//le buffer jouait, ne joue plus -> fini
		    buffer->waiting_callback=FALSE;
		    start_callback(buffer);
	    }
    } // # BCL1
  } // # BCL0
  while ((++buffer)<buffer_dispo_max);

// Le seuil de detection de parasite est tel que :
// La somme des volumes divise par le nombre de voies mixees est
// multiplie par le SEUIL_PARASITE calcule pour un volume moyen de 64

  if (NBVM)
    VolumeTotal /= NBVM;

// toutes les voies sont mixees .. on applique les effets globaux ,
// ou on mixe les buffers separes effets/nu

  if (iNbBytesToProvide)
    switch (Sound_Global_Effect)
    {
    case GREVERB1   :   CALL_GENERIC_FUNCTION_PERFORMANCE_3P(GlobalReverb,"Reverb type 1",pOutputBuffer,iNbBytesToProvide,0);break;
    case GREVERB2   :   CALL_GENERIC_FUNCTION_PERFORMANCE_3P(GlobalReverb,"Reverb type 2",pOutputBuffer,iNbBytesToProvide,1);break;
    case GREVERB3   :   CALL_GENERIC_FUNCTION_PERFORMANCE_3P(GlobalReverb,"Reverb type 3",pOutputBuffer,iNbBytesToProvide,2);break;
    case GREVERB4   :   CALL_GENERIC_FUNCTION_PERFORMANCE_3P(GlobalReverb,"Reverb type 4",pOutputBuffer,iNbBytesToProvide,3);break;
    case GFLANGE    :   CALL_GENERIC_FUNCTION_PERFORMANCE_3P(GlobalFlange,"Flange",pOutputBuffer,iNbBytesToProvide,0);break;
    case LOCAL      :   if (nbVoiesReverb)
                          CALL_GENERIC_FUNCTION_PERFORMANCE_2P(MixBuffers,"Mix des buffers d'effets",pOutputBuffer,iNbBytesToProvide);
    case NO_EFFECT  :   default :break;
    }
}

#ifdef SURROUND

//___________________________________________________________________________________________________________________
//	Calcul de la suite du buffer en mixage dolby surround
//	Entrées:pOutputBuffer=adresse cible
//	iNbBytesToProvide=nb d'octets à ecrire en sortie
//	Sortie:néant
//	Appelé par SynchroSnd()
//___________________________________________________________________________________________________________________
void MMX_genere_buffer_dolby(void * pOutputBuffer,unsigned long iNbBytesToProvide)
{
  BUFFER_DISPO *buffer,B0;
  int nbVoiesActives = 0;
  int nbVoiesReverb = 0;
  int VolumeTotal = 0;
  int nbVoiesJouees = 0;
  int nbVoiesMixees = 0;

  BOOL bFiltrage = TRUE;
  BOOL anti_parasitage = FALSE;

  NBVM = 0;
  buffer = buffer_dispo_min;

  // remise a zero du buffer de sortie avant mix
  CALL_GENERIC_FUNCTION_PERFORMANCE_3P(MMX_memset,"MemSet du buffer de sortie",pOutputBuffer,0,iNbBytesToProvide);
  // remise a zero du buffer de sortie avant mix
  CALL_GENERIC_FUNCTION_PERFORMANCE_2P(ResetBufferSurround,"Mise a zero du buffer de surround",(long)((int)pOutputBuffer-(int)DataBuffer)/2,iNbBytesToProvide/2);

  /* TEST DU NOMBRE D'OCTETS MULTIPLE DE SEIZE */
  if (iNbBytesToProvide % 16)   
	  nbVoiesReverb ++;

  B0 = *buffer;
  // mix des n voies utiles

  do // # BCL0
  {
    if (iNbBytesToProvide && buffer->is_playing) // # BCL1
    {
      if (buffer->is_mixing)
	    {
        VolumeTotal += buffer->vol;
        NBVM++;
        // Le son est en train d'etre joue et doit etre mixe (volume suffisant)
        if (buffer->pitchable) // # BCL2
        {
          if (Sound_Global_Effect == LOCAL)  // # BCL3
            switch  (buffer->dynamic_effects)
            {
              case 0x0:MIX_MONO_PERFORMANCE(MixVolPanPitch1,"MixVolPanPitch1 (pitch quelconque)",buffer,pOutputBuffer,iNbBytesToProvide,0,TRUE);nbVoiesActives++;break;
              case 0x1:MIX_MONO_PERFORMANCE(MixVolPanPitch1,"MixVolPanPitch1 (pitch quelconque)",buffer,pOutputBuffer,iNbBytesToProvide,0,TRUE);nbVoiesActives++;nbVoiesReverb=1;break;
              case 0x100:MIX_MONO_PERFORMANCE(MixVolPanPitchFlange,"MixVolPanPitchFlange",buffer,pOutputBuffer,iNbBytesToProvide,nbVoiesActives,TRUE);break;
            }
            else
            {
						  if (buffer->stereo)
                MixStereoBuffer(buffer,pOutputBuffer,iNbBytesToProvide);
							else
                MIX_MONO_DOLBY_PERFORMANCE(MixVolPanPitchDolby1,"MixVolPanPitchDolby (pitch quelconque)",buffer,pOutputBuffer,iNbBytesToProvide,0,(long)((int)pOutputBuffer-(int)DataBuffer)/2,TRUE);break;
							nbVoiesActives++;
            } // # BCL3
          }
          else // # BCL2
          {
            if (Sound_Global_Effect == LOCAL)
            {
              switch  (buffer->dynamic_effects)
              {
                case 0x0://MixVolPanDolby((long *)buffer,(long *)pOutputBuffer,(long)iNbBytesToProvide,0,(long)((int)pOutputBuffer-(int)DataBuffer)/2);break;
                case 0x1://MixVolPanDolby((long *)buffer,(long *)pOutputBuffer,(long)iNbBytesToProvide,0,(long)((int)pOutputBuffer-(int)DataBuffer)/2);nbVoiesReverb=1;break;
                case 0x100://MixVolPanDolby((long *)buffer,(long *)pOutputBuffer,(long)iNbBytesToProvide,0,(long)((int)pOutputBuffer-(int)DataBuffer)/2);break;
                  MIX_MONO_DOLBY_PERFORMANCE(MixVolPanDolby,"MixVolPanDolby ",buffer,(long *)pOutputBuffer,(long)iNbBytesToProvide,0,(long)((int)pOutputBuffer-(int)DataBuffer)/2,TRUE);break;
							}
            }
            else
            {
						  if (buffer->stereo)
                MixStereoBuffer(buffer,pOutputBuffer,iNbBytesToProvide);
							else
                MIX_MONO_DOLBY_PERFORMANCE(MixVolPanDolby,"MixVolPanDolby (pitch constant 1)",buffer,pOutputBuffer,iNbBytesToProvide,0,(long)((int)pOutputBuffer-(int)DataBuffer)/2,TRUE);break;
              nbVoiesActives++;
            }
          } // # BCL2
        } 
        else
        // Le son est en train d'etre joue et ne doit pas etre mixe (volume insuffisant)
        {
          buffer->pt_sign+=(iNbBytesToProvide/4)*buffer->inc_sign;

          if (buffer->pt_sign > buffer->pt_max)
            if (buffer->must_looping)
              buffer->pt_sign=buffer->pt_loop;
            else
              buffer->is_playing=0;
        }
				  
			  //--le sample s'est-il terminé?
			  if ((!buffer->is_playing) && (buffer->waiting_callback))
			  {//le buffer jouait, ne joue plus -> fini
				  start_callback(buffer);
			  }
          }// # BCL1
  } // # BCL0
  while ((++buffer)<buffer_dispo_max);

// toutes les voies sont mixees .. on applique les effets globaux ,
// ou on mixe les buffers separes effets/nu

  if (iNbBytesToProvide)
    switch (Sound_Global_Effect)
    {
    case GREVERB1 : CALL_GENERIC_FUNCTION_PERFORMANCE_3P(GlobalReverb,"Reverb type 1",pOutputBuffer,iNbBytesToProvide,0);break;
    case GREVERB2 :   CALL_GENERIC_FUNCTION_PERFORMANCE_3P(GlobalReverb,"Reverb type 2",pOutputBuffer,iNbBytesToProvide,1);break;
    case GREVERB3 :   CALL_GENERIC_FUNCTION_PERFORMANCE_3P(GlobalReverb,"Reverb type 3",pOutputBuffer,iNbBytesToProvide,2);break;
    case GREVERB4 :   CALL_GENERIC_FUNCTION_PERFORMANCE_3P(GlobalReverb,"Reverb type 4",pOutputBuffer,iNbBytesToProvide,3);break;
    case GFLANGE  :   CALL_GENERIC_FUNCTION_PERFORMANCE_3P(GlobalFlange,"Flange",pOutputBuffer,iNbBytesToProvide,0);break;
    case LOCAL    :   if (nbVoiesReverb)
                        CALL_GENERIC_FUNCTION_PERFORMANCE_2P(MixBuffers,"Mix des buffers d'effets",pOutputBuffer,iNbBytesToProvide);
    case NO_EFFECT  :   default :break;
    }
 
// Les voies sont mixees, on applique le filtre pour le dolby-surround
  if ((NBVM) && bFiltrage)
	  CALL_GENERIC_FUNCTION_PERFORMANCE_4P(FIR_MMX,"Filtrage dolby",pOutputBuffer,g_stDolbyFilter.psaCoefs,iNbBytesToProvide,(long)((int)pOutputBuffer-(int)DataBuffer)/2);
}

#endif


//___________________________________________________________________________________________________________________
//  Antiparasitage lors du changement brusque de sample dans
//  la gestion du split pour le bruitage du moteur
//  (travail sur le buffer de sortie (tableau de short)
//  Entrée: néant
//  Sortie: néant
//	Appelé depuis le menu principal
//___________________________________________________________________________________________________________________
void AntiParasite(void * pOutputBuffer,unsigned long iNbBytesToProvide,long SeuilParasite)
{
  short * ptrtstgauche;
  short * ptrtstdroite;
  short * ptrtstprecgauche;
  short * ptrtstprecdroite;
  unsigned long nb_samples_to_recalc,i;
  short offset_correction,offset_correction_total;
  short nb_decalages,delta;

  int type = ANTI_PARASITAGE_PONDERE;

  if (pOutputBuffer != DataBuffer)
  {
    ptrtstprecgauche = (short *)((int)pOutputBuffer-4);
    ptrtstprecdroite = (short *)((int)pOutputBuffer-2);
  }
  else
  {
    ptrtstprecgauche = (short *)((int)DataBuffer+taille_buffer-4);
    ptrtstprecdroite = (short *)((int)DataBuffer+taille_buffer-2);
  }

  ptrtstgauche = (short *)pOutputBuffer;
  ptrtstdroite = (short *)(((int)pOutputBuffer)+4);

  delta = (short)labs((int)(*ptrtstprecgauche-*ptrtstgauche));

  // on ne descend pas trop bas en seuil de volume parasite
  if (SeuilParasite < SEUIL_PARASITE/16)
  SeuilParasite = SEUIL_PARASITE/16;

  if (delta > SeuilParasite)
  {
    nb_samples_to_recalc = 32;//128;//256;
    nb_decalages = 5;

    reessaye0:

    if (nb_decalages)
    {
      if (iNbBytesToProvide > nb_samples_to_recalc*4)
      {
        switch (type)
        {
        case ANTI_PARASITAGE_PONDERE :
          for (i=1;i<nb_samples_to_recalc;i++)
          {
            *ptrtstgauche = (short)(((int)(*ptrtstgauche*i) + (int)(*ptrtstprecgauche*(nb_samples_to_recalc-i))) >> nb_decalages);
            ptrtstgauche++;ptrtstgauche++;
          }
          break;

        case ANTI_PARASITAGE_LINEAIRE :

          offset_correction = (short)(*((short *)(((int)ptrtstgauche)+(nb_samples_to_recalc*4)))-*ptrtstprecgauche);
          offset_correction >>= nb_decalages;
          offset_correction_total = offset_correction;
          for (i=1;i<nb_samples_to_recalc;i++)
          {
            *ptrtstgauche = (short)(*ptrtstprecgauche + offset_correction_total);
            offset_correction_total += offset_correction;
            ptrtstgauche++;ptrtstgauche++;
          }
          break;
        }
      }
      else
      {
        nb_samples_to_recalc /= 2;
        nb_decalages--;
        goto reessaye0;
      }
    }
  }

  delta = (short)labs((int)(*ptrtstprecdroite-*ptrtstdroite));

  if (delta >  SeuilParasite)
  {
    nb_samples_to_recalc = 32;//256;
    nb_decalages = 5;

    reessaye1:

    if (nb_decalages)
    {
      if (iNbBytesToProvide > nb_samples_to_recalc*4)
      {
        switch (type)
        {
          case ANTI_PARASITAGE_PONDERE :
            for (i=1;i<nb_samples_to_recalc;i++)
            {
              *ptrtstdroite = (short)(((int)(*ptrtstdroite*i) + (int)(*ptrtstprecdroite*(nb_samples_to_recalc-i))) >> nb_decalages);
              ptrtstdroite++;ptrtstdroite++;
            }
            break;

          case ANTI_PARASITAGE_LINEAIRE :
            offset_correction = (short)(*(short *)((int)ptrtstdroite+nb_samples_to_recalc*4)-*ptrtstprecdroite);
            offset_correction >>= nb_decalages;
            offset_correction_total = offset_correction;

            for (i=1;i<nb_samples_to_recalc;i++)
            {
              *ptrtstdroite = (short)(*ptrtstprecdroite + offset_correction_total);
              offset_correction_total += offset_correction;
              ptrtstdroite++;ptrtstdroite++;
            }
            break;
        }
      }
      else
      {
        nb_samples_to_recalc /= 2;
        nb_decalages--;
        goto reessaye1;
      }
    }
  }
}



//___________________________________________________________________________________________________________________
//	Inits spécifiques au Surround
//___________________________________________________________________________________________________________________
void InitSurround(void)
{
#ifdef FILTER_AFTER_INTERPOLATION
	g_stLowPassFilter.fLowFreq = 0.0f;
	g_stLowPassFilter.fHighFreq = (float)g_stFormatOutput.nSamplesPerSec/4;
	g_stLowPassFilter.iNbCoefs = 32;
	g_stLowPassFilter.iType = LOWPASS;
	g_stLowPassFilter.bCreated = FALSE;
	CreateFilter(&g_stLowPassFilter,g_stFormatOutput.nSamplesPerSec);
#endif
#ifdef SURROUND
	g_stDolbyFilter.fLowFreq = 100.0f;
	g_stDolbyFilter.fHighFreq = 7000.0f;
	g_stDolbyFilter.iNbCoefs = 32;
	g_stDolbyFilter.iType = BAND_AND_PHASE;
	g_stDolbyFilter.bCreated = FALSE;
	CreateFilter(&g_stDolbyFilter,g_stFormatOutput.nSamplesPerSec);
#endif
}
//___________________________________________________________________________________________________________________
//	Desinits spécifiques au Surround
//___________________________________________________________________________________________________________________
void DesInitSurround(void)
{
#ifdef FILTER_AFTER_INTERPOLATION
	DestroyFilter(g_stLowPassFilter);
#endif
#ifdef SURROUND
	DestroyFilter(g_stDolbyFilter);
#endif
}


#endif //DISABLE_WAVE

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


//-----------------------------------------------------
//	Retourne le label désignant la target associée à la DLL
//	Entrée:name=cible de 256 char
//-----------------------------------------------------
void SND_fn_vGetTargetLabelSample(char* name,int nb_char)
{
	strncpy(name,"mw",nb_char);
}

void SND_fn_vSetupTargetSample(void)
{
	//Erm_fn_iMessageBox(HWND_DESKTOP,"WinMM for MMX version","Sound Module",MB_OK);
	//rien
}

SndBool SND_fn_bCanSetupTargetSample(void)
{
	return TRUE;
}


#include "sndwave.cxx"
