// AudioFile.cpp: implementation of the CAudioFile class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "AudioFile.h"

#include "AcmCodec.h"

// G.711 and G.721 
#include "p_voice\pcm\g72x.h"
static struct g72x_state g_g721;
static struct g72x_state g_g721_enc;

//#define GSM_MS_2FRAME

// GSM 6.10
struct gsm_state;
extern "C" gsm_state * gsm_create();
extern "C" void gsm_destroy(gsm_state *);
extern "C" void gsm_encode(gsm_state *, const short *, BYTE *);
extern "C" void gsm_decode(gsm_state *, const BYTE *, short *);
extern "C" void gsm_bits_ms2normal(const BYTE *, BYTE *, BYTE *);
extern "C" void gsm_bits_normal2ms(const BYTE *, const BYTE *, BYTE *);
static struct gsm_state * g_gsm;
static struct gsm_state * g_gsm_enc;

// G.729
extern "C" void g729cp_init_encoder(int iVad);
extern "C" void g729cp_encode_frame(short * pSamples, char * pBits);
extern "C" void g729cp_init_decoder();
extern "C" void g729cp_decode_frame(short * pSamples, char * pBits);

// G.723.1
extern "C" void g723f_init_codec(int iVad, int iBitRate);
extern "C" void g723f_encode_frame(short * pSamples, char * pBits);
extern "C" void g723f_decode_frame(short * pSamples, char * pBits);

// iLBC
extern "C" void ilbc_init_codec(int iMode);
extern "C" void ilbc_encode_frame(short * pSamples, char * pBits);
extern "C" void ilbc_decode_frame(short * pSamples, char * pBits);

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

IMPLEMENT_DYNAMIC(CAudioFile, CFile)

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CAudioFile::CAudioFile() : CFile()
{
}

CAudioFile::~CAudioFile()
{
}

BOOL CAudioFile::Open(LPCTSTR lpszFileName, UINT nOpenFlags, CFileException* pError)
{
	return CFile::Open(lpszFileName, nOpenFlags, pError);
}

void CAudioFile::Close()
{
	CFile::Close();
	if (!m_bUseAcm)
	{
		if (m_iType == ACM_CODEC_GSM610)
		{
			FreeGSM();
		}
	}
}

UINT CAudioFile::Read(void* lpBuf, UINT nCount)
{
	int iSize;
	int iLength = GetLength();
	char * p = (char *)malloc(iLength);

	CFile::Read(p, iLength);

	if (m_bUseAcm)
	{
		CAcmCodec * m_pDec;
		m_pDec = new CAcmCodec;
		m_pDec->Init(m_iType, FALSE);
		iSize = m_pDec->Decode((char *)lpBuf, p, iLength);
		m_pDec->Free();
		delete m_pDec;
	}
	else
	{
		iSize = Decode((unsigned char *)p, (short *)lpBuf, iLength);
	}

	free(p);
	return iSize;
}

void CAudioFile::Write(const void* lpBuf, UINT nCount)
{
	int iSize;
	char * p = (char *)malloc(nCount);

	memset(p, 0, nCount);
	if (m_bUseAcm)
	{
		CAcmCodec * m_pEnc;
		m_pEnc = new CAcmCodec;
		m_pEnc->Init(m_iType, TRUE);
		iSize = m_pEnc->Encode((char *)lpBuf, p, nCount);
		m_pEnc->Free();
		delete m_pEnc;
	}
	else
	{
		iSize = Encode((short *)lpBuf, (unsigned char *)p, nCount/2);
	}
	CFile::Write(p, iSize);
	free(p);
}

int CAudioFile::Decode(unsigned char * pCode, short * pSample, int iCodeLength)
{
	int iSize;
	switch (m_iType)
	{
	case ACM_CODEC_PASS:
		iSize = iCodeLength;
		break;

	case ACM_CODEC_ALAW:
		iSize = DecodeG711a(pCode, pSample, iCodeLength);
		break;

	case ACM_CODEC_MULAW:	
		iSize = DecodeG711u(pCode, pSample, iCodeLength);
		break;

	case ACM_CODEC_ADPCM:	
		iSize = DecodeG721(pCode, pSample, iCodeLength);
		break;

	case ACM_CODEC_GSM610:
		iCodeLength -= iCodeLength % 36;
		iSize = DecodeGSM(pCode, pSample, iCodeLength);
		break;

	case ACM_CODEC_G729CP:
		iCodeLength -= iCodeLength % 12;
		iSize = DecodeG729cp(pCode, pSample, iCodeLength);
		break;

	case ACM_CODEC_G723_1:
		iCodeLength -= iCodeLength % 24;
		iSize = DecodeG723f(pCode, pSample, iCodeLength);
		break;

	case ACM_CODEC_ILBC:
		iCodeLength -= iCodeLength % 50;
		iSize = DecodeILBCf(pCode, pSample, iCodeLength);
		break;

	default:
		iSize = iCodeLength * 40;
		break;
	}
	return iSize;
}

int CAudioFile::Encode(short * pSample, unsigned char * pCode, int iSampleLength)
{
	int iSize;
	switch (m_iType)
	{
	case ACM_CODEC_PASS:
		iSize = iSampleLength;
		break;

	case ACM_CODEC_ALAW:
		iSize = EncodeG711a(pSample, pCode, iSampleLength);
		break;

	case ACM_CODEC_MULAW:	
		iSize = EncodeG711u(pSample, pCode, iSampleLength);
		break;

	case ACM_CODEC_ADPCM:	
		iSize = EncodeG721(pSample, pCode, iSampleLength);
		break;

	case ACM_CODEC_GSM610:
		iSampleLength -= iSampleLength % 320;
		iSize = EncodeGSM(pSample, pCode, iSampleLength);
		break;

	case ACM_CODEC_G729CP:
		iSampleLength -= iSampleLength % 160;
		iSize = EncodeG729cp(pSample, pCode, iSampleLength);
		break;

	case ACM_CODEC_G723_1:
		iSampleLength -= iSampleLength % 480;
		iSize = EncodeG723f(pSample, pCode, iSampleLength);
		break;

	case ACM_CODEC_ILBC:
		iSampleLength -= iSampleLength % 480;
		iSize = EncodeILBCf(pSample, pCode, iSampleLength);
		break;

	default:
		iSize = iSampleLength / 10;
		break;
	}
	return iSize;
}

int CAudioFile::EncodeG711a(short * pSample, unsigned char * pCode, int iSampleLength)
{
	int i;

	for (i = 0; i < iSampleLength; i ++)
	{
		pCode[i] = linear2alaw(pSample[i]);
	}
	return iSampleLength;
}

int CAudioFile::EncodeG711u(short * pSample, unsigned char * pCode, int iSampleLength)
{
	int i;

	for (i = 0; i < iSampleLength; i ++)
	{
		pCode[i] = linear2ulaw(pSample[i]);
	}
	return iSampleLength;
}

int CAudioFile::DecodeG711a(unsigned char * pCode, short * pSample, int iCodeLength)
{
	int i;

	for (i = 0; i < iCodeLength; i ++)
	{
		pSample[i] = alaw2linear(pCode[i]);
	}
	return iCodeLength * 2;
}

int CAudioFile::DecodeG711u(unsigned char * pCode, short * pSample, int iCodeLength)
{
	int i;

	for (i = 0; i < iCodeLength; i ++)
	{
		pSample[i] = ulaw2linear(pCode[i]);
	}
	return iCodeLength * 2;
}

void CAudioFile::InitG721()
{
	g72x_init_state(&g_g721);
	g72x_init_state(&g_g721_enc);
}

int CAudioFile::EncodeG721(short * pSample, unsigned char * pCode, int iSampleLength)
{
	int i, iVal1, iVal2;

	for (i = 0; i < iSampleLength; i += 2)
	{
		iVal1 = g721_encoder(pSample[i], AUDIO_ENCODING_LINEAR, &g_g721_enc);
		iVal2 = g721_encoder(pSample[i+1], AUDIO_ENCODING_LINEAR, &g_g721_enc);
		pCode[i/2] = (unsigned char)((iVal1<<4) + iVal2);
	}

	return iSampleLength/2;
}

int CAudioFile::DecodeG721(unsigned char * pCode, short * pSample, int iCodeLength)
{
	int i, iVal;

	for (i = 0; i < iCodeLength; i ++)
	{
		iVal = pCode[i];
		pSample[i*2] = (short)g721_decoder(((iVal>>4)&0x0f), AUDIO_ENCODING_LINEAR, &g_g721);
		pSample[i*2+1] = (short)g721_decoder((iVal&0x0f), AUDIO_ENCODING_LINEAR, &g_g721);
	}

	return iCodeLength * 4;
}

void CAudioFile::InitGSM()
{
	g_gsm = gsm_create();
	g_gsm_enc = gsm_create();
}

void CAudioFile::FreeGSM()
{
	gsm_destroy(g_gsm);
	gsm_destroy(g_gsm_enc);
}

int CAudioFile::DecodeGSM(unsigned char * pCode, short * pSample, int iCodeLength)
{
#ifdef GSM_MS_2FRAME
	int i, iSize = 0;
	unsigned char pCode1[36], pCode2[36];

	for (i = 0; i < iCodeLength; i += 65)
	{
		gsm_bits_ms2normal(pCode, pCode1, pCode2);
		gsm_decode(g_gsm, pCode1, pSample);
		pSample += 160;
		gsm_decode(g_gsm, pCode2, pSample);
		pSample += 160;
		pCode += 65;
		iSize += 640;
	}
	return iSize;
#else
	int i, iSize = 0;

	for (i = 0; i < iCodeLength; i += 36)
	{
		gsm_decode(g_gsm, pCode, pSample);
		pSample += 160;
		pCode += 36;
		iSize += 320;
	}
	return iSize;
#endif
}

int CAudioFile::EncodeGSM(short * pSample, unsigned char * pCode, int iSampleLength)
{
#ifdef GSM_MS_2FRAME
	int i, iSize = 0;
	unsigned char pCode1[36], pCode2[36];

	for (i = 0; i < iSampleLength; i += 320)
	{
		gsm_encode(g_gsm_enc, pSample, pCode1);
		pSample += 160;
		gsm_encode(g_gsm_enc, pSample, pCode2);
		pSample += 160;
		gsm_bits_normal2ms(pCode1, pCode2, pCode);
		pCode += 65;
		iSize += 65;
	}
	return iSize;
#else
	int i, iSize = 0;

	for (i = 0; i < iSampleLength; i += 160)
	{
		gsm_encode(g_gsm_enc, pSample, pCode);
		pSample += 160;
		pCode += 36;
		iSize += 36;
	}
	return iSize;
#endif
}

void CAudioFile::InitG729cp(BOOL bUseVAD)
{
	g729cp_init_encoder((int)bUseVAD);
	g729cp_init_decoder();
}

int CAudioFile::EncodeG729cp(short * pSample, unsigned char * pCode, int iSampleLength)
{
	int i, iSize = 0;

	for (i = 0; i < iSampleLength; i += 80)
	{
		g729cp_encode_frame(pSample, (char *)pCode);
		pSample += 80;
		pCode += 12;
		iSize += 12;
	}
	return iSize;
}

int CAudioFile::DecodeG729cp(unsigned char * pCode, short * pSample, int iCodeLength)
{
	int i, iSize = 0;

	for (i = 0; i < iCodeLength; i += 12)
	{
		g729cp_decode_frame(pSample, (char *)pCode);
		pSample += 80;
		pCode += 12;
		iSize += 160;
	}
	return iSize;
}

void CAudioFile::InitG723f(BOOL bUseVAD, BOOL bUseHighRate)
{
	g723f_init_codec((int)bUseVAD, (int)bUseHighRate);
}

int CAudioFile::EncodeG723f(short * pSample, unsigned char * pCode, int iSampleLength)
{
	int i, iSize = 0;

	for (i = 0; i < iSampleLength; i += 240)
	{
		g723f_encode_frame(pSample, (char *)pCode);
		pSample += 240;
		pCode += 24;
		iSize += 24;
	}
	return iSize;
}

int CAudioFile::DecodeG723f(unsigned char * pCode, short * pSample, int iCodeLength)
{
	int i, iSize = 0;

	for (i = 0; i < iCodeLength; i += 24)
	{
		g723f_decode_frame(pSample, (char *)pCode);
		pSample += 240;
		pCode += 24;
		iSize += 480;
	}
	return iSize;
}

void CAudioFile::InitILBCf()
{
	ilbc_init_codec(30);
}

int CAudioFile::EncodeILBCf(short * pSample, unsigned char * pCode, int iSampleLength)
{
	int i, iSize = 0;

	for (i = 0; i < iSampleLength; i += 240)
	{
		ilbc_encode_frame(pSample, (char *)pCode);
		pSample += 240;
		pCode += 50;
		iSize += 50;
	}
	return iSize;
}

int CAudioFile::DecodeILBCf(unsigned char * pCode, short * pSample, int iCodeLength)
{
	int i, iSize = 0;

	for (i = 0; i < iCodeLength; i += 50)
	{
		ilbc_decode_frame(pSample, (char *)pCode);
		pSample += 240;
		pCode += 50;
		iSize += 480;
	}
	return iSize;
}

int CAudioFile::SetOptions(int iType, BOOL bUseAcm, BOOL bUseVAD, BOOL bUseHighRate)
{
	int iSize, iLength = GetLength();

	m_iType = iType;
	m_bUseAcm = bUseAcm;
	switch (m_iType)
	{
	case ACM_CODEC_PASS:
		iSize = iLength;
		break;

	case ACM_CODEC_ALAW:
	case ACM_CODEC_MULAW:	
		iSize = iLength * 2;
		break;

	case ACM_CODEC_ADPCM:	
		iSize = iLength * 4;
		if (!m_bUseAcm)
		{
			InitG721();
		}
		break;

	case ACM_CODEC_GSM610:
		iSize = iLength * 10;
		if (!m_bUseAcm)
		{
			InitGSM();
		}
		break;

	case ACM_CODEC_G729CP:
		iSize = iLength * 15;
		if (!m_bUseAcm)
		{
			InitG729cp(bUseVAD);
		}
		break;

	case ACM_CODEC_G723_1:
		iSize = iLength * 25;
		if (!m_bUseAcm)
		{
			InitG723f(bUseVAD, bUseHighRate);
		}
		break;

	case ACM_CODEC_ILBC:
		iSize = iLength * 15;
		if (!m_bUseAcm)
		{
			InitILBCf();
		}
		break;

	default:
		iSize = iLength * 40;
		break;
	}
	return iSize;
}
