// WaveFile.cpp: implementation of the CWaveFile class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "WaveFile.h"

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

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

CWaveFile::CWaveFile()
{
	m_pWaveFormat = NULL;
	m_iDataSize = 0;
}

CWaveFile::~CWaveFile()
{

}

//iMode could be: MMIO_READ, MMIO_WRITE, MMIO_CREATE    

BOOL CWaveFile::Open(CString strFileName, int iMode)
{
	char szFileName[256];

	lstrcpy(szFileName, strFileName);
	m_iMode = iMode;
	if (m_iMode == MMIO_READ)
	{
		if(!(m_hMmio = mmioOpen(szFileName, NULL, m_iMode|MMIO_ALLOCBUF))) 
		{ 
			OutputDebugString("Failed to open file."); 
			return FALSE;
		}
		if (!ReadChunk())	return FALSE;
	}
	else if (m_iMode == MMIO_WRITE || m_iMode == (MMIO_WRITE|MMIO_CREATE))
	{
		if(!(m_hMmio = mmioOpen(szFileName, NULL, m_iMode|MMIO_ALLOCBUF))) 
		{ 
			OutputDebugString("Failed to open file."); 
			return FALSE;
		}
	}
	else
	{
		OutputDebugString("Invalid mode\n");
		return FALSE;
	}
	return TRUE;
}

void CWaveFile::Close()
{
	if (m_iMode == MMIO_WRITE || m_iMode == (MMIO_WRITE|MMIO_CREATE))
	{
		m_mmckinfoSubchunk.dwFlags = MMIO_DIRTY; 
		if (mmioAscend(m_hMmio, &m_mmckinfoSubchunk, 0) != 0)
		{
			OutputDebugString("Ascend out of 'data' chunk failed!\n");
			goto Error;
		}
		m_mmckinfoParent.dwFlags = MMIO_DIRTY; 
		if (mmioAscend(m_hMmio, &m_mmckinfoParent, 0) != 0)
		{
			OutputDebugString("Ascend out of 'RIFF' chunk failed!\n");
			goto Error;
		}
	}
Error:
	if (mmioClose(m_hMmio, 0) != 0)
		OutputDebugString(" Wave file close error!");
	if (m_pWaveFormat != NULL)
	{
		free(m_pWaveFormat);
		m_pWaveFormat = NULL;
	}
}


int CWaveFile::Read(unsigned char * pData, int iLength)
{
    return mmioRead(m_hMmio, (HPSTR) pData, iLength);
}

BOOL CWaveFile::Write(unsigned char * pData, int iLength)
{
	if (mmioWrite(m_hMmio, (HPSTR) pData, iLength) != iLength)
	{
		OutputDebugString("Write Error!\n");
		goto Error;
	}
	return TRUE;

Error:
	Close();
	return FALSE;
}

BOOL CWaveFile::SetFormat(int iType)
{
	int iFormatSize = sizeof(WAVEFORMATEX);
	char *cbExtra;
	
	m_pWaveFormat = (WAVEFORMATEX *)malloc(256);
	memset(m_pWaveFormat, 0, 256);
	cbExtra = (char *)(m_pWaveFormat) + sizeof(WAVEFORMATEX);

	m_pWaveFormat->nChannels = 1;
	m_pWaveFormat->nSamplesPerSec = 8000;
	switch (iType)
	{
	case WAVE_FILE_PCM:
		m_pWaveFormat->wFormatTag = WAVE_FORMAT_PCM;
		m_pWaveFormat->nAvgBytesPerSec = 16000;
		m_pWaveFormat->nBlockAlign = 2;
		m_pWaveFormat->wBitsPerSample = 16;
		m_pWaveFormat->cbSize = 0;

	case WAVE_FILE_ALAW:
		m_pWaveFormat->wFormatTag = WAVE_FORMAT_ALAW;
		m_pWaveFormat->nAvgBytesPerSec = 8000;
		m_pWaveFormat->nBlockAlign = 1;
		m_pWaveFormat->wBitsPerSample = 8;
		m_pWaveFormat->cbSize = 0;
		break;

	case WAVE_FILE_MULAW:
		m_pWaveFormat->wFormatTag = WAVE_FORMAT_MULAW;
		m_pWaveFormat->nAvgBytesPerSec = 8000;
		m_pWaveFormat->nBlockAlign = 1;
		m_pWaveFormat->wBitsPerSample = 8;
		m_pWaveFormat->cbSize = 0;
		break;

	case WAVE_FILE_ADPCM:
		m_pWaveFormat->wFormatTag = WAVE_FORMAT_ADPCM;
		m_pWaveFormat->nAvgBytesPerSec = 4096;
		m_pWaveFormat->nBlockAlign = 256;
		m_pWaveFormat->wBitsPerSample = 4;
		m_pWaveFormat->cbSize = 32;
		cbExtra[0] = -12;	cbExtra[1] = 1;		cbExtra[2] = 7;
		cbExtra[5] = 1;		cbExtra[9] = 2;		cbExtra[11] = -1;
		cbExtra[16] = -64;	cbExtra[18] = 64;	cbExtra[20] = -16;
		cbExtra[24] = -52;	cbExtra[25] = 1;	cbExtra[26] = 48;
		cbExtra[27] = -1;	cbExtra[28] = -120;	cbExtra[29] = 1;
		cbExtra[30] = 24;	cbExtra[31] = -1;
		break;

	case WAVE_FILE_GSM610:
		m_pWaveFormat->wFormatTag = WAVE_FORMAT_GSM610;
		m_pWaveFormat->nAvgBytesPerSec = 1625;
		m_pWaveFormat->nBlockAlign = 65;
		m_pWaveFormat->wBitsPerSample = 0;
		m_pWaveFormat->cbSize = 2;
		cbExtra[0] = 64;
		cbExtra[1] = 1;
		break;

	case WAVE_FILE_G729:
		m_pWaveFormat->wFormatTag = WAVE_FORMAT_GFDVR;
		m_pWaveFormat->nAvgBytesPerSec = 1100;
		m_pWaveFormat->nBlockAlign = 11;
		m_pWaveFormat->wBitsPerSample = 0;
		m_pWaveFormat->cbSize = 0;
		break;

	case WAVE_FILE_G723_1:
		m_pWaveFormat->wFormatTag = 66;
		m_pWaveFormat->nChannels = 1;
		m_pWaveFormat->nSamplesPerSec = 8000;
		m_pWaveFormat->nAvgBytesPerSec = 800;
		m_pWaveFormat->nBlockAlign = 24;
		m_pWaveFormat->wBitsPerSample = 0;
		m_pWaveFormat->cbSize = 10;

		cbExtra[0] = 2; cbExtra[1] = 0; cbExtra[2] = (char)0xce;
		cbExtra[3] = (char)0x9a; cbExtra[4] = (char)0x32; cbExtra[5] = (char)0xf7;
		cbExtra[6] = (char)0xa2; cbExtra[7] = (char)0xae; cbExtra[8] = (char)0xde;
		cbExtra[9] = (char)0xac;
		break;

	default:
		OutputDebugString("format not supported!\n");
		break;
	}

	iFormatSize += m_pWaveFormat->cbSize;

	if (!WriteChunk(iFormatSize))	return FALSE;
	else				return TRUE;
}

BOOL CWaveFile::SetFormat(WAVEFORMATEX * pFormat, int iFormatSize)
{
	m_pWaveFormat = (WAVEFORMATEX *)malloc(iFormatSize);
	memcpy(m_pWaveFormat, pFormat, iFormatSize);
	
	if (!WriteChunk(iFormatSize))	return FALSE;
	else				return TRUE;
}

BOOL CWaveFile::ReadChunk()
{
    // Locate a "RIFF" chunk with a "WAVE" form type to make 
    // sure the file is a waveform-audio file. 
    m_mmckinfoParent.fccType = mmioFOURCC('W', 'A', 'V', 'E'); 
    if (mmioDescend(m_hMmio, (LPMMCKINFO) &m_mmckinfoParent, NULL, MMIO_FINDRIFF)) 
    { 
        OutputDebugString("This is not a waveform-audio file."); 
		goto Error;
    } 
    // Find the "FMT" chunk (form type "FMT"); it must be 
    // a subchunk of the "RIFF" chunk. 
    m_mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' '); 
    if (mmioDescend(m_hMmio, &m_mmckinfoSubchunk, &m_mmckinfoParent, MMIO_FINDCHUNK)) 
    { 
		OutputDebugString("Waveform-audio file has no FMT chunk.\n"); 
		goto Error;
    } 

    // Get the size of the "FMT" chunk. Allocate 
    // and lock memory for it. 
	m_dwFormatSize = m_mmckinfoSubchunk.cksize; 
	m_pWaveFormat = (WAVEFORMATEX *)malloc(m_dwFormatSize);
    // Read the "FMT" chunk. 
	if (mmioRead(m_hMmio, (HPSTR) m_pWaveFormat, m_dwFormatSize) != (int)m_dwFormatSize)
	{ 
		OutputDebugString("Failed to read format chunk."); 
		goto Error;
    } 
 
    // Ascend out of the "FMT" subchunk. 
    mmioAscend(m_hMmio, &m_mmckinfoSubchunk, 0); 
 
    // Find the data subchunk. The current file position should be at 
    // the beginning of the data chunk; however, you should not make 
    // this assumption. Use mmioDescend to locate the data chunk. 
    m_mmckinfoSubchunk.ckid = mmioFOURCC('d', 'a', 't', 'a'); 
    if (mmioDescend(m_hMmio, &m_mmckinfoSubchunk, &m_mmckinfoParent, MMIO_FINDCHUNK)) 
    { 
		OutputDebugString("Waveform-audio file has no data chunk."); 
		goto Error;
    } 
 
    // Get the size of the data subchunk. 
    m_iDataSize = m_mmckinfoSubchunk.cksize; 
    if (m_iDataSize == 0L)
	{ 
        OutputDebugString("The data chunk contains no data."); 
		goto Error;
    } 
	return TRUE;

Error:
	Close();
	return FALSE;
}

BOOL CWaveFile::WriteChunk(int iFormatSize)
{
	m_mmckinfoParent.fccType = mmioFOURCC('W', 'A', 'V', 'E'); 
	m_mmckinfoParent.cksize = 0;
	if (mmioCreateChunk(m_hMmio, &m_mmckinfoParent, MMIO_CREATERIFF))
	{
		OutputDebugString("Create RIFF chunk failed!\n");
		goto Error;
	}

	m_mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' ');
	m_mmckinfoSubchunk.cksize = iFormatSize;		// we know the size of this ck.
	if (mmioCreateChunk(m_hMmio, &m_mmckinfoSubchunk, 0))
	{
		OutputDebugString("Create 'fmt' chunk failed!\n");
		goto Error;
	}
	if (mmioWrite(m_hMmio, (HPSTR) m_pWaveFormat, iFormatSize) != iFormatSize)
	{
		OutputDebugString("Write Format data failed!\n");
		goto Error;
	}
	// ascend out of the 'fmt' chunk, back into 'RIFF' chunk 
	if (mmioAscend(m_hMmio, &m_mmckinfoSubchunk, 0) != 0)
	{
		OutputDebugString("Ascend out of 'fmt' chunk failed!\n");
		goto Error;
	}
	// create the 'data' chunk that holds the waveform samples 
	m_mmckinfoSubchunk.ckid = mmioFOURCC('d', 'a', 't', 'a');
	m_mmckinfoSubchunk.cksize = 0;
	if (mmioCreateChunk(m_hMmio, &m_mmckinfoSubchunk, 0) != 0)
	{
		OutputDebugString("Create 'data' chunk failed!\n");
		goto Error;
	}
	return TRUE;

Error:
	Close();
	return FALSE;
}
