#include "precomp.h"
#include <nmdsprv.h>

#include "mixer.h"
#include "dscstream.h"
#include "agc.h"


// static member initialization
BOOL DSC_Manager::s_bInitialized = FALSE;
DSC_CAPTURE_INFO DSC_Manager::s_aDSC[MAX_NUMBER_DSCAPTURE_DEVICES];
int DSC_Manager::s_nCaptureDevices = 0;
HINSTANCE DSC_Manager::s_hDSCLib = NULL;
DS_CAP_CREATE DSC_Manager::s_pDSCapCreate = NULL;
DS_CAP_ENUM DSC_Manager::s_pDSCapEnum = NULL;




// static
HRESULT DSC_Manager::Initialize()
{

	if (s_bInitialized)
	{
		return S_OK;
	}


	// failsafe way to to turn DSC off, without turning
	// DirectSound support off.  Otherwise, the UI setting
	// to disable DS will also disable DSC.
	{
		BOOL bDisable;
		RegEntry re(DISABLE_DSC_REGKEY, HKEY_LOCAL_MACHINE, FALSE,0);

		bDisable = re.GetNumber(DISABLE_DSC_REGVALUE, FALSE);
		if (bDisable)
		{
			return E_FAIL;
		}
	}


	// initialize the array of structure descriptions

	s_hDSCLib = LoadLibrary(DSOUND_DLL);

	if (s_hDSCLib == NULL)
		return E_FAIL;


	s_pDSCapCreate = (DS_CAP_CREATE)GetProcAddress(s_hDSCLib, "DirectSoundCaptureCreate");
	s_pDSCapEnum = (DS_CAP_ENUM)GetProcAddress(s_hDSCLib, "DirectSoundCaptureEnumerateA");

	if ((s_pDSCapCreate) && (s_pDSCapEnum))
	{
		// enumerate!

		s_pDSCapEnum(DSC_Manager::DSEnumCallback, 0);

		if (s_nCaptureDevices != 0)
		{
			s_bInitialized = TRUE;
			return S_OK; // success
		}
	}

	FreeLibrary(s_hDSCLib);
	s_hDSCLib = NULL;
	return E_FAIL;

}



// static
BOOL CALLBACK DSC_Manager::DSEnumCallback(LPGUID lpGuid, LPCSTR lpcstrDescription,
	                           LPCSTR lpcstrModule, LPVOID lpContext)
{
	if (lpGuid == NULL)
	{
		s_aDSC[s_nCaptureDevices].guid = GUID_NULL;
	}
	else
	{
		s_aDSC[s_nCaptureDevices].guid = *lpGuid;
	}


	lstrcpyn(s_aDSC[s_nCaptureDevices].szDescription, lpcstrDescription, MAX_DSC_DESCRIPTION_STRING);

	s_aDSC[s_nCaptureDevices].uWaveId = WAVE_MAPPER;
	s_nCaptureDevices++;
	return TRUE;
}


// static
HRESULT DSC_Manager::CreateInstance(GUID *pGuid, IDirectSoundCapture **pDSC)
{
	HRESULT hr;

	if FAILED(Initialize())
	{
		return E_FAIL;
	}

	if (*pGuid == GUID_NULL)
		pGuid = NULL;

	hr = s_pDSCapCreate(pGuid, pDSC, NULL);

	return hr;
}


// static
HRESULT DSC_Manager::MapWaveIdToGuid(UINT uWaveID, GUID *pGuid)
{

	HRESULT hr;
	WAVEINCAPS waveInCaps;
	UINT uNumWaveDevs;
	GUID guid = GUID_NULL;
	int nIndex;
	MMRESULT mmr;
	HWAVEIN hWaveIn;
	WAVEFORMATEX waveFormat = {WAVE_FORMAT_PCM, 1, 8000, 16000, 2, 16, 0};
	IDirectSoundCapture *pDSC=NULL;

	*pGuid = GUID_NULL;
	
	if (FAILED( Initialize() ))
	{
		return E_FAIL;
	}

	// only one wave device, take the easy way out
	uNumWaveDevs = waveInGetNumDevs();

	if ((uNumWaveDevs <= 1) || (uWaveID == WAVE_MAPPER))
	{
		return S_OK;
	}

	// more than one wavein device
	mmr = waveInGetDevCaps(uWaveID, &waveInCaps, sizeof(WAVEINCAPS));
	if (mmr == MMSYSERR_NOERROR)
	{
		hr = DsprvGetWaveDeviceMapping(waveInCaps.szPname, TRUE, &guid);
		if (SUCCEEDED(hr))
		{
			*pGuid = guid;
			return S_OK;
		}
	}


	// scan through the DSC list to see if we've mapped this device
	// previously

	for (nIndex = 0; nIndex < s_nCaptureDevices; nIndex++)
	{
		if (s_aDSC[nIndex].uWaveId == uWaveID)
		{
			*pGuid = s_aDSC[nIndex].guid;
			return S_OK;
		}
	}

	//  hack approach to mapping the device to a guid
	mmr = waveInOpen(&hWaveIn, uWaveID, &waveFormat, 0,0,0);
	if (mmr != MMSYSERR_NOERROR)
	{
		return S_FALSE;
	}

	// find all the DSC devices that fail to open
	for (nIndex = 0; nIndex < s_nCaptureDevices; nIndex++)
	{
		s_aDSC[nIndex].bAllocated = FALSE;
		hr = CreateInstance(&(s_aDSC[nIndex].guid), &pDSC);
		if (FAILED(hr))
		{
			s_aDSC[nIndex].bAllocated = TRUE;
		}
		else
		{
			pDSC->Release();
			pDSC=NULL;
		}
	}

	waveInClose(hWaveIn);

	// scan through the list of allocated devices and
	// see which one opens
	for (nIndex = 0; nIndex < s_nCaptureDevices; nIndex++)
	{
		if (s_aDSC[nIndex].bAllocated)
		{
			hr = CreateInstance(&(s_aDSC[nIndex].guid), &pDSC);
			if (SUCCEEDED(hr))
			{
				// we have a winner
				pDSC->Release();
				pDSC = NULL;
				*pGuid = s_aDSC[nIndex].guid;
				s_aDSC[nIndex].uWaveId = uWaveID;
				return S_OK;
			}
		}
	}
	// if we got to this point, it means we failed to map a device
	// just use GUID_NULL and return an error
	return S_FALSE;
}






SendDSCStream::SendDSCStream() :
SendMediaStream(),
m_pAudioFilter(NULL),
m_lRefCount(0),
m_pDSC(NULL),
m_pDSCBuffer(NULL),
m_hEvent(NULL),
m_dwSamplesPerFrame(0),
m_dwNumFrames(0),
m_dwFrameSize(0),
m_dwDSCBufferSize(0),
m_dwSilenceTime(0),
m_dwFrameTimeMS(0),
m_bFullDuplex(TRUE),
m_bJammed(FALSE),
m_bCanSignalOpen(TRUE),
m_bCanSignalFail(TRUE),
m_nFailCount(0),
m_agc(NULL),
m_bAutoMix(FALSE),
m_pDTMF(NULL)
{
	return;
};


HRESULT SendDSCStream::Initialize(DataPump *pDP)
{
	HRESULT hr;


	m_pDP = pDP;

	hr = DSC_Manager::Initialize();
	if (FAILED(hr))
	{
		return hr;
	}

	m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
	if (m_hEvent == NULL)
	{
		return DPR_CANT_CREATE_EVENT;
	}

    DBG_SAVE_FILE_LINE
	m_pAudioFilter = new AcmFilter();
	if (!m_pAudioFilter)
	{
		return DPR_OUT_OF_MEMORY;
	}

    DBG_SAVE_FILE_LINE
	m_pDTMF = new DTMFQueue;
	if (!m_pDTMF)
	{
		return DPR_OUT_OF_MEMORY;
	}


	m_DPFlags = DP_FLAG_ACM|DP_FLAG_MMSYSTEM|DP_FLAG_DIRECTSOUND|DP_FLAG_SEND;

	m_SendTimestamp = m_SavedTickCount = timeGetTime();

	m_dwDstSize = 0;
	m_fSending = FALSE;

	m_hCapturingThread = NULL;
	m_CaptureThId = 0;
	m_ThreadFlags = 0;

	m_pRTPSend = NULL;
	m_RTPPayload = 0;

	m_CaptureDevice = -1;
	m_pRTPSend = NULL;

	ZeroMemory(m_aPackets, sizeof(m_aPackets));
	ZeroMemory(&m_mmioSrc, sizeof(m_mmioSrc));

	m_DPFlags = DP_FLAG_ACM | DP_FLAG_MMSYSTEM | DP_FLAG_AUTO_SILENCE_DETECT;
	m_DPFlags = (m_DPFlags & DP_MASK_PLATFORM) | DPFLAG_ENABLE_SEND;
	m_DPFlags |= DPFLAG_INITIALIZED;

	return S_OK;

}



SendDSCStream::~SendDSCStream()
{
	if (m_DPFlags & DPFLAG_INITIALIZED)
	{
		if (m_DPFlags & DPFLAG_CONFIGURED_SEND)
		{
			UnConfigure();
		}

		if (m_pRTPSend)
		{
			m_pRTPSend->Release();
			m_pRTPSend = NULL;
		}

		if (m_pDTMF)
		{
			delete m_pDTMF;
			m_pDTMF = NULL;
		}

		if (m_pAudioFilter)
		{
			delete m_pAudioFilter;
		}

		if (m_hEvent)
		{
			CloseHandle(m_hEvent);
		}

		m_pDP->RemoveMediaChannel(MCF_SEND|MCF_AUDIO, (IMediaChannel*)(SendMediaStream*)this);

		m_DPFlags &= ~DPFLAG_INITIALIZED;
	}
	
}



HRESULT STDMETHODCALLTYPE SendDSCStream::Configure(
	BYTE *pFormat,
	UINT cbFormat,
	BYTE *pChannelParams,
	UINT cbParams,
	IUnknown *pUnknown)
{
	AUDIO_CHANNEL_PARAMETERS audChannelParams;
	WAVEFORMATEX *pwfSend;
	MMRESULT mmr;
	MEDIAPACKETINIT mpi;
	DWORD dwSourceSize;
	int nIndex;
	HRESULT hr;

	FX_ENTRY ("SendDSCStream::Configure");


	// basic parameter checking
	if (! (m_DPFlags & DPFLAG_INITIALIZED))
	{
		return DPR_OUT_OF_MEMORY;
	}

	// Not a good idea to change anything while in mid-stream
	if (m_DPFlags & DPFLAG_STARTED_SEND)
	{
		return DPR_IO_PENDING; // anything better to return
	}

	if (m_DPFlags & DPFLAG_CONFIGURED_SEND)
	{
		DEBUGMSG(ZONE_DP, ("Stream Re-Configuration - calling UnConfigure"));
		UnConfigure();
	}

	if ((NULL == pFormat) || (NULL == pChannelParams) ||
		(cbParams < sizeof(AUDIO_CHANNEL_PARAMETERS)) ||
		(cbFormat < sizeof(WAVEFORMATEX)))
	{
		return DPR_INVALID_PARAMETER;
	}

	audChannelParams = *(AUDIO_CHANNEL_PARAMETERS *)pChannelParams;
	pwfSend = (WAVEFORMATEX *)pFormat;
	m_wfCompressed = *pwfSend;
	m_wfCompressed.cbSize = 0;

	// initialize the ACM filter
	mmr = AcmFilter::SuggestDecodeFormat(pwfSend, &m_wfPCM);
	if (mmr != MMSYSERR_NOERROR)
	{
		return DPR_INVALID_PARAMETER;
	}

	mmr = m_pAudioFilter->Open(&m_wfPCM, pwfSend);
	if (mmr != 0)
	{
		DEBUGMSG (ZONE_DP, ("%s: AcmFilter->Open failed, mmr=%d\r\n", _fx_, mmr));
		return DPR_CANT_OPEN_CODEC;
	}

	m_dwSamplesPerFrame = audChannelParams.ns_params.wFrameSize * audChannelParams.ns_params.wFramesPerPkt;
	m_dwFrameTimeMS = (m_dwSamplesPerFrame * 1000) / m_wfPCM.nSamplesPerSec;

	ASSERT(m_dwFrameTimeMS > 0);
	if (m_dwFrameTimeMS <= 0)
	{
		m_pAudioFilter->Close();
		return DPR_INVALID_PARAMETER;
	}

	m_dwNumFrames = 1000 / m_dwFrameTimeMS;
	if (m_dwNumFrames < MIN_NUM_DSC_SEGMENTS)
	{
		m_dwNumFrames = MIN_NUM_DSC_SEGMENTS;
	}


	m_dwFrameSize = m_dwSamplesPerFrame * m_wfPCM.nBlockAlign;
	m_pAudioFilter->SuggestDstSize(m_dwFrameSize, &m_dwDstSize);


	m_dwDSCBufferSize = m_dwFrameSize * m_dwNumFrames;

	// create the packets

	ZeroMemory(&mpi, sizeof(mpi));


	mpi.dwFlags = DP_FLAG_SEND | DP_FLAG_ACM | DP_FLAG_MMSYSTEM;
	mpi.cbOffsetNetData = sizeof(RTP_HDR);
	mpi.cbSizeNetData = m_dwDstSize;
	mpi.cbSizeDevData = m_dwFrameSize;
	mpi.cbSizeRawData = m_dwFrameSize;
	mpi.pDevFmt = &m_wfPCM;
	mpi.pStrmConvSrcFmt = &m_wfPCM;
	mpi.payload = audChannelParams.RTP_Payload;
	mpi.pStrmConvDstFmt = &m_wfCompressed;

	hr = CreateAudioPackets(&mpi);
	if (FAILED(hr))
	{
		m_pAudioFilter->Close();
		return hr;
	}

	AudioFile::OpenSourceFile(&m_mmioSrc, &m_wfPCM);

	m_pDTMF->Initialize(&m_wfPCM);
	m_pDTMF->ClearQueue();


	// Initialize RSVP structures
	InitAudioFlowspec(&m_flowspec, pwfSend, m_dwDstSize);


	// Initialize QOS structures
	if (m_pDP->m_pIQoS)
	{
		// Initialize our requests. One for CPU usage, one for bandwidth usage.
		m_aRRq.cResourceRequests = 2;
		m_aRRq.aResourceRequest[0].resourceID = RESOURCE_OUTGOING_BANDWIDTH;
		if (m_dwFrameTimeMS)
		{
			m_aRRq.aResourceRequest[0].nUnitsMin = (DWORD)(m_dwDstSize + sizeof(RTP_HDR) + IP_HEADER_SIZE + UDP_HEADER_SIZE) * 8000 / m_dwFrameTimeMS;
		}
		else
		{
			m_aRRq.aResourceRequest[0].nUnitsMin = 0;
		}
		m_aRRq.aResourceRequest[1].resourceID = RESOURCE_CPU_CYCLES;
		m_aRRq.aResourceRequest[1].nUnitsMin = 800;

//      BUGBUG. This is, in theory the correct calculation, but until we do more investigation, go with a known value
//		m_aRRq.aResourceRequest[1].nUnitsMin = (audDetails.wCPUUtilizationEncode+audDetails.wCPUUtilizationDecode)*10;

		// Initialize QoS structure
		ZeroMemory(&m_Stats, sizeof(m_Stats));

		// Initialize oldest QoS callback timestamp
		// Register with the QoS module. Even if this call fails, that's Ok, we'll do without the QoS support
		
		// The Callback is defined in SendAudioStream
		m_pDP->m_pIQoS->RequestResources((GUID *)&MEDIA_TYPE_H323AUDIO, (LPRESOURCEREQUESTLIST)&m_aRRq, SendAudioStream::QosNotifyAudioCB, (DWORD_PTR)this);
	}



	// Initialize Statview constats
	UPDATE_REPORT_ENTRY(g_prptCallParameters, pwfSend->wFormatTag, REP_SEND_AUDIO_FORMAT);
	UPDATE_REPORT_ENTRY(g_prptCallParameters, pwfSend->nSamplesPerSec, REP_SEND_AUDIO_SAMPLING);
	UPDATE_REPORT_ENTRY(g_prptCallParameters, pwfSend->nAvgBytesPerSec * 8, REP_SEND_AUDIO_BITRATE);
	RETAILMSG(("NAC: Audio Send Format: %s", (pwfSend->wFormatTag == 66) ? "G723.1" : (pwfSend->wFormatTag == 112) ? "LHCELP" : (pwfSend->wFormatTag == 113) ? "LHSB08" : (pwfSend->wFormatTag == 114) ? "LHSB12" : (pwfSend->wFormatTag == 115) ? "LHSB16" : (pwfSend->wFormatTag == 6) ? "MSALAW" : (pwfSend->wFormatTag == 7) ? "MSULAW" : (pwfSend->wFormatTag == 130) ? "MSRT24" : "??????"));
	RETAILMSG(("NAC: Audio Send Sampling Rate (Hz): %ld", pwfSend->nSamplesPerSec));
	RETAILMSG(("NAC: Audio Send Bitrate (w/o network overhead - bps): %ld", pwfSend->nAvgBytesPerSec*8));

	UPDATE_REPORT_ENTRY(g_prptCallParameters, m_dwSamplesPerFrame, REP_SEND_AUDIO_PACKET);
	RETAILMSG(("NAC: Audio Send Packetization (ms/packet): %ld", pwfSend->nSamplesPerSec ? m_dwSamplesPerFrame * 1000UL / pwfSend->nSamplesPerSec : 0));
	INIT_COUNTER_MAX(g_pctrAudioSendBytes, (pwfSend->nAvgBytesPerSec + pwfSend->nSamplesPerSec * (sizeof(RTP_HDR) + IP_HEADER_SIZE + UDP_HEADER_SIZE) / m_dwSamplesPerFrame) << 3);

	m_DPFlags |= DPFLAG_CONFIGURED_SEND;
	return S_OK;
}

void SendDSCStream::UnConfigure()
{
	if (m_DPFlags & DPFLAG_CONFIGURED_SEND)
	{
		Stop();

		m_pAudioFilter->Close();
		ReleaseAudioPackets();

		AudioFile::CloseSourceFile(&m_mmioSrc);

		m_ThreadFlags = 0;

		if (m_pDP->m_pIQoS)
		{
			m_pDP->m_pIQoS->ReleaseResources((GUID *)&MEDIA_TYPE_H323AUDIO, (LPRESOURCEREQUESTLIST)&m_aRRq);
		}

		m_DPFlags &= ~DPFLAG_CONFIGURED_SEND;

	}
}


DWORD CALLBACK SendDSCStream::StartRecordingThread (LPVOID pVoid)
{
	SendDSCStream *pThisStream = (SendDSCStream*)pVoid;
	return pThisStream->RecordingThread();
}


HRESULT STDMETHODCALLTYPE
SendDSCStream::Start()
{
	FX_ENTRY ("SendDSCStream::Start")

	if (m_DPFlags & DPFLAG_STARTED_SEND)
		return DPR_SUCCESS;

	if (!(m_DPFlags & DPFLAG_ENABLE_SEND))
		return DPR_SUCCESS;

	if ((!(m_DPFlags & DPFLAG_CONFIGURED_SEND)) || (m_pRTPSend==NULL))
		return DPR_NOT_CONFIGURED;

	ASSERT(!m_hCapturingThread);
	m_ThreadFlags &= ~(DPTFLAG_STOP_RECORD|DPTFLAG_STOP_SEND);

	SetFlowSpec();

	// Start recording thread
	if (!(m_ThreadFlags & DPTFLAG_STOP_RECORD))
		m_hCapturingThread = CreateThread(NULL,0, SendDSCStream::StartRecordingThread,(LPVOID)this,0,&m_CaptureThId);

	m_DPFlags |= DPFLAG_STARTED_SEND;

	DEBUGMSG (ZONE_DP, ("%s: Record threadid=%x,\r\n", _fx_, m_CaptureThId));
	return DPR_SUCCESS;
}

HRESULT
SendDSCStream::Stop()
{											
	DWORD dwWait;

	if(!(m_DPFlags & DPFLAG_STARTED_SEND))
	{
		return DPR_SUCCESS;
	}
	
	m_ThreadFlags = m_ThreadFlags  |
		DPTFLAG_STOP_SEND |  DPTFLAG_STOP_RECORD ;

	
	DEBUGMSG (ZONE_DP, ("SendDSCStream::Stop - Waiting for record thread to exit\r\n"));

	if (m_hCapturingThread)
	{
		dwWait = WaitForSingleObject (m_hCapturingThread, INFINITE);

		DEBUGMSG (ZONE_DP, ("SendDSCStream::Stop: Recording thread exited\r\n"));
		ASSERT(dwWait != WAIT_FAILED);
	
		CloseHandle(m_hCapturingThread);
		m_hCapturingThread = NULL;
	}
	m_DPFlags &= ~DPFLAG_STARTED_SEND;
	
	return DPR_SUCCESS;
}


HRESULT STDMETHODCALLTYPE SendDSCStream::SetMaxBitrate(UINT uMaxBitrate)
{
	return S_OK;
}


HRESULT STDMETHODCALLTYPE SendDSCStream::QueryInterface(REFIID iid, void **ppVoid)
{
	// resolve duplicate inheritance to the SendMediaStream;

	if (iid == IID_IUnknown)
	{
		*ppVoid = (IUnknown*)((SendMediaStream*)this);
	}
	else if (iid == IID_IMediaChannel)
	{
		*ppVoid = (IMediaChannel*)((SendMediaStream *)this);
	}
	else if (iid == IID_IAudioChannel)
	{
		*ppVoid = (IAudioChannel*)this;
	}
	else if (iid == IID_IDTMFSend)
	{
		*ppVoid = (IDTMFSend*)this;
	}
	else
	{
		*ppVoid = NULL;
		return E_NOINTERFACE;
	}
	AddRef();

	return S_OK;

}

ULONG STDMETHODCALLTYPE SendDSCStream::AddRef(void)
{
	return InterlockedIncrement(&m_lRefCount);
}

ULONG STDMETHODCALLTYPE SendDSCStream::Release(void)
{
	LONG lRet;

	lRet = InterlockedDecrement(&m_lRefCount);

	if (lRet == 0)
	{
		delete this;
		return 0;
	}

	else
		return lRet;

}

HRESULT STDMETHODCALLTYPE SendDSCStream::GetSignalLevel(UINT *pSignalStrength)
{
	UINT uLevel;
	DWORD dwJammed;

	if(!(m_DPFlags & DPFLAG_STARTED_SEND))
	{
		uLevel = 0;
	}
	else
	{
		uLevel = m_AudioMonitor.GetSignalStrength();

		if (m_bJammed)
		{
			uLevel = (2 << 16);  // 0x0200
		}
		else if (m_fSending)
		{
			uLevel |= (1 << 16); // 0x0100 + uLevel
		}
	}

	*pSignalStrength = uLevel;
	return S_OK;


	return 0;
}

HRESULT STDMETHODCALLTYPE SendDSCStream::GetProperty(DWORD dwProp, PVOID pBuf, LPUINT pcbBuf)
{
	HRESULT hr = DPR_SUCCESS;
	RTP_STATS RTPStats;
	DWORD dwValue;
	UINT len = sizeof(DWORD);	// most props are DWORDs

	if (!pBuf || *pcbBuf < len)
    {
		*pcbBuf = len;
		return DPR_INVALID_PARAMETER;
	}

	switch (dwProp)
    {

	case PROP_SILENCE_LEVEL:
		*(DWORD *)pBuf = m_AudioMonitor.GetSilenceLevel();
		break;

	case PROP_DUPLEX_TYPE:
		if(m_bFullDuplex == TRUE)
			*(DWORD*)pBuf = DUPLEX_TYPE_FULL;
		else
			*(DWORD*)pBuf =	DUPLEX_TYPE_HALF;
		break;

	case PROP_RECORD_ON:
		*(DWORD *)pBuf = (m_DPFlags & DPFLAG_ENABLE_SEND) !=0;
		break;

	case PROP_PAUSE_SEND:
		// To be determined
		break;

	case PROP_AUDIO_AUTOMIX:
		*(DWORD*)pBuf = m_bAutoMix;
		break;

	case PROP_RECORD_DEVICE:
		*(DWORD *)pBuf = m_CaptureDevice;
		break;

	default:
		hr = DPR_INVALID_PROP_ID;
		break;
	}
	return hr;
}


HRESULT STDMETHODCALLTYPE SendDSCStream::SetProperty(DWORD dwProp, PVOID pBuf, UINT cbBuf)
{
	DWORD dw;
	HRESULT hr = S_OK;
	
	if (cbBuf < sizeof (DWORD))
		return DPR_INVALID_PARAMETER;

	switch (dwProp)
    {
	case PROP_SILENCE_LEVEL:
		m_AudioMonitor.SetSilenceLevel(*(DWORD *)pBuf);
		break;


	case DP_PROP_DUPLEX_TYPE:
		m_bFullDuplex = (*(DWORD*)pBuf != 0);
		break;


	case PROP_AUDIO_AUTOMIX:
		m_bAutoMix = *(DWORD*)pBuf;
		break;


	case PROP_RECORD_DEVICE:
		m_CaptureDevice = *(DWORD*)pBuf;
		RETAILMSG(("NAC: Setting default record device to %d", m_CaptureDevice));
		break;

	case PROP_RECORD_ON:
	{
		DWORD flag = DPFLAG_ENABLE_SEND ;
		if (*(DWORD *)pBuf)
		{
			m_DPFlags |= flag; // set the flag
			Start();
		}
		else
		{
			m_DPFlags &= ~flag; // clear the flag
			Stop();
		}
		RETAILMSG(("DSCStream: %s", *(DWORD*)pBuf ? "Enabling Stream":"Pausing stream"));
		break;
	}	

	default:
		return DPR_INVALID_PROP_ID;
		break;
	}
	return hr;



}



void SendDSCStream::EndSend()
{
	return;
}



HRESULT SendDSCStream::CreateAudioPackets(MEDIAPACKETINIT *pmpi)
{
	int nIndex;
	HRESULT hr;

	ReleaseAudioPackets();

	for (nIndex = 0; nIndex < NUM_AUDIOPACKETS; nIndex++)
	{
        DBG_SAVE_FILE_LINE
		m_aPackets[nIndex] = new AudioPacket;
		if (m_aPackets[nIndex] == NULL)
		{
			return DPR_OUT_OF_MEMORY;
		}

		pmpi->index = nIndex;
		hr = m_aPackets[nIndex]->Initialize(pmpi);
		if (FAILED(hr))
		{
			ReleaseAudioPackets();
			return hr;
		}

	}

	m_pAudioFilter->PrepareAudioPackets(m_aPackets, NUM_AUDIOPACKETS, AP_ENCODE);


	return S_OK;
}


HRESULT SendDSCStream::ReleaseAudioPackets()
{


	for (int nIndex = 0; nIndex < NUM_AUDIOPACKETS; nIndex++)
	{
		if (m_aPackets[nIndex])
		{
			m_pAudioFilter->UnPrepareAudioPackets(&m_aPackets[nIndex], 1, AP_ENCODE);
			delete m_aPackets[nIndex];
			m_aPackets[nIndex] = NULL;
		}
	}
	return S_OK;
}


HRESULT SendDSCStream::CreateDSCBuffer()
{
	GUID guid = GUID_NULL;
	HRESULT hr;
	DSCBUFFERDESC dsBufDesc;
	DWORD dwIndex;
	DSBPOSITIONNOTIFY *aNotifyPos;
	IDirectSoundNotify *pNotify = NULL;


	if (!(m_DPFlags & DPFLAG_CONFIGURED_SEND))
	{
		return E_FAIL;
	}


	if (!m_pDSC)
	{
		ASSERT(m_pDSCBuffer==NULL);

		DSC_Manager::MapWaveIdToGuid(m_CaptureDevice, &guid);
		hr = DSC_Manager::CreateInstance(&guid, &m_pDSC);
		if (FAILED(hr))
		{
			return hr;
		}
	}

	if (!m_pDSCBuffer)
	{
		ZeroMemory(&dsBufDesc, sizeof(dsBufDesc));

		dsBufDesc.dwBufferBytes = m_dwDSCBufferSize;
		dsBufDesc.lpwfxFormat = &m_wfPCM;
		dsBufDesc.dwSize = sizeof(dsBufDesc);

		hr = m_pDSC->CreateCaptureBuffer(&dsBufDesc, &m_pDSCBuffer, NULL);
		if (FAILED(hr))
		{
			dsBufDesc.dwFlags = DSCBCAPS_WAVEMAPPED;
			hr = m_pDSC->CreateCaptureBuffer(&dsBufDesc, &m_pDSCBuffer, NULL);
		}

		if (FAILED(hr))
		{
			m_pDSC->Release();
			m_pDSC = NULL;
			return hr;
		}
		else
		{
			// do the notification positions
            DBG_SAVE_FILE_LINE
			aNotifyPos = new DSBPOSITIONNOTIFY[m_dwNumFrames];
			for (dwIndex = 0; dwIndex < m_dwNumFrames; dwIndex++)
			{
				aNotifyPos[dwIndex].hEventNotify = m_hEvent;
				aNotifyPos[dwIndex].dwOffset = m_dwFrameSize * dwIndex;
			}

			hr = m_pDSCBuffer->QueryInterface(IID_IDirectSoundNotify, (void**)&pNotify);
			if (SUCCEEDED(hr))
			{
				hr = pNotify->SetNotificationPositions(m_dwNumFrames, aNotifyPos);
			}
			if (FAILED(hr))
			{
				DEBUGMSG (ZONE_DP, ("Failed to set notification positions on DSC Buffer"));
			}
		}
	}

	if (aNotifyPos)
	{
		delete [] aNotifyPos;
	}
	if (pNotify)
	{
		pNotify->Release();
	}

	return S_OK;
}

HRESULT SendDSCStream::ReleaseDSCBuffer()
{
	if (m_pDSCBuffer)
	{
		m_pDSCBuffer->Stop();
		m_pDSCBuffer->Release();
		m_pDSCBuffer = NULL;
	}
	if (m_pDSC)
	{
		m_pDSC->Release();
		m_pDSC = NULL;
	}

	return S_OK;
}





// DTMF functions don't do anything if we aren't streaming
HRESULT __stdcall SendDSCStream::AddDigit(int nDigit)
{
	IMediaChannel *pIMC = NULL;
	RecvMediaStream *pRecv = NULL;
	BOOL bIsStarted;

	if ((!(m_DPFlags & DPFLAG_CONFIGURED_SEND)) || (m_pRTPSend==NULL))
	{
		return DPR_NOT_CONFIGURED;
	}

	bIsStarted = (m_DPFlags & DPFLAG_STARTED_SEND);

	if (bIsStarted)
	{
		Stop();
	}

	m_pDTMF->AddDigitToQueue(nDigit);
	SendDTMF();

	m_pDP->GetMediaChannelInterface(MCF_RECV | MCF_AUDIO, &pIMC);
	if (pIMC)
	{
		pRecv = static_cast<RecvMediaStream *> (pIMC);
		pRecv->DTMFBeep();
		pIMC->Release();
	}

	if (bIsStarted)
	{
		Start();
	}

	return S_OK;
}


// this function is ALMOST identical to SendAudioStream::SendDTMF
HRESULT __stdcall SendDSCStream::SendDTMF()
{
	HRESULT hr=S_OK;
	MediaPacket *pPacket=NULL;
	ULONG uCount;
	UINT uBufferSize, uBytesSent;
	void *pBuffer;
	bool bMark = true;
	MMRESULT mmr;
	HANDLE hEvent = m_pDTMF->GetEvent();
	UINT uTimerID;
	
	// since the stream is stopped, just grab any packet
	// from the packet ring

	pPacket = m_aPackets[0];
	pPacket->GetDevData(&pBuffer, &uBufferSize);

	timeBeginPeriod(5);
	ResetEvent(hEvent);
	uTimerID = timeSetEvent(m_dwFrameTimeMS-1, 5, (LPTIMECALLBACK )hEvent, 0, TIME_CALLBACK_EVENT_SET|TIME_PERIODIC);


	hr = m_pDTMF->ReadFromQueue((BYTE*)pBuffer, uBufferSize);


	while (SUCCEEDED(hr))
	{
		// there should be only 1 tone in the queue (it can handle more)
		// so assume we only need to set the mark bit on the first packet

		pPacket->m_fMark = bMark;
		bMark = false;

		pPacket->SetProp(MP_PROP_TIMESTAMP, m_SendTimestamp);
		m_SendTimestamp += m_dwSamplesPerFrame;

		pPacket->SetState (MP_STATE_RECORDED);

		// SendPacket will also compress
		SendPacket((AudioPacket*)pPacket);

		pPacket->m_fMark=false;
		pPacket->SetState(MP_STATE_RESET);

		hr = m_pDTMF->ReadFromQueue((BYTE*)pBuffer, uBufferSize);


		// so that we don't overload the receive jitter buffer on the remote
		// side, sleep a few milliseconds between sending packets
		if (SUCCEEDED(hr))
		{
			WaitForSingleObject(hEvent, m_dwFrameTimeMS);
			ResetEvent(hEvent);
		}
	}

	timeKillEvent(uTimerID);
	timeEndPeriod(5);
	return S_OK;
}

HRESULT __stdcall SendDSCStream::ResetDTMF()
{
	if(!(m_DPFlags & DPFLAG_STARTED_SEND))
	{
		return S_OK;
	}

	return m_pDTMF->ClearQueue();
}