Click here to Skip to main content
Click here to Skip to main content
 
Add your own
alternative version

CWaveForm

, 24 Feb 2006
A simple wave form API wrapper class
/*
+===========================================================================+
|				Copyright (C) Direct Line Corp. 1999-2000.					|
+---------------------------------------------------------------------------+
| File Name:																|
|																			|
|	WaveForm.cpp															|
|																			|
+---------------------------------------------------------------------------+
| Descriptions:																|
|																			|
|	Win32 wave form API object oriented implementation.						|
|																			|
+---------------------------------------------------------------------------+
| Developer(s):																|
|																			|
|	Xu Wen Bin.																|
|																			|
+===========================================================================+
|						C H A N GE		L O G								|
+---------------------------------------------------------------------------+
|																			|
|	07-20-01	1.00	Created.											|
|	07-27-01	1.01	Modified.											|
|	09-01-01	1.02	Modified.											|
|	02-22-06	1.03	Modified.											|
|																			|
+---------------------------------------------------------------------------+
| Notes:																	|
|																			|
|	1. Wave device open sequence : open()->start()->stop()->close().		|
|	2. Support seperate operation on wave in and out object.				|
|	3. Use standard stream device interface.								|
|																			|
+===========================================================================+
*/

#include "StdAfx.h"
#include "WaveForm.h"

/****************************************************************************
 * CWaveForm::CWaveForm()
 ****************************************************************************
 * CWaveForm constructor.
 */
CWaveForm::CWaveForm
(
	UINT           uDirection,
	UINT           uWaveInDeviceID,
	UINT           uWaveOutDeviceID,
	WAVEFORMATEX * lpWaveInFormatEx,
	WAVEFORMATEX * lpWaveOutFormatEx,
	DWORD          dwWaveInVolume,
	DWORD          dwWaveOutVolume
)
{
	/////////////////////////////////////////////////////////////////////////
	// Init wave format related variables.
	/////////////////////////////////////////////////////////////////////////
	m_RefCount = 0;

	/////////////////////////////////////////////////////////////////////////
	// Preset wave in/out device info.
	/////////////////////////////////////////////////////////////////////////
	m_uDirection      = uDirection;
	m_waveInDeviceID  = uWaveInDeviceID;
	m_waveOutDeviceID = uWaveOutDeviceID;

	if ((m_uDirection & WAVE_FORM_FLOW_IN) && (lpWaveInFormatEx != NULL))
	{
		memcpy(&m_waveInFormatEx, lpWaveInFormatEx, sizeof(WAVEFORMATEX));
	}

	if ((m_uDirection & WAVE_FORM_FLOW_OUT) && (lpWaveOutFormatEx != NULL))
	{
		memcpy(&m_waveOutFormatEx, lpWaveOutFormatEx, sizeof(WAVEFORMATEX));
	}

	m_hWaveIn  = NULL;
	m_hWaveOut = NULL;

	m_waveInState  = WAVE_STATE_CLOSE;
	m_waveOutState = WAVE_STATE_CLOSE;

	/////////////////////////////////////////////////////////////////////////
	// Initialize wave in critical section locks & free list.
	/////////////////////////////////////////////////////////////////////////
	if (m_uDirection & WAVE_FORM_FLOW_IN)
	{
        InitializeCriticalSection(&m_waveInFrameFreeListLock);
		InitializeCriticalSection(&m_waveInFrameWaitingListLock);
        InitializeCriticalSection(&m_waveInFrameProcessListLock);

        m_bWaveInProcessFlag = FALSE;
        m_hWaveInProcessEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
        m_hWaveInProcessThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProcIn, (LPVOID)this, 0, 0);
	}

	/////////////////////////////////////////////////////////////////////////
	// Initialize wave out critical section locks & free list.
	/////////////////////////////////////////////////////////////////////////
	if (m_uDirection & WAVE_FORM_FLOW_OUT)
	{
		InitializeCriticalSection(&m_waveOutFrameFreeListLock);
		InitializeCriticalSection(&m_waveOutFrameWaitingListLock);
        InitializeCriticalSection(&m_waveOutFrameProcessListLock);

        m_bWaveOutProcessFlag = FALSE;
        m_hWaveOutProcessEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
        m_hWaveOutProcessThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProcOut, (LPVOID)this, 0, 0);
	}

	/////////////////////////////////////////////////////////////////////////
	// Save wave in/out volumes.
	/////////////////////////////////////////////////////////////////////////
	m_waveInVolume = dwWaveInVolume;
	m_waveOutVolume = dwWaveOutVolume;

	/////////////////////////////////////////////////////////////////////////
	// Setup wave in/out outstanding monitor.
	/////////////////////////////////////////////////////////////////////////
	m_dwWaveInByteInDevice = 0;
	m_dwWaveOutByteInDevice = 0;
}

/****************************************************************************
 * CWaveForm::~CWaveForm()
 ****************************************************************************
 * CWaveForm destructor.
 */
CWaveForm::~CWaveForm()
{
	/////////////////////////////////////////////////////////////////////////
	// Delete wave in list lock & free free/waiting/process list.
	/////////////////////////////////////////////////////////////////////////
	if (m_uDirection & WAVE_FORM_FLOW_IN)
	{
        m_bWaveInProcessFlag = TRUE;
        SetEvent(m_hWaveInProcessEvent);
        WaitForSingleObject(m_hWaveInProcessThread, INFINITE);

		CloseHandle(m_hWaveInProcessEvent);
		CloseHandle(m_hWaveInProcessThread);

		DeleteCriticalSection(&m_waveInFrameFreeListLock);
		DeleteCriticalSection(&m_waveInFrameWaitingListLock);
        DeleteCriticalSection(&m_waveInFrameProcessListLock);
	}

	/////////////////////////////////////////////////////////////////////////
	// Delete wave out list lock & free free/waiting/process list.
	/////////////////////////////////////////////////////////////////////////
	if (m_uDirection & WAVE_FORM_FLOW_OUT)
	{
        m_bWaveOutProcessFlag = TRUE;
        SetEvent(m_hWaveOutProcessEvent);
        WaitForSingleObject(m_hWaveOutProcessThread, INFINITE);

        CloseHandle(m_hWaveOutProcessEvent);
		CloseHandle(m_hWaveOutProcessThread);

		DeleteCriticalSection(&m_waveOutFrameFreeListLock);
		DeleteCriticalSection(&m_waveOutFrameWaitingListLock);
        DeleteCriticalSection(&m_waveOutFrameProcessListLock);
	}
}

/****************************************************************************
 * CWaveForm::AddRef()
 ****************************************************************************
 * Increase reference count.
 */
long
CWaveForm::AddRef()
{
	InterlockedIncrement(&m_RefCount);
	return m_RefCount;
}

/****************************************************************************
 * CWaveForm::Release()
 ****************************************************************************
 * Decrease reference count, if 0, delete object.
 */
long
CWaveForm::Release()
{
	if (InterlockedDecrement(&m_RefCount) == 0)
	{
		delete this;
		return 0;
	}

	return m_RefCount;
}

/****************************************************************************
 * CWaveForm::Open()
 ****************************************************************************
 * Open wave in/out device.
 */
BOOL
CWaveForm::Open
(
	UINT	uDirection
)
{
	MMRESULT ret = MMSYSERR_INVALPARAM;

	/////////////////////////////////////////////////////////////////////////
	// Try to open wave in device, which support requested wave format.
	/////////////////////////////////////////////////////////////////////////
	if ((uDirection & WAVE_FORM_FLOW_IN) && (m_uDirection & WAVE_FORM_FLOW_IN))
	{
		if (m_waveInState == WAVE_STATE_CLOSE)
		{
			ret = ::waveInOpen
					(
						&m_hWaveIn,
						m_waveInDeviceID,
						&m_waveInFormatEx,
						DWORD(WaveInCallBackRoutine),
						DWORD(this),
						CALLBACK_FUNCTION
					);

			if (ret == MMSYSERR_NOERROR)
			{
				m_waveInState = WAVE_STATE_OPEN;
				SetVolume(WAVE_FORM_FLOW_IN, m_waveInVolume);
			}
		}
		else
		{
			::MessageBox
			(
				NULL,
				_T("Fail To Open Wave In Device!!!"),
				_T("Direct Line Service"),
				MB_OK | MB_ICONWARNING
			);

			return FALSE;
		}
	}
    else
    {
		::MessageBox
		(
			NULL,
			_T("Wave In Object Is Not Initialized!!!"),
			_T("Direct Line Service"),
			MB_OK | MB_ICONWARNING
		);

        return FALSE;
    }

	/////////////////////////////////////////////////////////////////////////
	// Try to open wave out device, which support requested wave format.
	/////////////////////////////////////////////////////////////////////////
	if ((uDirection & WAVE_FORM_FLOW_OUT) && (m_uDirection & WAVE_FORM_FLOW_OUT))
	{
		if (m_waveOutState == WAVE_STATE_CLOSE)
		{
			ret = ::waveOutOpen
					(
						&m_hWaveOut,
						m_waveOutDeviceID,
						&m_waveOutFormatEx,
						DWORD(WaveOutCallBackRoutine),
						DWORD(this),
						CALLBACK_FUNCTION
					);

			if (ret == MMSYSERR_NOERROR)
			{
				m_waveOutState = WAVE_STATE_OPEN;
				SetVolume(WAVE_FORM_FLOW_OUT, m_waveOutVolume);
			}
		}
		else
		{
			::MessageBox
			(
				NULL,
				_T("Fail To Open Wave Out Device!!!"),
				_T("Direct Line Service"),
				MB_OK | MB_ICONWARNING
			);

			return FALSE;
		}
	}
    else
    {
		::MessageBox
		(
			NULL,
			_T("Wave Out Object Is Not Initialized!!!"),
			_T("Direct Line Service"),
			MB_OK | MB_ICONWARNING
		);

        return FALSE;
    }

	return TRUE;
}

/****************************************************************************
 * CWaveForm::Close()
 ****************************************************************************
 * Close wave in/out device.
 */
BOOL
CWaveForm::Close
(
	UINT	uDirection
)
{
	MMRESULT ret = MMSYSERR_INVALPARAM;

	/////////////////////////////////////////////////////////////////////////
	// Try to close wave in device.
	/////////////////////////////////////////////////////////////////////////
	if ((uDirection & WAVE_FORM_FLOW_IN) && (m_uDirection & WAVE_FORM_FLOW_IN))
	{
		if (m_waveInState == WAVE_STATE_STOP)
		{
			if (m_hWaveIn)
			{
		        while (!m_waveInFrameFreeList.IsEmpty())
        		{
		        	PWAVEFRAME waveFrame = m_waveInFrameFreeList.RemoveHead();
			        ::waveInUnprepareHeader
        			(
		        		m_hWaveIn,
				        (LPWAVEHDR)waveFrame,
        				sizeof(WAVEHDR)
		        	);
			        delete waveFrame;
		        }
		        while (!m_waveInFrameWaitingList.IsEmpty())
		        {
			        PWAVEFRAME waveFrame = m_waveInFrameWaitingList.RemoveHead();
			        ::waveInUnprepareHeader
			        (
				        m_hWaveIn,
				        (LPWAVEHDR)waveFrame,
				        sizeof(WAVEHDR)
			        );
			        delete waveFrame;
		        }
		        while (!m_waveInFrameProcessList.IsEmpty())
                {
			        PWAVEFRAME waveFrame = m_waveInFrameProcessList.RemoveHead();
			        ::waveInUnprepareHeader
			        (
				        m_hWaveIn,
				        (LPWAVEHDR)waveFrame,
				        sizeof(WAVEHDR)
			        );
			        delete waveFrame;
		        }

                ::waveInClose(m_hWaveIn);
				m_hWaveIn = NULL;
				m_waveInState = WAVE_STATE_CLOSE;
			}
		}
		else
		{
			::MessageBox
			(
				NULL,
				_T("Wave In Device Must Be Stopped First!!!"),
				_T("Direct Line Service"),
				MB_OK | MB_ICONWARNING
			);

			return FALSE;
		}
	}
    else
    {
		::MessageBox
		(
			NULL,
			_T("No Matching Wave In Stream!!!"),
			_T("Direct Line Service"),
			MB_OK | MB_ICONWARNING
		);

        return FALSE;
    }

	/////////////////////////////////////////////////////////////////////////
	// Try to close wave out device.
	/////////////////////////////////////////////////////////////////////////
	if ((uDirection & WAVE_FORM_FLOW_OUT) && (m_uDirection & WAVE_FORM_FLOW_OUT))
	{
		if (m_waveOutState == WAVE_STATE_STOP)
		{
			if (m_hWaveOut)
			{
        		while (!m_waveOutFrameFreeList.IsEmpty())
        		{
        			PWAVEFRAME waveFrame = m_waveOutFrameFreeList.RemoveHead();
        			::waveOutUnprepareHeader
        			(
        				m_hWaveOut,
        				(LPWAVEHDR)waveFrame,
        				sizeof(WAVEHDR)
        			);
        			delete waveFrame;
        		}
        		while (!m_waveOutFrameWaitingList.IsEmpty())
        		{
        			PWAVEFRAME waveFrame = m_waveOutFrameWaitingList.RemoveHead();
        			::waveOutUnprepareHeader
        			(
        				m_hWaveOut,
        				(LPWAVEHDR)waveFrame,
        				sizeof(WAVEHDR)
        			);
        			delete waveFrame;
        		}
        		while (!m_waveOutFrameProcessList.IsEmpty())
        		{
        			PWAVEFRAME waveFrame = m_waveOutFrameProcessList.RemoveHead();
        			::waveOutUnprepareHeader
        			(
        				m_hWaveOut,
        				(LPWAVEHDR)waveFrame,
        				sizeof(WAVEHDR)
        			);
        			delete waveFrame;
                }

				::waveOutClose(m_hWaveOut);
				m_hWaveOut = NULL;
				m_waveOutState = WAVE_STATE_CLOSE;
			}
		}
		else
		{
			::MessageBox
			(
				NULL,
				_T("Wave Out Device Must Be Stopped First!!!"),
				_T("Direct Line Service"),
				MB_OK | MB_ICONWARNING
			);

			return FALSE;
		}
	}
    else
    {
		::MessageBox
		(
			NULL,
			_T("No Matching Wave Out Stream!!!"),
			_T("Direct Line Service"),
			MB_OK | MB_ICONWARNING
		);

        return FALSE;
    }

	return TRUE;
}

/****************************************************************************
 * CWaveForm::Start()
 ****************************************************************************
 * Start wave device.
 */
BOOL
CWaveForm::Start
(
	UINT	uDirection
)
{
	MMRESULT ret = MMSYSERR_INVALPARAM;

	/////////////////////////////////////////////////////////////////////////
	// Start wave in stream.
	/////////////////////////////////////////////////////////////////////////
	if ((uDirection & WAVE_FORM_FLOW_IN) && (m_uDirection & WAVE_FORM_FLOW_IN))
	{
		if (m_waveInState == WAVE_STATE_OPEN)
		{
			if (m_hWaveIn)
			{
                for (int i = 0; i < MIN_WAVE_NUM; i++)
                {
					PWAVEFRAME waveFrame = new WAVEFRAME;

					if (waveFrame)
					{
						waveFrame->waveHdr.lpData          = waveFrame->Data;
						waveFrame->waveHdr.dwBufferLength  = MAX_WAVE_BUFFER_LEN;
						waveFrame->waveHdr.dwBytesRecorded = 0;
						waveFrame->waveHdr.dwFlags         = 0;
						waveFrame->waveHdr.dwLoops         = 0;
						waveFrame->waveHdr.dwUser          = 0;
						waveFrame->waveHdr.lpNext          = 0;

						ret = ::waveInPrepareHeader
								(
									m_hWaveIn,
									(LPWAVEHDR)waveFrame,
									sizeof(WAVEHDR)
								);

						ret = ::waveInAddBuffer
								(
									m_hWaveIn,
									(LPWAVEHDR)waveFrame,
									sizeof(WAVEHDR)
								);

						m_dwWaveInByteInDevice += waveFrame->waveHdr.dwBufferLength;
					}
				}

				ret = ::waveInStart(m_hWaveIn);
				m_waveInState = WAVE_STATE_START;
			}
		}
		else
		{
			::MessageBox
			(
				NULL,
				_T("Fail To Start Wave In Device!!!"),
				_T("Direct Line Service"),
				MB_OK | MB_ICONWARNING
			);

			return FALSE;
		}
	}

	/////////////////////////////////////////////////////////////////////////
	// Start wave out stream.
	/////////////////////////////////////////////////////////////////////////
	if ((uDirection & WAVE_FORM_FLOW_OUT) && (m_uDirection & WAVE_FORM_FLOW_OUT))
	{
		if (m_waveOutState == WAVE_STATE_OPEN)
		{
			if (m_hWaveOut)
			{
				while (!m_waveOutFrameWaitingList.IsEmpty())
				{
					PWAVEFRAME waveFrame = m_waveOutFrameWaitingList.RemoveHead();

					if (waveFrame)
					{
						ret = ::waveOutWrite
								(
									m_hWaveOut,
									(LPWAVEHDR)waveFrame,
									sizeof(WAVEHDR)
								);

						m_dwWaveOutByteInDevice += waveFrame->waveHdr.dwBufferLength;
					}
				}

				m_waveOutState = WAVE_STATE_START;
			}
		}
		else
		{
			::MessageBox
			(
				NULL,
				_T("Fail To Start Wave Out Device!!!"),
				_T("Direct Line Service"),
				MB_OK | MB_ICONWARNING
			);

			return FALSE;
		}
	}

	return TRUE;
}

/****************************************************************************
 * CWaveForm::Stop()
 ****************************************************************************
 * Stop wave device.
 */
BOOL
CWaveForm::Stop
(
	UINT	uDirection
)
{
	MMRESULT ret = MMSYSERR_INVALPARAM;

	/////////////////////////////////////////////////////////////////////////
	// Stop wave in stream.
	/////////////////////////////////////////////////////////////////////////
	if ((uDirection & WAVE_FORM_FLOW_IN) && (m_uDirection & WAVE_FORM_FLOW_IN))
	{
		if (m_waveInState == WAVE_STATE_START)
		{
			if (m_hWaveIn)
			{
				m_waveInState = WAVE_STATE_RESET;
				ret = ::waveInReset(m_hWaveIn);
                while(m_dwWaveInByteInDevice > 0)
                {
                    Sleep(500);
                }
                SetEvent(m_hWaveInProcessEvent);
				m_waveInState = WAVE_STATE_STOP;
				ret = ::waveInStop(m_hWaveIn);
			}
		}
		else
		{
			::MessageBox
			(
				NULL,
				_T("Fail To Stop Wave In Device!!!"),
				_T("Direct Line Service"),
				MB_OK | MB_ICONWARNING
			);

			return FALSE;
		}
	}

	/////////////////////////////////////////////////////////////////////////
	// Stop wave out stream.
	/////////////////////////////////////////////////////////////////////////
	if ((uDirection & WAVE_FORM_FLOW_OUT) && (m_uDirection & WAVE_FORM_FLOW_OUT))
	{
		if (m_waveOutState == WAVE_STATE_START)
		{
			if (m_hWaveOut)
			{
				m_waveOutState = WAVE_STATE_RESET;
				ret = ::waveOutReset(m_hWaveOut);
                while(m_dwWaveOutByteInDevice > 0)
                {
                    Sleep(500);
                }
                SetEvent(m_hWaveOutProcessEvent);
				m_waveOutState = WAVE_STATE_STOP;
			}
		}
		else
		{
			::MessageBox
			(
				NULL,
				_T("Fail To Stop Wave Out Device!!!"),
				_T("Direct Line Service"),
				MB_OK | MB_ICONWARNING
			);

			return FALSE;
		}
	}

	return TRUE;
}

/****************************************************************************
 * CWaveForm::Pause()
 ****************************************************************************
 * Pause wave device.
 */
BOOL
CWaveForm::Pause
(
	UINT uDirection
)
{
	if ((uDirection & WAVE_FORM_FLOW_OUT) && (m_uDirection & WAVE_FORM_FLOW_OUT))
	{
		if (m_hWaveOut)
		{
			m_waveOutState = WAVE_STATE_PAUSE;
			::waveOutPause(m_hWaveOut);
			return TRUE;
		}
	}
	return FALSE;
}

/****************************************************************************
 * CWaveForm::Restart()
 ****************************************************************************
 * Restart wave device.
 */
BOOL
CWaveForm::Restart
(
	UINT uDirection
)
{
	if ((uDirection & WAVE_FORM_FLOW_OUT) && (m_uDirection & WAVE_FORM_FLOW_OUT))
	{
		if (m_hWaveOut)
		{
			m_waveOutState = WAVE_STATE_START;
			::waveOutRestart(m_hWaveOut);
			return TRUE;
		}
	}
	return FALSE;
}

/****************************************************************************
 * CWaveForm::Reset()
 ****************************************************************************
 * Reset wave device.
 */
BOOL
CWaveForm::Reset
(
	UINT uDirection
)
{
	if ((uDirection & WAVE_FORM_FLOW_IN) && (m_uDirection & WAVE_FORM_FLOW_IN))
	{
		if (m_hWaveIn)
		{
			m_waveInState = WAVE_STATE_RESET;
			::waveInReset(m_hWaveIn);
			m_waveInState = WAVE_STATE_STOP;
			return TRUE;
		}
	}

	if ((uDirection & WAVE_FORM_FLOW_OUT) && (m_uDirection & WAVE_FORM_FLOW_OUT))
	{
		if (m_hWaveOut)
		{
			m_waveOutState = WAVE_STATE_RESET;
			::waveOutReset(m_hWaveOut);
			m_waveOutState = WAVE_STATE_STOP;
			return TRUE;
		}
	}

	return FALSE;
}

/****************************************************************************
 * CWaveForm::GetDirection()
 ****************************************************************************
 * Get wave form flow direction.
 */
UINT
CWaveForm::GetDirection()
{
	return m_uDirection;
}

/****************************************************************************
 * CWaveForm::GetDirection()
 ****************************************************************************
 * Set wave form flow direction.
 * Please note: this function is not fully implemented.
 */
void
CWaveForm::SetDirection(UINT uDirection)
{
    ASSERT(0);
}

/****************************************************************************
 * CWaveForm::GetFormat()
 ****************************************************************************
 * Get wave in/out format.
 */
BOOL
CWaveForm::GetFormat
(
	UINT			uDirection,
	LPWAVEFORMATEX	lpWaveFormatEx
)
{
	if ((uDirection & WAVE_FORM_FLOW_IN) && (lpWaveFormatEx != NULL))
	{
		memcpy(lpWaveFormatEx, &m_waveInFormatEx, sizeof(WAVEFORMATEX));
		return TRUE;
	}

	if ((uDirection & WAVE_FORM_FLOW_OUT) && (lpWaveFormatEx != NULL))
	{
		memcpy(lpWaveFormatEx, &m_waveOutFormatEx, sizeof(WAVEFORMATEX));
		return TRUE;
	}

	return FALSE;
}

/****************************************************************************
 * CWaveForm::SetFormat()
 ****************************************************************************
 * Set wave in/out format.
 * Please note: this function is not fully implemented.
 */
BOOL
CWaveForm::SetFormat
(
	UINT			uDirection,
	LPWAVEFORMATEX	lpWaveFormatEx
)
{
	if ((uDirection & WAVE_FORM_FLOW_IN) && (lpWaveFormatEx != NULL))
	{
		memcpy(&m_waveInFormatEx, lpWaveFormatEx, sizeof(WAVEFORMATEX));
		return TRUE;
	}

	if ((uDirection & WAVE_FORM_FLOW_OUT) && (lpWaveFormatEx != NULL))
	{
		memcpy(&m_waveOutFormatEx, lpWaveFormatEx, sizeof(WAVEFORMATEX));
		return TRUE;
	}

	return FALSE;
}

/****************************************************************************
 * CWaveForm::UpdateFormat()
 ****************************************************************************
 * Update wave in/out format.
 * Please note: this function is not fully implemented.
 */
BOOL
CWaveForm::UpdateFormat
(
	UINT			uDirection,
	LPWAVEFORMATEX	lpWaveFormatEx
)
{
	if ((uDirection & WAVE_FORM_FLOW_IN) && (lpWaveFormatEx != NULL))
	{
		VALIDATE(lpWaveFormatEx->nChannels, m_waveInFormatEx.nChannels);
		VALIDATE(lpWaveFormatEx->nSamplesPerSec, m_waveInFormatEx.nSamplesPerSec);
		VALIDATE(lpWaveFormatEx->wBitsPerSample, m_waveInFormatEx.wBitsPerSample);

		lpWaveFormatEx->nAvgBytesPerSec = lpWaveFormatEx->nSamplesPerSec
											* lpWaveFormatEx->nChannels
											* (lpWaveFormatEx->wBitsPerSample / 8);
		lpWaveFormatEx->nBlockAlign = lpWaveFormatEx->nChannels
											* (lpWaveFormatEx->wBitsPerSample / 8);

		m_waveInFormatEx.nBlockAlign = lpWaveFormatEx->nBlockAlign;
		m_waveInFormatEx.nAvgBytesPerSec = lpWaveFormatEx->nAvgBytesPerSec;

		return TRUE;
	}

	if ((uDirection & WAVE_FORM_FLOW_OUT) && (lpWaveFormatEx != NULL))
	{
		VALIDATE(lpWaveFormatEx->nChannels, m_waveOutFormatEx.nChannels);
		VALIDATE(lpWaveFormatEx->nSamplesPerSec, m_waveOutFormatEx.nSamplesPerSec);
		VALIDATE(lpWaveFormatEx->wBitsPerSample, m_waveOutFormatEx.wBitsPerSample);

		lpWaveFormatEx->nAvgBytesPerSec = lpWaveFormatEx->nSamplesPerSec
											* lpWaveFormatEx->nChannels
											* (lpWaveFormatEx->wBitsPerSample / 8);
		lpWaveFormatEx->nBlockAlign = lpWaveFormatEx->nChannels
											* (lpWaveFormatEx->wBitsPerSample / 8);

		m_waveOutFormatEx.nBlockAlign = lpWaveFormatEx->nBlockAlign;
		m_waveOutFormatEx.nAvgBytesPerSec = lpWaveFormatEx->nAvgBytesPerSec;

		return TRUE;
	}

	return FALSE;
}

/****************************************************************************
 * CWaveForm::GetVolume()
 ****************************************************************************
 * Get wave form volume.
 */
BOOL
CWaveForm::GetVolume
(
	UINT	uDirection,
	PDWORD	pVolume
)
{
	MMRESULT ret = MMSYSERR_INVALPARAM;

	if (uDirection & WAVE_FORM_FLOW_IN)
	{
		if (m_hWaveIn)
		{
			HMIXER hMixer = NULL;
            ret = ::mixerOpen(&hMixer, (UINT)m_hWaveIn, NULL, NULL, MIXER_OBJECTF_HWAVEIN);

			if (ret == MMSYSERR_NOERROR)
			{
				// Get mixer recording dest line.
				MIXERLINE mixerDestLine;
				memset(&mixerDestLine, 0, sizeof(MIXERLINE));
				mixerDestLine.cbStruct = sizeof(MIXERLINE);
				mixerDestLine.dwDestination = 1; // 0 - Playback , 1 - Recording.

				ret = ::mixerGetLineInfo
				(
					(HMIXEROBJ)hMixer,
					&mixerDestLine,
					MIXER_GETLINEINFOF_DESTINATION
				);

				if (ret == MMSYSERR_NOERROR)
				{
					// Enumerate mixer recording source lines.
					UINT srcConnection = 0;
					MIXERLINE mixerSrcLine;

					for (; srcConnection < mixerDestLine.cConnections; srcConnection++)
					{
						memset(&mixerSrcLine, 0, sizeof(MIXERLINE));
						mixerSrcLine.cbStruct = sizeof(MIXERLINE);
						mixerSrcLine.dwDestination = 1; // 0 - Playback , 1 - Recording.
						mixerSrcLine.dwSource = srcConnection;

						ret = ::mixerGetLineInfo
						(
							(HMIXEROBJ)hMixer,
							&mixerSrcLine,
							MIXER_GETLINEINFOF_SOURCE
						);

						if (mixerSrcLine.dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE)
						{
							MIXERCONTROL mixerMicVolumeControl;
							memset(&mixerMicVolumeControl, 0, sizeof(MIXERCONTROL));
							mixerMicVolumeControl.cbStruct = sizeof(MIXERCONTROL);

							MIXERLINECONTROLS mixerMicLineControls;
							memset(&mixerMicLineControls, 0, sizeof(MIXERLINECONTROLS));
							mixerMicLineControls.cbStruct  = sizeof(MIXERLINECONTROLS);
							mixerMicLineControls.dwLineID  = mixerSrcLine.dwLineID;
							mixerMicLineControls.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
							mixerMicLineControls.cControls = 1;
							mixerMicLineControls.pamxctrl  = &mixerMicVolumeControl;
							mixerMicLineControls.cbmxctrl  = sizeof(MIXERCONTROL);

							ret = ::mixerGetLineControls
							(
								(HMIXEROBJ)hMixer,
								&mixerMicLineControls,
								MIXER_GETLINECONTROLSF_ONEBYTYPE
							);

							if (ret == MMSYSERR_NOERROR)
							{
								MIXERCONTROLDETAILS_UNSIGNED volumeLevel[2];
								volumeLevel[0].dwValue = 0;
								volumeLevel[1].dwValue = 0;

								MIXERCONTROLDETAILS mixerControlDetails;
								mixerControlDetails.cbStruct    = sizeof(MIXERCONTROLDETAILS);
								mixerControlDetails.dwControlID = mixerMicLineControls.pamxctrl->dwControlID;
								mixerControlDetails.cMultipleItems = 0;
								mixerControlDetails.cChannels = mixerSrcLine.cChannels;
								mixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED) * mixerSrcLine.cChannels;
								mixerControlDetails.paDetails = volumeLevel;

								ret = ::mixerGetControlDetails
								(
									(HMIXEROBJ)hMixer,
									&mixerControlDetails,
									MIXER_GETCONTROLDETAILSF_VALUE
								);

								*pVolume = volumeLevel[0].dwValue;
								*pVolume |= volumeLevel[1].dwValue << 16;
							}

							break;
						}
					}
				}

				ret = ::mixerClose(hMixer);
			}
		}
	}
	
	if (uDirection & WAVE_FORM_FLOW_OUT)
	{
		if (m_hWaveOut)
		{
			ret = ::waveOutGetVolume(m_hWaveOut, pVolume);
		}
	}

	if (ret == MMSYSERR_NOERROR)
	{
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}

/****************************************************************************
 * CWaveForm::SetVolume()
 ****************************************************************************
 * Set wave form volume.
 */
BOOL
CWaveForm::SetVolume
(
	UINT	uDirection,
	DWORD	dwVolume
)
{
	MMRESULT ret = MMSYSERR_INVALPARAM;

	if (uDirection & WAVE_FORM_FLOW_IN)
	{
		if (m_hWaveIn)
		{
			HMIXER hMixer = NULL;
			ret = ::mixerOpen(&hMixer, (UINT)m_hWaveIn, NULL, NULL, MIXER_OBJECTF_HWAVEIN);

			if (ret == MMSYSERR_NOERROR)
			{
				// Get mixer recording dest line.
				MIXERLINE mixerDestLine;
				memset(&mixerDestLine, 0, sizeof(MIXERLINE));
				mixerDestLine.cbStruct = sizeof(MIXERLINE);
				mixerDestLine.dwDestination = 1; // 0 - Playback , 1 - Recording.

				ret = ::mixerGetLineInfo
				(
					(HMIXEROBJ)hMixer,
					&mixerDestLine,
					MIXER_GETLINEINFOF_DESTINATION
				);

				if (ret == MMSYSERR_NOERROR)
				{
					// Enumerate mixer recording source lines.
					UINT srcConnection = 0;
					MIXERLINE mixerSrcLine;

					for (; srcConnection < mixerDestLine.cConnections; srcConnection++)
					{
						memset(&mixerSrcLine, 0, sizeof(MIXERLINE));
						mixerSrcLine.cbStruct = sizeof(MIXERLINE);
						mixerSrcLine.dwDestination = 1; // 0 - Playback , 1 - Recording.
						mixerSrcLine.dwSource = srcConnection;

						ret = ::mixerGetLineInfo
						(
							(HMIXEROBJ)hMixer,
							&mixerSrcLine,
							MIXER_GETLINEINFOF_SOURCE
						);

						if (mixerSrcLine.dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE)
						{
							MIXERCONTROL mixerMicVolumeControl;
							memset(&mixerMicVolumeControl, 0, sizeof(MIXERCONTROL));
							mixerMicVolumeControl.cbStruct = sizeof(MIXERCONTROL);

							MIXERLINECONTROLS mixerMicLineControls;
							memset(&mixerMicLineControls, 0, sizeof(MIXERLINECONTROLS));
							mixerMicLineControls.cbStruct  = sizeof(MIXERLINECONTROLS);
							mixerMicLineControls.dwLineID  = mixerSrcLine.dwLineID;
							mixerMicLineControls.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
							mixerMicLineControls.cControls = 1;
							mixerMicLineControls.pamxctrl  = &mixerMicVolumeControl;
							mixerMicLineControls.cbmxctrl  = sizeof(MIXERCONTROL);

							ret = ::mixerGetLineControls
							(
								(HMIXEROBJ)hMixer,
								&mixerMicLineControls,
								MIXER_GETLINECONTROLSF_ONEBYTYPE
							);

							if (ret == MMSYSERR_NOERROR)
							{
								MIXERCONTROLDETAILS_UNSIGNED volumeLevel[2];
								volumeLevel[0].dwValue = dwVolume & 0x0ffff;
								volumeLevel[1].dwValue = volumeLevel[0].dwValue;

								MIXERCONTROLDETAILS mixerControlDetails;
								mixerControlDetails.cbStruct    = sizeof(MIXERCONTROLDETAILS);
								mixerControlDetails.dwControlID = mixerMicLineControls.pamxctrl->dwControlID;
								mixerControlDetails.cMultipleItems = 0;
								mixerControlDetails.cChannels = mixerSrcLine.cChannels;
								mixerControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED) * mixerSrcLine.cChannels;
								mixerControlDetails.paDetails = volumeLevel;

								ret = ::mixerSetControlDetails
								(
									(HMIXEROBJ)hMixer,
									&mixerControlDetails,
									MIXER_SETCONTROLDETAILSF_VALUE
								);

								m_waveInVolume = dwVolume;
							}

							break;
						}
					}

					// Select microphone as the recording source.
					MIXERCONTROL mixerRecControl;
					memset(&mixerRecControl, 0, sizeof(MIXERCONTROL));
					mixerRecControl.cbStruct = sizeof(MIXERCONTROL);

					MIXERLINECONTROLS mixerRecLineControls;
					memset(&mixerRecLineControls, 0, sizeof(MIXERLINECONTROLS));
					mixerRecLineControls.cbStruct  = sizeof(MIXERLINECONTROLS);
					mixerRecLineControls.dwLineID  = mixerDestLine.dwLineID;
					mixerRecLineControls.dwControlType = MIXERCONTROL_CONTROLTYPE_MUX;
					mixerRecLineControls.cControls = 1;
					mixerRecLineControls.pamxctrl  = &mixerRecControl;
					mixerRecLineControls.cbmxctrl  = sizeof(MIXERCONTROL);

					ret = ::mixerGetLineControls
					(
						(HMIXEROBJ)hMixer,
						&mixerRecLineControls,
						MIXER_GETLINECONTROLSF_ONEBYTYPE
					);

					if (ret == MMSYSERR_NOERROR)
					{
						MIXERCONTROLDETAILS mixerControlDetails;
						mixerControlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
						mixerControlDetails.dwControlID = mixerRecLineControls.pamxctrl->dwControlID;
						mixerControlDetails.cMultipleItems = mixerDestLine.cConnections;
						mixerControlDetails.cChannels = mixerDestLine.cChannels;

						UINT BooleanArraySize = mixerControlDetails.cMultipleItems * sizeof(MIXERCONTROLDETAILS_BOOLEAN);
						LPMIXERCONTROLDETAILS_BOOLEAN BooleanArray = new MIXERCONTROLDETAILS_BOOLEAN[mixerControlDetails.cMultipleItems];

						if (BooleanArray)
						{
							ret = ::mixerGetControlDetails
							(
								(HMIXEROBJ)hMixer,
								&mixerControlDetails,
								MIXER_GETCONTROLDETAILSF_VALUE
							);

                            BooleanArray[srcConnection].fValue = 1; /* This "srcConnection" comes from above. */
							mixerControlDetails.cbDetails = BooleanArraySize;
							mixerControlDetails.paDetails = BooleanArray;

							ret = ::mixerSetControlDetails
							(
								(HMIXEROBJ)hMixer,
								&mixerControlDetails,
								MIXER_SETCONTROLDETAILSF_VALUE
							);

							delete BooleanArray;
						}
					}
				}

				ret = ::mixerClose(hMixer);
			}
		}
	}
	
	if (uDirection & WAVE_FORM_FLOW_OUT)
	{
		if (m_hWaveOut)
		{
			ret = ::waveOutSetVolume(m_hWaveOut, dwVolume);
			m_waveOutVolume = dwVolume;
		}
	}

	if (ret == MMSYSERR_NOERROR)
	{
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}

/****************************************************************************
 * CWaveForm::Read()
 ****************************************************************************
 * Read wave frames in waiting list.
 */
BOOL
CWaveForm::Read
(
	PPWAVEFRAME	ppwaveFrame
)
{
	if (m_waveInState == WAVE_STATE_START)
	{
		if ((m_uDirection & WAVE_FORM_FLOW_IN) && m_hWaveIn)
		{
			EnterCriticalSection(&m_waveInFrameWaitingListLock);

			if (!m_waveInFrameWaitingList.IsEmpty())
			{
				*ppwaveFrame = m_waveInFrameWaitingList.RemoveHead();
			}
			else
			{
				*ppwaveFrame = NULL;
			}

			LeaveCriticalSection(&m_waveInFrameWaitingListLock);

			return TRUE;
		}
	}
	else
	{
		*ppwaveFrame = NULL;
	}

	return FALSE;
}

/****************************************************************************
 * CWaveForm::ReadDone()
 ****************************************************************************
 * Return used wave frames.
 */
BOOL
CWaveForm::ReadDone
(
	PWAVEFRAME	waveFrame
)
{
	if (waveFrame)
	{
		if ((m_uDirection & WAVE_FORM_FLOW_IN) && m_hWaveIn)
		{
			// Clear used frame.
			waveFrame->waveHdr.dwBytesRecorded = 0;

			// Reuse it in free list.
			EnterCriticalSection(&m_waveInFrameFreeListLock);

			m_waveInFrameFreeList.AddTail(waveFrame);

			LeaveCriticalSection(&m_waveInFrameFreeListLock);

			return TRUE;
		}
	}

	return FALSE;
}

/****************************************************************************
 * CWaveForm::Write()
 ****************************************************************************
 * Ask to allocate wave frame.
 */
BOOL
CWaveForm::Write
(
	PPWAVEFRAME	ppwaveFrame
)
{
	if ((m_uDirection & WAVE_FORM_FLOW_OUT) && m_hWaveOut)
	{
		EnterCriticalSection(&m_waveOutFrameFreeListLock);

		if (!m_waveOutFrameFreeList.IsEmpty())
		{
			*ppwaveFrame = m_waveOutFrameFreeList.RemoveHead();
		}
		else
		{
			PWAVEFRAME waveFrame = NULL;
			waveFrame = new WAVEFRAME;

			if (waveFrame)
			{
				waveFrame->waveHdr.lpData          = waveFrame->Data;
				waveFrame->waveHdr.dwBufferLength  = MAX_WAVE_BUFFER_LEN;
				waveFrame->waveHdr.dwBytesRecorded = 0;
				waveFrame->waveHdr.dwFlags         = 0;
				waveFrame->waveHdr.dwLoops         = 0;
				waveFrame->waveHdr.dwUser          = 0;
				waveFrame->waveHdr.lpNext          = 0;

				MMRESULT ret = ::waveOutPrepareHeader
								(
									m_hWaveOut,
									(LPWAVEHDR)waveFrame,
									sizeof(WAVEHDR)
								);

				*ppwaveFrame = waveFrame;
			}
		}

		LeaveCriticalSection(&m_waveOutFrameFreeListLock);

		return TRUE;
	}

	return FALSE;
}

/****************************************************************************
 * CWaveForm::Write()
 ****************************************************************************
 * Write data to preallocated wave frame.
 */
BOOL
CWaveForm::Write
(
	PWAVEFRAME	waveFrame
)
{
	if (waveFrame)
	{
		if (m_waveOutState == WAVE_STATE_START)
		{
			if ((m_uDirection & WAVE_FORM_FLOW_OUT) && m_hWaveOut)
			{
				EnterCriticalSection(&m_waveOutFrameWaitingListLock);

				// Queue this coming frame.
				if (m_waveOutFrameWaitingList.GetCount() < MAX_WAVE_NUM)
				{
					m_waveOutFrameWaitingList.AddTail(waveFrame);
				}
				else // Waiting list is full.
				{
					EnterCriticalSection(&m_waveOutFrameFreeListLock);

					if (m_waveOutFrameFreeList.GetCount() < MAX_WAVE_NUM)
					{
						m_waveOutFrameFreeList.AddTail(waveFrame);
					}
					else // Free list is also full.
					{
						MMRESULT ret = ::waveOutUnprepareHeader
										(
											m_hWaveOut,
											(LPWAVEHDR)waveFrame,
											sizeof(WAVEHDR)
										);

						delete waveFrame;
					}

					LeaveCriticalSection(&m_waveOutFrameFreeListLock);
				}

				// Fetch new frame from waiting list and send to device.
				if (!m_waveOutFrameWaitingList.IsEmpty() &&
					(m_dwWaveOutByteInDevice < MAX_BUFFER_IN_QUEUE))
				{
					PWAVEFRAME newWaveFrame = NULL;
					newWaveFrame = m_waveOutFrameWaitingList.RemoveHead();

					if (newWaveFrame)
					{
						MMRESULT ret = ::waveOutWrite
										(
											m_hWaveOut,
											(LPWAVEHDR)newWaveFrame,
											sizeof(WAVEHDR)
										);

						m_dwWaveOutByteInDevice += newWaveFrame->waveHdr.dwBufferLength;
					}
				}

				LeaveCriticalSection(&m_waveOutFrameWaitingListLock);

				return TRUE;
			}
		}
		else
		{
			EnterCriticalSection(&m_waveOutFrameFreeListLock);

			if (m_waveOutFrameFreeList.GetCount() < MAX_WAVE_NUM)
			{
				m_waveOutFrameFreeList.AddTail(waveFrame);
			}
			else // Free list is full.
			{
				MMRESULT ret = ::waveOutUnprepareHeader
								(
									m_hWaveOut,
									(LPWAVEHDR)waveFrame,
									sizeof(WAVEHDR)
								);

				delete waveFrame;
			}

			LeaveCriticalSection(&m_waveOutFrameFreeListLock);
		}
	}

	return FALSE;
}

/****************************************************************************
 * CWaveForm::WaveInProcess()
 ****************************************************************************
 * Process wave in buffers.
 */
void
CWaveForm::WaveInProcess()
{
    while (TRUE)
    {
        WaitForSingleObject(m_hWaveInProcessEvent, INFINITE);

        if (m_bWaveInProcessFlag == FALSE) break;

        while (m_waveInFrameProcessList.GetCount() > 0)
        {
            PWAVEFRAME newWaveFrame = NULL;
            EnterCriticalSection(&m_waveInFrameProcessListLock);
            newWaveFrame = m_waveInFrameProcessList.RemoveHead();
            LeaveCriticalSection(&m_waveInFrameProcessListLock);        
            OnWaveInData(newWaveFrame);
        }
    }
}

/****************************************************************************
 * CWaveForm::WaveOutProcess()
 ****************************************************************************
 * Process wave out buffers.
 */
void
CWaveForm::WaveOutProcess()
{
    while (TRUE)
    {
        WaitForSingleObject(m_hWaveOutProcessEvent, INFINITE);

        if (m_bWaveOutProcessFlag == FALSE) break;

        while (m_waveOutFrameProcessList.GetCount() > 0)
        {
            PWAVEFRAME newWaveFrame = NULL;
            EnterCriticalSection(&m_waveOutFrameProcessListLock);
            newWaveFrame = m_waveOutFrameProcessList.RemoveHead();
            LeaveCriticalSection(&m_waveOutFrameProcessListLock);        
            OnWaveOutDone(newWaveFrame);
        }
    }
}

/****************************************************************************
 * CWaveForm::OnWaveInData()
 ****************************************************************************
 * Wave in data handler.
 */
void
CWaveForm::OnWaveInData
(
    PWAVEFRAME waveFrame
)
{
	if (m_dwWaveInByteInDevice != 0)
	{
		m_dwWaveInByteInDevice -= waveFrame->waveHdr.dwBufferLength;
	}

	/////////////////////////////////////////////////////////////////////////
	// Process incoming wave in data when device is ON.
	/////////////////////////////////////////////////////////////////////////
    if (waveFrame->waveHdr.dwBytesRecorded != 0)
	{
		EnterCriticalSection(&m_waveInFrameWaitingListLock);

		if (m_waveInFrameWaitingList.GetCount() < MAX_WAVE_NUM)
		{
			m_waveInFrameWaitingList.AddTail(waveFrame);
		}
		else // Waiting list is full.
		{
			EnterCriticalSection(&m_waveInFrameFreeListLock);

			if (m_waveInFrameFreeList.GetCount() < MAX_WAVE_NUM)
			{
				waveFrame->waveHdr.dwBytesRecorded = 0;
				m_waveInFrameFreeList.AddTail(waveFrame);
			}
			else // Free list is also full.
			{
				MMRESULT ret = ::waveInUnprepareHeader
								(
									m_hWaveIn,
									(LPWAVEHDR)waveFrame,
									sizeof(WAVEHDR)
								);

				delete waveFrame;
			}

			LeaveCriticalSection(&m_waveInFrameFreeListLock);
		}

		LeaveCriticalSection(&m_waveInFrameWaitingListLock);

		// Notify client.
		Invoke(WM_STREAM_NOTIFY, SUB_EVENT_WAVE_IN, 0);
	}
	else // (waveFrame->waveHdr.dwBytesRecorded == 0)
	{
		EnterCriticalSection(&m_waveInFrameFreeListLock);

		if (m_waveInFrameFreeList.GetCount() < MAX_WAVE_NUM)
		{
			waveFrame->waveHdr.dwBytesRecorded = 0;
			m_waveInFrameFreeList.AddTail(waveFrame);
		}
		else // Free list is full.
		{
			MMRESULT ret = ::waveInUnprepareHeader
							(
								m_hWaveIn,
								(LPWAVEHDR)waveFrame,
								sizeof(WAVEHDR)
							);

			delete waveFrame;
		}

		LeaveCriticalSection(&m_waveInFrameFreeListLock);
	}

	/////////////////////////////////////////////////////////////////////////
	// Find or allocate a free wave in frame and add it into device.
	/////////////////////////////////////////////////////////////////////////
	if ((m_waveInState == WAVE_STATE_START) && 
        (m_dwWaveInByteInDevice < MAX_BUFFER_IN_QUEUE))
	{
		PWAVEFRAME newWaveFrame = NULL;
        MMRESULT ret = MMSYSERR_NOERROR;

		EnterCriticalSection(&m_waveInFrameFreeListLock);

		if (!m_waveInFrameFreeList.IsEmpty())
		{
			newWaveFrame = m_waveInFrameFreeList.RemoveHead();
		}
		else
		{
			newWaveFrame = new WAVEFRAME;

			if (newWaveFrame)
			{
				newWaveFrame->waveHdr.lpData          = newWaveFrame->Data;
				newWaveFrame->waveHdr.dwBufferLength  = MAX_WAVE_BUFFER_LEN;
				newWaveFrame->waveHdr.dwBytesRecorded = 0;
				newWaveFrame->waveHdr.dwFlags         = 0;
				newWaveFrame->waveHdr.dwLoops         = 0;
				newWaveFrame->waveHdr.dwUser          = 0;
				newWaveFrame->waveHdr.lpNext          = 0;

				ret = ::waveInPrepareHeader
						(
							m_hWaveIn,
							(LPWAVEHDR)newWaveFrame,
							sizeof(WAVEHDR)
						);
			}
		}

		LeaveCriticalSection(&m_waveInFrameFreeListLock);

		if (newWaveFrame)
		{
			ret = ::waveInAddBuffer
					(
						m_hWaveIn,
						(LPWAVEHDR)newWaveFrame,
						sizeof(WAVEHDR)
					);

			m_dwWaveInByteInDevice += newWaveFrame->waveHdr.dwBufferLength;
		}
	}
}

/****************************************************************************
 * CWaveForm::OnWaveOutDone()
 ****************************************************************************
 * Wave out done handler.
 */
void
CWaveForm::OnWaveOutDone
(
    PWAVEFRAME waveFrame
)
{
	if (m_dwWaveOutByteInDevice != 0)
	{
		m_dwWaveOutByteInDevice -= waveFrame->waveHdr.dwBufferLength;
	}

	/////////////////////////////////////////////////////////////////////////
	// Processing used wave out frame.
	/////////////////////////////////////////////////////////////////////////
	EnterCriticalSection(&m_waveOutFrameFreeListLock);

	if (m_waveOutFrameFreeList.GetCount() < MAX_WAVE_NUM)
	{
		m_waveOutFrameFreeList.AddTail(waveFrame);
	}
	else // Free list is full.
	{
		MMRESULT ret = ::waveOutUnprepareHeader
							(
								m_hWaveOut,
								(LPWAVEHDR)waveFrame,
								sizeof(WAVEHDR)
							);

		delete waveFrame;
	}

	LeaveCriticalSection(&m_waveOutFrameFreeListLock);

	/////////////////////////////////////////////////////////////////////////
	// If device is running, fetch new data from waiting list.
	/////////////////////////////////////////////////////////////////////////
	if ((m_waveOutState == WAVE_STATE_START) &&
		(m_dwWaveOutByteInDevice < MAX_BUFFER_IN_QUEUE))
	{
		PWAVEFRAME newWaveFrame = NULL;
        MMRESULT ret = MMSYSERR_NOERROR;

		EnterCriticalSection(&m_waveOutFrameWaitingListLock);

		if (!m_waveOutFrameWaitingList.IsEmpty())
		{
			newWaveFrame = m_waveOutFrameWaitingList.RemoveHead();
		}

		LeaveCriticalSection(&m_waveOutFrameWaitingListLock);

		if (newWaveFrame)
		{
			ret = ::waveOutWrite
					(
						m_hWaveOut,
						(LPWAVEHDR)newWaveFrame,
						sizeof(WAVEHDR)
					);

			m_dwWaveOutByteInDevice += newWaveFrame->waveHdr.dwBufferLength;
		}
	}
}

/****************************************************************************
 * CWaveForm::Compress()
 ****************************************************************************
 * Compress data.
 */
BOOL
CWaveForm::Compress
(
	PVOID	inData,
	ULONG	inDataLength,
	PVOID	outData,
	PULONG	outDataLength
)
{
	memcpy(outData, inData, inDataLength);
	*outDataLength = inDataLength;
	return TRUE;
}

/****************************************************************************
 * CWaveForm::Uncompress()
 ****************************************************************************
 * Uncompress data.
 */
BOOL
CWaveForm::Uncompress
(
	PVOID	inData,
	ULONG	inDataLength,
	PVOID	outData,
	PULONG	outDataLength
)
{
	memcpy(outData, inData, inDataLength);
	*outDataLength = inDataLength;
	return TRUE;
}

/****************************************************************************
 * WaveInCallBackRoutine()
 ****************************************************************************
 * Wave in callback routine.
 */
void
CALLBACK
WaveInCallBackRoutine
(
	HWAVEIN  hwi,
	UINT     uMsg,
	DWORD    dwInstance,
	DWORD    dwParam1,
	DWORD    dwParam2
)
{
	CWaveForm * that = (CWaveForm *)dwInstance;
    PWAVEFRAME waveFrame = (PWAVEFRAME)dwParam1;

	if (hwi == that->m_hWaveIn)
	{
		switch (uMsg)
		{
			case WIM_OPEN:
				break;

			case WIM_DATA:
                EnterCriticalSection(&that->m_waveInFrameProcessListLock);
                that->m_waveInFrameProcessList.AddTail(waveFrame);
                LeaveCriticalSection(&that->m_waveInFrameProcessListLock);
                SetEvent(that->m_hWaveInProcessEvent);
				break;

			case WIM_CLOSE:
				break;
		}
	}
}

/****************************************************************************
 * WaveOutCallBackRoutine()
 ****************************************************************************
 * Wave out call back routine.
 */
void
CALLBACK
WaveOutCallBackRoutine
(
	HWAVEOUT  hwo,
	UINT      uMsg,
	DWORD     dwInstance,
	DWORD     dwParam1,
	DWORD     dwParam2
)
{
	CWaveForm * that = (CWaveForm *)dwInstance;
    PWAVEFRAME waveFrame = (PWAVEFRAME)dwParam1;

	if (hwo == that->m_hWaveOut)
	{
		switch (uMsg)
		{
			case WOM_OPEN:
				break;

			case WOM_DONE:
                EnterCriticalSection(&that->m_waveOutFrameProcessListLock);
                that->m_waveOutFrameProcessList.AddTail(waveFrame);
                LeaveCriticalSection(&that->m_waveOutFrameProcessListLock);
                SetEvent(that->m_hWaveOutProcessEvent);
				break;

			case WOM_CLOSE:
				break;
		}
	}
}

/****************************************************************************
 * ThreadProcIn()
 ****************************************************************************
 * Wave in data process thread.
 */
DWORD
ThreadProcIn
(
    LPVOID lpData
)
{
    CWaveForm * pi = (CWaveForm *)lpData;
    pi->WaveInProcess();
    return 0;
}

/****************************************************************************
 * ThreadProcOut()
 ****************************************************************************
 * Wave out data process thread.
 */
DWORD
ThreadProcOut
(
    LPVOID lpData
)
{
    CWaveForm * po = (CWaveForm *)lpData;
    po->WaveOutProcess();
    return 0;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Skeeter
Program Manager Microsoft
China China
Graduated from Shanghai Tongji university in 1997 with bachelor degree in computer science, Wenbin got his first job as a system engineer at Bell Alcatel Mobile Shanghai office responsible for telcom-class mobile system development and deployment. Since then, Wenbin has in turn worked for Intel and Microsoft both inside and outside China, first as senior engineer, later project manager and then senior product manager.
 
With 15-year experience working with the world top IT companies, Wenbin has developed solid skill in C/C++, C#, Java, software engineering, agile development, etc, and multiple talents in product management, public presentation and speech, etc. He has always been an active member at PMI (Project Management Institution) and a regular lecture at Intel Developer Forum, Microsoft TechED conference as well as many other world-class industrial conferences. His wide-ranged industrial practice and high-level personal maturity have made him one of the best in public speech and professional training.
 
Over the years, Wenbin has cultivated his very own style in public speech, which is considered informative, engaging and refined. Since last year, Wenbin has also taken new adventure in project and product management consulting business and has proven high capacity through his work with many local emergent IT firms. Wenbin’s specialty in management consulting is on project management methodologies and processes, project management tools (e.g. MS Project), and team recruitment, build, and motivation.
 
In addition, Wenbin has received many professional qualifications including MCSE (Microsoft Certified System Engineer), MCSD (Microsoft Certified System Developer), MCDBA (Microsoft Certified Database Administrator), SCJP 2 (Sun Certified Java Programmer 2), and PMP (PMI Certified Project Management Professional). On top of that, Wenbin has got several on-duty inventions and one of them was successfully patented by United States Patent and Trademark Office.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.150302.1 | Last Updated 24 Feb 2006
Article Copyright 2001 by Skeeter
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid