/*==============================================================================================
 *							FILE "SNDADPCM.C"
 *
 *		Created    :    31 / 10 / 97
 *		Author     :    Stephane Ronse & Nicolas Meyer
 *		Description:    Code source file which manage ADPCM conversion
 *		                and play. Works with Dx3D, Dx2D, WinMM DLL.
 *		Formalism  :    Public functions are prefixed with "SND_"
 *		                Ubi guidelines
 *
 *		Copyright (C) 1997 by Ubi Sound Studio Entertainment
 *		All rights Reserved
 *==============================================================================================*/

/* Super Public*/
#if 0
#include <crtdbg.h>
#endif
#include "SNDinc.h"
#include "sndres.h"
#include "sndxd.h"

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


#ifndef DISABLE_ADPCM

#ifndef NO_ACP_SOUND

/* Moyen public*/
#include "Sndadpcm.h"
#include "Sndthrd.h"
#include "ApmThrd.h"/*En-tete contenant du code multi tâche*/
/* Prive*/
#include "Adpcm.h"
#include "Adpcm.hxx"
#include "Adpcm.cxx"
/* DECLARATION DES FONCTIONS*/
/*==================================================*/
void SND_CALL fn_vRefreshSynchroneADPCM( long lIdBuffer, unsigned long ulStart, unsigned long ulSizeByte, void* pvBuffer );
void SND_CALL fn_vRefreshASynchroneADPCM( long lIdBuffer, unsigned long ulStart, unsigned long ulSizeByte, void* pvBuffer );
SndBool       fn_bConvertBlockResDiskADPCMtoWAV(tdstBlockResourceDisk* pstDiskADPCM,void* ptrDataADPCM,tdstBlockResourceDisk* pstDiskWAV);
SndBool       fn_bConvertDataADPCMtoWAV(tdstBlockResourceDisk* pstDiskADPCM,void* ptrDataADPCM,void* ptrDataWAV);
/*==================================================*/

/*
 * STRUCTURE DEFINITIONS
 */
typedef struct {
	long          lBufferId;
	unsigned long ulSizeLast;
	unsigned long ulSizePos;
} tdstBufferDescriptor;
/*
 * GLOBALES VARIABLES
 */
static tdstBufferDescriptor         gstASynch;
static tdstBufferDescriptor         gstSynch;
static long                         gbDecompress;                /* Mutex qui gere le multi-tâche*/
static SndBool                      gbClientPause;                /* Flag qui indique que le buffer client est dejà en pause*/
static SndBool                      gbInitDone = FALSE;    /* Flag qui indique l'init du module "ADPCM" c'est faite sans encombre*/
static SND_tdstADPCMCriticalSection gstSection;
/*
 * Current file name & error information
 */
static char gchFileName[] = "SndAdpcm.c";
static char gchBuffer[256];
/* 
 * DEFINITION DES FONCTIONS
 */
void SND_fn_vConvertResDiskToMemSampleADPCM( tdstBlockResourceDisk *disk, tdstBlockResourceMem *mem, void* ptrBegin )
{
	snd_assert(disk->eType==TYPE_SAMPLE);
	snd_assert( disk->uRes.stSample.eZip == SAMPLE_ADPCM );

	mem->Id                         = disk->Id;
	mem->eType                      = disk->eType;
	mem->eStorage                   = disk->eStorage;
	mem->ucVolume                   = disk->ucVolume;
	mem->bIsLoaded                  = TRUE;
	/*partie specifique au type de ressource*/
	mem->uRes.stSample.eZip         = SAMPLE_ADPCM;
	mem->uRes.stSample.bPitchable   = disk->uRes.stSample.bPitchable;
	mem->uRes.stSample.bVolable     = disk->uRes.stSample.bVolable;
	mem->uRes.stSample.bPanable     = disk->uRes.stSample.bPanable;
	mem->uRes.stSample.bSpacable    = disk->uRes.stSample.bSpacable;
	mem->uRes.stSample.bReverbable  = disk->uRes.stSample.bReverbable;
	mem->uRes.stSample.bStream      = disk->uRes.stSample.bStream;
	mem->uRes.stSample.bLoop        = disk->uRes.stSample.bLoop;
	mem->uRes.stSample.ulFreq       = disk->uRes.stSample.ulFreq;
	mem->uRes.stSample.uwResolution = disk->uRes.stSample.uwResolution;
	mem->uRes.stSample.uwNbChannels = disk->uRes.stSample.uwNbChannels;

	if( disk->uRes.stSample.bStream ) {
		/*sample streame*/
		strcpy( mem->uRes.stSample.uData.stStream.szFileName, disk->uRes.stSample.czFileName );
		mem->uRes.stSample.uData.stStream.ulOffsetFirst = disk->ulDataOffset;
		if( ! disk->uRes.stSample.bLoop ) {
			mem->uRes.stSample.uData.stStream.ulOffsetLoop = 0;                    /* Indice de sample*/
			mem->uRes.stSample.uData.stStream.ulOffsetLast = disk->ulDataOffset+disk->ulDataSize;    /* Nombre de samples*/
		}
		else
		{
			mem->uRes.stSample.uData.stStream.ulOffsetLoop = disk->ulDataOffset+disk->uRes.stSample.ulStartLoop;
			mem->uRes.stSample.uData.stStream.ulOffsetLast = disk->ulDataOffset+disk->uRes.stSample.ulStartLoop+disk->uRes.stSample.ulLoopLength;
		}
	}
	else {/*sample non streame*/
		mem->uRes.stSample.uData.stMem.ulNbEchLoop        =    disk->uRes.stSample.ulStartLoop;
		mem->uRes.stSample.uData.stMem.ulNbEch            =    disk->uRes.stSample.ulLoopLength;
		mem->uRes.stSample.uData.stMem.pvPtrFirst        =    ptrBegin;
		snd_assert( ! ( (unsigned long)mem->uRes.stSample.uData.stMem.pvPtrFirst % 4 ) );
	}
	/*le sample est pret à l'emplois*/
}

char gchNoVoice[] = "No active voice.";
char gchInvalPa[] = "Address non affected.";
char gchNoTheme[] = "None possibility to play theme.";
char gchNotAdpm[] = "Is not ADPCM datas.";
char gchBadForm[] = "Bad format.";
long SND_fn_lPlaySampleADPCM( tduRefRes res, SampleParam *par, long prio, SND_td_pfn_vSoundCallback fn_callback, long par_callback )
{
	long            lId;
	char*            pchString;

	if( gbInitDone ) {
		SND_fn_vEnterCriticalSection( & gstSection );

		switch( fn_ulAddVoiceADPCM( res.pstPtr
		, par
		, fn_callback
		, par_callback
		, NULL
		, & lId
		, gstSynch.ulSizePos ) ) {
		/* L'allocation de la voie a ete faite*/
		case ADPCM_NOERROR:
			break;
		case ADPCM_NOVOICELEFT:
			pchString    =    gchNoVoice;
			lId        =    C_PLAY_FAILED;
			break;
		case ADPCM_INVALIDPARAM:
			pchString    =    gchInvalPa;
			lId        =    C_PLAY_FAILED;
			break;
		case ADPCM_NOTHEMELEFT:
			pchString    =    gchNoTheme;
			lId        =    C_PLAY_FAILED;
			break;
		case ADPCM_NOTADPCMDATAS:
			pchString    =    gchNotAdpm;
			lId        =    C_PLAY_FAILED;
			break;
		default:
			pchString    =    gchBadForm;
			lId        =    C_PLAY_FAILED;
		}
		SND_fn_vLeaveCriticalSection( & gstSection );
		if( lId == C_PLAY_FAILED )
			SND_fn_vDisplayError( E_uwSndSystemError, pchString );
	}
	return lId;
}

void SND_fn_vRemoveCallbackSampleADPCM( long voice )
{
	if( gbInitDone ) {
		SND_fn_vEnterCriticalSection( & gstSection );/* Entree en section critique*/
		fn_ulRemoveVoiceCallbackADPCM( voice );
		SND_fn_vLeaveCriticalSection( & gstSection );/* Sortie de section critique*/
	}
}

SndBool SND_fn_bSetParamSampleADPCM( long voice, SampleParam *par )
{
	SndBool bRet    =    FALSE;

	if( gbInitDone ) {
		SND_fn_vEnterCriticalSection( & gstSection );/* Entree en section critique*/
		if( fn_ulSetParams( voice, par ) == ADPCM_NOERROR ) {
			bRet    =    TRUE;
		}
		SND_fn_vLeaveCriticalSection( & gstSection );/* Sortie de section critique*/
	}
	return bRet;
}

SndBool SND_fn_bTestIsPlayingSampleADPCM( long voice )
{
    if( gbInitDone ) {
        if( fn_bVoiceIsPlayingADPCM( voice ) )
            return TRUE;
    }
    return FALSE;
}

void SND_fn_vStopSampleADPCM( long voice )
{
    if( gbInitDone ) {
        if( voice ) {
            SND_fn_vEnterCriticalSection( & gstSection );
            fn_ulRemoveVoiceADPCM( voice );
            SND_fn_vLeaveCriticalSection( & gstSection );
        }
    }
}

void SND_fn_vPauseSampleADPCM( long voice )
{
	if( gbInitDone ) {
		SND_fn_vEnterCriticalSection( & gstSection );
		fn_ulPauseVoiceADPCM( voice );
		SND_fn_vLeaveCriticalSection( & gstSection );
	}
}

void SND_fn_vResumeSampleADPCM(long voice)
{
	if( gbInitDone ) {
		SND_fn_vEnterCriticalSection( & gstSection );
		fn_ulResumeVoiceADPCM( voice );
		SND_fn_vLeaveCriticalSection( & gstSection );
	}
}

SndBool SND_fn_bIsResourceLoopingSampleADPCM(tduRefRes res)
{
	return res.pstPtr->uRes.stSample.bLoop;
}

SndReal SND_fn_rGetPosSampleADPCM( long voice )
{
	SndReal rAnswer;

	rAnswer = SND_C_LENGTH_UNKNOWN;
	if( gbInitDone ) {
		double dTime;

		switch( fn_ulGetPosVoiceADPCM( voice, &dTime, gstASynch.ulSizePos ) ) {
		case ADPCM_NOERROR:
			rAnswer = M_DoubleToRealSnd( dTime );    /* Retourne un reel 16 16*/
			break;
		case ADPCM_ENDREACH:
			rAnswer = SND_C_POS_ENDED;
			break;
		}
	}
	return rAnswer;
}

SndReal SND_fn_rGetLengthSampleADPCM(long voice)
{
	SndReal rAnswer;

	rAnswer = SND_C_LENGTH_UNKNOWN;
	if( gbInitDone ) {
		unsigned long ulLength;

		if( fn_ulGetLengthVoiceADPCM( voice, & ulLength ) == ADPCM_NOERROR ) {
			double dTemp;

	/* La valeur à retourner est en seconde. ( SND_ADPCM_BUFFERFREQ samples font 1s )*/
			dTemp   = (double)ulLength / (double)SND_ADPCM_BUFFERFREQ;
			rAnswer = M_DoubleToRealSnd( dTemp );    /* Retourne un reel 16 16*/
	/*        return ulLength << 16;    // Retourne un reel 16 16*/
		}
	}
	return rAnswer;
}
/*************************************************************************/
/* FONCTIONS INUTILES*/
void SND_fn_vSetSoundVolumeSampleADPCM(unsigned char vol)
{
}

unsigned char SND_fn_ucGetSoundVolumeSampleADPCM()
{
    return 0;
}

void SND_fn_vSetStereoSampleADPCM(SndBool active)
{
}

SndBool SND_fn_bGetStereoSampleADPCM(void)
{
	return TRUE;
}

void SND_fn_vSetReverseStereoSampleADPCM(SndBool active)
{
	fn_vSetReverseStereoSampleADPCM(active);
}

SndBool SND_fn_bGetReverseStereoSampleADPCM(void)
{
	return fn_bGetReverseStereoSampleADPCM();
}
/* fin FONCTIONS INUTILES*/
/****************************************************************************/
int SND_fn_iInitSampleADPCM( SND_tdstInitStruct *pInitStruct )
{
	unsigned long       ulAnswer;
	SND_tdstFormat        stFormatClient;
	SND_tdstCallback    stCallback;

	ulAnswer = C_INIT_FAILED;
	if( fn_ulInitModuleADPCM( ) == ADPCM_NOERROR ) {
		/*
		 * Client buffer for "One Shot Voices"
		 */
		stFormatClient.eZip                         = SAMPLE_PCM;
		stFormatClient.uFormat.stPCM.ulNbSamples    = 0;
		stFormatClient.uFormat.stPCM.uwResolution   = 16;
		stFormatClient.uFormat.stPCM.uwNbChannels   = 2;
		stFormatClient.uFormat.stPCM.ulFreq         = SND_ADPCM_BUFFERFREQ;
		memset( & stCallback, 0, sizeof(SND_tdstCallback) );
		stCallback.eType                            = BUFFER_SYNCHRONE;
		stCallback.uCallback.CallbackASynchrone     = fn_vRefreshSynchroneADPCM;

		gstSynch.lBufferId    =    SND_fn_lCreateNewBufferExSxd( & stFormatClient, &stCallback, NULL, 0 );
		if( gstSynch.lBufferId != C_PLAY_FAILED ) {
			/*
			 * Client buffer for "Streaming Voices"
			 */
			stFormatClient.uFormat.stPCM.ulNbSamples = M_SIZE_ASSYNCHRONE;
			stCallback.eType                         = BUFFER_ASYNCHRONE;
			stCallback.uCallback.CallbackASynchrone  = fn_vRefreshASynchroneADPCM;
			stCallback.uInfo.rCallbackPeriod         = 0;

			gstASynch.lBufferId = SND_fn_lCreateNewBufferExSxd( & stFormatClient, &stCallback, NULL, 0 );
			if( gstASynch.lBufferId != C_PLAY_FAILED ) {
				SND_fn_vInitializeCriticalSection( & gstSection );/* Creation d'une section critique */
				gbClientPause = FALSE;
				gbInitDone    = TRUE;
				ulAnswer      = C_INIT_OK;
			}
			else
				SND_fn_vDeleteBufferSxd( gstSynch.lBufferId );
		}
		/* Client Buffer cannot be created when SoundDriver is busy -> no assert please
		snd_assert( gstSynch.lBufferId != C_PLAY_FAILED );
		snd_assert( gstASynch.lBufferId != C_PLAY_FAILED );
		*/
	}
	return ulAnswer;
}

SndBool SND_fn_bTestInitSampleADPCM( void )
{
	return TRUE;
}

void SND_fn_vDesInitSampleADPCM( void )
{
	if( gbInitDone ) {
		SND_fn_vDeleteBufferSxd( gstSynch.lBufferId );
		SND_fn_vDeleteBufferSxd( gstASynch.lBufferId );
		SND_fn_vDeleteCriticalSection( & gstSection );/* Destruction d'une section critique */
	}
}

void SND_fn_vSetEffectSampleADPCM(long num)
{
}

void SND_fn_vCreateMicroSampleADPCM(MicroParam* par,long id)
{
}

void SND_fn_vSetMicroParamSampleADPCM(long sxdId,MicroParam* par)
{
}

void SND_fn_vDestroyMicroSampleADPCM(long sxdId)
{
}

/*gestion de buffers A VOIR*/
long SND_fn_lCreateNewBufferSampleADPCM(unsigned long nb_samples,unsigned short uwResolution,unsigned short uwNbChannels,unsigned long ulFreq,SampleParam* par,td_pfn_vRefreshBufferClient callback,long user_id)
{
	return C_PLAY_FAILED;
}

void SND_fn_vSetParamBufferSampleADPCM(long id_buffer,SampleParam *par)
{
}

void SND_fn_vDeleteBufferSampleADPCM(long id_buffer)
{
}

void SND_fn_vPauseBufferSampleADPCM( long id_buffer )
{
	if( gbInitDone ) {
		if( ! gbClientPause ) {
			SND_fn_vPauseBufferSxd( gstSynch.lBufferId );
			SND_fn_vPauseBufferSxd( gstASynch.lBufferId );
			gbClientPause = TRUE;
		}
	}
}

void SND_fn_vResumeBufferSampleADPCM( long id_buffer )
{
	if( gbInitDone ) {
		if( gbClientPause ) {
			SND_fn_vResumeBufferSxd( gstSynch.lBufferId );
			SND_fn_vResumeBufferSxd( gstASynch.lBufferId );
			gbClientPause = FALSE;
		}
	}
}

/* OBSOLETE*/
void* SND_fn_pvGetPtrBufferSampleADPCM(long id_buffer)
{
	return NULL;
}

SndReal SND_fn_rGetPosBufferSampleADPCM(long id_buffer)
{
	return SND_C_POS_UNKNOWN;
}

#if !defined(NO_ACP_SCRIPT) || defined(_DLL_COMPILATION_MODE)
/*this function must be defined in Script/Hybrid or DLL mode*/
SndBool SND_fn_bLoadResScriptSampleADPCM( tdstBlockResourceDisk *disk, tdstBlockResourceMem *mem )
{
	SndBool bAnswer;
	void*   pvData;
	char    file_name[ 256 ];

	bAnswer = FALSE;
	if( gbInitDone ) {

		if( !disk->uRes.stSample.bStream ) {
			pvData = SND_fn_pvMallocSnd( disk->ulDataSize );
			if( pvData ) {
				/* Le "ResolveFileName" est tres important ici*/
				SND_fn_vResolveFileName( disk->uRes.stSample.czFileName, file_name );
				if ( !SND_fn_bLoadDataInMem( pvData, disk->ulDataSize, file_name, disk->ulDataOffset ) ) {
					sprintf( gchBuffer, "Src %s\nData %s\nDisk %x", gchFileName, disk->uRes.stSample.czFileName, disk );
					SND_fn_vDisplayError( E_uwSndErrorReadingFile, gchBuffer );
					SND_fn_vFreeSnd( pvData );
					pvData = NULL;
				}
				else
					bAnswer = TRUE;
			}
		}
		else {
			bAnswer = TRUE;
			pvData  = NULL;
		}
		if( bAnswer ) {
			/*
			 * en script, chaque ressources charge ces propres data (on ne tient pas compte de eStorage)
			 */
			SND_fn_vConvertResDiskToMemSampleADPCM( disk, mem, pvData );
			mem->eStorage=TYPE_EXTERNAL;
		}
	}
	return bAnswer;
}
#endif

SndBool SND_fn_bLoadResBinarySampleADPCM(tdstBlockResourceDisk *disk,tdstBlockResourceMem *mem,char *pDataBloc)
{
	void* pData;
	char  file_name[ 256 ];

	if (!gbInitDone)
		return FALSE;

	if (!disk->uRes.stSample.bStream)
	{    /* Sample is not streaming: must be really loaded in memory*/
		if (disk->eStorage!=TYPE_EXTERNAL)
		{    /* Sample is in the bank: its position is known relatively to pDataBloc:*/
			pData=(void *)(pDataBloc +disk->ulDataOffset);
		}
		else 
		{    /* Sample is external: it must be loaded from a file:*/
			pData=SND_fn_pvMallocSnd(disk->ulDataSize);
			SND_fn_vResolveFileName(disk->uRes.stSample.czFileName,file_name);
			if (!SND_fn_bLoadDataInMem(pData,disk->ulDataSize,file_name,disk->ulDataOffset))
			{
				sprintf( gchBuffer, "Src %s\nData %s\nDisk %x", gchFileName, disk->uRes.stSample.czFileName, disk );
				SND_fn_vDisplayError( E_uwSndErrorReadingFile, gchBuffer );
				SND_fn_vFreeSnd(pData);
				pData=NULL;
			}
		}
	}
	else 
		pData=NULL; /* Sample is streaming*/

	SND_fn_vConvertResDiskToMemSampleADPCM( disk, mem, pData );

	return TRUE;
}

#if !defined(NO_ACP_SCRIPT) || defined(_DLL_COMPILATION_MODE)
/*this function must be defined in Script/Hybrid or DLL mode*/
/*conversion d'un BlockResDisk ADPCM en BlockResMem WAV pour les non streames*/
SndBool SND_fn_bLoadResScriptAndConvertToWAVSampleADPCM( tdstBlockResourceDisk *disk, tdstBlockResourceMem *mem )
{
	void *pDataWAV,*pDataADPCM;
	tdstBlockResourceDisk stPseudoRes;
	char file_name[ 256 ];

	if (!gbInitDone)
		return FALSE;

	if( ! disk->uRes.stSample.bStream ) {
		/*chargement de l'ADPCM*/
		pDataADPCM    =    SND_fn_pvMallocSnd( disk->ulDataSize );
		if( pDataADPCM == NULL )
			return FALSE;
		/* Le "ResolveFileName" est tres important ici*/
		SND_fn_vResolveFileName( disk->uRes.stSample.czFileName, file_name );
		if ( ! SND_fn_bLoadDataInMem( pDataADPCM, disk->ulDataSize, file_name, disk->ulDataOffset ) ) {
			sprintf( gchBuffer, "Src %s\nData %s\nDisk %x", gchFileName, disk->uRes.stSample.czFileName, disk );
			SND_fn_vDisplayError( E_uwSndErrorReadingFile, gchBuffer );
			SND_fn_vFreeSnd( pDataADPCM );
			return FALSE;
		}

		memcpy(&stPseudoRes,disk,sizeof(stPseudoRes));

		/*conversion du BlockResDisk*/
		if (!fn_bConvertBlockResDiskADPCMtoWAV(disk,pDataADPCM,&stPseudoRes))
		{
			sprintf( gchBuffer, "Src %s\nData %s\nDisk %x\nConvertion block res failed", gchFileName, disk->uRes.stSample.czFileName, disk );
			SND_fn_vDisplayError( E_uwSndErrorReadingFile, gchBuffer );
			SND_fn_vFreeSnd( pDataADPCM );
			return FALSE;
		}

		/*malloc pour les data WAV*/
		pDataWAV=SND_fn_pvMallocSnd(stPseudoRes.ulDataSize);

		/*conversion des data*/
		if (!fn_bConvertDataADPCMtoWAV(disk,pDataADPCM,pDataWAV))
		{
			sprintf( gchBuffer, "Src %s\nData %s\nDisk %x\nConvertion data failed", gchFileName, disk->uRes.stSample.czFileName, disk );
			SND_fn_vDisplayError( E_uwSndErrorReadingFile, gchBuffer );
			SND_fn_vFreeSnd( pDataADPCM );
			SND_fn_vFreeSnd( pDataWAV );
			return FALSE;
		}
		if (disk->eStorage==TYPE_EXTERNAL)
			SND_fn_vFreeSnd( pDataADPCM );

		/*generation du BlockResMem*/
		SND_fn_vConvertResDiskToMemSxd( &stPseudoRes, mem, pDataWAV );

		/*en script, chaque ressources charge ces propres data (on ne tient pas compte de eStorage)*/
		mem->eStorage=TYPE_EXTERNAL;

		return TRUE;
	}
	else {
		/*stream sample are not converted to WAV, but played in APDMC*/
		return SND_fn_bLoadResScriptSampleADPCM(disk,mem);
	}
}
#endif

/*conversion d'un BlockResDisk ADPCM en BlockResMem WAV pour les non streames*/
SndBool SND_fn_bLoadResBinaryAndConvertToWAVSampleADPCM(tdstBlockResourceDisk *disk,tdstBlockResourceMem *mem,char *pDataBloc)
{
	void *pDataWAV,*pDataADPCM;
	tdstBlockResourceDisk stPseudoRes;
	 char file_name[ 256 ];

	if (!gbInitDone)
		return FALSE;

	/*
	 * Sample is not streaming: must be really loaded in memory
	 */
	if( !disk->uRes.stSample.bStream ) {
		if (disk->eStorage!=TYPE_EXTERNAL)
		{    /* Sample is in the bank: its position is known relatively to pDataBloc:*/
			pDataADPCM=(void *)(pDataBloc +disk->ulDataOffset);
		}
		else 
		{    /* Sample is external: it must be loaded from a file:*/
			pDataADPCM=SND_fn_pvMallocSnd(disk->ulDataSize);
			SND_fn_vResolveFileName(disk->uRes.stSample.czFileName,file_name);
			if (!SND_fn_bLoadDataInMem(pDataADPCM,disk->ulDataSize,file_name,disk->ulDataOffset))
			{
				sprintf( gchBuffer, "Src %s\nData %s\nDisk %x\nConvertion data failed", gchFileName, disk->uRes.stSample.czFileName, disk );
				SND_fn_vDisplayError( E_uwSndErrorReadingFile, gchBuffer );
				SND_fn_vFreeSnd(pDataADPCM);
				return FALSE;
			}
		}

		memcpy(&stPseudoRes,disk,sizeof(stPseudoRes));
		/*conversion du BlockResDisk*/
		if( !fn_bConvertBlockResDiskADPCMtoWAV(disk,pDataADPCM,&stPseudoRes))
		{
//			snd_assert( 0 );
			sprintf( gchBuffer, "Src %s\nData %s\nDisk %x\nConvertion block failed", gchFileName, disk->uRes.stSample.czFileName, disk );
			SND_fn_vDisplayError( E_uwSndErrorReadingFile, gchBuffer );
			SND_fn_vFreeSnd( pDataADPCM );
			return FALSE;
		}

		/*malloc pour les data WAV*/
		pDataWAV=SND_fn_pvMallocSnd(stPseudoRes.ulDataSize);

		/*conversion des data*/
		if (!fn_bConvertDataADPCMtoWAV(disk,pDataADPCM,pDataWAV))
		{
			snd_assert( 0 );
			SND_fn_vDisplayError( E_uwSndInvalidTypeOfResource, "Cannot convert ADPCM resource");
			SND_fn_vFreeSnd( pDataADPCM );
			SND_fn_vFreeSnd( pDataWAV );
			return FALSE;
		}
		if (disk->eStorage==TYPE_EXTERNAL)
			SND_fn_vFreeSnd( pDataADPCM );

		/*generation du BlockResMem*/
		SND_fn_vConvertResDiskToMemSxd( &stPseudoRes, mem, pDataWAV );

		/*en script, chaque ressources charge ces propres data (on ne tient pas compte de eStorage)*/
		mem->eStorage=TYPE_EXTERNAL;

		return TRUE;
	}
	else 
	{
		return SND_fn_bLoadResBinarySampleADPCM(disk,mem,pDataBloc);
	}
}

void SND_fn_vUnLoadResSampleADPCM( tdstBlockResourceMem* mem )
{
	if( !mem->uRes.stSample.bStream ) {
		if( ( mem->uRes.stSample.uData.stMem.pvPtrFirst != NULL ) && ( mem->eStorage == TYPE_EXTERNAL ) )
			SND_fn_vFreeSnd( mem->uRes.stSample.uData.stMem.pvPtrFirst );
	}
	mem->uRes.stSample.uData.stMem.pvPtrFirst = NULL;
	mem->eType                                = TYPE_INVALID;
}

SndBool SND_fn_bIsResLoadedSampleADPCM( tdstBlockResourceMem *mem )
{
	return FALSE;
}

/*---themes*/
long SND_fn_lPlayTransitionSampleADPCM( tduRefRes FirstRes, tduRefRes NextRes, SND_td_pfn_vSoundCallback transition_callback, long param_callback, SampleParam* par )
{
	long lId    =    C_PLAY_FAILED;

	snd_assert(FirstRes.pstPtr->uRes.stSample.eZip==SAMPLE_ADPCM);snd_assert(NextRes.pstPtr->uRes.stSample.eZip==SAMPLE_ADPCM);

	if( gbInitDone ) {
		SND_fn_vEnterCriticalSection( & gstSection );/* Entree en section critique*/
		if( fn_ulAddVoiceADPCM( FirstRes.pstPtr
				, par
				, transition_callback
				, param_callback
				, NextRes.pstPtr
				, & lId
				, gstSynch.ulSizePos ) != ADPCM_NOERROR )
			lId    =    C_PLAY_FAILED;
		SND_fn_vLeaveCriticalSection( & gstSection );/* Sortie de section critique*/
	}
	return lId;
}

SndBool SND_fn_bSetParamTransitionSampleADPCM( long voice, SampleParam *par )
{
    SndBool bRet=FALSE;
 
    if( gbInitDone ) {
        if( voice ) {
            SND_fn_vEnterCriticalSection( & gstSection );
            if( fn_ulSetParams( voice, par ) == ADPCM_NOERROR )
                bRet=TRUE;
            else
                bRet=FALSE;
            SND_fn_vLeaveCriticalSection( & gstSection );
        }
    }
    return bRet;
}

SndBool SND_fn_bSetNextTransitionSampleADPCM( long voice, tduRefRes new_res )
{
    SndBool bRet    =    FALSE;
 
    if( gbInitDone ) {
        SND_fn_vEnterCriticalSection( & gstSection );/* Entree en section critique*/
        if( fn_ulSetNextThemeADPCM( voice, new_res.pstPtr ) == ADPCM_NOERROR )
            bRet    =    TRUE;
        SND_fn_vLeaveCriticalSection( & gstSection );/* Sortie de section critique*/
    }
    return bRet;
}

/* SEUL CAS OU ARRETE UNE PART EN COURS*/
SndBool SND_fn_bDoTransitionWithFadeSampleADPCM( long voice, tduRefRes new_res, SndReal rDuration )
{
    SndBool bRet    =    FALSE;
    if( gbInitDone ) {
        SND_fn_vEnterCriticalSection( & gstSection );/* Entree en section critique*/
        bRet    =    fn_bFadeThemeADPCM( voice, new_res.pstPtr, rDuration );
        SND_fn_vLeaveCriticalSection( & gstSection );/* Sortie de section critique*/
    }
    return bRet;
}

void SND_fn_vStopTransitionSampleADPCM( long voice )
{
    if( gbInitDone ) {
        SND_fn_vEnterCriticalSection( & gstSection );
        fn_vStopThemeADPCM( voice );
        SND_fn_vLeaveCriticalSection( & gstSection );
    }
}

void SND_fn_vPauseTransitionSampleADPCM( long voice )
{
    if( gbInitDone ) {
        SND_fn_vEnterCriticalSection( & gstSection );
        fn_ulPauseVoiceADPCM( voice );
        SND_fn_vLeaveCriticalSection( & gstSection );
    }
}

void SND_fn_vResumeTransitionSampleADPCM( long voice )
{
    if( gbInitDone ) {
        SND_fn_vEnterCriticalSection( & gstSection );
        fn_ulResumeVoiceADPCM( voice );
        SND_fn_vLeaveCriticalSection( & gstSection );
    }
}

#ifndef NO_ACP_LDBIN
SndBool SND_fn_bCanFreeDataSampleADPCM(void)
{
    return FALSE;
}
#endif

/*--relache-reprise a chaud des drives*/
void SND_fn_vReleaseDriverSampleADPCM(void)
{
}

void SND_fn_vRestoreDriverSampleADPCM(void)
{
}

void SND_fn_vGetTargetLabelSampleADPCM(char* name,int nb_char)
{
}

void SND_fn_vSetupTargetSampleADPCM()
{
}

SndBool SND_fn_bCanSetupTargetSampleADPCM()
{
    return FALSE;
}

void SND_fn_vSynchroSampleADPCM()
{
}

long SND_fn_lCreateNewBufferExSampleADPCM(SND_tdstFormat* pformat,SND_tdstCallback* pCallback,SoundParam* par,long user_id)
{
    return C_PLAY_FAILED;
}

long SND_fn_lGetPosBufferExSampleADPCM(long id_buffer)
{
    return SND_C_POS_UNKNOWN;
}

long SND_fn_lPushBufferExSampleADPCM( long id_buffer, SND_tdstStackBuffer* pStack )
{
    return C_PLAY_FAILED;
}

#endif /*NO_ACP_SOUND*/

//#define M_SAVE_MIX_APM

void SND_CALL fn_vRefreshSynchroneADPCM( long lIdBuffer, unsigned long ulStart, unsigned long ulSizeByte, void* pvBuffer )
{
    /* Play only memory resource */
    static SndBool            bRun        =    FALSE;
    unsigned long            uMixerState;
#ifdef M_SAVE_MIX_APM
    HANDLE hBinFile;
    DWORD  dwWrite;
#endif

    /* Protege reentrance*/
    if( gbInitDone ) {
        if( ! bRun ) {
            bRun                = TRUE;
            SND_fn_vEnterCriticalSection( & gstSection );
            gbDecompress        = TRUE;
            uMixerState         = fn_ulMixerStereoADPCM( FALSE, ulSizeByte, (short*)pvBuffer, gstSynch.ulSizePos );
            gstSynch.ulSizeLast = ulSizeByte;
            gstSynch.ulSizePos += ulSizeByte;
#ifdef M_SAVE_MIX_APM
            hBinFile = CreateFile( "c:\\binapm.raw", GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_ALWAYS, 0, NULL );
            SetFilePointer( hBinFile, 0, NULL, FILE_END );
            WriteFile( hBinFile, pvBuffer, ulSizeByte << 2, &dwWrite, NULL );
            CloseHandle( hBinFile );
#endif
            if( ( uMixerState == ADPCM_NOVOICELEFT ) || ( uMixerState == ADPCM_NOVOICEACTIVE ) )
                memset( pvBuffer, 0, ulSizeByte << 2 );
            gbDecompress        =    FALSE;
            SND_fn_vLeaveCriticalSection( & gstSection );
            bRun                =    FALSE;
        }
    }
}

//#define M_SAVE_MIX_STRM_APM
void SND_CALL fn_vRefreshASynchroneADPCM( long lIdBuffer, unsigned long ulStart, unsigned long ulSizeByte, void* pvBuffer )
{
    if( gbInitDone ) {
		static SndBool bRun;
		/* Play only streamed resource */
		unsigned long  uMixerState;
#ifdef M_SAVE_MIX_STRM_APM
		HANDLE         hBinFile;
		DWORD          dwWrite;
#endif

	    /*
		 * Protege reentrance
		 */
		bRun = FALSE;
        if( ! bRun ) {
            bRun = TRUE;
            SND_fn_vEnterCriticalSection( &gstSection );
            /*
             * Actualize Asynchronous buffer envirronement
             */
            gbDecompress         = TRUE;
            uMixerState          = fn_ulMixerStereoADPCM( TRUE, ulSizeByte, (short*)pvBuffer, gstASynch.ulSizePos );
            /*
             * Store addition of all bytes refresh size
             */
            gstASynch.ulSizeLast = ulSizeByte;
            gstASynch.ulSizePos += ulSizeByte;
#ifdef M_SAVE_MIX_STRM_APM
            hBinFile = CreateFile( "c:\\binapmstr.raw", GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_ALWAYS, 0, NULL );
            SetFilePointer( hBinFile, 0, NULL, FILE_END );
            WriteFile( hBinFile, pvBuffer, ulSizeByte << 2, &dwWrite, NULL );
            CloseHandle( hBinFile );
#endif
            if( ( uMixerState == ADPCM_NOVOICELEFT ) || ( uMixerState == ADPCM_NOVOICEACTIVE ) )
                memset( pvBuffer, 0, ulSizeByte << 2 );
            gbDecompress        = FALSE;
            SND_fn_vLeaveCriticalSection( & gstSection );
            bRun                = FALSE;
        }
    }
}

SndBool SND_fn_bCheckVersionResourceSampleADPCM( tdstBlockResourceDisk* disk )
{
    SndBool    bResult    =    FALSE;
    void*    data;
    char file_name[ 256 ];

 
    data    =    SND_fn_pvMallocSnd( disk->ulDataSize );
    /* Le "ResolveFileName" est tres important ici*/
    SND_fn_vResolveFileName( disk->uRes.stSample.czFileName, file_name );
    if ( ! SND_fn_bLoadDataInMem( data, disk->ulDataSize, file_name, disk->ulDataOffset ) ) {
        SND_fn_vDisplayError( E_uwSndErrorReadingFile, gchFileName );
        SND_fn_vFreeSnd( data );
        data    =    NULL;
    }
    else {
        if( fn_ulResolveVersion( ( (lpSND_tdstWAVEFORMATEX)data )->ulCompressId ) == ADPCM_NOERROR )
            bResult    =    TRUE;
        SND_fn_vFreeSnd( data );
    }
    return bResult;
}

/*------------------------------------------------------*/
/*    SND_fn_lGetNbVoiceWishedSampleADPCM: returns the maximum number of voices to be played in the same time*/
/*------------------------------------------------------*/
long SND_fn_lGetNbVoiceWishedSampleADPCM()
{
    return 0x10;
}

/*!!!!!!!!!!!!!!!!!!fonctions de conversion ADPCM->WAV au chargement!!!!!!!!!!!!!!!!!!!!!!*/

/*---------------------------------------------------------*/
/*    fn_bConvertBlockResDiskADPCMtoWAV: */
/*    generation du BlockResDisk de la ressources WAV equivalente à une ressources ADPCM*/
/*Entrees:*/
/*    pstDiskADPCM:descripteur de la ressources ADPCM (ne pas exploiter le FileName)*/
/*    ptrDataADPCM:pointeur sur les data ADPMC dejà chargees*/
/*    pstDiskWAV:descripteur de la ressources WAV à generer (un memcpy a dejà eu lieu)*/
/*Sortie:TRUE si OK, FALSE si echec (annulation de la conversion)*/
/*------------------------------------------------------------*/
SndBool fn_bConvertBlockResDiskADPCMtoWAV( tdstBlockResourceDisk* pstDiskADPCM, void* ptrDataADPCM, tdstBlockResourceDisk* pstDiskWAV )
{
	SndBool bAnswer;

	bAnswer = FALSE;
	memcpy( pstDiskWAV, pstDiskADPCM, sizeof(tdstBlockResourceDisk) );
	/*
	 * Modification de quelques champs
	 */
	pstDiskWAV->eType                         = TYPE_SAMPLE;
	pstDiskWAV->eStorage                      = 0;
	pstDiskWAV->ulDataOffset                  = 0;
	pstDiskWAV->uRes.stSample.eZip            = SAMPLE_PCM;
	pstDiskWAV->uRes.stSample.czFileName[ 0 ] = 0;
	pstDiskWAV->uRes.stSample.bStream         = FALSE;
	if( fn_ulFillSizeAndLoopADPCM( pstDiskWAV, ptrDataADPCM ) == ADPCM_NOERROR )
		bAnswer = TRUE;
	return bAnswer;
}

/*-------------------------------------------------------------------*/
/*    fn_bConvertDataADPCMtoWAV:*/
/*    generation en memoire des data WAV correspondantes à une ressource ADPCM*/
/*Entrees:*/
/*    pstDiskWAV:descripteur de la ressource WAV à conevrtir*/
/*    ptrDataADPCM:pointeur sur les data ADPCM dejà chargees en memoire*/
/*    ptrDataWAV:pointeur cible des donnees à generer*/
/*Retour:TRUE si OK, FALSE si echec (annulation de la conversion)*/
/*------------------------------------------------------------*/
SndBool fn_bConvertDataADPCMtoWAV( tdstBlockResourceDisk* pstDiskADPCM, void* ptrDataADPCM, void* ptrDataWAV )
{
	SndBool bAnswer;

	bAnswer = FALSE;
	if( fn_ulConvertDataADPCM( ptrDataADPCM, ptrDataWAV ) == ADPCM_NOERROR )
		bAnswer = TRUE;
	return bAnswer;
}

SndBool SND_fn_bSetResourceStaticVolumeSampleADPCM(tdstBlockResourceMem* pstRes,unsigned char ucVolume)
{
	int i;

	pstRes->ucVolume=ucVolume;

	for (i=0;i<NB_MAX_ADPCM_VOICE;i++)
		if (gstVoice[i].uRefRes.pstPtr!=NULL)
			if (gstVoice[i].uRefRes.pstPtr==pstRes)
				gstVoice[i].ulStaticVolume=ucVolume;

	for (i=0;i<NB_MAX_ADPCM_THEME;i++)
		if (gstTheme[i].uRefRes.pstPtr!=NULL)
			if (gstTheme[i].uRefRes.pstPtr==pstRes)
				gstTheme[i].ulStaticVolume=ucVolume;

	if (gstVoiceFade.uRefRes.pstPtr!=NULL)
		if (gstVoiceFade.uRefRes.pstPtr==pstRes)
			gstVoiceFade.ulStaticVolume=ucVolume;

	return TRUE;
}

#endif
/*
 * DISABLE_ADPCM
 */