/****************************************************************
*                                                               *
*               Module SNDPLAY.C - Pod/Rayman3D                 *
*                       version Windows95                       *
*                                                               *
*       Objet:  exploitation bas-niveau des ressources bruitages*
*               Mixage manuel                                   *
*****************************************************************/
#ifndef __SNDPLAY_HPP__
#define __SNDPLAY_HPP__


#include <windows.h>
//#include <commctrl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

#define NEW_MIX
#define NEW_MIX_PLUS
//#define MIX_MULDEC
//#define MIX_FLOAT

#include "SNDinc.H"
#include "sndres.h"
#include "sndwave.h"
#include "sndwin95.h"
#include "sndmixer.h"
//#include "sndxd.h"
#ifndef DISABLE_RECORD
#include "sndrec.h"
#endif

//-----------------------------------------------------------------------------
#pragma message("\n---------------------------------------------------------------")
//-----------------------------------------------------------------------------

#if defined(_DLL_COMPILATION_MODE) || defined(USE_DBG_MODULE)
#pragma message("\nVersion dll => #define _DLL_COMPILATION_MODE")
#include "snddll.h"
#else
#include "snddbg.h"
#endif

#include "sndtest.h"

#include "sndwin95.h"
#include "effects.h"

#ifndef DISABLE_WAVE
//#pragma message("\nVersion mixe 100% manuel pour Win95 => #define DISABLE_WAVE")

/************************************************************************
        Définitions propres au module
************************************************************************/

#ifdef __WATCOMC__
#define __inline
#endif

//-----------------------------------------------------------------------------
#define HARDSND
#ifdef HARDSND
#pragma message("\nVersion active => #define HARDSND")
#else
#pragma message("\nVersion inactive => #undef HARDSND")
#endif
//-----------------------------------------------------------------------------
#define REVERB
#ifdef REVERB
#pragma message("\nVersion reverb active => #define REVERB")
#else
#pragma message("\nVersion reverb inactive => #undef REVERB")
#endif
//-----------------------------------------------------------------------------
#define SURROUND
//#define FILTRE_SURROUND
#ifdef SURROUND
#ifdef FILTRE_SURROUND
#pragma message("\nVersion dolby actif avec filtrage => #define SURROUND && #define FILTRE_SURROUND")
#else
#pragma message("\nVersion dolby actif sans filtrage => #define SURROUND && #undef FILTRE_SURROUND")
#endif
#else
#pragma message("\nVersion dolby inactif => #undef SURROUND")
#endif
//-----------------------------------------------------------------------------
//#define FILTER_AFTER_INTERPOLATION
#ifdef FILTER_AFTER_INTERPOLATION
#pragma message("\nMix avec filtrage apres interpolation => #define FILTER_AFTER_INTERPOLATION")
#else
#pragma message("\nMix sans filtrage apres interpolation => #undef FILTER_AFTER_INTERPOLATION")
#endif

//#define TIMER_PERIODIC//timer callback en périodic(!=one-shoot)=peu debugable
//#define SND_DUMP        //permet d'avoir le DumpDebug

#ifndef HARDSND
#undef EDITOR_SND
#endif

#define VOL_MINI 3

//nb de voies virtuelles gérées 
#define NB_VOICE 64
#ifdef NB_VOICE 
#pragma message("\nVersion voies virtuelles")
#else
#pragma message("\nVersion pas de voies virtuelles")
#endif
//codes de retour
//#define VOICE_STREAMING NB_VOICE+1

//nb de banques simultanément ouverte max
#define NB_BANK_USED_MAX 32

//pour le bouclage
#define LOOPING_RABE 10000
#define OFFSET_BIT_INC 8

typedef struct BUFFER_DISPO_ BUFFER_DISPO;
struct BUFFER_DISPO_{
        unsigned long pt_sign;  //en ech    
        unsigned long inc_sign; //en ech
        unsigned long pt_max;   //en ech
        unsigned long pt_loop;
        unsigned long pt_zero;
        long    vol;		//BE CAREFUL !!! This volume MUST be half of dynamic volume for PCM stereo resources
							//		but NOT for Stereo ClientBuffer
        tduRefRes sample;
        SND_td_pfn_vSoundCallback fct_callback;
        long    pan;
        BOOL    resol8;                    //TRUE si 8 bits,sinon 16 bits
        BOOL    must_looping;      //TRUE si sample bouclant
        BOOL    is_playing;
        BOOL    is_looping;
        BOOL    waiting_callback;  
		long    pitchable;
		long    dynamic_effects; // ww : effet 1 xx : effet 2 yy : effet 3 zz : effet 4         
		BOOL    is_mixing;
		long    space;
        long    param_callback;
		long    prio;
		long    DeltaPitch;     //à chaque genere_buffer, inc_sign+=DeltaPitch
		long    DeltaTemps;     //nb de genere_buffer depuis la dernière remise à jour
        long    DeltaReste;     //nb de trames restant à appliquer le DeltäPitch
		BOOL    paused;         //TRUE si voie en pause
		BOOL    stereo;			//TRUE si sample stereo
        BOOL	bufferclient;	//TRUE si buffer client
		double	coef_mag;
} ;

/*************************************************************************
        Spécificités Windows95
**************************************************************************/
#define NB_VOICE_MIXED 10

#define SEUIL_VOLUME_MINI       3
#define SEUIL_PARASITE          0x2000
#define ANTI_PARASITAGE_PONDERE                         0
#define ANTI_PARASITAGE_LINEAIRE                        1

/*************************************************************************
        Spécialités du périgord Filtres
**************************************************************************/
#define LOWPASS                 1
#define BANDPASS                2
#define HIGHPASS                3
#define PHASE                   4
#define BAND_AND_PHASE			5



static HWAVEOUT WaveOut;
#ifdef _WIN32
static WAVEFORMATEX g_stFormatOutput;
#else
static WAVEFORMAT g_stFormatOutput;
#endif

static HANDLE handle_snd_thread;
static DWORD id_snd_thread;
static UINT snd_msg_InitSample,snd_msg_DesInitSample;
//UINT snd_msg_PlaySnd,snd_msg_SetParamSnd,snd_msg_StopSnd;
static UINT snd_msg_LibereDriver,snd_msg_TryPrendDriver;
static double moy_done,moy_inqueue,moy_avance;
static int nb_mesurement;
static BOOL mesurement=FALSE;
static int nb_miss;
static char texte_info[256]; 

static enum {
        ePlaySample,
        eStopSample,
        eSetParamSample,
        egenere_buffer,
        eLibereDriver,
        eLibereDriverLocal,
        ePrendDriver,
        eTryPrendDriverLocal,
        eInitSampleLocal,
        eDesInitSampleLocal,
		eSetReverse,
		eCreateNewBufferExAsynchrone,
		eCreateNewBufferExSynchrone,
		eDeleteBufferSample
} mouchard;

static HANDLE hMutexAPI;

//!!variable exportée
void* DataBuffer; 
void* DataBufferReverb;
void* DataBufferReverbMax;

//static void* DataBufferInt;

//volume général
static BOOL stereo_reverse=FALSE;
#ifdef SURROUND
static BOOL dolby_actif=FALSE;
#endif
//état de la stereo
static BOOL stereo_active=TRUE;

// nombre de bruitages en mode is_playing et is_mixing
//static short nb_samples_actifs;

// nombre de passages dans TimerSynchroSnd pour entre 2 passages dans SetParamSnd
//static short nb_timer_synchro;
//static short nb_timer_synchro_total;

//pour le mix manuel
//taille_buffer=2à3 fois delay_timer*176
//!!variable exportée
int taille_buffer=32000;        //en octets
//static unsigned long pt_buffer=0;      //en échantillon (modulo taille_buffer)
//static UINT delay_timer=10;
//static UINT resolution_timer=1;
//static MMRESULT id_timer;
//static long inc_timer;                 //nb de bytes par période de timer

static BOOL resol8;                            //TRUE si mix 8bits, sinon 16 bits
static int nb_voice_mixed=0;
#define RES_OUTPUT 16
#define FREQ_OUTPUT 22050
#define NB_CHANNEL_OUTPUT 2

//-----------------------------------------------------------------------------
#pragma message("\n---------------------------------------------------------------")
//-----------------------------------------------------------------------------

//--blocks des WaveOutWrite
#define NB_SOUND_BLOCK_DEFAULT 100          //nb de petits blocks
#define NB_SOUND_BLOCK_AVANCE   25         
#define DELAY_SOUND_BLOCK       10          //durée d'un petits blocks en ms    

static int nb_sound_block=NB_SOUND_BLOCK_DEFAULT;                 //nb de petits block constituant la totalité du buffer
static int nb_sound_block_delay=NB_SOUND_BLOCK_AVANCE;            //nb de petits blocks d'avance
static int size_sound_block=(DELAY_SOUND_BLOCK*FREQ_OUTPUT/4000)<<4;//880 à 22000Hz //taille d'un petit block en octets(multiple de 16)
static int ech_avance=NB_SOUND_BLOCK_AVANCE*DELAY_SOUND_BLOCK*FREQ_OUTPUT/1000;	//avance en echantillons

static int next_sound_block_to_sent=0;

static WAVEHDR WaveHdr[NB_SOUND_BLOCK_DEFAULT];
static BOOL CallbackRecu[NB_SOUND_BLOCK_DEFAULT];

static void* DataBlock[NB_SOUND_BLOCK_DEFAULT];

//!!!variables exportées vers le module ASM
//pour le suivi des voies logiques
BUFFER_DISPO buffer_dispo[NB_VOICE];
BUFFER_DISPO *buffer_dispo_min=&(buffer_dispo[0]);
BUFFER_DISPO *buffer_dispo_max=&(buffer_dispo[NB_VOICE]);
long SIZE_BUFFER_DISPO=sizeof(BUFFER_DISPO);
long NBVM,NBVJ;

#ifdef STREAMING
//pour le streaming
/*
BUFFER_DISPO_STREAMING buffer_dispo_streaming;
#define TAILLE_BUFFER_STREAMING 50000
char data_streaming[TAILLE_BUFFER_STREAMING];
char* data_streaming_first=data_streaming;
char* data_streaming_half=data_streaming+TAILLE_BUFFER_STREAMING/2;
char* data_streaming_last=data_streaming+TAILLE_BUFFER_STREAMING;
*/
//----
typedef struct BUFFER_DISPO_STREAMING_ BUFFER_DISPO_STREAMING;
struct BUFFER_DISPO_STREAMING_ {
    unsigned long pt_max;//en octets
    unsigned long pt_loop;//en octets
	BOOL bMustLoop;
	BOOL bIsActive;//TRUE tant que le streaming est actif (de Play à Stop ou fin définitive)
	long lVoiceUsed;//voie squattée
	BOOL bIsLooping;//TRUE après le 1° loop
	BOOL bIsEnding;//TRUE quand on a déjà lu le dernier sample
	tdhSndFile hFile;//fichier associé restant ouvert
	long lNextPtr;//prochain sample à lire (en octets)
	int iNextBuffer;//prochaine demi buffer à rafraichir
	unsigned long pt_sign_middle;//valeur de pt_sign au denmi buffer
} ;
#define SIZE_BUFFER_STREAMING 40000	//taille du demi-buffer de streaming (en octets)
#define DELAY_STREAMING 1000*SIZE_BUFFER_STREAMING/((44100*4)*4)	//période du timer pour streaming (en ms)
static BUFFER_DISPO_STREAMING buffer_dispo_streaming;
static HANDLE hEvtStreaming=NULL;
static LPVOID hDataStreaming[2];
static tduRefRes tdResStreaming;
static tdstBlockResourceMem tdBlockResStreaming;
MMRESULT SND_g_mmIdTimerStreaming;
#endif

static BOOL driver_SND_OK=FALSE;//TRUE si driver alloué
static BOOL init_SND_OK=FALSE;//TRUE à la fin de InitSnd
static BOOL wanna_driver_SND=TRUE;

static HANDLE hMutexBuffer;//pendant genere_buffer,on ne touche à rien

//static BOOL g_bLostFocus=TRUE;

#ifdef MIX_MULDEC
int CodedPanVol[128] ={{0x01010000},
/* 1024.000000 */ {0x07070001},{0x07070001},{0x07070001},
/* 2048.000000 */ {0x06060001},{0x06060001},{0x06060001},
/* 3072.000000 */ {0x05060001},{0x05060001},{0x05060001},
/* 4096.000000 */ {0x05050001},{0x05050001},{0x05050001},
/* 4608.000000 */ {0x04070001},{0x04070001},{0x04070001},
/* 5120.000000 */ {0x06040001},{0x06040001},{0x06040001},
/* 6144.000000 */ {0x05040001},{0x05040001},{0x05040001},
/* 8192.000000 */ {0x04040001},{0x04040001},{0x04040001},
/* 8704.000000 */ {0x07030001},{0x07030001},{0x07030001},
/* 9216.000000 */ {0x06030001},{0x06030001},{0x06030001},
/* 10240.000000*/ {0x05030001},{0x05030001},{0x05030001},
/* 12288.000000*/ {0x03040001},{0x03040001},{0x03040001},
/* 16384.000000*/ {0x03030001},{0x03030001},{0x03030001},
/* 17408.000000*/ {0x02060001},{0x02060001},{0x02060001},
/* 18432.000000*/ {0x02050001},{0x02050001},{0x02050001},
/* 20480.000000*/ {0x02040001},{0x02040001},{0x02040001},
/* 24576.000000*/ {0x02030001},{0x02030001},{0x02030001},
/* 36864.000000*/ {0x01040000},{0x01040000},{0x01040000},
/* 34816.000000*/ {0x05010000},{0x05010000},{0x05010000},
/* 33792.000000*/ {0x01060000},{0x01060000},{0x01060000},
/* 32768.000000*/ {0x02020000},{0x02020000},{0x02020000},
/* 32768.000000*/ {0x02020001},{0x02020001},{0x02020001},
/* 33792.000000*/ {0x01060001},{0x01060001},{0x01060001},
/* 34816.000000*/ {0x05010001},{0x05010001},{0x05010001},
/* 36864.000000*/ {0x01040001},{0x01040001},{0x01040001},
/* 24576.000000*/ {0x02030000},{0x02030000},{0x02030000},
/* 20480.000000*/ {0x02040000},{0x02040000},{0x02040000},
/* 18432.000000*/ {0x02050000},{0x02050000},{0x02050000},
/* 17408.000000*/ {0x02060000},{0x02060000},{0x02060000},
/* 16384.000000*/ {0x03030000},{0x03030000},{0x03030000},
/* 12288.000000*/ {0x03040000},{0x03040000},{0x03040000},
/* 10240.000000*/ {0x05030000},{0x05030000},{0x05030000},
/* 9216.000000 */ {0x06030000},{0x06030000},{0x06030000},
/* 8704.000000 */ {0x07030000},{0x07030000},{0x07030000},
/* 8192.000000 */ {0x04040000},{0x04040000},{0x04040000},
/* 6144.000000 */ {0x05040000},{0x05040000},{0x05040000},
/* 5120.000000 */ {0x06040000},{0x06040000},{0x06040000},
/* 4608.000000 */ {0x04070000},{0x04070000},{0x04070000},
/* 4096.000000 */ {0x05050000},{0x05050000},{0x05050000},
/* 3072.000000 */ {0x05060000},{0x05060000},{0x05060000},
/* 2048.000000 */ {0x06060000},{0x06060000},{0x06060000},
/* 1024.000000 */ {0x07070000},{0x07070000},{0x07070000}
};
#endif

long buffer_thread(void);
//static int id_buffer=-1;

//buffer clients
typedef struct _tdstBufferClientAsynchrone {
    unsigned long           ulSamples;		//taille du buffer en sample
    unsigned long           ulDecalBytes;	//nb_sample<<buffer->ulDecalBytes=nb_octet
    unsigned long           ulNextToCompute;//id du prochaine sample àcalculer
    td_pfn_vRefreshBufferClient pfnCallback;//fonction callback buffer client
    long                    lUserId;		
} tdstBufferClientAsynchrone;

typedef struct _tdstBufferClientSynchrone {
    unsigned long           ulSamples;		//taille du buffer en sample
    unsigned long           ulDecalBytes;	//nb_sample<<buffer->ulDecalBytes=nb_octet
    td_pfn_vRefreshBufferClient pfnCallback;//fonction callback buffer client
    long                    lUserId;		
} tdstBufferClientSynchrone;


typedef struct _tdstBufferClient *p_tdstBufferClient;

SNDLST2_M_DynamicUseListOf(p_tdstBufferClient);

typedef struct _tdstBufferClient {
	SND_tduTypeCallback	eType;
    long                    lVoice;			//voie sample associée
	SndBool					bPaused;		//TRUE si buffer paused
    tduRefRes               uPseudoRes;		//uPseudoRes=&tdstPseudoRes
    tdstBlockResourceMem    tdstPseudoRes;	//pseudo ressource pour le mixage sample
	union {
		tdstBufferClientAsynchrone stAsynchrone;
		tdstBufferClientSynchrone stSynchrone;
	};
    SNDLST2_M_DynamicElementDeclaration(p_tdstBufferClient)
} tdstBufferClient;

typedef struct _tdstBufferClientList {
    SNDLST2_M_DynamicAnchorDeclaration(p_tdstBufferClient) pList;
} tdstBufferClientList;

static tdstBufferClientList buffers_clients;


void trie_les_voies_a_mixer(void);

static BOOL bDriverReleased=FALSE;
static BOOL bDriverForcedReleased=FALSE;

#define PROFILE_WAVE
//#define PROFILE_WAVE_IN_FILE

#ifdef PROFILE_WAVE
int timeNbHisto;
LARGE_INTEGER timeEnd,timeBegin,timeFreq,timeMoy,timeHisto;
double timeDelta,timeCumul;
#ifdef PROFILE_WAVE_IN_FILE
HANDLE timeFile;
char timeFileName[256];
char timeBuffer[256];
DWORD timeNbWriten;
#endif
#endif

#endif //DISABLE_WAVE

SampleParam C_SAMPLEPARAM_MUET={C_SNDREAL_1,C_SOUNDVOL_MINI,C_SOUNDPAN_MEDIAN,C_SOUNDSPACE_FRONT,NO_REVERB};
SampleParam C_SAMPLEPARAM_NEUTRE={C_SNDREAL_1,C_SOUNDVOL_MAXI,C_SOUNDPAN_MEDIAN,C_SOUNDSPACE_FRONT,NO_REVERB};

#endif//SNDPLAY_HPP
