/*

MODULE DE GESTION DU CD
pour WIN95 en mode MCI 

   UBI SOUND STUDIOS   
   Olivier Amiaud      

*/


/*****************************************************************************
	Prototype de la fonction de gestion d'erreur
	void SND_fn_vDisplayError(unsigned short uwErrNum,char *message)

  Liste des erreurs
  -----------------
    
  E_uwSndCDLoadTrack
  E_uwSndCDStatus
  E_uwSndCDSet
  E_uwSndCDPlay
  E_uwSndCDStop
  E_uwSndCDNotifyFailure
  E_uwSndCDNotifyAborted
  E_uwSndCDNotifySuperseded
  E_uwSndCDCreateThread
  E_uwSndCDClose
  E_uwSndCDFade

******************************************************************************/


#include <windows.h>
#include <windowsx.h>
#include <stdio.h>
#include <process.h> 


#include "SNDinc.h"

#include "sndres.h"
#include "sndwin95.h"

//#include "sndmusct.h"

#include "sndCD.h"

#if defined(_DLL_COMPILATION_MODE) 
#include  "SNDDLL.h"
#endif
#include "sndthrd.h"

//#include "snderr.h"
//#include "sndmem.h"

#ifndef DISABLE_CD

//#define CD_MCI

//----------------------------------
//- Partie concernant le CD mixer -
//----------------------------------

static MIXERCAPS   mixerCapsCD;
static HMIXER      hMixerCD;
static UINT        mixerIdentifierCD;
static UINT        uiNbrMixersCD;
static DWORD       dwNbrAudioLinesCD;

static MIXERLINE                           mixerLineCD;
static MIXERLINECONTROLS                   mixerLineControlsCD;
static MIXERCONTROL                        mixerControlCD;
static MIXERCONTROLDETAILS                 mixerControlDetailsCD;
static MIXERCONTROLDETAILS_UNSIGNED        valeurCD;

static BOOL CDControlAvailable = FALSE;

//----------------------------------
//- Partie concernant le CD driver -
//----------------------------------

#define MSG_ERR_CD					"CD Device Error"
#define MSG_ERR_CD_FILE				"CD File Error"
#define MSG_ERR_CD_NOT_READY		"CD Not Ready"
#define MSG_ERR_BAD_CD_ID_TRACK		"Bad CD Track Number"


#define SND_CD_NOT_IN_CD_PLAYER					10
#define SND_CD_NOT_THE_GOOD_ONE_AND_NOT_AUDIO	11
#define SND_CD_NOT_THE_GOOD_ONE_BUT_AUDIO		12
#define SND_CD_USED_BY_A_PROGRAM				13
#define FADE_AND_STOP_CD                        12

#define NB_MAX_CD_TRACKS						20
#define NB_CD_TRACKS							12

#define OFFSET_CD_VOICE							0x3000
#define MASK_OFFSET_CD_VOICE					0x0FFF
#define NO_CD_VOICE								0x127
#define CD_UNAVAILABLE							0x0004

#define CD_STATE_NORMAL					        1
#define CD_STATE_USED					        2
#define CD_STATE_ALL_TRACKS				        3
#define CD_STATE_UNUSED					        4

#define TIMER_CD						        34
#define VOL_MAX_CD						        60

#define SND_MAKE_TMSF(t,m,s,f)	((DWORD)t)|(((DWORD)m)<<8)|(((DWORD)s)<<16)|(((DWORD)f)<<24)
#define SND_TMSF_T(tmsf)		((BYTE)(tmsf))
#define SND_TMSF_M(tmsf)		((BYTE)(((WORD)(tmsf)) >> 8))
#define SND_TMSF_S(tmsf)		((BYTE)((tmsf)>>16))
#define SND_TMSF_F(tmsf)		((BYTE)((tmsf)>>24))

#define SND_TMSF_MS(tmsf)	\
((((((((tmsf & 0xff00) >> 8) * 60)	+	((tmsf & 0xff0000) >> 16))	* 75)	+	((tmsf & 0xff000000)>>24)) * 1000) / 75)



//	VARIABLES GLOBALES EXTERNES
//	TYPEDEF LOCAUX 	

// Definition ensemble des pistes du jeu
typedef struct _tdstCDTrack
{
	SndBool bLoop;
	int iNbLoops; 

	DWORD tmsfStartTrack;
	DWORD tmsfEndTrack;
	DWORD dwStartingFrame;
	DWORD dwEndingFrame;
	DWORD dwStartLoopingFrame;
	DWORD dwEndLoopingFrame;

	tduRefRes uCDTrack;
} tdstCDTrack;

// Definition piste active a l'instant t
typedef struct _tdstActiveCDTrack
{
	SndBool bActive;
	
	SndBool bPaused;						// le CD a été mi sen pause
	long	lPausePos;						// Postition physique de la pause

	SndBool bLoop;
	int iNbLoops; 
	long lVoice;
	long lIndexVoice;
	unsigned long ulTrackLengthInMs;
    SoundParam * pstSoundParam;
    long lPrio;
    SND_td_pfn_vSoundCallback pfnSoundCallback;
    long lParSoundCallback;
} tdstActiveCDTrack;

// Definition data necessaire au driver
typedef struct _stCDDriver
{
	SndBool bCdDriverOk;
	SndBool bRequestCdDriver;
	unsigned char ucState;
	MCIDEVICEID uiCdDeviceId;
	long lNbCdTracks;
	int iLastCDVolume;
} tdstCDDriver;


// -------------------------------------------
//	VARIABLES GLOBALES LOCALES


static SndBool bCDFadeIn = FALSE;
static UINT uiTimerCDFadeIn;// Gestion du fade-in
static unsigned long ulCountDownFadeIn;
static unsigned long ulTimerPeriodFadeIn;
static long lNbIterFadeIn;

static SndBool bCDFadeOut= FALSE;
static UINT uiTimerCDFadeOut;// Gestion du fade-out
static unsigned long ulCountDownFadeOut;
static unsigned long ulTimerPeriodFadeOut;
static long lNbIterFadeOut;

static unsigned char ucFacteurVolume;// Volume global CD

// Pistes CD du jeu et piste active
static tdstCDTrack stCDTracks[NB_MAX_CD_TRACKS];
static tdstActiveCDTrack stActiveCDTrack;

static tdstCDDriver stCDDriver;// CD driver
static int iNbCdTracks;

static char texte_erreur[1024];

// -------------------------------------------
// HANDLE ET ID DE THREADS ET FENETRES


static HWND hSndCdWnd;// handle de la fenetre de gestion de messages du CD
static DWORD dwCDThreadId;
static HANDLE hCDThread=NULL;	
static HANDLE hCDThreadCallBack;
static DWORD dwCDThreadIdCallBack;

//********************************************************************** BIDOUILLE CD PLAYER

// -------------------------------------------
// VARIABLES CONCERNANT LA GESTION DES THREADS


UINT  SND_PLAYCD_MSG;
UINT  SND_STOPCD_MSG;
UINT  SND_LOADCD_MSG;
UINT  SND_PAUSECD_MSG;
UINT  SND_RESUMECD_MSG;
UINT  SND_NEXTTRACKCD_MSG;
UINT  SND_DESINITCD_MSG;
UINT  SND_RELEASECD_MSG;
UINT  SND_RESTORECD_MSG;
UINT  SND_GETPOSCD_MSG;

static DWORD dw_a5_Params[5];
static DWORD dw_a2_threadParams[2];



#endif //DISABLE_CD


// -------------------------------------------
// PROTOTYPES DES FONCTIONS LOCALES


SndBool SND_fn_bLoadCDTrack(tdstBlockResourceMem * pstPtr,int);
SndBool SND_fn_bUnloadCDTrack(int i);
SndBool SND_fn_bMediaPresent(void);
long    SND_fn_lGetActiveCdTrack(void);
long    SND_fn_lGetCurrentCdMode(void);
long    SND_fn_lGetCDLength(void);
long    SND_fn_lGetCdTrack(tdxId Id);
long    SND_fn_lGetCurrentCdPosition(void);
void    SND_fn_vCloseCDDoor(void);


int     SND_fn_iMixerInitCD(void);
int     SND_fn_iMixerDesInitCD(void);
DWORD   SND_fn_dwGetCDMax(void);
DWORD   SND_fn_dwGetCDMin(void);
DWORD   SND_fn_dwGetCDValue(void);
void    SND_fn_vSetCDValue(DWORD val);
 
void SND_CALL SND_fn_vReleaseDriverCDThread();
void SND_CALL SND_fn_vRestoreDriverCDThread();


// prototypes des fonctions appelees par le thread

int     SND_fn_iInitCDThread();
SndBool SND_fn_bLoadCDTrackThread(tdstBlockResourceMem * pstPtr,int numTrack);

void    SND_fn_vDesInitCDThread(void);
long    SND_fn_lPlayCDThread(tduRefRes res,SoundParam *par,long prio,SND_td_pfn_vSoundCallback fn_callback,long par_callback);
void    SND_fn_vStopCDThread(long voice);
long    SND_fn_lNextCDTrackThread();
void    SND_fn_vPauseCDThread(long voice);
void    SND_fn_vResumeCDThread(long voice);

SndReal SND_CALL SND_fn_rGetPosCDThread(long voice);


LRESULT CALLBACK SND_fn_vCdMSG(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam);

//#ifdef CD_MCI


//-----------------------------------------------------------
//	ConvertRsvDiskToMem
//
//	But			: Convertir un BlockResourceDisk en Mem
//	Entrées : reference au structs ressources mem et disk
//	Sorties : neant
//------------------------------------------------------------
void SND_CALL SND_fn_vConvertResDiskToMemCD(tdstBlockResourceDisk *disk,tdstBlockResourceMem *mem,void * ptrBegin)
{	
#ifndef DISABLE_CD
	HANDLE hEventSynchro;

    mem->Id=disk->Id;
    mem->eType=disk->eType;
    mem->eStorage=disk->eStorage;
    mem->ucVolume=disk->ucVolume;
    mem->bIsLoaded=TRUE;
        
	//partie spécifique au type de ressource
	mem->uRes.stCD.bVolable = disk->uRes.stCD.bVolable;
	mem->uRes.stCD.wTrackId = disk->uRes.stCD.wTrackId;
	mem->uRes.stCD.bLoop = disk->uRes.stCD.bLoop;

	mem->uRes.stCD.ulStartingFrame = disk->uRes.stCD.ulStartingFrame;
	mem->uRes.stCD.ulEndingFrame = disk->uRes.stCD.ulEndingFrame;

	if (mem->uRes.stCD.bLoop)
	{
	mem->uRes.stCD.ulStartLoop = disk->uRes.stCD.ulStartLoop;
	mem->uRes.stCD.ulEndLoop = disk->uRes.stCD.ulEndLoop;
	mem->uRes.stCD.iNbLoops = disk->uRes.stCD.iNbLoops;
	}
	else
	{
	mem->uRes.stCD.ulStartLoop = mem->uRes.stCD.ulEndLoop = 0;
		mem->uRes.stCD.iNbLoops = 0;
	}


	if (stCDDriver.bCdDriverOk)
	{
		hEventSynchro = CreateEvent(NULL,FALSE,FALSE,NULL);
		dw_a5_Params[0] = (DWORD)mem;
		dw_a5_Params[1] = (DWORD)iNbCdTracks++;
		if (!PostThreadMessage(dwCDThreadId,(UINT)SND_LOADCD_MSG,(WPARAM)dw_a5_Params,(LPARAM)hEventSynchro))
			SND_fn_vDisplayError(E_uwSndCDLoadTrack,"PostThreadMessage Load Track");

		WaitForSingleObject(hEventSynchro,INFINITE);
		CloseHandle(hEventSynchro);
	}
#endif //DISABLE_CD
}

SndBool SND_CALL SND_fn_bLoadResScriptCd(tdstBlockResourceDisk *disk,tdstBlockResourceMem *mem)
{
#ifndef DISABLE_CD
	SND_fn_vConvertResDiskToMemCD(disk,mem,NULL);
	return TRUE;
#else //DISABLE_CD
	return FALSE;
#endif //DISABLE_CD
}

SndBool SND_CALL SND_fn_bLoadResBinaryCd(tdstBlockResourceDisk *disk,tdstBlockResourceMem *mem,char *pDataBloc)
{
#ifndef DISABLE_CD
	SND_fn_vConvertResDiskToMemCD(disk,mem,NULL);
#endif //DISABLE_CD
	return FALSE;
}

#ifndef DISABLE_CD
//-----------------------------------------------------------
//	SND_fn_bMediaPresent
//
//	But			: Dire si le CD est dans le lecteur
//	Entrées : neant
//	Sorties : True,False
//------------------------------------------------------------
SndBool SND_fn_bMediaPresent()
{
	MCI_STATUS_PARMS status;
	MCIERROR mciReturn;
	// Mode actuel de la piste
	status.dwItem = MCI_STATUS_MEDIA_PRESENT;  
	if (mciReturn=mciSendCommand(stCDDriver.uiCdDeviceId,MCI_STATUS,MCI_STATUS_ITEM,(DWORD)(LPVOID)&status))
	{
		mciGetErrorString(mciReturn,texte_erreur,sizeof(texte_erreur));
		SND_fn_vDisplayError(E_uwSndCDStatus,texte_erreur);
		return (0);
	}
	return(status.dwReturn);
}

//-----------------------------------------------------------
//	SND_fn_lGetTypeCd(short Id)
//
//	But			: Donne le type d'une piste du CDROM
//	Entrées : Id de la piste a tester
//	Sorties : Type (MCI_CDA_TRACK_AUDIO ou MCI_CDA_TRACK_OTHER ou 0)
//------------------------------------------------------------
long SND_fn_lGetTypeCdTrack(short Id)
{
	MCI_STATUS_PARMS status;
	MCIERROR mciReturn;
	// Mode actuel de la piste
	status.dwItem = MCI_CDA_STATUS_TYPE_TRACK;  
	status.dwTrack = Id;
	if (mciReturn=mciSendCommand(stCDDriver.uiCdDeviceId,MCI_STATUS,MCI_STATUS_ITEM|MCI_TRACK,(DWORD)(LPVOID)&status))
	{
		mciGetErrorString(mciReturn,texte_erreur,sizeof(texte_erreur));
		SND_fn_vDisplayError(E_uwSndCDStatus,texte_erreur);
		return (0);
	}
	return(status.dwReturn);
}

//-----------------------------------------------------------
//	SND_fn_lGetNbCdTracks()
//
//	But			: Donne le nombre de pistes du CD
//	Entrées : neant
//	Sorties : nb pistes
//------------------------------------------------------------
long SND_fn_lGetNbCdTracks()
{
	MCI_STATUS_PARMS status;
	MCIERROR mciReturn;

	status.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;  
	if (mciReturn=mciSendCommand(stCDDriver.uiCdDeviceId,MCI_STATUS,MCI_STATUS_ITEM,(DWORD)(LPVOID)&status))
	{
		mciGetErrorString(mciReturn,texte_erreur,sizeof(texte_erreur));
		SND_fn_vDisplayError(E_uwSndCDStatus,texte_erreur);
		return (0);
	}
	return(status.dwReturn);
}


//-----------------------------------------------------------
//	SND_fn_lTimerIn()
//
//	But			: Callback de gestion du temps pour les fade-in.
//	Entrées : neant
//	Sorties : neant
//------------------------------------------------------------
void CALLBACK SND_fn_lCDTimerIn(UINT IDEvent,UINT msg,DWORD par,DWORD dw1,DWORD dw2)
{
	if (!stActiveCDTrack.bPaused && stActiveCDTrack.bActive)
		if (bCDFadeIn)
		{
			if (lNbIterFadeIn--)
				SND_fn_vSetCDValue(ucFacteurVolume++);
			else
			{
				bCDFadeIn = FALSE;
				ulCountDownFadeIn = 0;
			}
	}
}		
//-----------------------------------------------------------
//	SND_fn_lTimerOut()
//
//	But			: Callback de gestion du temps pour les fade-in.
//	Entrées : neant
//	Sorties : neant
//------------------------------------------------------------
void CALLBACK SND_fn_lCDTimerOut(UINT IDEvent,UINT msg,DWORD par,DWORD dw1,DWORD dw2)
{
	unsigned long ulRealTime = 0;
	if (!stActiveCDTrack.bPaused && stActiveCDTrack.bActive)
		if (bCDFadeOut)
		{
			ulCountDownFadeOut += ulTimerPeriodFadeOut;
			ulRealTime = stActiveCDTrack.ulTrackLengthInMs - lNbIterFadeOut * ulTimerPeriodFadeOut;
			if (ulCountDownFadeOut > ulRealTime)
			{
				SND_fn_vSetCDValue(ucFacteurVolume);
				if (ucFacteurVolume-- == 0)
				{
					bCDFadeOut = FALSE;
					ulCountDownFadeOut = 0;
					if (par == FADE_AND_STOP_CD)
					{
						SND_fn_vStopCD(0);
						PostMessage(hSndCdWnd,MM_MCINOTIFY,MCI_NOTIFY_SUCCESSFUL,0);
					}
				}
			}
		}
}



//-----------------------------------------------------------
//	SND_fn_iCdThreadCallBack
//
//	But			: Procédure de la thread qui contient la fenetre qui traite les messages MCI
//	Entrées : void * : paramètre inutilisé
//	Sorties : void
//------------------------------------------------------------

int WINAPI SND_fn_iCDThreadCallBack(long arglist)
{
    long * pArg = (void *)arglist;
    SND_td_pfn_vSoundCallback 	pfnCallback = (SND_td_pfn_vSoundCallback)pArg[0];
    long lParCallback = pArg[1];

	SND_fn_vEnterCriticalSectionThreadSnd();

    if (pfnCallback != NULL)
	    (*pfnCallback)(lParCallback);
    
	SND_fn_vQuitCriticalSectionThreadSnd();

    return (0);
}


//-----------------------------------------------------------
//	SND_fn_vCdMSG
//
//	But			: Effectuer la gestion des messages du CD
//	Entrées : Messages et leurs parametres
//	Sorties : Ok ou Failed
//------------------------------------------------------------

LRESULT CALLBACK SND_fn_vCdMSG(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
	char texte[256];
	long current_mode;
	SECURITY_ATTRIBUTES sa;

	switch(message)
	{
		case WM_CREATE :	break;
		case WM_COMMAND : break;
		case WM_TIMER : break;
		case MM_MCINOTIFY :
			switch (wParam)
			{
			case MCI_NOTIFY_FAILURE:
				sprintf(texte,"MM_MCINOTIFY FAILURE wpar=%d lpar=%d",wParam,lParam);
     			SND_fn_vDisplayError(E_uwSndCDNotifyFailure,texte);
				break;
			case MCI_NOTIFY_ABORTED:
				//sprintf(texte,"MM_MCINOTIFY ABORTED wpar=%d lpar=%d",wParam,lParam);
		   		//SND_fn_vDisplayError(E_uwSndCDNotifyAborted,texte);
				break;
			case MCI_NOTIFY_SUPERSEDED:
				sprintf(texte,"MM_MCINOTIFY SUPERSEDED wpar=%d lpar=%d",wParam,lParam);
				SND_fn_vDisplayError(E_uwSndCDNotifySuperseded,texte);
				break;
			case MCI_NOTIFY_SUCCESSFUL:
				current_mode = SND_fn_lGetCurrentCdMode();
				switch (current_mode)
				{
				case MCI_MODE_STOP :
					if (!stActiveCDTrack.bPaused)
					{
						if ((stActiveCDTrack.bLoop)&&(stActiveCDTrack.iNbLoops--))
						{
							SND_fn_lPlayCDThread(	stCDTracks[stActiveCDTrack.lVoice].uCDTrack,
													stActiveCDTrack.pstSoundParam,
													stActiveCDTrack.lPrio,
													stActiveCDTrack.pfnSoundCallback,
													stActiveCDTrack.lParSoundCallback);
						}
						else
						{
							if (uiTimerCDFadeIn)
								timeKillEvent(uiTimerCDFadeIn);
							if (uiTimerCDFadeOut)
								timeKillEvent(uiTimerCDFadeOut);

							stActiveCDTrack.bPaused = FALSE;
							stActiveCDTrack.bActive = FALSE;
							stActiveCDTrack.bLoop = FALSE;
							stActiveCDTrack.lVoice = NO_CD_VOICE;
							stActiveCDTrack.pstSoundParam = NULL;
							stActiveCDTrack.lPrio = 0;

                        	
	                        if (stActiveCDTrack.pfnSoundCallback)
							{
								sa.nLength = sizeof(SECURITY_ATTRIBUTES);
								sa.bInheritHandle = TRUE;
								sa.lpSecurityDescriptor = NULL;
								dw_a2_threadParams[0] = (DWORD)stActiveCDTrack.pfnSoundCallback;
								dw_a2_threadParams[1] = (DWORD)stActiveCDTrack.lParSoundCallback;
								if (!(hCDThreadCallBack = CreateThread(&sa,0,(LPTHREAD_START_ROUTINE)SND_fn_iCDThreadCallBack,(LPVOID)dw_a2_threadParams,0,&dwCDThreadIdCallBack)))
									SND_fn_vDisplayError(E_uwSndCDCreateThread,"");
								stActiveCDTrack.pfnSoundCallback = NULL;
								stActiveCDTrack.lParSoundCallback = 0;
							}
						}
					}
					break;
				case MCI_MODE_PAUSE : break;
				case MCI_MODE_PLAY : break;
				case MCI_MODE_NOT_READY : break;
				default : break;
			}
			break;
		}
		return 0;
		case WM_DESTROY:	
			if (uiTimerCDFadeIn)
				timeKillEvent(uiTimerCDFadeIn);
			if (uiTimerCDFadeOut)
				timeKillEvent(uiTimerCDFadeOut);
			PostQuitMessage(0);
			break;
		default:	return DefWindowProc(hwnd,message,wParam,lParam);
	}
 	return 0;
}


//-----------------------------------------------------------
//      SND_fn_iErreurInit
//
//      But                     : Initialisation de la fenetre de dialogue 
//                                   des erreurs a l'init du driver Midi
//      Entrées : constantes numero d'erreur
//      Sorties : code de retour de la boite de dialogue
//------------------------------------------------------------
/*
int SND_CALL SND_fn_iErreurInitCD()
{
    int ret;

    ret=Erm_fn_iMessageBox(hwndMainSnd,"Insert a CD or\nClose the application using your CD and try again ...\nelse you'll not be able to hear any music","CD-Driver Problem",MB_ABORTRETRYIGNORE|MB_TOPMOST);
    switch (ret)
    {
    case IDABORT:
        return INIT_FAILED;
    case IDIGNORE:
        return INIT_IGNORE;
    case IDRETRY:
        return INIT_RETRY;
    default:
        return INIT_IGNORE;
    }
}
*/

//-----------------------------------------------------------
//	SND_fn_iCdThreadProc
//
//	But			: Procédure de la thread qui contient la fenetre qui traite les messages MCI
//	Entrées : void * : paramètre inutilisé
//	Sorties : void
//------------------------------------------------------------

int __stdcall SND_fn_iCDThreadProc(HANDLE arglist)
{
	MSG msg;
	WNDCLASS wcCdWndClass;
	DWORD *	pdwParams;
	
	PeekMessage(&msg,NULL,0,0,PM_NOREMOVE);

	if (SND_fn_iInitCDThread() == C_INIT_FAILED)
	{
		stCDDriver.bCdDriverOk = FALSE;
		stCDDriver.ucState = CD_STATE_USED;
		ExitThread(C_INIT_FAILED);
	}

	//definit la window-classe
	wcCdWndClass.hInstance = SND_hGlobalInst;
	wcCdWndClass.lpszClassName = "SoundAcpCd";
	wcCdWndClass.lpfnWndProc = SND_fn_vCdMSG;//fonction associée à la fonction
	wcCdWndClass.style=0;
	wcCdWndClass.hIcon=0;
	wcCdWndClass.hCursor=0;
	wcCdWndClass.lpszMenuName='\0';
	wcCdWndClass.cbClsExtra=0;
	wcCdWndClass.cbWndExtra=0;
	wcCdWndClass.hbrBackground=0;

	//enregistrement de la classe CdWnd
	if (!RegisterClass(&wcCdWndClass)) 
		SND_fn_vDisplayError(E_uwSndCDRegisterWndClass,"");
	
	SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_ABOVE_NORMAL);
	
	//création de la fenetre cd
	hSndCdWnd = CreateWindow(wcCdWndClass.lpszClassName,"CdWindow",WS_OVERLAPPEDWINDOW,0,0,0,0,HWND_DESKTOP,NULL,SND_hGlobalInst,NULL);

	// synchro d'initialisation du thread et de la pompe a messages quand tout c'est bien passé
	SetEvent(arglist);


	while (GetMessage(&msg,NULL,0,0))
	{
		if (msg.message == SND_PLAYCD_MSG) 
		{
			pdwParams = (DWORD *)msg.wParam;
			pdwParams[0] = SND_fn_lPlayCDThread(*(tduRefRes *)pdwParams[0],(SoundParam *)pdwParams[1],(long)pdwParams[2] ,(SND_td_pfn_vSoundCallback)pdwParams[3] ,(long)pdwParams[4] );
			// on debloque le thread appelant
			SetEvent((HANDLE)msg.lParam);
		}
		else
		if (msg.message == SND_STOPCD_MSG) 
		{
			pdwParams = (DWORD *)msg.wParam;
			SND_fn_vStopCDThread((long)pdwParams[0]);
			// on debloque le thread appelant
			SetEvent((HANDLE)msg.lParam);
		}
		else
		if (msg.message == SND_LOADCD_MSG)
		{
			pdwParams = (DWORD *)msg.wParam;
			pdwParams[0] = SND_fn_bLoadCDTrackThread((tdstBlockResourceMem *)pdwParams[0],(int)pdwParams[1]);
			// on debloque le thread appelant
			SetEvent((HANDLE)msg.lParam);
		}
		else
		if (msg.message == SND_PAUSECD_MSG)
		{
			pdwParams = (DWORD *)msg.wParam;
			SND_fn_vPauseCDThread((long)pdwParams[0]);
			SetEvent((HANDLE)msg.lParam);
		}
		else
		if (msg.message == SND_RESUMECD_MSG)
		{
			pdwParams = (DWORD *)msg.wParam;
			SND_fn_vResumeCDThread((long)pdwParams[0]);
			SetEvent((HANDLE)msg.lParam);
		}
		else
		if (msg.message == SND_NEXTTRACKCD_MSG)
		{
			pdwParams = (DWORD *)msg.wParam;
			pdwParams[0] = SND_fn_lNextCDTrackThread();
			// on debloque le thread appelant
			SetEvent((HANDLE)msg.lParam);
		}
		else
		if (msg.message == SND_DESINITCD_MSG)
		{
			SND_fn_vDesInitCDThread();
			SetEvent((HANDLE)msg.lParam);
			ExitThread(0);
		}
		else
		if (msg.message == SND_RELEASECD_MSG)
		{
			SND_fn_vReleaseDriverCDThread();
			SetEvent((HANDLE)msg.lParam);
		}
		else
		if (msg.message == SND_RESTORECD_MSG)
		{
			SND_fn_vRestoreDriverCDThread();
			SetEvent((HANDLE)msg.lParam);
		}
		if (msg.message == SND_GETPOSCD_MSG) 
		{
			pdwParams = (DWORD *)msg.wParam;
			pdwParams[0] = (DWORD) SND_fn_rGetPosCDThread((long)pdwParams[0]);
			// on debloque le thread appelant
			SetEvent((HANDLE)msg.lParam);
		}
		else
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
	return 0;
}
#endif //DISABLE_CD

//-----------------------------------------------------------
//	SND_fn_iInitCd
//
//	But			: Realiser l'init bas niveau du driver midi
//	Entrées : neant
//	Sorties : C_INIT_OK ou C_INIT_FAILED
//------------------------------------------------------------
int SND_CALL SND_fn_iInitCD(SND_tdstInitStruct *pInitStruct)
{
#ifndef DISABLE_CD
	DWORD dwExitCode = 0;
	HANDLE hEventSynchro;
	HANDLE hHandlesForWait[2];
	SECURITY_ATTRIBUTES sa;

#if defined(_DLL_COMPILATION_MODE)
    SND_DllInitEntryPoints_StaticFunctions((HMODULE)pInitStruct->hProcessInstance );
#endif

	stCDDriver.bCdDriverOk = CD_UNAVAILABLE;
	uiTimerCDFadeIn = uiTimerCDFadeOut = 0;

//	Init toutes pistes CD

    //init de hwndSnd et hinst ,et SND_hResourceHandle:
    hwndMainSnd=pInitStruct->hMainWindow;
	SND_hGlobalInst=pInitStruct->hProcessInstance;
	if (SND_g_hResourceHandle==NULL)	
		SND_g_hResourceHandle=SND_hGlobalInst;


	// on init les points d'entree fonctions externes DLL

#ifdef _DLL_COMPILATION_MODE
	SND_DllInitEntryPoints_StaticFunctions((HMODULE)SND_hGlobalInst);
#endif

	// on enregistre les differents types de messages recus par le thread
	SND_PLAYCD_MSG = RegisterWindowMessage("PlayCd");
	SND_STOPCD_MSG = RegisterWindowMessage("StopCd");
	SND_LOADCD_MSG = RegisterWindowMessage("LoadCd");
	SND_PAUSECD_MSG = RegisterWindowMessage("PauseCd");
	SND_RESUMECD_MSG = RegisterWindowMessage("ResumeCd");
	SND_NEXTTRACKCD_MSG = RegisterWindowMessage("NextTrackCd");
	SND_DESINITCD_MSG = RegisterWindowMessage("DesinitCd");

	SND_RELEASECD_MSG = RegisterWindowMessage("ReleaseCd");
	SND_RESTORECD_MSG = RegisterWindowMessage("RestoreCd");
	SND_GETPOSCD_MSG = RegisterWindowMessage("GetPosCd");

	// on cree le thread de gestion CD l'ID du thread est dwCDThreadId
	sa.nLength = sizeof(SECURITY_ATTRIBUTES);
	sa.bInheritHandle = TRUE;
	sa.lpSecurityDescriptor = NULL;

	hEventSynchro = CreateEvent(NULL,FALSE,FALSE,NULL);

	while (TRUE)
	{
		if (!(hCDThread = CreateThread(&sa,0,(LPTHREAD_START_ROUTINE)SND_fn_iCDThreadProc,hEventSynchro,0,&dwCDThreadId)))
		{
			SND_fn_vDisplayError(E_uwSndCDCreateThread,"");
			return C_INIT_FAILED;
		}
		else
		{
			hHandlesForWait[0] = hEventSynchro;
			hHandlesForWait[1] = hCDThread;

			// on attend la synchronisation d'init., l'attente se termine quand un des deux events arrive
			WaitForMultipleObjects(2,hHandlesForWait,FALSE,INFINITE);
			GetExitCodeThread(hCDThread,&dwExitCode);
			if (dwExitCode == STILL_ACTIVE)
			{
				stCDDriver.bCdDriverOk = TRUE;
				CloseHandle(hEventSynchro);
				return C_INIT_OK;
			}
			else
			{
				if (dwExitCode == C_INIT_FAILED)
				{
					CloseHandle(hCDThread);
					hCDThread=NULL;
					switch (SND_fn_iErreurInit(0))
					{
						case INIT_IGNORE:
							// on joue sans CD
							CloseHandle(hEventSynchro);
							return C_INIT_FAILED;
							
						case INIT_RETRY:
							// on reinit jusqu'a appli utilisatrice libere driver
							break;
						case INIT_FAILED:
							// on sort du program
							stCDDriver.bCdDriverOk = FALSE;
							CloseHandle(hEventSynchro);
							exit(0);
					}
				}
			}
		}
	}
	return C_INIT_OK;
#else
    return C_INIT_FAILED;
#endif //DISABLE_CD
}


#ifndef DISABLE_CD
//-----------------------------------------------------------
//	SND_fn_iInitCdThread
//
//	But			: Realiser l'init bas niveau du driver midi
//	Entrées : neant
//	Sorties : C_INIT_OK ou C_INIT_FAILED
//------------------------------------------------------------
int SND_fn_iInitCDThread()
{
	int i;

	MCI_OPEN_PARMS open;
	MCI_SET_PARMS set;
	MCIERROR mciReturn;
	MCI_SYSINFO_PARMS sysinfo;
    long lOpeningMode = 0;
    
	stCDDriver.bCdDriverOk = CD_UNAVAILABLE;
	uiTimerCDFadeIn = uiTimerCDFadeOut = 0;

    
    //	Init toutes pistes CD

	for (i=0;i<NB_MAX_CD_TRACKS;i++)
	{
		stCDTracks[i].bLoop = FALSE;
		stCDTracks[i].tmsfStartTrack = 0;
		stCDTracks[i].tmsfEndTrack = 0;
		stCDTracks[i].dwStartingFrame = 0;
		stCDTracks[i].dwEndingFrame = 0;
		stCDTracks[i].dwStartLoopingFrame = 0;
		stCDTracks[i].dwEndLoopingFrame = 0;
		stCDTracks[i].uCDTrack.Id = 0;
	}

	//	Init piste CD active

	stActiveCDTrack.bPaused = FALSE;
	stActiveCDTrack.bActive = FALSE;
	stActiveCDTrack.bLoop = FALSE;
	stActiveCDTrack.lVoice = NO_CD_VOICE;
	stActiveCDTrack.pfnSoundCallback= NULL;
	stActiveCDTrack.lParSoundCallback= 0;
	stActiveCDTrack.pstSoundParam = NULL;
	stActiveCDTrack.lPrio = 0;

	iNbCdTracks = 0;

	open.lpstrDeviceType=(LPCSTR)(MCI_DEVTYPE_CD_AUDIO);
	do 
	{ 
		stCDDriver.bCdDriverOk = FALSE;
		if (mciReturn=mciSendCommand((DWORD)NULL,MCI_OPEN,MCI_OPEN_SHAREABLE|MCI_OPEN_TYPE|MCI_OPEN_TYPE_ID,(DWORD)(LPVOID)&open))
		{
			return C_INIT_FAILED;
		}
		else
			stCDDriver.bCdDriverOk = TRUE;
	} while (!stCDDriver.bCdDriverOk);


	if (stCDDriver.bCdDriverOk)
	{
		stCDDriver.uiCdDeviceId = open.wDeviceID;
	
        lOpeningMode = SND_fn_lGetCurrentCdMode();
        switch (lOpeningMode)
        {
            // MCI_OPEN : un CD est en train d'etre joué
            case MCI_OPEN : break;
            // MCI_MODE_NOT_READY :  aucun player CD actif
            case MCI_MODE_NOT_READY: break;
            // MCI_MODE_PAUSE : un player CD est actif
            case MCI_MODE_PAUSE: break;
            // MCI_MODE_PLAY : un player CD est actif
            case MCI_MODE_PLAY: break;
            // MCI_MODE_STOP : un player CD est actif
            case MCI_MODE_STOP: break;
            // MCI_MODE_OPEN : la porte du lecteur CD est ouverte
            case MCI_MODE_OPEN: SND_fn_vCloseCDDoor();break;
            // MCI_MODE_RECORD : peu probable
            case MCI_MODE_RECORD: break;
            // MCI_MODE_SEEK : un player CD est actif
            case MCI_MODE_SEEK : break;
            default : SND_fn_vDisplayError(E_uwSndSystemError,"SND_fn_lGetCurrentCdMode returns unknown value");
        }

		// Le CD est présent ou pas dans le lecteur
		if (!SND_fn_bMediaPresent())
			return C_INIT_FAILED;

		// Un CD est present, on teste sa validite, en fonction du nombre de tracks

#ifdef _RIGHT_CD_NB_TRACKS        
        
        while ((stCDDriver.lNbCdTracks=SND_fn_lGetNbCdTracks()) != NB_CD_TRACKS)
			{
				// Le CD contient au moins une piste audio
				if (stCDDriver.lNbCdTracks)
					mciReturn = SND_fn_iErreurInit(SND_CD_NOT_THE_GOOD_ONE_BUT_AUDIO);
				else
					mciReturn = SND_fn_iErreurInit(SND_CD_NOT_THE_GOOD_ONE_AND_NOT_AUDIO);

                switch (mciReturn)
				{
				// on joue ce CD dans l'ordre des tracks au premier evenement PLAY
				case INIT_PLAY:
					stCDDriver.ucState = CD_STATE_ALL_TRACKS;
					goto INIT_CD_OK;
				// on joue ce CD comme si c'etait le bon
				case INIT_PLAYASIF:
					stCDDriver.ucState = CD_STATE_NORMAL;
					goto INIT_CD_OK;
				// on continue sans musique CD
				case INIT_IGNORE:
					stCDDriver.ucState = CD_STATE_UNUSED;
					stCDDriver.bCdDriverOk = FALSE;
					return C_INIT_FAILED;
				// on sort du programme
				case INIT_FAILED:
					stCDDriver.bCdDriverOk = FALSE;
					return C_INIT_FAILED;
				// on reessaye jusqu'a avoir le bon CD
				case INIT_RETRY:
					break;
				default:
					SND_fn_vDisplayError(E_uwSndResourceMissing,"");
					return C_INIT_FAILED;
				}
			}
INIT_CD_OK : 

#else
        // on initialise le nombre de piste du CD
        stCDDriver.lNbCdTracks = SND_fn_lGetNbCdTracks();
        stCDDriver.ucState = CD_STATE_NORMAL;
#endif

		stCDDriver.bCdDriverOk = TRUE;
		
		// Set the time format to track/minute/second/frame (TMSF).
		set.dwTimeFormat=MCI_FORMAT_TMSF;
		if (mciReturn=mciSendCommand(stCDDriver.uiCdDeviceId,MCI_SET,MCI_SET_TIME_FORMAT,(DWORD)(LPVOID)&set))
		{
			stCDDriver.bCdDriverOk = FALSE;
		}
	}
	
	// Demande du nom d'installation du driver CD

	sysinfo.lpstrReturn = texte_erreur;
	sysinfo.dwRetSize = 1024;
	sysinfo.wDeviceType = MCI_DEVTYPE_CD_AUDIO; 

	if (mciReturn = mciSendCommand(stCDDriver.uiCdDeviceId, MCI_SYSINFO, MCI_SYSINFO_INSTALLNAME , (DWORD) (LPMCI_SYSINFO_PARMS) &sysinfo))
	{
		mciGetErrorString(mciReturn,texte_erreur,sizeof(texte_erreur));
		SND_fn_vDisplayError(E_uwSndResourceMissing,texte_erreur);
	}

	if (!SND_fn_iMixerInitCD())
	{
		// On sauve l'ancienne valeur du volume CD
		stCDDriver.iLastCDVolume = SND_fn_dwGetCDValue();
		ucFacteurVolume = stCDDriver.iLastCDVolume;
		SND_fn_vSetCDValue(ucFacteurVolume);
	}

	return C_INIT_OK;
}
#endif //DISABLE_CD

//-----------------------------------------------------------
//	SND_fn_iTestInitCd
//
//	But			: Verifier l'etat d'init du driver midi
//	Entrées : neant
//	Sorties : True ou False suivant l'etat
//------------------------------------------------------------
SndBool SND_CALL SND_fn_bTestInitCD(void)
{
#ifndef DISABLE_CD
	return (stCDDriver.bCdDriverOk);
#else
    return FALSE;
#endif //DISABLE_CD
}

//-----------------------------------------------------------
//	SND_fn_iDesInitCd
//
//	But			: Realiser la desnit bas niveau du driver midi
//	Entrées : neant
//	Sorties : neant
//------------------------------------------------------------
void SND_CALL SND_fn_vDesInitCD(void)
{
#ifndef DISABLE_CD
	HANDLE hEventSynchro;

    if (hCDThread)
    {
		hEventSynchro = CreateEvent(NULL,FALSE,FALSE,NULL);
		if (!PostThreadMessage(dwCDThreadId,(UINT)SND_DESINITCD_MSG,(WPARAM)dw_a5_Params,(LPARAM)hEventSynchro))
   			SND_fn_vDisplayError(E_uwSndCDClose,"PostThreadMessage Desinit");

        WaitForSingleObject(hEventSynchro,INFINITE);
		CloseHandle(hEventSynchro);
		CloseHandle(hCDThread);
    }
#endif //DISABLE_CD
}

#ifndef DISABLE_CD
//-----------------------------------------------------------
//	SND_fn_iDesInitCdThread
//
//	But		: Realiser la desnit bas niveau du driver midi
//	Entrées : neant
//	Sorties : neant
//------------------------------------------------------------
void SND_fn_vDesInitCDThread(void)
{
	int i;
	MCIERROR mciReturn;
	
	for (i=0;i<iNbCdTracks;i++)
	{
		SND_fn_bUnloadCDTrack(i);
	}
	
	//	Desinit toutes pistes CD

	for (i=0;i<NB_MAX_CD_TRACKS;i++)
	{
		stCDTracks[i].tmsfStartTrack = 0;
		stCDTracks[i].tmsfEndTrack = 0;
		stCDTracks[i].dwStartingFrame = 0;
		stCDTracks[i].dwEndingFrame = 0;
		stCDTracks[i].dwStartLoopingFrame = 0;
		stCDTracks[i].dwEndLoopingFrame = 0;

	}

	//	Desinit piste CD active

	stActiveCDTrack.bPaused = FALSE;
	stActiveCDTrack.bActive = FALSE;
	stActiveCDTrack.bLoop = FALSE;
	stActiveCDTrack.lVoice = NO_CD_VOICE;
	stActiveCDTrack.pfnSoundCallback= NULL;
	stActiveCDTrack.lParSoundCallback=0;
	stActiveCDTrack.pstSoundParam = NULL;
	stActiveCDTrack.lPrio = 0;


	if (stCDDriver.bCdDriverOk)
	{
		if (mciReturn=mciSendCommand(stCDDriver.uiCdDeviceId,MCI_CLOSE,MCI_WAIT,(DWORD)NULL))
		{
			mciGetErrorString(mciReturn,texte_erreur,sizeof(texte_erreur));
			SND_fn_vDisplayError(E_uwSndCDClose,texte_erreur);
		}
		// On restaure l'ancienne valeur du volume CD
		SND_fn_vSetCDValue(stCDDriver.iLastCDVolume);

	    SND_fn_iMixerDesInitCD();
	}
	stCDDriver.bCdDriverOk = FALSE;

	DestroyWindow(hSndCdWnd);
}
#endif //DISABLE_CD

//-----------------------------------------------------------
//	SND_fn_vRemoveCallbackCd
//
//	But			: Remettre a zero la callback de la ressource 
//						joue sur la voie voice
//	Entrées : voie jouee
//	Sorties : neant
//------------------------------------------------------------
void SND_CALL SND_fn_vRemoveCallbackCD(long voice)
{
#ifndef DISABLE_CD
	stActiveCDTrack.pfnSoundCallback= NULL;
	stActiveCDTrack.lParSoundCallback= 0;
#endif //DISABLE_CD
}
//-----------------------------------------------------------
//	SND_fn_bSetParamCd
//
//	But			: Faire varier en temps reel les parametres
//						de la ressource midi
//	Entrées : voie jouee, parametres sonores
//	Sorties : TRUE si maj possible FALSE sinon
//------------------------------------------------------------
SndBool SND_CALL SND_fn_bSetParamCD(long voice,SoundParam *par)
{
#ifndef DISABLE_CD
	return (stActiveCDTrack.bActive);
#else
    return FALSE;
#endif //DISABLE_CD
}
//-----------------------------------------------------------
//	SND_fn_bTestIsPlayingCd
//
//	But			: Une ressource midi est-elle active
//	Entrées : voie jouee
//	Sorties : Activite midi si voie bonne, FALSE sinon
//------------------------------------------------------------
SndBool SND_CALL SND_fn_bTestIsPlayingCD(long voice)
{
#ifndef DISABLE_CD
	return (stActiveCDTrack.bActive);
#else
    return FALSE;
#endif //DISABLE_CD
}

#ifndef DISABLE_CD
int nbCalled = 0;

//-----------------------------------------------------------
//	SND_fn_lGetCurrentMode
//
//	But			: Renvoie la callback midi
//	Entrées : neant
//	Sorties : Mode du device correspondant a la voie
//------------------------------------------------------------
void SND_fn_vCloseCDDoor()
{
	MCI_SET_PARMS set;
	MCIERROR mciReturn;

	if (mciReturn = mciSendCommand(stCDDriver.uiCdDeviceId,MCI_SET,MCI_WAIT|MCI_SET_DOOR_CLOSED,(DWORD)(LPVOID)&set))
	{
		mciGetErrorString(mciReturn,texte_erreur,sizeof(texte_erreur));
		SND_fn_vDisplayError(E_uwSndCDSet,texte_erreur);
	}
}


//-----------------------------------------------------------
//	SND_fn_lGetCdTrack
//
//	But			: Renvoie la voie bas niveau sur laquelle
//						est chargee une ressource
//	Entrées : Id de la ressource
//	Sorties : voie bas niveau si resource chargee, NO_CD_VOICE sinon
//------------------------------------------------------------
long SND_fn_lGetCdTrack(tdxId Id)
{
	int i = 0;
	while (	(stCDTracks[i].uCDTrack.pstPtr) &&
					(stCDTracks[i].uCDTrack.pstPtr->Id != Id) &&
					(++i < NB_MAX_CD_TRACKS));

	if ((i == NB_MAX_CD_TRACKS) || ((!i)&&(!stCDTracks[i].uCDTrack.pstPtr)))
		return (NO_CD_VOICE);
	else
		return (i);
}
//-----------------------------------------------------------
//	SND_fn_lGetActiveTrack
//
//	But			: Renvoie la voie active
//	Entrées : neant
//	Sorties : voie bas niveau active, NO_CD_VOICE sinon
//------------------------------------------------------------
long SND_fn_lGetActiveCdTrack()
{
	return (stActiveCDTrack.lVoice);
}
//-----------------------------------------------------------
//	SND_fn_lGetCurrentMode
//
//	But			: Renvoie la callback midi
//	Entrées : neant
//	Sorties : Mode du device correspondant a la voie
//------------------------------------------------------------
long SND_fn_lGetCurrentCdMode()
{
  MCI_STATUS_PARMS status;
  MCIERROR mciReturn;

	if (stCDDriver.bCdDriverOk) 
	{
		// Mode actuel de la piste
		status.dwItem = MCI_STATUS_MODE;  
		if (mciReturn=mciSendCommand(stCDDriver.uiCdDeviceId,MCI_STATUS,MCI_STATUS_ITEM,(DWORD)(LPVOID)&status))
		{
			mciGetErrorString(mciReturn,texte_erreur,sizeof(texte_erreur));
  	    	SND_fn_vDisplayError(E_uwSndCDStatus,texte_erreur);
			return (0);
		}
		return(status.dwReturn);
	}
	else
		return (0);
}

//-----------------------------------------------------------
//	SND_fn_lGetCDLength
//
//	But			: Renvoie la longueur totale d'un CD
//	Entrées : neant
//	Sorties : longueur totale du CD
//------------------------------------------------------------
long SND_fn_lGetCDLength()
{
  MCI_STATUS_PARMS status;
  MCIERROR mciReturn;

	if (stCDDriver.bCdDriverOk)
	{
		// Mode actuel de la piste
		status.dwItem = MCI_STATUS_LENGTH;  
		if (mciReturn=mciSendCommand(stCDDriver.uiCdDeviceId,MCI_STATUS,MCI_STATUS_ITEM,(DWORD)(LPVOID)&status))
		{
			mciGetErrorString(mciReturn,texte_erreur,sizeof(texte_erreur));
			SND_fn_vDisplayError(E_uwSndCDStatus,texte_erreur);
  		return (0);
		}
		return(status.dwReturn);
	}
	else
		return (0);
}

//-----------------------------------------------------------
//	SND_fn_l_GetCurrentPosition
//
//	But			: Renvoie la position courante de lecture
//						de la voie active
//	Entrées : neant
//	Sorties : position en millisecondes
//------------------------------------------------------------
long SND_fn_lGetCurrentCdPosition()
{
  MCI_STATUS_PARMS status;
  MCIERROR mciReturn;
  
	// Mode actuel de la piste
	status.dwItem = MCI_STATUS_MODE;  
	if (mciReturn=mciSendCommand(stCDDriver.uiCdDeviceId,MCI_STATUS,MCI_STATUS_ITEM,(DWORD)(LPVOID)&status))
	{
		mciGetErrorString(mciReturn,texte_erreur,sizeof(texte_erreur));
		SND_fn_vDisplayError(E_uwSndCDStatus,texte_erreur);
		return (0);
	}
	if ((status.dwReturn == MCI_MODE_PLAY)||(status.dwReturn == MCI_MODE_STOP))
	{
		// Si la piste est en mode play ou en pause
		status.dwItem = MCI_STATUS_POSITION;  
		if (mciReturn=mciSendCommand(stCDDriver.uiCdDeviceId,MCI_STATUS,MCI_STATUS_ITEM,(DWORD)(LPVOID)&status))
		{
			mciGetErrorString(mciReturn,texte_erreur,sizeof(texte_erreur));
			SND_fn_vDisplayError(E_uwSndCDStatus,texte_erreur);
	  		return (0);
		}

		return (status.dwReturn);
	}
	else return (0);
}


//-----------------------------------------------------------
//	SND_ulGetCDTrackLengthInMs
//
//	But			: Calcule la duree exacte de la piste en millisecondes
//	Entrées : La voie active
//	Sorties : La position en tmsf 
//------------------------
unsigned long SND_fn_ulGetCDTrackLengthInMs(unsigned long ulVoice)
{
	unsigned long ulTrackLength = 0, debut = 0, fin = 0, ulRealLength;
	unsigned long debutLoop = 0, finLoop = 0;
	
	debut = SND_TMSF_MS(stCDTracks[ulVoice].dwStartingFrame);
	fin = SND_TMSF_MS(stCDTracks[ulVoice].dwEndingFrame);
	debutLoop = SND_TMSF_MS(stCDTracks[ulVoice].dwStartLoopingFrame);
	finLoop = SND_TMSF_MS(stCDTracks[ulVoice].dwEndLoopingFrame);
	ulTrackLength = SND_TMSF_MS(stCDTracks[ulVoice].tmsfEndTrack);

	if (fin)
		if (debut)
			ulRealLength = fin - debut;
		else
			ulRealLength = fin;
	else
		if (debut)
			ulRealLength = ulTrackLength - debut;
		else
			ulRealLength = ulTrackLength;


	if (stActiveCDTrack.bLoop && stActiveCDTrack.iNbLoops > 0)
	{
		if (debutLoop && finLoop)
			ulRealLength += (stActiveCDTrack.iNbLoops) * (finLoop - debutLoop);
		else
			if (debutLoop)
				ulRealLength += (stActiveCDTrack.iNbLoops) * (fin - debutLoop);
			else
				ulRealLength += (stActiveCDTrack.iNbLoops) * (finLoop - debut);

	}
	return (ulRealLength);
}


//-----------------------------------------------------------
//	SND_fn_ulInitFadeCD
//
//	But			: Initialise les variables de fading
//	Entrées : 
//	Sorties : 
//------------------------
void SND_fn_ulInitFadeCD(	unsigned long ulFadeInDuration,
							unsigned long ulFadeOutDuration)
{
	lNbIterFadeIn =	VOL_MAX_CD;
	lNbIterFadeOut = VOL_MAX_CD;

	if (ulFadeInDuration+ulFadeOutDuration < stActiveCDTrack.ulTrackLengthInMs)
	{
		bCDFadeIn = (SndBool)ulFadeInDuration;
		bCDFadeOut = (SndBool)ulFadeOutDuration;

		if (bCDFadeIn)
		{
			ulTimerPeriodFadeIn = ulFadeInDuration / lNbIterFadeIn;
			ulCountDownFadeIn = 0;
			ucFacteurVolume = 0;
			// init du timer cross-fade
			uiTimerCDFadeIn = timeSetEvent(ulTimerPeriodFadeIn,10,(LPTIMECALLBACK)SND_fn_lCDTimerIn,(DWORD)0,TIME_PERIODIC);
		}
		if (bCDFadeOut)
		{
			ulTimerPeriodFadeOut = ulFadeOutDuration / lNbIterFadeIn;
			ulCountDownFadeOut = 0;
			if (!bCDFadeIn)
			{
				ucFacteurVolume = VOL_MAX_CD;
			}
			// init du timer cross-fade
			uiTimerCDFadeOut = timeSetEvent(ulTimerPeriodFadeOut,10,(LPTIMECALLBACK)SND_fn_lCDTimerOut,(DWORD)0,TIME_PERIODIC);
		}
		SND_fn_vSetCDValue(ucFacteurVolume);
	}
	else
	{
		SND_fn_vSetCDValue(VOL_MAX_CD);
		SND_fn_vDisplayError(E_uwSndCDFade,"Bad Fading Parameters");
	}
}


//-----------------------------------------------------------
//	SND_fn_dwCalculateFrom
//
//	But			: Calcule le point de depart du play
//	Entrées : La voie active
//	Sorties : La position en tmsf 
//------------------------------------------------------------
DWORD SND_fn_dwCalculateFrom(long lVoice)
{
	DWORD dwFrom = 0;
	tdstCDTrack * pstActiveCDTrack = &stCDTracks[lVoice];

	// ce n'est pas le premier tour de boucle
	if (stActiveCDTrack.iNbLoops < pstActiveCDTrack->iNbLoops)
	{
		// le debut de boucle est different de zero
		if (pstActiveCDTrack->dwStartLoopingFrame)
			dwFrom = pstActiveCDTrack->dwStartLoopingFrame;	
		else
			// le debut de boucle est egal a zero
			if (pstActiveCDTrack->dwStartingFrame)
				dwFrom = pstActiveCDTrack->dwStartingFrame;	
	}
	else	
	// premier tour
		if (pstActiveCDTrack->dwStartingFrame)
			dwFrom = pstActiveCDTrack->dwStartingFrame;
		
	dwFrom |= pstActiveCDTrack->tmsfStartTrack;
	return dwFrom;
}

//-----------------------------------------------------------
//	SND_fn_dwCalculateTo
//
//	But			: 
//	But			: Calcule le point de fin du play
//	Entrées : La voie active
//	Sorties : La position en tmsf 
//------------------------------------------------------------

DWORD SND_fn_dwCalculateTo(long lVoice)
{
	DWORD dwTo = 0;
	tdstCDTrack * pstActiveCDTrack = &stCDTracks[lVoice];

	// cas du dernier tour de boucle, l'indice est a zero
	if (!stActiveCDTrack.iNbLoops)
	{
		// on finit la lecture soit sur la fin de trame, soit sur la fin de piste
		if (pstActiveCDTrack->dwEndingFrame)
			dwTo = pstActiveCDTrack->dwEndingFrame;
		else
			dwTo = pstActiveCDTrack->tmsfEndTrack;
	}
	else
	// cas des n-1 premiers tours, on boucle sur la fin de bouclage
		if (pstActiveCDTrack->dwEndLoopingFrame)
			dwTo = pstActiveCDTrack->dwEndLoopingFrame;
		else
			// pas de fin de bouclage, on boucle sur la fin de trame ou de piste
			if (pstActiveCDTrack->dwEndingFrame)
				dwTo = pstActiveCDTrack->dwEndingFrame;
			else
				dwTo = pstActiveCDTrack->tmsfEndTrack;
	
	dwTo |= pstActiveCDTrack->tmsfStartTrack;
	return dwTo;
}
#endif //DISABLE_CD

//-----------------------------------------------------------
//	SND_fn_lPlayCD
//
//	But			: Joue la ressource CD res
//	Entrées : La ressource, parametres son, la priorite et 
//						la callback
//	Sorties : voie allouee pour jouee la ressource
//------------------------------------------------------------
long SND_CALL SND_fn_lPlayCD(tduRefRes res,SoundParam *par,long prio,SND_td_pfn_vSoundCallback fn_callback,long par_callback)
{
#ifndef DISABLE_CD
	HANDLE hEventSynchro;

	if (stCDDriver.bCdDriverOk)
	{
		hEventSynchro = CreateEvent(NULL,FALSE,FALSE,NULL);
		dw_a5_Params[0] = (DWORD)&res;
		dw_a5_Params[1] = (DWORD)par;
		dw_a5_Params[2] = (DWORD)prio;
		dw_a5_Params[3] = (DWORD)fn_callback;
		dw_a5_Params[4] = (DWORD)par_callback;

		if (!PostThreadMessage(dwCDThreadId,(UINT)SND_PLAYCD_MSG,(WPARAM)dw_a5_Params,(LPARAM)hEventSynchro))
   			SND_fn_vDisplayError(E_uwSndCDPlay,"PostThreadMessage");
	
		WaitForSingleObject(hEventSynchro,INFINITE);
		CloseHandle(hEventSynchro);
	
		// dans ce cas, on retourne le numero de voie dans dw_a5_Params[0]
		return (dw_a5_Params[0] | OFFSET_CD_VOICE);
	}
	else
#endif //DISABLE_CD
		return (C_PLAY_FAILED);
}

#ifndef DISABLE_CD
//-----------------------------------------------------------
//	SND_fn_lPlayCD
//
//	But			: Joue la ressource CD res
//	Entrées : La ressource, parametres son, la priorite et 
//						la callback
//	Sorties : voie allouee pour jouee la ressource
//------------------------------------------------------------
long SND_fn_lPlayCDThread(tduRefRes res,SoundParam *par,long prio,SND_td_pfn_vSoundCallback fn_callback,long par_callback)
{
	MCIERROR mciReturn;
	long lVoice = 0,lIndexVoice;
	MCI_PLAY_PARMS play;

	if (stCDDriver.bCdDriverOk)
	{
		// ceci correspond au premier play d'une piste bouclante
		if (stActiveCDTrack.lVoice == NO_CD_VOICE)
		{
	
			lVoice = SND_fn_lGetCdTrack(res.pstPtr->Id);

debut:
			lIndexVoice = SND_fn_lGetCdTrack(res.pstPtr->Id);

			// on recharge la ressource au cas ou elle aurait ete modifiée par l'editeur
			if (!SND_fn_bLoadCDTrackThread(res.pstPtr,lIndexVoice))
				return (C_PLAY_FAILED);

			stActiveCDTrack.bLoop = stCDTracks[lIndexVoice].bLoop;
			stActiveCDTrack.iNbLoops = stCDTracks[lIndexVoice].iNbLoops;
			stActiveCDTrack.lIndexVoice = lIndexVoice;
			stActiveCDTrack.lVoice = lVoice;
			stActiveCDTrack.bActive = TRUE; 
			stActiveCDTrack.bPaused = FALSE;
			stActiveCDTrack.lPrio = prio;
			stActiveCDTrack.pstSoundParam = par;
			stActiveCDTrack.pfnSoundCallback=fn_callback;
			stActiveCDTrack.lParSoundCallback=par_callback;
			stActiveCDTrack.ulTrackLengthInMs = SND_fn_ulGetCDTrackLengthInMs(lIndexVoice);
		}
		else
		{
			// on a un ressource bouclante qui en est a son deuxieme tout minimum
			if ((stActiveCDTrack.bActive && stActiveCDTrack.bLoop)&&
				(stActiveCDTrack.iNbLoops != stCDTracks[stActiveCDTrack.lIndexVoice].iNbLoops))
				lVoice = stActiveCDTrack.lVoice;
			// on a une ressource pas bouclante mais il y a eu un play sans stop de la ressource precedente
			else
			{
				// on doit reallouer la meme voix pour la ressource suivante
				lVoice = stActiveCDTrack.lVoice;
				SND_fn_vStopCDThread(stActiveCDTrack.lVoice);
				goto debut;
			}

		}
		
		play.dwCallback = (DWORD)hSndCdWnd;
		if (stCDDriver.ucState == CD_STATE_ALL_TRACKS)
		{
			if (mciReturn=mciSendCommand(stCDDriver.uiCdDeviceId,MCI_PLAY,MCI_NOTIFY,(DWORD)(LPVOID)&play))
			{
				mciGetErrorString(mciReturn,texte_erreur,sizeof(texte_erreur));
    			SND_fn_vDisplayError(E_uwSndCDPlay,texte_erreur);
		  		return (C_PLAY_FAILED);
			}
		}
		else
		{
			play.dwFrom = SND_fn_dwCalculateFrom(stActiveCDTrack.lIndexVoice);
			play.dwTo = SND_fn_dwCalculateTo(stActiveCDTrack.lIndexVoice);

			if (mciReturn=mciSendCommand(stCDDriver.uiCdDeviceId,MCI_PLAY,MCI_FROM|MCI_TO|MCI_NOTIFY,(DWORD)(LPVOID)&play))
			{
				mciGetErrorString(mciReturn,texte_erreur,sizeof(texte_erreur));
    			SND_fn_vDisplayError(E_uwSndCDPlay,texte_erreur);
		  		return (C_PLAY_FAILED);
			}

		}

		return (lVoice | OFFSET_CD_VOICE);
	}
	else
		return (C_PLAY_FAILED);
}

//-----------------------------------------------------------
//	SND_fn_lPlayCDWithFade
//
//	But			: Joue la ressource CD res avec un fade
//	Entrées : La ressource, parametres son, la priorite et 
//						la callback
//	Sorties : voie allouee pour jouee la ressource
//------------------------------------------------------------
long SND_CALL SND_fn_lPlayCDWithFade(tduRefRes res,SoundParam *par,long prio,SND_td_pfn_vSoundCallback fn_callback,long par_callback,unsigned long ulFadeIn,unsigned long ulFadeOut)
{
	long result;
	result = SND_fn_lPlayCD(res,par,prio,fn_callback,par_callback);
	SND_fn_ulInitFadeCD(ulFadeIn,ulFadeOut);
	return (result);
}
#endif //DISABLE_CD


//-----------------------------------------------------------
//	SND_fn_vStopCd
//
//	But			: Stopper une voie
//	Entrées : la voie a stopper
//	Sorties : neant
//------------------------------------------------------------
void SND_CALL SND_fn_vStopCD(long voice)
{
#ifndef DISABLE_CD
	HANDLE hEventSynchro;
	dw_a5_Params[0] = (DWORD)voice;
	
	if (stCDDriver.bCdDriverOk)
	{
		hEventSynchro = CreateEvent(NULL,FALSE,FALSE,NULL);
		if (!PostThreadMessage(dwCDThreadId,(UINT)SND_STOPCD_MSG,(WPARAM)dw_a5_Params,(LPARAM)hEventSynchro))
   			SND_fn_vDisplayError(E_uwSndCDStop,"PostThreadMessage Resume");

		WaitForSingleObject(hEventSynchro,INFINITE);
		CloseHandle(hEventSynchro);
	}
#endif //DISABLE_CD
}

#ifndef DISABLE_CD
//-----------------------------------------------------------
//	SND_fn_vStopCdThread
//
//	But			: Stopper une voie
//	Entrées : la voie a stopper
//	Sorties : neant
//------------------------------------------------------------
void SND_fn_vStopCDThread(long voice)
{
	MCIERROR mciReturn;
	MCI_GENERIC_PARMS stop;
   	SECURITY_ATTRIBUTES sa;

	long lRealVoice = voice & MASK_OFFSET_CD_VOICE;

	if ((stCDDriver.bCdDriverOk) && (lRealVoice < NB_MAX_CD_TRACKS))
	{
		stop.dwCallback = (DWORD)hSndCdWnd;
		if (mciReturn=mciSendCommand(stCDDriver.uiCdDeviceId,MCI_STOP,MCI_WAIT,(DWORD)NULL))
		{
			mciGetErrorString(mciReturn,texte_erreur,sizeof(texte_erreur));
			SND_fn_vDisplayError(E_uwSndCDStop,texte_erreur);
		}

        sa.nLength = sizeof(SECURITY_ATTRIBUTES);
	    sa.bInheritHandle = TRUE;
	    sa.lpSecurityDescriptor = NULL;
        dw_a2_threadParams[0] = (DWORD)stActiveCDTrack.pfnSoundCallback;
        dw_a2_threadParams[1] = (DWORD)stActiveCDTrack.lParSoundCallback;
        if (!(hCDThreadCallBack = CreateThread(&sa,0,(LPTHREAD_START_ROUTINE)SND_fn_iCDThreadCallBack,(LPVOID)dw_a2_threadParams,0,&dwCDThreadIdCallBack)))
		    SND_fn_vDisplayError(E_uwSndCDCreateThread,"");

		//	Desinit piste CD active

		stActiveCDTrack.bPaused = FALSE;
		stActiveCDTrack.bActive = FALSE;
		stActiveCDTrack.bLoop = FALSE;
		stActiveCDTrack.lVoice = NO_CD_VOICE;
		stActiveCDTrack.pstSoundParam = NULL;
		stActiveCDTrack.lPrio = 0;
	}
}

//-----------------------------------------------------------
//	SND_fn_vStopCdWithFade
//
//	But			: Stopper une voie en faisant un fade 
//	Entrées : la voie a stopper
//	Sorties : neant
//------------------------------------------------------------
void SND_CALL SND_fn_vStopCDWithFade(long voice,unsigned long ulFadeOutDuration)
{
	lNbIterFadeOut = SND_fn_dwGetCDValue();

	bCDFadeOut = (SndBool)ulFadeOutDuration;
	if (lNbIterFadeOut)
		ulTimerPeriodFadeOut = ulFadeOutDuration / lNbIterFadeOut;
	else
		ulTimerPeriodFadeOut = 100;
	ulCountDownFadeOut = 0;
	// Si le timer etait deja programme pour un fade out alors, on le reprogramme
	// on passe un parametre special pour la callback FADE_AND_STOP_CD

	if (uiTimerCDFadeOut != 0)
		timeKillEvent(uiTimerCDFadeOut);

	uiTimerCDFadeOut = timeSetEvent(ulTimerPeriodFadeOut,10,(LPTIMECALLBACK)SND_fn_lCDTimerOut,(DWORD)FADE_AND_STOP_CD,TIME_PERIODIC);
	stActiveCDTrack.ulTrackLengthInMs = ulFadeOutDuration;
}
#endif //DISABLE_CD

//-----------------------------------------------------------
//	SND_fn_vPauseCd
//
//	But			: Pauser une voie
//	Entrées : la voie a pauser
//	Sorties : true,false
//------------------------------------------------------------
void SND_CALL SND_fn_vPauseCD(long voice)
{
#ifndef DISABLE_CD
	HANDLE hEventSynchro;
	dw_a5_Params[0] = (DWORD)voice;
	
	if (stCDDriver.bCdDriverOk)
	{
		hEventSynchro = CreateEvent(NULL,FALSE,FALSE,NULL);
		if (!PostThreadMessage(dwCDThreadId,(UINT)SND_PAUSECD_MSG,(WPARAM)dw_a5_Params,(LPARAM)hEventSynchro))
   			SND_fn_vDisplayError(E_uwSndCDPlay,"PostThreadMessage Pause");

		WaitForSingleObject(hEventSynchro,INFINITE);
		CloseHandle(hEventSynchro);
	}
#endif //DISABLE_CD
}

#ifndef DISABLE_CD
//-----------------------------------------------------------
//	SND_fn_vPauseCdThread
//
//	But			: Pauser une voie
//	Entrées : la voie a pauser
//	Sorties : true,false
//------------------------------------------------------------
void SND_fn_vPauseCDThread(long voice)
{
	MCIERROR mciReturn;
	MCI_GENERIC_PARMS pause;

	if (stCDDriver.bCdDriverOk) 
	{
		pause.dwCallback = (DWORD)hSndCdWnd;
//		if (mciReturn=mciSendCommand(stCDDriver.uiCdDeviceId,MCI_STOP,MCI_NOTIFY,(DWORD)&pause))
		// on sauve la position avant la pause
		stActiveCDTrack.lPausePos = SND_fn_lGetCurrentCdPosition();
		if (mciReturn=mciSendCommand(stCDDriver.uiCdDeviceId,MCI_PAUSE,MCI_WAIT,(DWORD)&pause))
		{
			mciGetErrorString(mciReturn,texte_erreur,sizeof(texte_erreur));
			SND_fn_vDisplayError(E_uwSndCDStop,texte_erreur);
            return;
		}
		stActiveCDTrack.bPaused = TRUE;
	}
}
#endif //DISABLE_CD


//-----------------------------------------------------------
//	SND_fn_vResumeCd
//
//	But			: Resumer une voie
//	Entrées : la voie a resumer
//	Sorties : True,false
//------------------------------------------------------------
void SND_CALL SND_fn_vResumeCD(long voice)
{
#ifndef DISABLE_CD
	HANDLE hEventSynchro;
	dw_a5_Params[0] = (DWORD)voice;
	
	if (stCDDriver.bCdDriverOk)
	{
		hEventSynchro = CreateEvent(NULL,FALSE,FALSE,NULL);
		if (!PostThreadMessage(dwCDThreadId,(UINT)SND_RESUMECD_MSG,(WPARAM)dw_a5_Params,(LPARAM)hEventSynchro))
   			SND_fn_vDisplayError(E_uwSndCDPlay,"PostThreadMessage Resume");

		WaitForSingleObject(hEventSynchro,INFINITE);
		CloseHandle(hEventSynchro);
	}
#endif //DISABLE_CD
}


#ifndef DISABLE_CD
//-----------------------------------------------------------
//	SND_fn_vResumeCd
//
//	But			: Resumer une voie
//	Entrées : la voie a resumer
//	Sorties : True,false
//------------------------------------------------------------
void SND_fn_vResumeCDThread(long voice)
{
	MCIERROR mciReturn;
	MCI_PLAY_PARMS playresume;

	if (stCDDriver.bCdDriverOk) 
	{
		playresume.dwCallback = (DWORD)hSndCdWnd;
		if (stActiveCDTrack.bPaused)
			if (stCDDriver.ucState == CD_STATE_ALL_TRACKS)
			{
				if (mciReturn=mciSendCommand(stCDDriver.uiCdDeviceId,MCI_PLAY,MCI_NOTIFY,(DWORD)(LPVOID)&playresume))
				{
					mciGetErrorString(mciReturn,texte_erreur,sizeof(texte_erreur));
					SND_fn_vDisplayError(E_uwSndCDPlay,texte_erreur);
                    return;
				}
			}
			else
			{
				playresume.dwTo = SND_fn_dwCalculateTo(stActiveCDTrack.lVoice);
				// on restaure la position sauve lors du pause
				playresume.dwFrom = stActiveCDTrack.lPausePos;
				if (mciReturn=mciSendCommand(stCDDriver.uiCdDeviceId,MCI_PLAY,MCI_FROM|MCI_TO|MCI_NOTIFY,(DWORD)(LPVOID)&playresume))
				{
					mciGetErrorString(mciReturn,texte_erreur,sizeof(texte_erreur));
					SND_fn_vDisplayError(E_uwSndCDPlay,texte_erreur);
                    return;
				}
				/*
				if (mciReturn=mciSendCommand(stCDDriver.uiCdDeviceId,MCI_RESUME,MCI_NOTIFY,(DWORD)(LPVOID)&playresume))
				{
					mciGetErrorString(mciReturn,texte_erreur,sizeof(texte_erreur));
					SND_fn_vDisplayError(E_uwSndCDPlay,texte_erreur);
				}*/
			}
		stActiveCDTrack.bPaused = FALSE;
	}
}

//-----------------------------------------------------------
//	SND_fn_vNextCDTrack
//
//	But			: passe a la voie suivante 
//	Entrées : neant
//	Sorties : Id de la track suivante
//------------------------------------------------------------
long SND_CALL SND_fn_lNextCDTrack()
{
	HANDLE hEventSynchro;
	
	if (stCDDriver.bCdDriverOk)
	{
		hEventSynchro = CreateEvent(NULL,FALSE,FALSE,NULL);
		if (!PostThreadMessage(dwCDThreadId,(UINT)SND_NEXTTRACKCD_MSG,(WPARAM)dw_a5_Params,(LPARAM)hEventSynchro))
   			SND_fn_vDisplayError(E_uwSndCDPlay,"PostThreadMessage");
	
		WaitForSingleObject(hEventSynchro,INFINITE);
		CloseHandle(hEventSynchro);
		
		// dans ce cas, on retourne le numero de voie dans dw_a5_Params[0]
		return (dw_a5_Params[0]);
	}
	else
		return (0);
}


//-----------------------------------------------------------
//	SND_fn_vNextCDTrack
//
//	But			: passe a la voie suivante 
//	Entrées : neant
//	Sorties : Id de la track suivante
//------------------------------------------------------------
long SND_fn_lNextCDTrackThread()
{
	MCIERROR mciReturn;
	MCI_SEEK_PARMS seek;
	MCI_STATUS_PARMS status;
	MCI_PLAY_PARMS play;
	DWORD dwTrackNumber;

	if (stCDDriver.bCdDriverOk)
	{
		status.dwItem = MCI_STATUS_CURRENT_TRACK;  
		if (mciReturn=mciSendCommand(stCDDriver.uiCdDeviceId,MCI_STATUS,MCI_STATUS_ITEM,(DWORD)(LPVOID)&status))
		{
			mciGetErrorString(mciReturn,texte_erreur,sizeof(texte_erreur));
			SND_fn_vDisplayError(E_uwSndCDStatus,texte_erreur);
			return (0L);
		}

		seek.dwCallback = (DWORD)hSndCdWnd;
		dwTrackNumber = (status.dwReturn+1) % SND_fn_lGetNbCdTracks();
		seek.dwTo = MCI_MAKE_TMSF(dwTrackNumber,0,0,0);

		if (mciReturn=mciSendCommand(stCDDriver.uiCdDeviceId,MCI_SEEK,MCI_TO|MCI_NOTIFY,(DWORD)&seek))
		{
			mciGetErrorString(mciReturn,texte_erreur,sizeof(texte_erreur));
			SND_fn_vDisplayError(E_uwSndCDSeek,texte_erreur);
			return (0L);
		}

		play.dwCallback = (DWORD)hSndCdWnd;

		if (mciReturn=mciSendCommand(stCDDriver.uiCdDeviceId,MCI_PLAY,MCI_NOTIFY,(DWORD)&play))
		{
			mciGetErrorString(mciReturn,texte_erreur,sizeof(texte_erreur));
			SND_fn_vDisplayError(E_uwSndCDPlay,texte_erreur);
			return (0L);
		}
		return (dwTrackNumber);
	}
	else
		return (0L);
}


//-----------------------------------------------------------
//	SND_fn_bLoadCdTrack
//
//	But			: Charge une ressource CD, a partir du block ressource
//						au depart ou lorsqu'elle est modifiée
//	Entrées : Le block ressource et le nom du fichier midi
//	Sorties : true,false
//------------------------------------------------------------
SndBool SND_fn_bLoadCDTrack(tdstBlockResourceMem * pstPtr,int numTrack)
{
	HANDLE hEventSynchro;

	if (stCDDriver.bCdDriverOk)
	{
		hEventSynchro = CreateEvent(NULL,FALSE,FALSE,NULL);
		dw_a5_Params[0] = (DWORD)pstPtr;
		dw_a5_Params[1] = (DWORD)numTrack;

		if (!PostThreadMessage(dwCDThreadId,(UINT)SND_LOADCD_MSG,(WPARAM)dw_a5_Params,(LPARAM)hEventSynchro))
   			SND_fn_vDisplayError(E_uwSndCDLoadTrack,"PostThreadMessage");
	
		WaitForSingleObject(hEventSynchro,INFINITE);
		CloseHandle(hEventSynchro);
	
		// dans ce cas, on retourne le numero de voie dans dw_a5_Params[0]
		return (dw_a5_Params[0]);
	}
	else
		return (FALSE);
}

//-----------------------------------------------------------
//	SND_fn_bLoadCdTrackThread
//
//	But			: Charge une ressource CD, a partir du block ressource
//						au depart ou lorsqu'elle est modifiée
//	Entrées : Le block ressource et le nom du fichier midi
//	Sorties : true,false
//------------------------------------------------------------
SndBool SND_fn_bLoadCDTrackThread(tdstBlockResourceMem * pstPtr,int numTrack)
{
	MCI_STATUS_PARMS status;
	MCIERROR mciReturn;
	DWORD dwS,dwSL,dwFL,dwF;

	status.dwTrack = pstPtr->uRes.stCD.wTrackId;

	if (stCDDriver.lNbCdTracks >= (long)status.dwTrack)
	{

		//-----------------------------------------------------------------
		// Calcul du debut et de la fin de la piste stockes dans stCDTrack   
		//-----------------------------------------------------------------

		stCDTracks[numTrack].tmsfStartTrack = SND_MAKE_TMSF(status.dwTrack,0,0,0); 
		status.dwCallback = status.dwReturn = 0;
		status.dwItem = MCI_STATUS_LENGTH;
		if (mciReturn=mciSendCommand(stCDDriver.uiCdDeviceId,MCI_STATUS,MCI_STATUS_ITEM|MCI_TRACK,(DWORD) (LPMCI_STATUS_PARMS)&status))
		{
		    mciGetErrorString(mciReturn,texte_erreur,sizeof(texte_erreur));
		    SND_fn_vDisplayError(E_uwSndCDStatus,texte_erreur);
		}
		else
    		stCDTracks[numTrack].tmsfEndTrack = status.dwReturn << 8 | status.dwTrack;

		//-----------------------------------------------------------------
		// Sauvegarde des bornes du play global
		//-----------------------------------------------------------------

        if (pstPtr->uRes.stCD.ulStartingFrame)
            stCDTracks[numTrack].dwStartingFrame = pstPtr->uRes.stCD.ulStartingFrame | stCDTracks[numTrack].tmsfStartTrack;
	    else
            stCDTracks[numTrack].dwStartingFrame = stCDTracks[numTrack].tmsfStartTrack;
        
        if (pstPtr->uRes.stCD.ulEndingFrame)
            stCDTracks[numTrack].dwEndingFrame = pstPtr->uRes.stCD.ulEndingFrame | stCDTracks[numTrack].tmsfStartTrack;
        else
            stCDTracks[numTrack].dwEndingFrame = stCDTracks[numTrack].tmsfEndTrack;
        
		stCDTracks[numTrack].dwStartLoopingFrame = pstPtr->uRes.stCD.ulStartLoop | stCDTracks[numTrack].tmsfStartTrack;
		stCDTracks[numTrack].dwEndLoopingFrame = pstPtr->uRes.stCD.ulEndLoop | stCDTracks[numTrack].tmsfStartTrack;

		dwS = SND_TMSF_MS(stCDTracks[numTrack].dwStartingFrame);
		dwSL = SND_TMSF_MS(stCDTracks[numTrack].dwStartLoopingFrame);
		dwFL = SND_TMSF_MS(stCDTracks[numTrack].dwEndLoopingFrame);
		dwF = SND_TMSF_MS(stCDTracks[numTrack].dwEndingFrame);

		if ((dwSL < dwS)||(dwSL > dwFL))
    		stCDTracks[numTrack].dwStartLoopingFrame = 0;
		if ((dwFL < dwSL)||(dwFL > dwF))
	    	stCDTracks[numTrack].dwEndLoopingFrame = 0;

		//-----------------------------------------------------------------
		// Sauvegarde des parametres utiles a la gestion de la piste active
		//-----------------------------------------------------------------

		// on sauve une reference a la ressource dans la liste des piste midi
		stCDTracks[numTrack].uCDTrack.pstPtr = pstPtr;
		stCDTracks[numTrack].bLoop = pstPtr->uRes.stCD.bLoop;
		stCDTracks[numTrack].iNbLoops = pstPtr->uRes.stCD.iNbLoops;
		return TRUE;
	}
	else
	{
		//SND_fn_vDisplayError(E_uwSndCDNbTracks,MSG_ERR_BAD_CD_ID_TRACK);
		return FALSE;
	}
}


//-----------------------------------------------------------
//	SND_fn_bUnloadCdTrack
//
//	But			: Decharge une piste midi, a partir de la voie
//	Entrées : La voie a decharger
//	Sorties : true,false
//------------------------------------------------------------
SndBool SND_fn_bUnloadCDTrack(int i)
{
  return TRUE;
}

#endif //DISABLE_CD

//#endif //CD_MCI
//waitforsingleobject



//-----------------------------------------------------------
//-----------------------------------------------------------
//-----------------------------------------------------------
//-----------------------------------------------------------
//-----------------------------------------------------------
// GESTION DU MIXER CD
//-----------------------------------------------------------
//-----------------------------------------------------------
//-----------------------------------------------------------
//-----------------------------------------------------------
//-----------------------------------------------------------

int SND_fn_iMixerInitCD()
{
#ifndef DISABLE_CD
	uiNbrMixersCD = mixerGetNumDevs(); //OK
	if (uiNbrMixersCD == 0)
	{
		SND_fn_vDisplayError(E_uwSndMixerNoMixer,"");
		return 1;
	}

	if(mixerGetID((HMIXEROBJ)0,&mixerIdentifierCD,MIXER_OBJECTF_MIXER) !=MMSYSERR_NOERROR)
	{
		SND_fn_vDisplayError(E_uwSndMixerNoId,"");
		return 1;
	}


	if(mixerOpen(&hMixerCD,mixerIdentifierCD,0,0,MIXER_OBJECTF_MIXER/* MIXER_OBJECTF_HWAVEOUT*/) != MMSYSERR_NOERROR)
	{
		SND_fn_vDisplayError(E_uwSndMixerOpen,"");
		return 1;
	}

	if(mixerGetDevCaps((UINT)hMixerCD,&mixerCapsCD,sizeof(MIXERCAPS)) != MMSYSERR_NOERROR)
	{
		SND_fn_vDisplayError(E_uwSndMixerCapacities,"");
		return 1;
	}

	dwNbrAudioLinesCD = mixerCapsCD.cDestinations;

//==============
// MIXERLINE CD
//==============
	mixerLineCD.cbStruct = sizeof(MIXERLINE);
	mixerLineCD.dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC;
	if (mixerGetLineInfo((HMIXEROBJ)hMixerCD,&mixerLineCD,MIXER_GETLINEINFOF_COMPONENTTYPE|MIXER_OBJECTF_HMIXER) != MMSYSERR_NOERROR)
    {
	  //return MIXER_ERR_GETLINEINFOCD;
	    CDControlAvailable = FALSE;
    }
    else
    {
	    CDControlAvailable = TRUE;
    }


//=====================
// MIXERLINECONTROL CD
//=====================
	mixerLineControlsCD.cbStruct = sizeof(MIXERLINECONTROLS);
	mixerLineControlsCD.dwLineID = mixerLineCD.dwLineID;
	mixerLineControlsCD.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
	mixerLineControlsCD.cControls = 1;
	mixerLineControlsCD.cbmxctrl  = sizeof(MIXERCONTROL);
	mixerLineControlsCD.pamxctrl =(MIXERCONTROL *) &mixerControlCD;
	if(mixerGetLineControls((HMIXEROBJ)hMixerCD,&mixerLineControlsCD,MIXER_GETLINECONTROLSF_ONEBYTYPE|MIXER_OBJECTF_HMIXER) != MMSYSERR_NOERROR)
  {		
   //    return MIXER_ERR_GETLINECONTROLSCD;
    CDControlAvailable = FALSE;
  }
  else
  {
	  CDControlAvailable = TRUE;
  }

//============================
// MIXERLINECONTROLDETAILS CD
//============================
	mixerControlDetailsCD.cbStruct = sizeof(MIXERCONTROLDETAILS); 
	mixerControlDetailsCD.dwControlID = mixerControlCD.dwControlID;
	mixerControlDetailsCD.cChannels = 1/*mixerLineCD.cChannels 1*/;
	mixerControlDetailsCD.cMultipleItems = mixerControlCD.cMultipleItems;
	mixerControlDetailsCD.cbDetails =sizeof(MIXERCONTROLDETAILS_UNSIGNED);
	mixerControlDetailsCD.paDetails= &valeurCD; 
#endif //DISABLE_CD
	return 0;
}




DWORD SND_fn_dwGetCDMax()
{
#ifndef DISABLE_CD
  return mixerControlCD.Bounds.dwMaximum;
#else
  return 0;
#endif //DISABLE_CD
}
DWORD SND_fn_dwGetCDMin()
{
#ifndef DISABLE_CD
  return mixerControlCD.Bounds.dwMinimum; 
#else
  return 0;
#endif //DISABLE_CD
}

//=======================================
//void SND_fn_dwGetCDValue(DWORD val)
//=======================================
// Positionne le volume du CD en %
// val entre 0 et 100
//=======================================
DWORD SND_fn_dwGetCDValue()
{
#ifndef DISABLE_CD
  DWORD inc;
  DWORD result;

  if (CDControlAvailable)
  {
    inc = (SND_fn_dwGetCDMax()-SND_fn_dwGetCDMin())/100;

    if(mixerGetControlDetails((HMIXEROBJ)hMixerCD,&mixerControlDetailsCD,MIXER_GETCONTROLDETAILSF_VALUE|MIXER_OBJECTF_HMIXER) != MMSYSERR_NOERROR)
      return 1;

    result = (valeurCD.dwValue - SND_fn_dwGetCDMin())/inc;

    //snd_assert(result>=0);
    snd_assert(result<=100);
    return result;
}
else
#endif //DISABLE_CD
  return 0;
}


//=======================================
//void SND_fn_vSetCDValue(DWORD val)
//=======================================
// Positionne le volume du CD en %
// val entre 0 et 100
//=======================================
void SND_fn_vSetCDValue(DWORD val)
{
#ifndef DISABLE_CD
  // val est en pourcentage
  DWORD inc;

  if (CDControlAvailable)
  {
    //snd_assert(val>=0);
    snd_assert(val<=100);

    inc = (SND_fn_dwGetCDMax()-SND_fn_dwGetCDMin())/100;
    val = (val* inc) + SND_fn_dwGetCDMin();

    snd_assert(val>=SND_fn_dwGetCDMin());
    snd_assert(val<=SND_fn_dwGetCDMax());

    valeurCD.dwValue = val;
 
    if(mixerSetControlDetails((HMIXEROBJ)hMixerCD,&mixerControlDetailsCD,MIXER_SETCONTROLDETAILSF_VALUE|MIXER_OBJECTF_HMIXER) != MMSYSERR_NOERROR)
    {
    //return 1;
    }
  }
#endif //DISABLE_CD
}

BOOL SND_fn_bIsCDControlAvailable()
{
#ifndef DISABLE_CD
  return CDControlAvailable;
#else
  return FALSE;
#endif //DISABLE_CD
}


int SND_fn_iMixerDesInitCD()
{         
#ifndef DISABLE_CD
  if(mixerClose(hMixerCD) != MMSYSERR_NOERROR)
  {
		SND_fn_vDisplayError(E_uwSndMixerDesinit,"");
		return 1;
  }
#endif //DISABLE_CD
  return 0;
}



/*--------------------------------------------------
relache-reprise a chaud des drives
---------------------------------------------------------*/
#define PAUSED_BY_RESTORE_RELEASE	2
void SND_CALL SND_fn_vReleaseDriverCDThread()
{
#ifndef DISABLE_CD
	// si le Cd n'est pas deja en pause, on le pause mais pause type 2
	if (! stActiveCDTrack.bPaused)
	{
		SND_fn_vPauseCDThread(stActiveCDTrack.lVoice);
		stActiveCDTrack.bPaused = (BOOL)PAUSED_BY_RESTORE_RELEASE;
	}
#endif //DISABLE_CD
}

void SND_CALL SND_fn_vReleaseDriverCD()
{
#ifndef DISABLE_CD
	HANDLE hEventSynchro;
	
	if (stCDDriver.bCdDriverOk && stActiveCDTrack.bActive) 
	{
		hEventSynchro = CreateEvent(NULL,FALSE,FALSE,NULL);
		if (!PostThreadMessage(dwCDThreadId,(UINT)SND_RELEASECD_MSG,(WPARAM)dw_a5_Params,(LPARAM)hEventSynchro))
   			SND_fn_vDisplayError(E_uwSndCDStop,"PostThreadMessage Release");

		WaitForSingleObject(hEventSynchro,INFINITE);
		CloseHandle(hEventSynchro);
	}
#endif //DISABLE_CD
}

void SND_CALL SND_fn_vRestoreDriverCDThread()
{
#ifndef DISABLE_CD
	// si le CD etait en pause du fait du release, on le relance
	// sinon, il etait en pause, on reste en pause
	if (stActiveCDTrack.bPaused == (BOOL)PAUSED_BY_RESTORE_RELEASE)
		SND_fn_vResumeCDThread(stActiveCDTrack.lVoice);
#endif //DISABLE_CD
}

void SND_CALL SND_fn_vRestoreDriverCD()
{
#ifndef DISABLE_CD
	HANDLE hEventSynchro;
	
	if (stCDDriver.bCdDriverOk && stActiveCDTrack.bActive)
	{
		hEventSynchro = CreateEvent(NULL,FALSE,FALSE,NULL);
		if (!PostThreadMessage(dwCDThreadId,(UINT)SND_RESTORECD_MSG,(WPARAM)dw_a5_Params,(LPARAM)hEventSynchro))
   			SND_fn_vDisplayError(E_uwSndCDStop,"PostThreadMessage Restore");

		WaitForSingleObject(hEventSynchro,INFINITE);
		CloseHandle(hEventSynchro);
	}
#endif //DISABLE_CD
}



//---------------------------------------------------------
//	GetPos: retourne la position "théorique" de la ressource
//			exprimé en seconde depuis le début
//			différent du temps absolu (car pitch, dérive..)
//	Entrée: voie concernée
//	Sortie: temps en S (SndReal)
//----------------------------------------------------------
SndReal SND_CALL SND_fn_rGetPosCDThread(long voie)
{
	double dfCDTrackPos = 0.0;
#ifndef DISABLE_CD
	dfCDTrackPos=(double)SND_fn_lGetCurrentCdPosition(voie & MASK_OFFSET_CD_VOICE) / 1000;
#endif
	return (M_DoubleToRealSnd(dfCDTrackPos));
}

//---------------------------------------------------------
//	GetPos: retourne la position "théorique" de la ressource
//			exprimé en seconde depuis le début
//			différent du temps absolu (car pitch, dérive..)
//	Entrée: voie concernée
//	Sortie: temps en S (SndReal)
//----------------------------------------------------------
SndReal SND_CALL SND_fn_rGetPosCD(long voie)
{
#ifndef DISABLE_CD
	HANDLE hEventSynchro;
	
	if (stCDDriver.bCdDriverOk && stActiveCDTrack.bActive)
	{
		dw_a5_Params[0] = (DWORD)voie;
		hEventSynchro = CreateEvent(NULL,FALSE,FALSE,NULL);
		if (!PostThreadMessage(dwCDThreadId,(UINT)SND_GETPOSCD_MSG,(WPARAM)dw_a5_Params,(LPARAM)hEventSynchro))
   			SND_fn_vDisplayError(E_uwSndCDStop,"Get Pos error");

		WaitForSingleObject(hEventSynchro,INFINITE);
		CloseHandle(hEventSynchro);
		return (dw_a5_Params[0]);
	}
	else
		return SND_C_POS_UNKNOWN;
#endif //DISABLE_CD
	return SND_C_POS_UNKNOWN;
}

//---------------------------------------------------------
//	GetLength: retourne la durée "théorique" de la ressource
//			exprimé en seconde (constant pour une ressource donnée)
//	Entrée: voie concernée
//	Sortie: durée en S (SndReal)
//----------------------------------------------------------
SndReal SND_CALL SND_fn_rGetLengthCD(long voie)
{
	double dfCDTrackTime = 0.0;
#ifndef DISABLE_CD
	dfCDTrackTime=(double)SND_fn_ulGetCDTrackLengthInMs(voie & MASK_OFFSET_CD_VOICE) / 1000;
#endif
	return (M_DoubleToRealSnd(dfCDTrackTime));
	//return SND_C_POS_UNKNOWN;
}

//-------------------------------------------------
// Fonction de synchro moteur
//	ne sert à rien pour CD
//-------------------------------------------------
void SND_CALL SND_fn_vSynchroCD(void)
{
}

SndBool SND_CALL SND_fn_bCheckVersionResourceCD(tdstBlockResourceDisk* disk)
{
	return TRUE;
}

SndBool SND_CALL SND_fn_bSetResourceStaticVolumeCD(tdstBlockResourceMem* pstRes,unsigned char ucVolume)
{
	pstRes->ucVolume=ucVolume;

	return TRUE;
}

// Partie de gestion de player de CD ouvert en partageable


