// WaveIn.cpp: implementation of the CWaveIn class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"

#include "WaveForm.h"

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

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

CWaveIn::CWaveIn()
{
	m_iRecordDeviceNumber = waveInGetNumDevs();
	m_hWaveIn = NULL;
}

CWaveIn::~CWaveIn()
{
}

BOOL CWaveIn::Init(HWND hWnd, int iTime, int iRecordDeviceID, int iSamplingRate, int iChannel, int iBits)
{
	int i;

	m_hWnd = hWnd;
	m_iRecordDeviceID = iRecordDeviceID;
	m_bRecord = (m_iRecordDeviceID >= 0) ? TRUE : FALSE;

	m_wavFormat.wFormatTag = WAVE_FORMAT_PCM;
	m_wavFormat.nChannels = iChannel;
	m_wavFormat.nSamplesPerSec = iSamplingRate;
	m_wavFormat.nAvgBytesPerSec = iSamplingRate * iChannel * iBits / 8;
	m_wavFormat.nBlockAlign = iChannel * iBits / 8;
	m_wavFormat.wBitsPerSample = iBits;

	m_iWaveLength = iSamplingRate * iChannel * (iBits / 8) * iTime / 1000;

	for (i = 0; i < WAVE_FORM_BUFFER_NUMBER; i ++)
	{
		m_hWaveInHeader[i] = m_pWaveInHeader[i] = NULL;
		m_hWaveInData[i] = NULL;
		m_pWaveInData[i] = NULL;
	}

	if (m_bRecord)
	{
		if (m_iRecordDeviceNumber < m_iRecordDeviceID + 1)
		{
			OutputDebugString("WaveIn: No device available\n");
			goto Error;
		}

		// return 0 is supported; device is NOT opened after calling;
		if (waveInOpen(NULL, m_iRecordDeviceID, &m_wavFormat, NULL, NULL, WAVE_FORMAT_QUERY) != 0)
		{
			OutputDebugString("WaveIn: Format not surportted\n");
			goto Error;
		}

		for (i = 0; i < WAVE_FORM_BUFFER_NUMBER; i ++)
		{
			m_hWaveInData[i] = GlobalAlloc(GMEM_MOVEABLE|GMEM_SHARE, m_iWaveLength);
			if (m_hWaveInData[i] == NULL) goto Error;
			m_pWaveInData[i] = (LPSTR)GlobalLock(m_hWaveInData[i]);
			if (m_pWaveInData[i] == NULL) goto Error;

			m_hWaveInHeader[i] = GlobalAlloc(GMEM_MOVEABLE|GMEM_SHARE, sizeof(WAVEHDR));
			if (m_hWaveInHeader[i] == NULL) goto Error;
			m_pWaveInHeader[i] = (LPWAVEHDR)GlobalLock(m_hWaveInHeader[i]);
			if (m_pWaveInHeader[i] == NULL) goto Error;
		}
	}

	return TRUE;

Error:
	Free();
	return FALSE;
}

void CWaveIn::Free()
{
	int i;

	if (m_hWaveIn != NULL) 
	{
		Stop();
	}

	for (i = 0; i < WAVE_FORM_BUFFER_NUMBER; i ++)
	{
		if (m_pWaveInHeader[i] != NULL) GlobalUnlock(m_hWaveInHeader[i]);
		if (m_hWaveInHeader[i] != NULL)
		{
			GlobalFree(m_hWaveInHeader[i]);
			m_hWaveInHeader[i] = NULL;
		}
		if (m_pWaveInData[i] != NULL) GlobalUnlock(m_hWaveInData[i]);
		if (m_hWaveInData[i] != NULL)
		{
			GlobalFree(m_hWaveInData[i]);
			m_hWaveInData[i] = NULL;
		}
	}
	m_hWaveIn = NULL;
	m_hWnd = NULL;
}

BOOL CWaveIn::Start()
{
	int i;

	if (m_hWnd == NULL || m_hWaveIn != NULL)
	{
		return FALSE;
	}

	m_iWaveInNextBuffer = 0;

	if (m_bRecord)
	{
		m_bRecord = FALSE;
		if (waveInOpen(&m_hWaveIn, m_iRecordDeviceID, &m_wavFormat, (DWORD)m_hWnd, NULL, CALLBACK_WINDOW) != 0)
		{
			OutputDebugString("WaveIn: Open device failed\n");
			m_hWaveIn = NULL;
			goto Error;
		}

		for (i = 0; i < WAVE_FORM_BUFFER_NUMBER; i ++)
		{
			m_pWaveInHeader[i]->lpData = m_pWaveInData[i];
			m_pWaveInHeader[i]->dwBufferLength = m_iWaveLength;
			m_pWaveInHeader[i]->dwFlags = 0;
			m_pWaveInHeader[i]->dwLoops = 0;

			if (waveInPrepareHeader(m_hWaveIn, m_pWaveInHeader[i], sizeof(WAVEHDR)) != 0)
			{
				OutputDebugString("WaveIn: Prepare head failed\n");
				goto Error;
			}
			if (waveInAddBuffer(m_hWaveIn, m_pWaveInHeader[i], sizeof(WAVEHDR)) != 0)
			{
				OutputDebugString("WaveIn: Add buffer fail\n");
				goto Error;
			}
		}

		if (waveInStart(m_hWaveIn) != 0)
		{
			OutputDebugString("WaveIn: Start fail\n");
			goto Error;
		}
		m_bRecord = TRUE;
	}

	OutputDebugString("WaveIn started ok\n");
	return TRUE;

Error:
	Stop();
	return FALSE;
}

void CWaveIn::Stop()
{
	int i;

	if (m_bRecord)
	{
		m_bRecord = FALSE;
		if (m_hWaveIn != NULL)
		{
			waveInReset(m_hWaveIn);
			waveInStop(m_hWaveIn);
			for (i = 0; i < WAVE_FORM_BUFFER_NUMBER; i ++)
			{
				waveInUnprepareHeader(m_hWaveIn, m_pWaveInHeader[i], sizeof(WAVEHDR));
			}
			waveInClose(m_hWaveIn);
			m_hWaveIn = NULL;
			m_bRecord = TRUE;
		}
	}

	OutputDebugString("WaveIn stopped\n");
}


BOOL CWaveIn::ContinueRecording()
{
	if (m_hWaveIn == NULL || m_bRecord == FALSE)
	{
		OutputDebugString("Recording not ready\n");
		return FALSE;
	}

	if (waveInUnprepareHeader(m_hWaveIn, m_pWaveInHeader[m_iWaveInNextBuffer], sizeof(WAVEHDR)) != 0)	
	{
		OutputDebugString("WaveIn: Unprepare head failed\n");
		return FALSE;
	}

	m_pWaveInHeader[m_iWaveInNextBuffer]->lpData = m_pWaveInData[m_iWaveInNextBuffer];
	m_pWaveInHeader[m_iWaveInNextBuffer]->dwBufferLength = m_iWaveLength;
	m_pWaveInHeader[m_iWaveInNextBuffer]->dwFlags = 0;
	m_pWaveInHeader[m_iWaveInNextBuffer]->dwLoops = 0;

	if (waveInPrepareHeader(m_hWaveIn, m_pWaveInHeader[m_iWaveInNextBuffer], sizeof(WAVEHDR)) == 0)
	{
		if (waveInAddBuffer(m_hWaveIn, m_pWaveInHeader[m_iWaveInNextBuffer], sizeof(WAVEHDR)) != 0)
		{
			OutputDebugString("WaveIn: Write data failed\n");
			return FALSE;
		}
	}
	else
	{
		OutputDebugString("WaveIn: Prepare head failed\n");
		return FALSE;
	}

	m_iWaveInNextBuffer ++;
	if (m_iWaveInNextBuffer == WAVE_FORM_BUFFER_NUMBER) m_iWaveInNextBuffer = 0;

	return TRUE;
}

void CWaveIn::GetRecordDeviceNames(CStringList& strList)
{
	int i;
	WAVEINCAPS cap;

	for (i = 0; i < m_iRecordDeviceNumber; i++)
	{
		waveInGetDevCaps(i, &cap, sizeof(WAVEINCAPS)); 
		strList.AddTail(cap.szPname);
	}
}
