/****************************************************************
	SNDREC for DirectSound 5.0 (starring SoundCapture)

  MODIFICATIONS:
    modified 11/09/98 - Joel Vibert - Test on pointer to avoid crash if error
							+ ForceReleaseDriver in case card is half duplex
							+ set bDriverInRecording to TRUE
    author: Stephane Ronse
******************************************************************/

#define INITGUID
#include <windows.h>
#include <mmsystem.h>
#include <dsound.h>

#include "SNDinc.H"
#include "sndrec.h"
#include "sndlst2.h"
#include "sndxd.h"

#if defined(_DLL_COMPILATION_MODE) || defined(USE_DBG_MODULE)
#include "snddll.h"
#else
#include "snddbg.h"
#endif

typedef struct RECORD_BUFFER_ *pRECORD_BUFFER;
typedef struct RECORD_VOICE_ *pRECORD_VOICE;

SNDLST2_M_DynamicUseListOf(pRECORD_BUFFER);

typedef struct RECORD_BUFFER_{
	void*			ptrData;
	unsigned long	ulNbBytesMax;//capacity of this data buffer
	unsigned long	ulNbBytesFree;//nb of sample copied into this buffer
	pRECORD_VOICE	pVoice;
	SNDLST2_M_DynamicElementDeclaration(pRECORD_BUFFER)
} RECORD_BUFFER;

typedef struct RECORD_VOICE_{
	long			lIdBufferClient;
	long			lSampleToBytesShift;
	SndBool			bUsed;
	SND_tdstFormat	stFormat;
	td_pfn_vBufferClientPoped CallbackStack;
	SNDLST2_M_DynamicAnchorDeclaration(pRECORD_BUFFER) pBufferList;
} RECORD_VOICE;

static RECORD_VOICE RecordVoice;

static SndBool bDriverInRecording=FALSE;

static LPDIRECTSOUNDCAPTURE lpDirectSoundCapture=NULL;
static LPDIRECTSOUNDCAPTUREBUFFER lpDirectSoundCaptureBuffer=NULL;
static LPDIRECTSOUNDNOTIFY lpDsNotify=NULL;
static HANDLE hRecordThread=NULL;
static HANDLE hEventStopRecord=NULL;

//notify point location
#define NB_BUFFERS 16
static HANDLE hEventNotify[NB_BUFFERS+1];
static DWORD dwOffsetBytes[NB_BUFFERS];
static DWORD dwOffsetSamples[NB_BUFFERS];
static DWORD dwSizeBufferBytes;
static DWORD dwSizeBufferSamples;

//static UINT snd_msg_StartRecord,snd_msg_StopRecord,snd_msg_PushBuffer;
static HANDLE hMutexRecord;
static DWORD id_snd_thread=0;
static SndBool bDriverOutReleased=TRUE;

static long cumul_vumeter=0;
static long nb_element_vumeter=0;

//------------- DSoundTry(fct(x,y)) appelle fct(x,y) et affiche une éventuelle erreur
static void GetErrorDSound(HRESULT err,char* texte,int size_texte)
{
    if (err!=DS_OK)
    {
        switch (err)
        {
                case DSERR_CONTROLUNAVAIL:
                        strncpy(texte,"not available",size_texte);break;
                case DSERR_ALLOCATED:
                        strncpy(texte,"allocated",size_texte);break;
                case DSERR_BADFORMAT:
                        strncpy(texte,"bad format",size_texte);break;
                case DSERR_INVALIDPARAM:
                        strncpy(texte,"invalid param",size_texte);break;
                case DSERR_NOAGGREGATION:       
                        strncpy(texte,"no aggregation",size_texte);break;
                case DSERR_OUTOFMEMORY: 
                        strncpy(texte,"out of memory",size_texte);break;
                case DSERR_BUFFERLOST:
                        strncpy(texte,"bufferlost",size_texte);break;
                case DSERR_PRIOLEVELNEEDED:
                        strncpy(texte,"priolevelneeded",size_texte);break;
                case DSERR_INVALIDCALL:
                        strncpy(texte,"invalidcall",size_texte);break;
                default:
                        strncpy(texte,"unidentified error",size_texte);
        }		
	}
	else
		strncpy(texte,"No error",size_texte);
}

static HRESULT DSoundTry(HRESULT res)
{									
    char texte[80];

	if (res!=DS_OK)			
	{
		GetErrorDSound(res,texte,sizeof(texte));
		SND_fn_vDisplayError(E_uwSndSystemError,texte);
		//DisplayErrorDSound(res);	
	}
	return res;
}


void refresh_vumeter(void* pData,long nb_data)
{
	signed char *ptrByte;
	signed short *ptrWord;
	long cumul=0;
	int i;

	cumul_vumeter=0;
	nb_element_vumeter=0;

	/*
	switch (RecordVoice.stFormat.uFormat.stPCM.uwResolution)
	{//nota: pas de différence entre mono et stereo (vumeter mono)
	case 8:
		for (i=0,ptrByte=pData;i<nb_data;i++,ptrByte++)
		{
			cumul_vumeter+=abs(*ptrByte);
		}
		nb_element_vumeter+=nb_data;
		break;
	case 16:
		for (i=0,ptrWord=pData;i<nb_data/2;i++,ptrWord++)
		{
			cumul_vumeter+=abs(*ptrWord)>>8;
		}
		nb_element_vumeter+=nb_data/2;
		break;
	default:
		snd_assert(FALSE);
		break;
	}
	*/
	switch (RecordVoice.stFormat.uFormat.stPCM.uwResolution)
	{//nota: pas de différence entre mono et stereo (vumeter mono)
	case 8:
		for (i=0,ptrByte=pData;i<nb_data;i++,ptrByte++)
		{
			cumul_vumeter=max(cumul_vumeter,*ptrByte);
		}
		nb_element_vumeter=1;
		break;
	case 16:
		for (i=0,ptrWord=pData;i<nb_data/2;i++,ptrWord++)
		{
			cumul_vumeter=max(cumul_vumeter,*ptrWord);
		}
		nb_element_vumeter=1;
		break;
	default:
		snd_assert(FALSE);
		break;
	}
}

unsigned char get_vumeter_value()
{
	if (nb_element_vumeter)
	{
		return (unsigned char)(cumul_vumeter/nb_element_vumeter);
	}
	else
		return 0;
}

DWORD WINAPI callback_thread(long* ptr_param)
{
	(*(td_pfn_vBufferClientPoped)ptr_param[0])(ptr_param[1],ptr_param[2]);

	GlobalFree(ptr_param);

	return 0;
}

void start_callback(pRECORD_BUFFER buffer)
{
	/*
	DWORD id_thread;
	long *ptr_param=GlobalAlloc(GMEM_FIXED,3*sizeof(long));

	if (buffer->pVoice->CallbackStack)
	{
		ptr_param=GlobalAlloc(GMEM_FIXED,3*sizeof(long));

		ptr_param[0]=(long)buffer->pVoice->CallbackStack;
		ptr_param[1]=(long)buffer->pVoice;
		ptr_param[2]=(long)buffer;

		CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)callback_thread,(LPVOID)ptr_param,0,&id_thread);
	}
*/
	(*(td_pfn_vBufferClientPoped)buffer->pVoice->CallbackStack)((long)buffer->pVoice,(long)buffer);
}

DWORD WINAPI RecordThread(long* ptr_param)
{
	DWORD ret;
	DWORD dwOffset,dwSize;
	DWORD dwData1,dwData2;
	LPVOID pvData1,pvData2;
	HRESULT res;
	DWORD data_locked,nb_to_copy;
	int i;
	pRECORD_BUFFER pstPushedBuffer,pstPushedBufferNext;


	SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_ABOVE_NORMAL);

	while ((ret=WaitForMultipleObjects(NB_BUFFERS+1,hEventNotify,FALSE,INFINITE))!=WAIT_OBJECT_0+NB_BUFFERS)
	{//wait for any event to be signaled
		//LockMutex
		WaitForSingleObject(hMutexRecord,INFINITE);

		if (lpDirectSoundCaptureBuffer)
		{
			dwOffset=dwOffsetBytes[ret-WAIT_OBJECT_0];
			dwSize=dwSizeBufferBytes;

			//Lock DSound data
			res=DSoundTry(IDirectSoundCaptureBuffer_Lock(
				lpDirectSoundCaptureBuffer,
				dwOffset,
				dwSize,
				&pvData1,
				&dwData1,
				&pvData2,
				&dwData2,
				0));
			snd_assert(!dwData2);snd_assert(dwData1==dwSize);

//		_RPTF3(_CRT_WARN,"received %d ->Lock %d to %d\n",ret,dwOffset,dwOffset+dwSize);

			refresh_vumeter(pvData1,dwData1);

			data_locked=dwData1;
			SNDLST2_M_DynamicForEachMovingElementOf(&RecordVoice.pBufferList,pstPushedBuffer,pstPushedBufferNext,i)
			{//pop data buffer until there is data enough
				nb_to_copy=min(data_locked,pstPushedBuffer->ulNbBytesFree);
				CopyMemory((char*)pstPushedBuffer->ptrData+pstPushedBuffer->ulNbBytesMax-pstPushedBuffer->ulNbBytesFree,
					pvData1,
					nb_to_copy);

				pstPushedBuffer->ulNbBytesFree-=nb_to_copy;
				data_locked-=nb_to_copy;
		
				if (!pstPushedBuffer->ulNbBytesFree)
				{//Databuffer can be poped
					SNDLST2_M_DynamicIsolate(pstPushedBuffer);

					start_callback(pstPushedBuffer);

					SND_fn_vFreeSndEx(E_ucSndBlockMain,pstPushedBuffer);			
				}

				if (!data_locked)
					//no more data locked to pop
					break;
			}
#ifdef SNDREC_REPORT
			if (data_locked)
				_RPTF1(_CRT_WARN,"databuffer lack (%d bytes)\n",data_locked);
#endif

			res=DSoundTry(IDirectSoundCaptureBuffer_Unlock(
				lpDirectSoundCaptureBuffer,
				pvData1,
				dwData1,
				pvData2,
				dwData2));
		}
		ReleaseMutex(hMutexRecord);
	}
#ifdef SNDREC_REPORT
	_RPTF0(_CRT_WARN,"record stop\n");
#endif

	ExitThread(0);
	return 0;
}

//-------------------------------------------------------------
//	SND_fn_vInitRecord: init SNDREC
//-------------------------------------------------------------
int SND_CALL SND_fn_iInitRecord(SND_tdstInitStruct *pInitStruct)
{
	int i;

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

	hMutexRecord=CreateMutex(NULL,FALSE,NULL);

	hEventStopRecord=CreateEvent(NULL,FALSE,FALSE,NULL);
	hEventNotify[NB_BUFFERS]=hEventStopRecord;

	for (i=0;i<NB_BUFFERS;i++)
		hEventNotify[i]=CreateEvent(NULL,FALSE,FALSE,NULL);

	memset(&RecordVoice,0,sizeof(RecordVoice));

	return C_INIT_OK;
}

//-------------------------------------------------------------
//	SND_fn_vDesInitRecord: desinit SNDREC
//-------------------------------------------------------------
void SND_CALL SND_fn_vDesInitRecord(void)
{
	if (bDriverInRecording)
		SND_fn_vStopRecord((long)&RecordVoice);
}

//---------------------------------------------------------------
//	SND_fn_bTestFormatRecord:test is a fromat is valid for recording
//	Parameters:
//		pformat: format to be tested; some fields may be filled depending of flags value
//			to return a effective valid format
//		flags: boolean combinaison of following
//			FORMATREC_RESOLUTION: test Resolution field value; will fill pFormat with a valid value
//			FORMATREC_FREQUENCY: test Frequency field value; will fill pFormat with a valid value
//			FORMATREC_NBCHANNEL: test NbChannlel field value; will fill pFormat with a valid value
//			FORMATREC_ZIP: test eZip field value; will fill pFormat with a valid value
//	Return:
//		TRUE when a calid format was found; some filed of pFormat could be changed
//		FALSE when no format is availlable regardless to flags value
//---------------------------------------------------------------
SndBool SND_CALL SND_fn_bTestFormatRecord(SND_tdstFormat* pformat,int flags)
{
	MMRESULT ret;
	WAVEFORMATEX WaveFormatEx;
	WAVEINCAPS WaveInCaps;

#define MASK_8BITS		(WAVE_FORMAT_1M08 | WAVE_FORMAT_1S08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_4S08)
#define MASK_16BITS		(WAVE_FORMAT_1M16 | WAVE_FORMAT_1S16 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S16 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4S16)
#define MASK_MONO		(WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_2M16 | WAVE_FORMAT_4M16)
#define MASK_STEREO		(WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_1S16 | WAVE_FORMAT_2S16 | WAVE_FORMAT_4S16)


	memset(&WaveInCaps,0,sizeof(WaveInCaps));
	if ((ret=waveInGetDevCaps(WAVE_MAPPER,&WaveInCaps,sizeof(WaveInCaps)))!=MMSYSERR_NOERROR)
	{
		return FALSE;
	}

	//check eZip
	if (flags & FORMATREC_ZIP)
	{//eZip is forced to a value
		if (pformat->eZip!=SAMPLE_PCM)
			//PCM is currently the only format
			return FALSE;
	}
	else
		//return the best eZip value
		pformat->eZip=SAMPLE_PCM;

	//check resolution
	if (flags & FORMATREC_RESOLUTION)
	{//eResolution is forced to a value
		if (pformat->uFormat.stPCM.uwResolution==8)
			WaveInCaps.dwFormats^=MASK_8BITS;
		if (pformat->uFormat.stPCM.uwResolution==16)
			WaveInCaps.dwFormats^=MASK_16BITS;

		//no more compatible format
		if (!WaveInCaps.dwFormats)
			return FALSE;
	}
	else
	{
		if (WaveInCaps.dwFormats & MASK_16BITS)
			WaveInCaps.dwFormats&=MASK_16BITS;

		pformat->uFormat.stPCM.uwResolution=(WaveInCaps.dwFormats & MASK_8BITS) ? 8 : 16;
	}

	//check channel
	if (flags & FORMATREC_NBCHANNEL)
	{//nbchannel is forced to a value
		if (pformat->uFormat.stPCM.uwNbChannels==1)
			WaveInCaps.dwFormats^=MASK_MONO;
		if (pformat->uFormat.stPCM.uwNbChannels==2)
			WaveInCaps.dwFormats^=MASK_STEREO;

		//no more compatible format
		if (!WaveInCaps.dwFormats)
			return FALSE;
	}
	else
	{
		if (WaveInCaps.dwFormats & MASK_STEREO)
			WaveInCaps.dwFormats&=MASK_STEREO;
	
		pformat->uFormat.stPCM.uwNbChannels=(WaveInCaps.dwFormats & MASK_MONO) ? 1 : 2; 
	}

	if (!(flags & FORMATREC_FREQUENCY))
	{
		if (WaveInCaps.dwFormats>=WAVE_FORMAT_4M08)
			pformat->uFormat.stPCM.ulFreq=44100;
		else
			if (WaveInCaps.dwFormats>=WAVE_FORMAT_2M08)
				pformat->uFormat.stPCM.ulFreq=22050;
			else
				pformat->uFormat.stPCM.ulFreq=11025;
	}

	//first try with exact value for frequency
	memset(&WaveFormatEx,0,sizeof(WaveFormatEx));
	WaveFormatEx.wFormatTag=WAVE_FORMAT_PCM;
	WaveFormatEx.nChannels=pformat->uFormat.stPCM.uwNbChannels; 
    WaveFormatEx.wBitsPerSample=pformat->uFormat.stPCM.uwResolution; 
    WaveFormatEx.nSamplesPerSec=pformat->uFormat.stPCM.ulFreq; 
    WaveFormatEx.nBlockAlign=(WORD)(WaveFormatEx.nChannels*WaveFormatEx.wBitsPerSample/8); 
    WaveFormatEx.nAvgBytesPerSec=WaveFormatEx.nSamplesPerSec*WaveFormatEx.nBlockAlign; 
    WaveFormatEx.cbSize=0;

	if ((ret=waveInOpen(NULL,WAVE_MAPPER,&WaveFormatEx,0,0,WAVE_FORMAT_QUERY))==MMSYSERR_NOERROR)
		return TRUE;
	else
		return FALSE;
}

//-----------------------------------------------------------
//	SND_fn_lCreateBufferRecord: create a recording buffer
//	Parameters:
//		SND_tdstFormat: format souhaité
//		pCallback; type de callback
//	Return:
//		C_PLAY_FAILED if failed; else, Buffer is ready to record
//-----------------------------------------------------------
long SND_CALL SND_fn_lCreateBufferRecord(SND_tdstFormat* pformat,SND_tdstCallback* pCallback)
{
	long ret;

	snd_assert(pCallback->eType==BUFFER_STACK);

	WaitForSingleObject(hMutexRecord,INFINITE);

	if (!SND_fn_bTestFormatRecord(pformat,FORMATREC_RESOLUTION|FORMATREC_FREQUENCY|FORMATREC_NBCHANNEL|FORMATREC_ZIP)
		|| RecordVoice.bUsed)
		ret=C_PLAY_FAILED;
	else
	{
		SNDLST2_M_DynamicInitAnchor(&RecordVoice.pBufferList);

		memcpy(&RecordVoice.stFormat,pformat,sizeof(RecordVoice.stFormat));

		RecordVoice.lSampleToBytesShift=0;
		if (RecordVoice.stFormat.uFormat.stPCM.uwResolution==16) RecordVoice.lSampleToBytesShift++;
		if (RecordVoice.stFormat.uFormat.stPCM.uwNbChannels==2) RecordVoice.lSampleToBytesShift++;

		dwSizeBufferSamples=RecordVoice.stFormat.uFormat.stPCM.ulFreq/NB_BUFFERS;
		dwSizeBufferBytes=dwSizeBufferSamples<<RecordVoice.lSampleToBytesShift;

		RecordVoice.CallbackStack=pCallback->uCallback.CallbackStack;
		RecordVoice.stFormat.uFormat.stPCM.ulNbSamples=NB_BUFFERS*dwSizeBufferSamples;

		ret=(long)&RecordVoice;
	}

	ReleaseMutex(hMutexRecord);

	return ret;
}

//---------------------------------------------------------------
//	SND_fn_ucGetInstantVuMeterRecordSound: get current value for VuMeter
//	Return:
//		volume (0=silence; 127= maximum power)
//-----------------------------------------------------------------
unsigned char SND_CALL SND_fn_ucGetInstantVuMeterRecord(void)
{
	unsigned char tmp;

	WaitForSingleObject(hMutexRecord,INFINITE);

	tmp=get_vumeter_value();

	ReleaseMutex(hMutexRecord);

	return tmp;
}

//---------------------------------------------------------------
//	SND_fn_lPushBufferToRecord: add a new buffer to record data into
//	Parameters:
//		id_buffer: id of RecordBuffer
//		ptr: pointer where copy recorded data
//		size_ptr: nb bytes to copy
//	Return:
//		id of data buffer
//-----------------------------------------------------------------
long SND_CALL SND_fn_lPushBufferToRecord(long id_buffer,void* ptr,int size_ptr)
{
	pRECORD_BUFFER pstPushedBuffer;

	WaitForSingleObject(hMutexRecord,INFINITE);

	pstPushedBuffer=SND_fn_pvMallocSndEx(E_ucSndBlockMain,sizeof(RECORD_BUFFER));
    SNDLST2_M_DynamicInitElement(pstPushedBuffer);

	//size_ptr must agree with data alignement
	snd_assert(!(size_ptr & ((1<<RecordVoice.lSampleToBytesShift)>>1)));

	pstPushedBuffer->ptrData=ptr;
	pstPushedBuffer->ulNbBytesMax=size_ptr;
	pstPushedBuffer->ulNbBytesFree=size_ptr;
	snd_assert(id_buffer==(long)&RecordVoice);
	pstPushedBuffer->pVoice=&RecordVoice;

    SNDLST2_M_DynamicAddTail(&RecordVoice.pBufferList,pstPushedBuffer);

	ReleaseMutex(hMutexRecord);

	return (long)pstPushedBuffer;
}

int fn_iTryStartRecord(long id_buffer)
{
	HRESULT res;
	DSCBUFFERDESC buffer_desc;
	WAVEFORMATEX WaveFormatEx;
	DSBPOSITIONNOTIFY PositionNotify[NB_BUFFERS];
	//DWORD id_thread;
	int i;

	//hRecordThread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)RecordThread,NULL,0,&id_thread);

	WaitForSingleObject(hMutexRecord,INFINITE);

	if ((res=DSoundTry(DirectSoundCaptureCreate(
			NULL,
			&lpDirectSoundCapture,
			NULL)))!=DS_OK)
	{
		/*MessageBox(NULL,"","DirectSoundCaptureCreate failed",MB_OK); */

		ReleaseMutex(hMutexRecord);
		return -1;
	}

	//create a DSCapture for 1 second
	memset(&WaveFormatEx,0,sizeof(WaveFormatEx));
	WaveFormatEx.wFormatTag=WAVE_FORMAT_PCM;
	WaveFormatEx.nChannels=RecordVoice.stFormat.uFormat.stPCM.uwNbChannels; 
    WaveFormatEx.wBitsPerSample=RecordVoice.stFormat.uFormat.stPCM.uwResolution; 
    WaveFormatEx.nSamplesPerSec=RecordVoice.stFormat.uFormat.stPCM.ulFreq; 
    WaveFormatEx.nBlockAlign=(WORD)(WaveFormatEx.nChannels*WaveFormatEx.wBitsPerSample/8); 
    WaveFormatEx.nAvgBytesPerSec=WaveFormatEx.nSamplesPerSec*WaveFormatEx.nBlockAlign; 
    WaveFormatEx.cbSize=0;
	memset(&buffer_desc,0,sizeof(buffer_desc));
	buffer_desc.dwSize=sizeof(buffer_desc);
	buffer_desc.dwFlags=DSCBCAPS_WAVEMAPPED;
	buffer_desc.dwBufferBytes=RecordVoice.stFormat.uFormat.stPCM.ulNbSamples<<RecordVoice.lSampleToBytesShift;
	buffer_desc.lpwfxFormat=&WaveFormatEx;

	if ((res=DSoundTry(IDirectSoundCapture_CreateCaptureBuffer(
		lpDirectSoundCapture,
		&buffer_desc,
		&lpDirectSoundCaptureBuffer,
		NULL)))!=DS_OK)
	{
		/*MessageBox(NULL,"","IDirectSoundCapture_CreateCaptureBuffer failed",MB_OK);*/
		IDirectSoundCapture_Release(lpDirectSoundCapture);

		ReleaseMutex(hMutexRecord);
		return -2;
	}

	res=DSoundTry(IDirectSoundCapture_QueryInterface(
		lpDirectSoundCaptureBuffer,
		&IID_IDirectSoundNotify,
		&lpDsNotify));
	
	for (i=0;i<NB_BUFFERS;i++)
	{
		dwOffsetBytes[i]=i*dwSizeBufferBytes;
		dwOffsetSamples[i]=dwOffsetBytes[i]>>RecordVoice.lSampleToBytesShift;
	}
	for (i=0;i<NB_BUFFERS;i++)
	{
		PositionNotify[i].dwOffset=dwOffsetBytes[(i+1)%NB_BUFFERS];
		PositionNotify[i].hEventNotify=hEventNotify[i];
	}

	res=DSoundTry(IDirectSoundNotify_SetNotificationPositions(
		lpDsNotify,
		NB_BUFFERS,
		PositionNotify));

	if ((res=DSoundTry(IDirectSoundCaptureBuffer_Start(
		lpDirectSoundCaptureBuffer,
		DSCBSTART_LOOPING)))!=DS_OK)
	{
		/* MessageBox(NULL,"","IDirectSoundCaptureBuffer_Start failed",MB_OK); */
		IDirectSoundCaptureBuffer_Release(lpDirectSoundCaptureBuffer);
		IDirectSoundCapture_Release(lpDirectSoundCapture);

		ReleaseMutex(hMutexRecord);
		return -3;
	}

	bDriverInRecording=TRUE;
	ReleaseMutex(hMutexRecord);

	return 0;
}


static SndBool bReleaseForced=FALSE;

SndBool SND_CALL SND_fn_bStartRecord(long id_buffer)
{
	DWORD id_thread;

	hRecordThread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)RecordThread,NULL,0,&id_thread);
	switch(fn_iTryStartRecord(id_buffer))
	{
	case  0: return TRUE; /* succeed */
	case -1: /* DirectCaptureObject: creation failure */
	case -2: /* DirectCaptureBuffer: creation failure */
	case -3: /* Start failure */
		if (bReleaseForced) 
		{
			CloseHandle(hRecordThread);
			hRecordThread=NULL;
			return FALSE;
		}

		/* release driver and retry (in case card is half duplex) */
		bReleaseForced=TRUE;
		SND_fn_vForceReleaseDriverSxd();
		if (fn_iTryStartRecord(id_buffer)!=0) /* error */
		{			
			/* failed again=>report error */
			SND_fn_vForceRestoreDriverSxd();

			CloseHandle(hRecordThread);
			hRecordThread=NULL;

			bReleaseForced=FALSE;
			return FALSE;
		}
		return TRUE;

	default: return FALSE;
	}
}

void SND_CALL SND_fn_vStopRecord(long id_buffer)
{
	HRESULT res;
	int i;
	pRECORD_BUFFER pstPushedBuffer,pstPushedBufferNext;

	WaitForSingleObject(hMutexRecord,INFINITE);

	if (lpDirectSoundCaptureBuffer) /* test: jv */
		res=DSoundTry(IDirectSoundCaptureBuffer_Stop(lpDirectSoundCaptureBuffer));

	if (lpDsNotify)
		IDirectSoundNotify_Release(lpDsNotify);
	lpDsNotify=NULL;

	//purge databuffer list
	SNDLST2_M_DynamicForEachMovingElementOf(&RecordVoice.pBufferList,pstPushedBuffer,pstPushedBufferNext,i)
	{
		SNDLST2_M_DynamicIsolate(pstPushedBuffer);

		SND_fn_vFreeSndEx(E_ucSndBlockMain,pstPushedBuffer);			
	}

	if (lpDirectSoundCaptureBuffer)
		IDirectSoundCaptureBuffer_Release(lpDirectSoundCaptureBuffer);
	lpDirectSoundCaptureBuffer=NULL;

	bDriverInRecording=FALSE;

	if (lpDirectSoundCapture)
		IDirectSoundCapture_Release(lpDirectSoundCapture);
	lpDirectSoundCapture=NULL;

	ReleaseMutex(hMutexRecord);

	SetEvent(hEventStopRecord);

	if (bReleaseForced)
	{
		bReleaseForced=FALSE;
		SND_fn_vForceRestoreDriverSxd();
	}
}

SndBool SND_CALL SND_fn_bIsBufferFullDuplexRecord(long id_buffer)
{
	return FALSE;
}

SndBool SND_CALL SND_fn_bIsWorkingRecord(long id_buffer)
{
	return bDriverInRecording;
}

//---------------------------------------------------------
//	SND_fn_bManageMsgForRecord: traitement des messages envoyés à la Thread Wave
//	Paramètres:
//		msg: message to be processed
//	Return:
//		TRUE if message was processed  for WaveIn
//		FALSE if message must be processed by the thread
//---------------------------------------------------------
SndBool SND_CALL SND_fn_bManageMsgForRecord(long p_msg)
{
	return FALSE;
}


