// UdpAudioThread.cpp : implementation file
//

#include "stdafx.h"
#include "UdpAudioThread.h"

#include "AudioFile.h"
#include "Global.h"

#ifdef _DEBUG
//#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

HANDLE CUdpAudioThread::m_hEventThreadKilled;

/////////////////////////////////////////////////////////////////////////////
// CUdpAudioThread

IMPLEMENT_DYNCREATE(CUdpAudioThread, CWinThread)

CUdpAudioThread::CUdpAudioThread()
{
}

CUdpAudioThread::~CUdpAudioThread()
{
}

void CUdpAudioThread::operator delete(void* p)
{
	// The exiting main application thread waits for this event before completely
	// terminating in order to avoid a false memory leak detection. 

	CWinThread::operator delete(p);
	SetEvent(m_hEventThreadKilled);
}

BOOL CUdpAudioThread::InitInstance()
{
	if (m_wndUdpAudio.Create() == FALSE)
		return FALSE;

	// It is important to set CWinThread::m_pMainWnd to the user interface
	// window.  This is required so that when the m_pMainWnd is destroyed,
	// the CWinThread is also automatically destroyed.  For insight into
	// how the CWinThread is automatically destroyed when the m_pMainWnd
	// window is destroyed, see the implementation of CWnd::OnNcDestroy
	// in wincore.cpp of the MFC sources.

	m_pMainWnd = &m_wndUdpAudio;
	return TRUE;
}

int CUdpAudioThread::ExitInstance()
{
	// TODO:  perform any per-thread cleanup here
	return CWinThread::ExitInstance();
}

void CUdpAudioThread::PrepareEvent()
{
	m_hEventThreadKilled = CreateEvent(NULL, FALSE, FALSE, NULL); 
}

void CUdpAudioThread::UnprepareEvent()
{
	CloseHandle(m_hEventThreadKilled);
}


BEGIN_MESSAGE_MAP(CUdpAudioThread, CWinThread)
	//{{AFX_MSG_MAP(CUdpAudioThread)
		// NOTE - the ClassWizard will add and remove mapping macros here.
	//}}AFX_MSG_MAP
#if _MSC_VER >= 1300
	ON_THREAD_MESSAGE(WM_UDP_AUDIO_DISCONNECT, OnDisconnect)
	ON_THREAD_MESSAGE(WM_UDP_AUDIO_TEST, OnTest)
#else
	ON_MESSAGE(WM_UDP_AUDIO_DISCONNECT, OnDisconnect)
	ON_MESSAGE(WM_UDP_AUDIO_TEST, OnTest)
#endif
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CUdpAudioThread message handlers

void CUdpAudioThread::OnDisconnect(WPARAM, LPARAM)
{
	m_wndUdpAudio.DestroyWindow();
}

void CUdpAudioThread::OnTest(WPARAM, LPARAM)
{
	m_wndUdpAudio.DebugTest();
}

/////////////////////////////////////////////////////////////////////////////
// CUdpAudioWnd

CUdpAudioWnd::CUdpAudioWnd()
{
}

CUdpAudioWnd::~CUdpAudioWnd()
{
}

BOOL CUdpAudioWnd::Create()
{
	// Register a custom WndClass and create a window.
	// This must be done because CUdpAudioWnd has a custom cursor, and no icon.
	LPCTSTR lpszUdpAudioWndClass = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW,
													LoadCursor(NULL, IDC_ARROW),
													(HBRUSH)(COLOR_WINDOW+1), NULL);

	return CWnd::CreateEx(0, lpszUdpAudioWndClass, _T(""), WS_POPUPWINDOW,
							0, 0, 0, 0, NULL, 0);
}

int CUdpAudioWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CWnd::OnCreate(lpCreateStruct) == -1)
		return -1;
	
	// TODO: Add your specialized creation code here
	m_iPlayDataLength = 0;

	ULONG uPeerIP = AfxGetApp()->GetProfileInt(_T("Peer"), _T("IPAddress"), 0xc0a80101);
//	ULONG uPeerIP = CTcpSocket::Name2IP("192.168.1.8");
	uPeerIP = MAKELONG(MAKEWORD(HIBYTE(HIWORD(uPeerIP)), LOBYTE(HIWORD(uPeerIP))), MAKEWORD(HIBYTE(LOWORD(uPeerIP)), LOBYTE(LOWORD(uPeerIP))));
	m_udp.Enter(GetSafeHwnd(), WM_UDP_SOCKET_DATA, uPeerIP, UDP_DEFAULT_PORT+1);
	m_iAudioType = AfxGetApp()->GetProfileInt("Audio", "CodecType", 0);

	CString strDstName = AfxGetApp()->GetProfileString(_T("UdpAudio"), _T("DstName"), _T("udpaudio.dat"));
	m_bAudioRealtime = (BOOL)AfxGetApp()->GetProfileInt(_T("UdpAudio"), _T("Realtime"), 1);
	m_bAudioSaveFile = (BOOL)AfxGetApp()->GetProfileInt(_T("UdpAudio"), _T("SaveFile"), 0);
	m_bAudioRealtimeOut = (BOOL)AfxGetApp()->GetProfileInt(_T("UdpAudio"), _T("RealtimeOut"), 1);
	m_bAudioNoAcm = (BOOL)AfxGetApp()->GetProfileInt(_T("UdpAudio"), _T("NoAcm"), 1);
	m_bUseVAD = (BOOL)AfxGetApp()->GetProfileInt(_T("UdpAudio"), _T("UseVAD"), 1);
	m_bUseHighRate = (BOOL)AfxGetApp()->GetProfileInt(_T("UdpAudio"), _T("UseHighRate"), 1);

	if (m_bAudioSaveFile)
	{
		m_file.Open(strDstName, CFile::modeCreate|CFile::modeWrite);
	}

	if (!m_bAudioNoAcm)
	{
		m_pDec = new CAcmCodec;
		m_pDec->Init(m_iAudioType, FALSE);
		m_pEnc = new CAcmCodec;
		m_pEnc->Init(m_iAudioType, TRUE);
	}
	else
	{
		if (m_iAudioType == ACM_CODEC_ADPCM)
		{
			CAudioFile::InitG721();
		}
		else if (m_iAudioType == ACM_CODEC_GSM610)
		{
			CAudioFile::InitGSM();
		}
		else if (m_iAudioType == ACM_CODEC_G729CP)
		{
			CAudioFile::InitG729cp(m_bUseVAD);
		}
		else if (m_iAudioType == ACM_CODEC_G723_1)
		{
			CAudioFile::InitG723f(m_bUseVAD, m_bUseHighRate);
		}
		else if (m_iAudioType == ACM_CODEC_ILBC)
		{
			CAudioFile::InitILBCf();
		}
	}

	if (m_bAudioRealtime)
	{
		m_waveOut.Init(GetSafeHwnd(), AUDIO_TIME, 0);
		m_waveOut.Start();
	}

	if (m_bAudioRealtimeOut)
	{
		m_waveIn.Init(GetSafeHwnd(), AUDIO_TIME, 0);
		m_waveIn.Start();
	}

	UdpSendStart();
	return 0;
}

void CUdpAudioWnd::OnDestroy() 
{
	CWnd::OnDestroy();
	
	// TODO: Add your message handler code here
	UdpSendStop();
	Sleep(100);
	m_udp.Leave();

	if (m_bAudioRealtime)
	{
		m_waveOut.Stop();
		m_waveOut.Free();
	}
	if (m_bAudioRealtimeOut)
	{
		m_waveIn.Stop();
		m_waveIn.Free();
	}
	if (!m_bAudioNoAcm)
	{
		m_pDec->Free();
		delete m_pDec;
		m_pDec = NULL;
		m_pEnc->Free();
		delete m_pEnc;
		m_pEnc = NULL;
	}
	else
	{
		if (m_iAudioType == ACM_CODEC_GSM610)
		{
			CAudioFile::FreeGSM();
		}
	}

	if (m_bAudioSaveFile)
	{
		m_file.Close();
	}
}

/////////////////////////////////////////////////////////////////////////////
// CUdpAudioWnd helper functions

void CUdpAudioWnd::UdpSendStart()
{
	int iLength = 4;
	char * p = (char *)malloc(iLength);
	p[0] = '#';
	p[1] = m_bAudioRealtime ? '3' : '4';
	p[2] = '*';
	// see keypad.txt for p[3]'s value
	switch (m_iAudioType)
	{
	case ACM_CODEC_PASS:			
		p[3] = '0';
		break;
	case ACM_CODEC_ALAW:
		p[3] = '2';
		break;
	case ACM_CODEC_MULAW:
		p[3] = '1';
		break;
	case ACM_CODEC_ADPCM:
		p[3] = '6';
		break;
	case ACM_CODEC_GSM610:
		p[3] = '5';
		break;
	case ACM_CODEC_G729CP:
		p[3] = '3';
		break;
	case ACM_CODEC_G723_1:
		p[3] = '4';
		break;
	case ACM_CODEC_ILBC:
		p[3] = '7';
		break;
	}
	m_udp.Write(p, iLength);
}

void CUdpAudioWnd::UdpSendStop()
{
	int iLength = 4;
	char * p = (char *)malloc(iLength);
	p[0] = '#';
	p[1] = m_bAudioRealtime ? '3' : '4';
	p[2] = '*';
	p[3] = '7';
	m_udp.Write(p, iLength);
}

BEGIN_MESSAGE_MAP(CUdpAudioWnd, CWnd)
	//{{AFX_MSG_MAP(CUdpAudioWnd)
	ON_WM_CREATE()
	ON_WM_DESTROY()
	//}}AFX_MSG_MAP
	ON_MESSAGE(WM_UDP_SOCKET_DATA, OnUdpData)
	ON_MESSAGE(MM_WIM_OPEN, OnWaveInOpen)
	ON_MESSAGE(MM_WIM_CLOSE, OnWaveInClose)
	ON_MESSAGE(MM_WIM_DATA, OnWaveInData)
	ON_MESSAGE(MM_WOM_OPEN, OnWaveOutOpen)
	ON_MESSAGE(MM_WOM_CLOSE, OnWaveOutClose)
	ON_MESSAGE(MM_WOM_DONE, OnWaveOutDone)
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// CUdpAudioWnd message handlers
#if _MSC_VER >= 1300
LRESULT CUdpAudioWnd::OnUdpData(WPARAM wParam, LPARAM lParam)
#else
void CUdpAudioWnd::OnUdpData(WPARAM wParam, LPARAM lParam)
#endif
{
//	static int iUdpNumber = 0;

	ULONG uPeerIP = (ULONG)wParam;
	WSABUF * p = (WSABUF *)lParam;
	BYTE * pData = (BYTE *)p->buf;
	int i;

	if (p->len >= 255)
	{
		OutputDebugBinary(pData, p->len);
		free(p->buf);
		free(p);
#if _MSC_VER >= 1300
		return ERROR;
#else
		return;
#endif
	}

//	OutputDebugBinary(pData, p->len);
	if (m_bAudioSaveFile)
	{
		m_file.Write(pData, p->len);
	}
	if (m_bAudioRealtime)
	{
		if (m_iAudioType == ACM_CODEC_PASS)
		{
			for (i = 0; i < (int)p->len; i += 2)
			{
				m_pPlayData[m_iPlayDataLength+i] = pData[i+1];
				m_pPlayData[m_iPlayDataLength+i+1] = pData[i];
			}
			m_iPlayDataLength += p->len;
		}
		else
		{
			if (m_bAudioNoAcm)
			{
				switch (m_iAudioType)
				{
				case ACM_CODEC_ALAW:
					m_iPlayDataLength += CAudioFile::DecodeG711a(pData, (short *)(m_pPlayData+m_iPlayDataLength), p->len);
					break;

				case ACM_CODEC_MULAW:	
					m_iPlayDataLength += CAudioFile::DecodeG711u(pData, (short *)(m_pPlayData+m_iPlayDataLength), p->len);
					break;

				case ACM_CODEC_ADPCM:	
					m_iPlayDataLength += CAudioFile::DecodeG721(pData, (short *)(m_pPlayData+m_iPlayDataLength), p->len);
					break;

				case ACM_CODEC_GSM610:
					m_iPlayDataLength += CAudioFile::DecodeGSM(pData, (short *)(m_pPlayData+m_iPlayDataLength), p->len);
					break;

				case ACM_CODEC_G729CP:
					m_iPlayDataLength += CAudioFile::DecodeG729cp(pData, (short *)(m_pPlayData+m_iPlayDataLength), p->len);
					break;

				case ACM_CODEC_G723_1:
					m_iPlayDataLength += CAudioFile::DecodeG723f(pData, (short *)(m_pPlayData+m_iPlayDataLength), p->len);
					break;

				case ACM_CODEC_ILBC:
					m_iPlayDataLength += CAudioFile::DecodeILBCf(pData, (short *)(m_pPlayData+m_iPlayDataLength), p->len);
					break;

				default:
					break;
				}
			}
			else
			{
				m_iPlayDataLength += m_pDec->Decode((char *)(m_pPlayData+m_iPlayDataLength), (char *)pData, p->len);
			}
		}
		if (m_iPlayDataLength >= AUDIO_LENGTH)
		{
			m_iPlayDataLength = 0;
			m_waveOut.Play(m_pPlayData, AUDIO_LENGTH);
		}
	}

	free(p->buf);
	free(p);
#if _MSC_VER >= 1300
	return 0;
#endif
}

#if _MSC_VER >= 1300
LRESULT CUdpAudioWnd::OnWaveInOpen(WPARAM wParam, LPARAM)
#else
void CUdpAudioWnd::OnWaveInOpen(WPARAM wParam, LPARAM)
#endif
{
//	OutputDebugString("WaveIn opened\n");
#if _MSC_VER >= 1300
	return 0;
#endif
}

#if _MSC_VER >= 1300
LRESULT CUdpAudioWnd::OnWaveInClose(WPARAM wParam, LPARAM)
#else
void CUdpAudioWnd::OnWaveInClose(WPARAM wParam, LPARAM)
#endif
{
//	OutputDebugString("WaveIn closed\n");
#if _MSC_VER >= 1300
	return 0;
#endif
}

#if _MSC_VER >= 1300
LRESULT CUdpAudioWnd::OnWaveInData(WPARAM wParam, LPARAM lParam)
#else
void CUdpAudioWnd::OnWaveInData(WPARAM wParam, LPARAM lParam)
#endif
{
//	OutputDebugString("WaveIn data\n");
	WAVEHDR * hdr = (WAVEHDR *)lParam;
	m_waveIn.ContinueRecording();

#if _MSC_VER >= 1300
	if (!m_bAudioRealtimeOut)				return ERROR;
#else
	if (!m_bAudioRealtimeOut)				return;		
#endif


	int i, j, iLength, iEncodeLength;
	short * s = (short *)(hdr->lpData);
	char * c = (char *)(hdr->lpData);
	char * p;

	iLength = hdr->dwBytesRecorded;
	switch (m_iAudioType)
	{
	case ACM_CODEC_PASS:
		for (i = 0; i < iLength; i += 160)
		{
			p = (char *)malloc(256);
			for (j = 0; j < 160; j += 2)
			{
				p[j] = c[j+1];
				p[j+1] = c[j];
			}
			c += 160;
			m_udp.Write(p, 160);
		}
		break;

	case ACM_CODEC_ALAW:
		for (i = 0; i < iLength; i += 160)
		{
			p = (char *)malloc(256);
			if (m_bAudioNoAcm)
			{
				iEncodeLength = CAudioFile::EncodeG711a(s, (unsigned char *)p, 80);
				s += 80;
			}
			else
			{
				iEncodeLength = m_pEnc->Encode(c, p, 160);
				c += 160;
			}
			m_udp.Write(p, iEncodeLength);
		}
		break;

	case ACM_CODEC_MULAW:	
		for (i = 0; i < iLength; i += 160)
		{
			p = (char *)malloc(256);
			if (m_bAudioNoAcm)
			{
				iEncodeLength = CAudioFile::EncodeG711u(s, (unsigned char *)p, 80);
				s += 80;
			}
			else
			{
				iEncodeLength = m_pEnc->Encode(c, p, 160);
				c += 160;
			}
			m_udp.Write(p, iEncodeLength);
		}
		break;

	case ACM_CODEC_ADPCM:	
		for (i = 0; i < iLength; i += 640)
		{
			p = (char *)malloc(256);
			if (m_bAudioNoAcm)
			{
				iEncodeLength = CAudioFile::EncodeG721(s, (unsigned char *)p, 320);
				s += 320;
			}
			else
			{
				iEncodeLength = m_pEnc->Encode(c, p, 640);
				c += 640;
			}
			m_udp.Write(p, iEncodeLength);
		}
		break;

	case ACM_CODEC_GSM610:
		for (i = 0; i < iLength; i += 320)
		{
			p = (char *)malloc(256);
			if (m_bAudioNoAcm)
			{
				iEncodeLength = CAudioFile::EncodeGSM(s, (unsigned char *)p, 160);
				s += 160;
			}
			else
			{
				iEncodeLength = m_pEnc->Encode(c, p, 320);
				c += 320;
			}
			m_udp.Write(p, iEncodeLength);
		}
		break;

	case ACM_CODEC_G729CP:
		for (i = 0; i < iLength; i += 160)
		{
			p = (char *)malloc(256);
			if (m_bAudioNoAcm)
			{
				iEncodeLength = CAudioFile::EncodeG729cp(s, (unsigned char *)p, 80);
				s += 80;
			}
			else
			{
				iEncodeLength = 0;
			}
			if (iEncodeLength > 1)
			{
				m_udp.Write(p, iEncodeLength);
			}
		}
		break;

	case ACM_CODEC_G723_1:
		for (i = 0; i < iLength; i += 480)
		{
			p = (char *)malloc(256);
			if (m_bAudioNoAcm)
			{
				iEncodeLength = CAudioFile::EncodeG723f(s, (unsigned char *)p, 240);
				s += 240;
			}
			else
			{
				iEncodeLength = m_pEnc->Encode(c, p, 480);
				c += 480;
			}
			if (iEncodeLength > 1)
			{
				m_udp.Write(p, iEncodeLength);
			}
		}
		break;

	case ACM_CODEC_ILBC:
		for (i = 0; i < iLength; i += 480)
		{
			p = (char *)malloc(256);
			if (m_bAudioNoAcm)
			{
				iEncodeLength = CAudioFile::EncodeILBCf(s, (unsigned char *)p, 240);
				s += 240;
			}
			else
			{
				iEncodeLength = m_pEnc->Encode(c, p, 480);
				c += 480;
			}
			if (iEncodeLength > 1)
			{
				m_udp.Write(p, iEncodeLength);
			}
		}
		break;
	}
#if _MSC_VER >= 1300
	return 0;
#endif
}

#if _MSC_VER >= 1300
LRESULT CUdpAudioWnd::OnWaveOutOpen(WPARAM wParam, LPARAM)
#else
void CUdpAudioWnd::OnWaveOutOpen(WPARAM wParam, LPARAM)
#endif
{
//	OutputDebugString("WaveOut opened\n");
#if _MSC_VER >= 1300
	return 0;
#endif
}

#if _MSC_VER >= 1300
LRESULT CUdpAudioWnd::OnWaveOutClose(WPARAM wParam, LPARAM)
#else
void CUdpAudioWnd::OnWaveOutClose(WPARAM wParam, LPARAM)
#endif
{
//	OutputDebugString("WaveOut closed\n");
#if _MSC_VER >= 1300
	return 0;
#endif
}

#if _MSC_VER >= 1300
LRESULT CUdpAudioWnd::OnWaveOutDone(WPARAM wParam, LPARAM lParam)
#else
void CUdpAudioWnd::OnWaveOutDone(WPARAM wParam, LPARAM lParam)
#endif
{
//	OutputDebugString("WaveOut done\n");
	m_waveOut.ContinuePlaying();
#if _MSC_VER >= 1300
	return 0;
#endif
}

void CUdpAudioWnd::DebugTest()
{
	int i, iLength = UDP_DEFAULT_DATA_SIZE;
	char * p = (char *)malloc(iLength);
	short * s = (short *)p;

	for (i = 0; i < iLength/2; i ++)
	{
		s[i] = i;
	}
	m_udp.Write(p, iLength);
}

