/*
//////////////////////////////////////////////////////////////////////
//      Module SNDSPACE.CXX: converions API3D -> API2D                                  //
//////////////////////////////////////////////////////////////////////
*/
 
#include "SND\snddef.h"
 
#include "sndinc.h"
 
#include "snd.h"
 
#include "sndspace.h"
#include "sndvect.h"
 
#if defined(_DLL_COMPILATION_MODE)
#include "snddll.h"
#endif
 
#define CELERITE_SON 300 /*int exprime en m/s*/
 
static long g_lSoundSpeed=CELERITE_SON;/*m/s*/
static SndReal g_rDistSatur=0x80000;
static SndReal g_rDistFade=8*0x80000;
static SndReal g_rDistBackGround=C_ROLLOFFPARAM_INFINITE;
static unsigned char ucVolumeBackGround=0;
 
/*	Cas A					Cas B*/
/*^						^*/
/*!						!*/
/*!---+\				!----+\							.*/
/*!   . \				!	 .	\						.*/
/*!	  .	 \				!	 .	  \						.*/
/*!   .   \				!	 .		\+----------*/
/*!   .     \			!	 .		  \					.*/
/*+---------+-->		+---------------+-------*/
/*	  .	    .				 .		 .  .*/
/*DistSatur .		  DistSatur		 .  DistFade*/
/*		DistFade			DistBackGround*/
 
 
 
 
/*------------------------------------------------------------------------------*/
/*	SetDistanceForVolumae:regle les par. permettant de convertir une distance en volume*/
/*	Entrees:dist_satur=distance jusqu'à laquelle le volume est maxi*/
/*		dist_back=distance à partir de laquelle le volume est constant apres le fade*/
/*			constante particuliere:C_ROLLOFFPARAM_INFINITE pour l'infini*/
/*	Sortie: neant*/
/*	Noat: entre dist_satur et dist_fade, le volume decroit lineairement*/
/*--------------------------------------------------------------------------------*/
void SND_CALL SND_fn_vSetDistanceForVolume(SndReal dist_satur,SndReal dist_back)
{
	g_rDistSatur=dist_satur;
	g_rDistFade=8*g_rDistSatur;
	g_rDistBackGround=dist_back;
 
	if (g_rDistBackGround<g_rDistSatur)
		g_rDistBackGround=C_ROLLOFFPARAM_INFINITE;
 
	if (g_rDistBackGround>=g_rDistFade)
		ucVolumeBackGround=C_SOUNDVOL_MINI;
	else
		ucVolumeBackGround=(unsigned char)SND_fn_rMulRealRealSnd(C_SOUNDVOL_MAXI,SND_fn_rDivRealRealSnd(g_rDistFade-g_rDistBackGround,g_rDistFade-g_rDistSatur));
}
 
void SND_CALL SND_fn_vSetDefaultRollOff(RollOffParam* rolloff)
{
	RollOffParam roll;
 
	if (rolloff->rDistSatur!=C_ROLLOFFPARAM_DEFAULT)
		roll.rDistSatur=rolloff->rDistSatur;
	else
		roll.rDistSatur=g_rDistSatur;
 
	if (rolloff->rDistBackGround!=C_ROLLOFFPARAM_DEFAULT)
		roll.rDistBackGround=rolloff->rDistBackGround;
	else
		roll.rDistBackGround=g_rDistBackGround;
 
	SND_fn_vSetDistanceForVolume(rolloff->rDistSatur,rolloff->rDistBackGround);
}
 
void SND_CALL SND_fn_vGetDefaultRollOff(RollOffParam* rolloff)
{
	rolloff->rDistSatur=g_rDistSatur;
	rolloff->rDistBackGround=g_rDistBackGround;
}
 
 
/*-----------------------------------------------------------------------------*/
/*      DistanceToVolume:obtenir le volume d'une source en fonction d'une distance*/
/*			pour le valeurs de RollOff par defaut*/
/*      Entree: dist=distance en m*/
/*      Sortie: volume midi*/
/*----------------------------------------------------------------------------*/
unsigned char SND_CALL SND_fn_ucDistanceToVolume(SndReal dist)
{
        if (dist<g_rDistSatur)
                return C_SOUNDVOL_MAXI;
        if (dist>g_rDistBackGround)
		{
			return ucVolumeBackGround;
		}
		else
		{
			if (dist>g_rDistFade)
				return C_SOUNDVOL_MINI;
			else
				return (unsigned char)SND_fn_rMulRealRealSnd(C_SOUNDVOL_MAXI,SND_fn_rDivRealRealSnd(g_rDistFade-dist,g_rDistFade-g_rDistSatur));
		}
}
 
/*-----------------------------------------------------------------------------*/
/*      DistanceToVolumeEx:obtenir le volume d'une source en fonction d'une distance*/
/*			pour le valeurs de RollOff par defaut*/
/*      Entree: dist=distance en m*/
/*      Sortie: volume midi*/
/*----------------------------------------------------------------------------*/
unsigned char SND_CALL SND_fn_ucDistanceToVolumeEx(SndReal dist,RollOffParam* rolloff)
{
	if (rolloff)
	{
		if (rolloff->rDistBackGround<rolloff->rDistSatur)
			rolloff->rDistBackGround=C_ROLLOFFPARAM_INFINITE;
 
        if (dist<rolloff->rDistSatur)
                return C_SOUNDVOL_MAXI;
        if (dist>rolloff->rDistBackGround)
		{
			if (rolloff->rDistBackGround>8*rolloff->rDistSatur)
				return C_SOUNDVOL_MINI;
			else
				return (unsigned char)SND_fn_rMulRealRealSnd(C_SOUNDVOL_MAXI,SND_fn_rDivRealRealSnd(8*rolloff->rDistSatur-rolloff->rDistBackGround,(8-1)*rolloff->rDistSatur));
		}
		else
		{
			if (dist>8*rolloff->rDistSatur)
				return C_SOUNDVOL_MINI;
			else
				return (unsigned char)SND_fn_rMulRealRealSnd(C_SOUNDVOL_MAXI,SND_fn_rDivRealRealSnd(8*rolloff->rDistSatur-dist,(8-1)*rolloff->rDistSatur));
		}
	}
	else
		return SND_fn_ucDistanceToVolume(dist);
}
 
/*----------------------------------------------------------------------------*/
/*      DopplerPitch:obtenir le rapport de frequence du à l'effet doppler*/
/*      Entrees:src_pos=position de la source*/
/*              src_vit=vitesse de la source*/
/*              mic_pos=position du micro*/
/*              mic_vit=vitesse du micro*/
/*      Sortie:rapport de frequence (>1.0 -> plus aigu; <1.0 ->plus grave)*/
/**/
/*		Si Fo=frequence effective de la source, Fd=frequence subjective au micro*/
/*			__														 ->	->*/
/*			Vo=projetee de la vitesse de la source sur la drite OS = Vo.OS/OS*/
/*			__												*/
/*			Vm=projetee de la vitesse du micro sur la drite OS*/
/**/
/*			D=OS distance entre la source et le micro*/
/*						  __           __*/
/*	On a: Fd = Fo * ( C - Vm ) / ( C - Vo ) */
/*								 -> ->             -> ->*/
/*		soit : Fd = Fo * ( C*D - Vm.OS ) / ( C*D - Vo.OS)*/
/*----------------------------------------------------------------------------*/
SndReal SND_CALL SND_fn_rDopplerPitch(SndVector *src_pos,SndVector *src_vit,SndVector *mic_pos,SndVector *mic_vit)
{
        SndVector dir;/*OS/2^8 (division par 256 pour empecher la saturation)*/
        SndReal nor;/*dist(OS)/2^8*/
        SndReal retour;
 
		if (g_lSoundSpeed)
		{
			dir.x=(src_pos->x-mic_pos->x)>>8;
			dir.y=(src_pos->y-mic_pos->y)>>8;
			dir.z=(src_pos->z-mic_pos->z)>>8;
			nor=SND_fn_rPseudoNormeVectorSnd(&dir);
 
			if (nor==0) return 0x10000;
 
					if (nor>0x400000)
							return 0x10000;
			retour=nor*g_lSoundSpeed;
 
			/*if retour==0, this may mean that DopplerEffect is too low; set id to No_DOPPLER*/
			snd_assert(retour);
 
			retour=SND_fn_rDivRealRealSnd(retour-SND_fn_rScalaireVectorSnd(src_vit,&dir)
							,retour-SND_fn_rScalaireVectorSnd(mic_vit,&dir));
			return retour;
		}
		else
		{
			return C_SNDREAL_1;
		}
}
 

/*------------------------------------------------------------*/
/*      SetDopplerFactorSnd:change Doppler effect range*/
/*      Entree:new factor for Doppler effect (1.0=normal effect=physicallly realistic*/
/*				0.0=no doppler effect*/
/*      Sortie:neant*/
/*------------------------------------------------------------*/
void SND_CALL SND_fn_vSetDopplerFactor(SndReal factor)
{
	if (factor)
		g_lSoundSpeed=M_RealToIntSnd(
			SND_fn_rDivRealRealSnd(
				M_IntToRealSnd(CELERITE_SON),
				factor)
				);
	else
		g_lSoundSpeed=0;
}

/*------------------------------------------------------------*/
/*      GetDopplerFactorSnd: retrieve the global Doppler factor
/*      Entree: None
/*				
/*      Sortie: Factor
/*------------------------------------------------------------*/
SndReal SND_CALL SND_fn_rGetDopplerFactor()
{

	//If gl_SoundSpeed == 0, we dont want to divide by 0
	if(g_lSoundSpeed)
		return SND_fn_rDivRealRealSnd(M_IntToRealSnd(CELERITE_SON), M_IntToRealSnd(g_lSoundSpeed));
	else
		return C_SNDREAL_0;

}

 
/*#define VALID_POSITIONTOPAN*/
#ifdef VALID_POSITIONTOPAN
#include <crtdbg.h>
void _valid_PositionToPan(void)
{
        SndVector src,mic,mic_nor,mic_tan;
        int i;
        int d=0x20000;
 
        mic.x=0;
        mic.y=0;
        mic.z=0;
 
        mic_nor.x=0;
        mic_nor.y=0x10000;
        mic_nor.z=0;
 
        mic_tan.x=0x10000;
        mic_tan.y=0;
        mic_tan.z=0;
 
        for (i=0;i<360;i+=10)
        {
                src.x=d*cos(3.1415*i/180);src.y=d*sin(3.1415*i/180);src.z=0;
                _RPT2(_CRT_WARN,"angle:%d pan:%d\n",i,(long)SND_fn_ucPositionToPan(&src,&mic,&mic_nor,&mic_tan));
        }
}
#endif /*VALID_POSITIONTOPAN*/
 
/*----------------------------------------------------------------------------*/
/*      PositionToPan: obtenir le pan à partie de la position d'une source*/
/*      Entrees:src=position de la source*/
/*              mic=position du micro*/
/*              mic_nor=direction normale du micro (vers l'avant de l'ecran)*/
/*              mic_tan=direction horizontale du icro (tangent à l'ecran)*/
/*      Sortie:pan midi (0 tout à gauche,127 tout à droite)*/
/*----------------------------------------------------------------------------*/
/*        mic_nor*/
/*         ^                                                    */
/*       s'!......+     o=Objet                                         ->*/
/*         !   n /.                             n=norme du vect mo*/
/*         !_b /  .                                                                     ------>       ------> ->        _______*/
/*         ! /)a  .                             s=proj. de o sur (m,mic_tan)=scal(mic_tan,mo)=n*mic_tan*cos(a)*/
/*       m *------+-> mic_tan*/
/*      micro     s                             pan=MAX en dehors de +/-45°                                        _______*/
/*                                                      pan prop à sin(b) = prop à cos(a) = prop à s/n (si mic_tan=1)*/
 
unsigned char SND_CALL SND_fn_ucPositionToPan(SndVector *src,SndVector *mic,SndVector *mic_nor,SndVector *mic_tan)
{
    SndReal s,n;
    SndVector diro;
    long pp;
 
    snd_assert((mic_nor->x!=0)||(mic_nor->y!=0)||(mic_nor->z!=0));
    snd_assert((mic_tan->x!=0)||(mic_tan->y!=0)||(mic_tan->z!=0));
 
    /*diro=vecteur allant de Micro à projete(Source) dans plan (Micro,x,y)*/
    diro.x=src->x-mic->x;
    diro.y=src->y-mic->y;
    diro.z=src->z-mic->z;
 
    n=SND_fn_rPseudoNormeVectorSnd(&diro);
 
        /*s=n_diro*cos(tan,diro)*/
        s=SND_fn_rPseudoScalaireNormeVectorSnd(&diro,mic_tan);
        
        if (2*n/256==0)
                return C_SOUNDPAN_MEDIAN;
        if (s<=-n)
                return C_SOUNDPAN_LEFT;
        if (s>=n)
                return C_SOUNDPAN_RIGHT;
        /*if ((s>-n) && (s<n))*/
		pp=C_SOUNDPAN_MEDIAN+(s/256)*(C_SOUNDPAN_RIGHT-C_SOUNDPAN_LEFT)/(2*n/256);
 
        if (pp<C_SOUNDPAN_LEFT) pp=C_SOUNDPAN_LEFT;
        if (pp>C_SOUNDPAN_RIGHT) pp=C_SOUNDPAN_RIGHT;
 
        return (unsigned char)pp;
}
 
/*#define VALID_POSITIONTODOLBY*/
#ifdef VALID_POSITIONTODOLBY
#include <crtdbg.h>
void _valid_PositionToDolby(void)
{
        SndVector src,mic,mic_nor;
        int i;
        int d=0x20000;
 
        mic.x=0;
        mic.y=0;
        mic.z=0;
 
        mic_nor.x=0;
        mic_nor.y=0x10000;
        mic_nor.z=0;
 
        for (i=0;i<360;i+=10)
        {
                src.x=d*cos(3.1415*i/180);src.y=d*sin(3.1415*i/180);src.z=0;
                _RPT2(_CRT_WARN,"angle:%d dolby:%d\n",i,(long)SND_fn_ucPositionToDolby(&src,&mic,&mic_nor));
        }
}
#endif /*VALID_POSITIONTODOLBY*/
 
/*------------------------------------------------------------------------------*/
/*      PositionToDolby: obtenir le parametre dolby à partir de la position*/
/*      Entrees:src=position de la source*/
/*              mic=position du micro*/
/*              mic_nor=direction normale au micro*/
/*      Sortie:parametre Dolby (0 tout devant, 127 tout derriere)*/
/*----------------------------------------------------------------------------*/
/*        mic_nor*/
/*         ^                                                    */
/*       s'!......+     o=Objet                                         ->*/
/*         !   n /.                             n=norme du vect mo*/
/*         !_b /  .                                                                      ------>           ------> ->    _______*/
/*         ! /)a  .                             s'=proj. de o sur (m,mic_nor)=scal(mic_nor,mo)=n*mic_nor*cos(b)*/
/*       m *------+-> mic_tan*/
/*      micro     s                                                                                                                                 _______*/
/*                                                      pan prop à sin(a) = prop à cos(b) = prop à s'/n (si mic_tan=1)*/
unsigned char SND_CALL SND_fn_ucPositionToDolby(SndVector *src,SndVector *mic,SndVector *mic_nor)
{
    SndReal s,n;
    SndVector diro;
    long pp;
 
    snd_assert((mic_nor->x!=0)||(mic_nor->y!=0)||(mic_nor->z!=0));
 
    /*diro=vecteur allant de Micro à projete(Source) dans plan (Micro,x,y)*/
    diro.x=src->x-mic->x;
    diro.y=src->y-mic->y;
    diro.z=src->z-mic->z;
 
    n=SND_fn_rPseudoNormeVectorSnd(&diro);
 
        /*s=n_diro*cos(tan,diro)*/
        s=SND_fn_rPseudoScalaireNormeVectorSnd(&diro,mic_nor);
        
		if (2*n/256==0)
			return (C_SOUNDSPACE_FRONT+C_SOUNDSPACE_BACK)/2;
 
        if (s<=-n)
                return C_SOUNDSPACE_BACK;
        if (s>=n)
                return C_SOUNDSPACE_FRONT;
        /*if ((s>-n) && (s<n))*/
        pp=(C_SOUNDSPACE_FRONT+C_SOUNDSPACE_BACK)/2+(s/256)*(C_SOUNDSPACE_FRONT-C_SOUNDSPACE_BACK)/(2*n/256);
 
        if (pp<C_SOUNDSPACE_FRONT) pp=C_SOUNDSPACE_FRONT;
        if (pp>C_SOUNDSPACE_BACK) pp=C_SOUNDSPACE_BACK;
 
        return (unsigned char)pp;
}
 
 
