// WaveOut.cpp: implementation of the CWaveOut class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"

#include "WaveForm.h"

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

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

CWaveOut::CWaveOut()
{
	m_iPlayDeviceNumber = waveOutGetNumDevs();
	m_hWaveOut = NULL;
}

CWaveOut::~CWaveOut()
{
}

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

	m_hWnd = hWnd;
	m_iPlayDeviceID = iPlayDeviceID;
	m_bPlay = (m_iPlayDeviceID >= 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_hWaveOutHeader[i] = m_hWaveOutData[i] = NULL;
		m_pWaveOutHeader[i] = NULL;
		m_pWaveOutData[i] = NULL;
	}

	if (m_bPlay)
	{
		if (m_iPlayDeviceNumber < m_iPlayDeviceID + 1)
		{
			OutputDebugString("WaveOut: No device available\n");
			goto Error;
		}

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

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

			m_hWaveOutHeader[i] = GlobalAlloc(GMEM_MOVEABLE|GMEM_SHARE, sizeof(WAVEHDR));
			if (m_hWaveOutHeader[i] == NULL) goto Error;
			m_pWaveOutHeader[i] = (LPWAVEHDR)GlobalLock(m_hWaveOutHeader[i]);
			if (m_pWaveOutHeader[i] == NULL) goto Error;
		}
	}
	return TRUE;

Error:
	Free();
	return FALSE;
}

void CWaveOut::Free()
{
	int i;

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

	for (i = 0; i < WAVE_FORM_BUFFER_NUMBER; i ++)
	{
		if (m_pWaveOutHeader[i] != NULL) GlobalUnlock(m_hWaveOutHeader[i]);
		if (m_hWaveOutHeader[i] != NULL)
		{
			GlobalFree(m_hWaveOutHeader[i]);
			m_hWaveOutHeader[i] = NULL;
		}
		if (m_pWaveOutData[i] != NULL) GlobalUnlock(m_hWaveOutData[i]);
		if (m_hWaveOutData[i] != NULL)
		{
			GlobalFree(m_hWaveOutData[i]);
			m_hWaveOutData[i] = NULL;
		}
	}
	m_hWaveOut = NULL;
	m_hWnd = NULL;
}

BOOL CWaveOut::Start()
{
	int i;

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

	m_iWaveOutIdleBuffer = WAVE_FORM_BUFFER_NUMBER;
	m_iWaveOutNextBuffer = 0;

	if (m_bPlay)
	{
		m_bPlay = FALSE;
		if (waveOutOpen(&m_hWaveOut, m_iPlayDeviceID, &m_wavFormat, (DWORD)m_hWnd, NULL, CALLBACK_WINDOW) != 0)
		{
			OutputDebugString("WaveOut: Open device failed\n");
			m_hWaveOut = NULL;
			goto Error;
		}

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

			if (waveOutPrepareHeader(m_hWaveOut, m_pWaveOutHeader[i], sizeof(WAVEHDR)) != 0)
			{
				OutputDebugString("WaveOut: Prepare head failed\n");
				goto Error;
			}
		}

		for (i = WAVE_FORM_BUFFER_NUMBER - 2; i < WAVE_FORM_BUFFER_NUMBER; i ++)
		{
			memset(m_pWaveOutData[i], 0, m_iWaveLength);
			if (waveOutWrite(m_hWaveOut, m_pWaveOutHeader[i], sizeof(WAVEHDR)) != 0)
			{
				OutputDebugString("WaveOut: Write data failed\n");
				goto Error;
			}
			m_iWaveOutIdleBuffer --;
			m_bPlay = TRUE;
		}
	}

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

Error:
	Stop();
	return FALSE;
}

void CWaveOut::Stop()
{
	int i;

	if (m_bPlay)
	{
		m_bPlay = FALSE;
		if (m_hWaveOut != NULL)
		{
			waveOutReset(m_hWaveOut);
			for (i = 0; i < WAVE_FORM_BUFFER_NUMBER; i ++)
			{
				waveOutUnprepareHeader(m_hWaveOut, m_pWaveOutHeader[i], sizeof(WAVEHDR));
			}
			waveOutClose(m_hWaveOut);
			m_hWaveOut = NULL;
			m_bPlay = TRUE;
		}
	}

	OutputDebugString("WaveOut stopped\n");
}

BOOL CWaveOut::Play(char * pData, int iLength)
{
	if (m_hWaveOut == NULL || m_bPlay == FALSE)
	{
		OutputDebugString("Playing not ready\n");
		return FALSE;
	}

	if (m_iWaveOutIdleBuffer > 0)
	{
		if (waveOutUnprepareHeader(m_hWaveOut, m_pWaveOutHeader[m_iWaveOutNextBuffer], sizeof(WAVEHDR)) != 0)	
		{
			OutputDebugString("WaveOut: Unprepare head failed\n");
			return FALSE;
		}

		memcpy(m_pWaveOutData[m_iWaveOutNextBuffer], pData, iLength);
		
		m_pWaveOutHeader[m_iWaveOutNextBuffer]->lpData = m_pWaveOutData[m_iWaveOutNextBuffer];
		m_pWaveOutHeader[m_iWaveOutNextBuffer]->dwBufferLength = iLength;
		m_pWaveOutHeader[m_iWaveOutNextBuffer]->dwFlags = 0;
		m_pWaveOutHeader[m_iWaveOutNextBuffer]->dwLoops = 0;

	    if (waveOutPrepareHeader(m_hWaveOut, m_pWaveOutHeader[m_iWaveOutNextBuffer], sizeof(WAVEHDR)) == 0)
		{
			if (waveOutWrite(m_hWaveOut, m_pWaveOutHeader[m_iWaveOutNextBuffer], sizeof(WAVEHDR)) != 0)
			{
				OutputDebugString("WaveOut: Write data failed\n");
				return FALSE;
			}
		}
		else
		{
			OutputDebugString("WaveOut: Prepare head failed\n");
			return FALSE;
		}

		m_iWaveOutIdleBuffer --;
		m_iWaveOutNextBuffer ++;
		if (m_iWaveOutNextBuffer == WAVE_FORM_BUFFER_NUMBER) m_iWaveOutNextBuffer = 0;

		if (m_iWaveOutIdleBuffer > WAVE_FORM_BUFFER_NUMBER - 2)	// Keep at least 3 sound buffers working
			return Play(pData, iLength);
	}
	else
	{
		OutputDebugString("WaveOut Buffer full\n");
//		waveOutReset(m_hWaveOut);
		return FALSE;
	}
	return TRUE;
}


BOOL CWaveOut::ContinuePlaying()
{
	m_iWaveOutIdleBuffer ++;
	return TRUE;
}

void CWaveOut::GetPlayDeviceNames(CStringList& strList)
{
	int i;
	WAVEOUTCAPS cap;

	for (i = 0; i < m_iPlayDeviceNumber; i++)
	{
		waveOutGetDevCaps(i, &cap, sizeof(WAVEOUTCAPS)); 
		strList.AddTail(cap.szPname);
	}
}

