www.pudn.com > CP_IVR.zip > HSound.cpp


 /***************************************************************** 
 * HSound.cpp: implementation of the CHSound class. 
 * 
 * WARNINGS: 
 *			  - Just 1 and only 1 instance of this class must be 
 *				created. 
 * 
 * 
 * Auther: Hamed.M. 
 * eMail : HamedMosavi @ hotmail.com 
 *		   HamedMosavi @ gmail.com 
 *****************************************************************/ 
  
#include "stdafx.h" 
#include "HSound.h" 
 
#ifdef _DEBUG 
#undef THIS_FILE 
static char THIS_FILE[]=__FILE__; 
#define new DEBUG_NEW 
#endif 
 
#define SET			 0 
#define KILL		 1 
 
DWORD	CHSound::m_dwStatus = STT_ASLEEP; 
BOOL	CHSound::m_bContinue = TRUE; 
BOOL	CHSound::m_bThreadContinue = TRUE; 
HANDLE	CHSound::m_hStatEvent = NULL; 
LPWAVEHDR		CHSound::m_lpWHdr = NULL; 
 
 /***************************************************************** 
 * Constructor 
 *****************************************************************/ 
//HANDLE CHSound::s_hEvent = NULL; 
CHSound::CHSound() 
{ 
	InitializeCriticalSection(&m_statusCrtcSec); 
	InitializeCriticalSection(&m_externalResetCrtcSec); 
	 
	m_hStatEvent = CreateEvent(NULL, TRUE/*ManualReset*/, FALSE, NULL); 
 
	m_bThreadContinue = TRUE;			/* Don't exit the thread */ 
						  /* Run a thread to cleanup wave buffer */ 
	m_pCallBackThread = AfxBeginThread ( 
		CallBackThread, 
		(LPVOID) this, 
		THREAD_PRIORITY_NORMAL); 
 
	m_dwMinBufSize	= 10240;//61440;//10240;//5120;					// = 5 KBytes 
 
	m_bExitAfter = FALSE; 
	m_bForceClose = FALSE; 
	m_sndIndex   = 0; 
	m_wndTimer   = NULL; 
	m_dwDevId	 = 0; 
 
	m_playList.Reset(); 
} 
 
 /***************************************************************** 
 * Destructor 
 *****************************************************************/ 
CHSound::~CHSound() 
{ 
	m_bThreadContinue = FALSE; 
	DeleteCriticalSection(&m_statusCrtcSec); 
	DeleteCriticalSection(&m_externalResetCrtcSec); 
} 
 
 /***************************************************************** 
 * Plays a given file, to the given device 
 *****************************************************************/ 
BOOL CHSound::Play(DWORD dwDevId, LPCTSTR szWaveFile) 
{ 
	if (GetStatus() != STT_ASLEEP) 
		return FALSE; 
 
	SetStatus(STT_PREPARE); 
 
	m_dwDevId	 = dwDevId; 
	HPSTR lpData = NULL;				 /* Pointer to Wave data */ 
	LPWAVEFORMATEX	lpWaveFormatEx = NULL; 
	MMRESULT mmRes; 
	LPWAVEHDR		lpWaveHdr = NULL; 
 
	m_dwBufCnt		= 0; 
	m_dwCurrtBufIndex=0; 
	m_dwDataSize	= 0; 
 
 
	SetStatus(STT_FILEOPN); 
	lpData = Open( szWaveFile,			 /* Open & read WaveFile */ 
		  lpWaveFormatEx, 
		  m_dwDataSize); 
	SetStatus(STT_PREPARE); 
 
	if ( NULL == lpData )	{			   /* File opening error */ 
		goto CLEAN_EXIT; 
	} 
 
	if (!m_bContinue) { 
		goto CLEAN_EXIT; 
	} 
								 /* Allocate a buffer for header */ 
	lpWaveHdr = (LPWAVEHDR) calloc (1,(DWORD)sizeof(WAVEHDR)); 
	 
	if (! lpWaveHdr) {						/* Can't allocate memory */ 
		goto CLEAN_EXIT; 
	} 
 
	if (!m_bContinue) { 
		goto CLEAN_EXIT; 
	} 
 
	if (m_dwDataSize>m_dwMinBufSize) { 
		m_dwBufCnt = m_dwDataSize / m_dwMinBufSize; 
 
		// a 13 KB file for example 
		if ( (m_dwBufCnt*m_dwMinBufSize) < m_dwDataSize) 
			m_dwBufCnt++; 
	 
		lpWaveHdr->dwBufferLength = m_dwMinBufSize; 
		m_dwBLength = ( (m_dwDataSize-m_dwMinBufSize)>m_dwMinBufSize)?m_dwMinBufSize:(m_dwDataSize-m_dwMinBufSize); 
		m_lpBuf2 = lpData; 
		m_lpBuf2 += m_dwMinBufSize; 
	} else { 
		lpWaveHdr->dwBufferLength = m_dwDataSize; 
	} 
 
	m_lpWHdr = lpWaveHdr; 
	lpWaveHdr->lpData = lpData; 
	m_lpData = lpData; 
 
	SetStatus(STT_OPENING); 
				   /* Open the device, and set callback function */ 
	mmRes = waveOutOpen ( 
		&m_hWaveOut, 
		dwDevId, 
		lpWaveFormatEx, 
		(DWORD)m_pCallBackThread->m_nThreadID, 
		0L, 
		CALLBACK_THREAD | WAVE_MAPPED); 
	 
								  /* Can't Open specified device */ 
	if ( MMSYSERR_NOERROR != mmRes ) { 
		SetStatus(STT_CLOSING); 
		waveOutClose(m_hWaveOut); 
		return FALSE; 
	} 
	 
	free(lpWaveFormatEx); 
	lpWaveFormatEx = NULL; 
 
	if (!m_bContinue) { 
		SetStatus(STT_CLOSING); 
		waveOutClose(m_hWaveOut); 
		goto CLEAN_EXIT; 
	} 
											   /* Prepare header */ 
	mmRes = waveOutPrepareHeader( 
		m_hWaveOut, 
		lpWaveHdr, 
		sizeof (WAVEHDR) ); 
 
										 /* Can't Prepare header */ 
	if ( MMSYSERR_NOERROR != mmRes ) { 
		SetStatus(STT_CLOSING); 
		waveOutClose(m_hWaveOut); 
		goto CLEAN_EXIT; 
	} 
 
	if (!m_bContinue) { 
		SetStatus(STT_RSTTING); 
		waveOutUnprepareHeader(m_hWaveOut,lpWaveHdr,sizeof(WAVEHDR)); 
		SetStatus(STT_CLOSING); 
		waveOutClose(m_hWaveOut); 
		goto CLEAN_EXIT; 
	} 
				   /* Modem's are weak! Loud as much as possible */ 
	waveOutSetVolume(m_hWaveOut,0xFF00FF00); 
 
	SetStatus(STT_PLAYING); 
						  /* Write a block of data to the device */ 
	mmRes = waveOutWrite( 
		m_hWaveOut, 
		lpWaveHdr, 
		sizeof(WAVEHDR) ); 
 
									/* Can't Do the last job :(( */ 
	if ( MMSYSERR_NOERROR != mmRes ) { 
		SetStatus(STT_RSTTING); 
		waveOutUnprepareHeader(m_hWaveOut,lpWaveHdr,sizeof(WAVEHDR)); 
		SetStatus(STT_CLOSING); 
		waveOutClose(m_hWaveOut); 
		goto CLEAN_EXIT; 
	} 
 
	if (m_dwDataSize>m_dwMinBufSize) { 
		m_lpWHdr->lpData = m_lpBuf2; 
		m_lpWHdr->dwBufferLength = m_dwBLength; 
		m_dwCurrtBufIndex++; 
	} 
	/* Playback started, events will be received by callback 
	   function, then it will notify thread to cleanup.			 */ 
	return TRUE; 
 
CLEAN_EXIT:; 
	SetStatus(STT_RSTTING); 
 
	if (lpData) 
		free(lpData); 
	 
	if (lpWaveFormatEx) 
		free(lpWaveFormatEx); 
 
	if (lpWaveHdr) 
		free(lpWaveHdr); 
 
	SetStatus(STT_ASLEEP); 
 
	return FALSE; 
} 
 
 /***************************************************************** 
 * Opens a wave file, buffers wave data 
 * Returns a pointer to buffer 
 *****************************************************************/ 
HPSTR CHSound::Open(LPCTSTR szWaveFile, LPWAVEFORMATEX& lpWaveFmt, DWORD& dwDataSize) 
{ 
	HMMIO hmmio; 
    MMCKINFO        mmckinfoParent; 
    MMCKINFO        mmckinfoSubchunk; 
	DWORD			dwFmtSize = 0; 
 
    hmmio = mmioOpen((unsigned short*)		   /* Open Wave file */ 
		szWaveFile, NULL, MMIO_READ | MMIO_ALLOCBUF); 
 
    if(!hmmio)									   /* Can't open */ 
    { 
        return NULL; 
    } 
 
	if (!m_bContinue) 
	{ 
        mmioClose(hmmio, 0); 
        return NULL; 
	} 
 
								/* Make sure file format is Wave */ 
    mmckinfoParent.fccType = MAKEFOURCC('W', 'A', 'V', 'E'); 
    if (mmioDescend(hmmio, (LPMMCKINFO) &mmckinfoParent,  
		NULL, MMIO_FINDRIFF)) 
    {								/* File format is NOT 'Wave' */ 
        mmioClose(hmmio, 0); 
        return NULL; 
    } 
 
	if (!m_bContinue) 
	{ 
        mmioClose(hmmio, 0); 
        return NULL; 
	} 
     
							/* Make sure there is a 'fmt ' chunk */ 
    mmckinfoSubchunk.ckid = MAKEFOURCC('f', 'm', 't', ' '); 
    if (mmioDescend(hmmio, (LPMMCKINFO)&mmckinfoSubchunk,  
		(LPMMCKINFO)&mmckinfoParent, MMIO_FINDCHUNK)) 
    {										  /* No 'fmt ' chunk */ 
        mmioClose(hmmio, 0); 
        return NULL; 
    } 
	if (!m_bContinue) 
	{ 
        mmioClose(hmmio, 0); 
        return NULL; 
	} 
 
								/* Find size of the 'fmt ' chunk */ 
    dwFmtSize = mmckinfoSubchunk.cksize; 
 
							 /* Allocate memory for format chunk */ 
    lpWaveFmt = (LPWAVEFORMATEX) calloc (1, dwFmtSize); 
//		HeapAlloc(m_hHeap, HEAP_ZERO_MEMORY, dwFmtSize); 
    if (!lpWaveFmt)				 /* Can't alocate memory */ 
    { 
        mmioClose(hmmio, 0); 
        return NULL; 
    } 
 
	if (!m_bContinue) 
	{ 
		free(lpWaveFmt); 
        mmioClose(hmmio, 0); 
        return NULL; 
	} 
											/* Read 'fmt ' chunk */ 
    if (mmioRead(hmmio, (HPSTR) lpWaveFmt, dwFmtSize) !=  
		(LONG) dwFmtSize) 
    {									 /* Corrupt 'fmt ' chunk */ 
		free(lpWaveFmt); 
        mmioClose(hmmio, 0); 
        return NULL; 
    } 
	if (!m_bContinue) 
	{ 
		free(lpWaveFmt); 
        mmioClose(hmmio, 0); 
        return NULL; 
	} 
							/* Ascend out of the 'fmt ' subchunk */ 
    mmioAscend(hmmio, &mmckinfoSubchunk, 0); 
 
									   /* Find the data subchunk */ 
    mmckinfoSubchunk.ckid = MAKEFOURCC('d', 'a', 't', 'a'); 
    if (mmioDescend(hmmio, &mmckinfoSubchunk, &mmckinfoParent,  
		MMIO_FINDCHUNK)) 
    {										/* No data subchunk! */ 
		free(lpWaveFmt); 
        mmioClose(hmmio, 0); 
        return NULL; 
    } 
 
							/* Get the size of the data subchunk */ 
    dwDataSize = mmckinfoSubchunk.cksize; 
    if ( 0L == dwDataSize )	 /* No data in the data subchunk */ 
    { 
		free(lpWaveFmt); 
        mmioClose(hmmio, 0); 
        return NULL; 
    } 
 
	if (!m_bContinue) 
	{ 
		free(lpWaveFmt); 
        mmioClose(hmmio, 0); 
		dwDataSize = 0; 
        return NULL; 
	} 
					/* Allocate memory for data, no need to lock */ 
	HPSTR lpData = (HPSTR) calloc (1, dwDataSize ); 
//		HeapAlloc(m_hHeap, HEAP_ZERO_MEMORY, dwDataSize); 
 
							  /* Read the waveform data subchunk */ 
    if(mmioRead(hmmio, (HPSTR) lpData, dwDataSize)  
		!= (LONG) dwDataSize) 
    { 
		free(lpWaveFmt); 
		free(lpData); 
        mmioClose(hmmio, 0); 
		dwDataSize = 0; 
        return NULL; 
    } 
					 /* We're done with the file, let's close it */ 
    mmioClose(hmmio, 0); 
 
	if (!m_bContinue) 
	{ 
		free(lpWaveFmt); 
		free(lpData); 
		dwDataSize = 0; 
        return NULL; 
	} 
	return lpData; 
} 
 
 /***************************************************************** 
 * Pauses playing 
 *****************************************************************/ 
BOOL CHSound::Pause() 
{ 
	return FALSE; 
} 
 
 /***************************************************************** 
 * Stops playing 
 *****************************************************************/ 
BOOL CHSound::Stop() 
{ 
	return FALSE; 
} 
 
 /***************************************************************** 
 * Closes the device 
 *****************************************************************/ 
BOOL CHSound::Close() 
{ 
	m_bContinue = FALSE; 
	m_bForceClose = TRUE; 
 
	SetStatus(STT_RSTTING); 
	MMRESULT mmRes = waveOutReset(m_hWaveOut); 
 
	int	ptcn = 0; 
						/* If we were playng */ 
	if (mmRes==0) { 
		while(GetStatus()!=STT_ASLEEP) { 
			WaitForSingleObject(m_hStatEvent,100); 
			if (ptcn++>10) break; 
			ResetEvent(m_hStatEvent); 
		} 
//		return TRUE; 
	} 
 
	KillTimer(); 
	CleanPlayList(); 
	SetStatus(STT_ASLEEP); 
 
	return TRUE; 
} 
 
 /***************************************************************** 
 * Indicates wheather this device is playing a sound or not 
 *****************************************************************/ 
BOOL CHSound::IsPlaying() 
{ 
	if (GetStatus()==STT_PLAYING) 
		return TRUE; 
 
	return FALSE; 
} 
 
 /***************************************************************** 
 * Thread to receive and process sound messages 
 ****************************************************************** 
 * 
 * MSDN says it's not possible to do waveOutUnprepareHeader in the 
 * callback function, so I do it here + I do cleanup. 
 * 
 *****************************************************************/ 
UINT CHSound::CallBackThread(LPVOID pParam) 
{ 
	CHSound* pParent = (CHSound*) pParam; 
	MSG msg; 
 
	while (m_bThreadContinue) { 
		if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) { 
									/* One file playback is done */ 
			if ( msg.message==MM_WOM_DONE ) { 
				if (msg.lParam) 
					pParent->PlayNextBuffer(msg);/* Inform parent */ 
			} 
		} else Sleep(5); 
	} 
 
	return 0; 
} 
 
void CHSound::SetStatus(DWORD dwCurStat) 
{ 
	EnterCriticalSection(&m_statusCrtcSec); 
	m_dwStatus ^= m_dwStatus; 
	m_dwStatus |= dwCurStat; 
	LeaveCriticalSection(&m_statusCrtcSec); 
	 
	SetEvent(m_hStatEvent); 
} 
 
DWORD CHSound::GetStatus() 
{ 
	return m_dwStatus; 
} 
 
void CHSound::PlayNextBuffer(MSG msg) 
{ 
	if (!m_bContinue) { 
		goto PNB_EXIT; 
							// Any more buffers to read? 
	} else if (m_dwCurrtBufIndexlpData = m_lpBuf2; 
		m_lpWHdr->dwBufferLength = m_dwBLength; 
	} else { 
//		m_dwCurrtBufIndex = 0; 
//		m_dwBufCnt = 0; 
		goto PNB_EXIT; 
	} 
	return; 
 
PNB_EXIT:; 
	SetStatus(STT_RSTTING); 
	m_lpWHdr->dwBufferLength = m_dwDataSize; 
	m_lpWHdr->lpData = m_lpData; 
 
	waveOutUnprepareHeader( 
		m_hWaveOut, 
		m_lpWHdr, 
		sizeof(WAVEHDR)); 
	SetStatus(STT_CLOSING); 
											/* close wave device */ 
	waveOutClose((HWAVEOUT)msg.wParam); 
										   /* free the wave data */ 
	SetStatus(STT_RSTTING); 
	free (m_lpData); 
	free (m_lpWHdr);						  /* free the header */ 
	SetStatus(STT_ASLEEP); 
 
	if (m_bContinue) 
		PlayNextFile(); 
} 
 
BOOL CHSound::PlayNextFile() 
{ 
	if (!m_bContinue) return FALSE; 
 
	int patience = 0,retryP = 0; 
	CString str = _T(""); 
 
retry:; 
	  patience = 0; 
									/* Give me next file to play */ 
						/* If, for any reason, FileName is empty */ 
	while (str==_T("")) { 
		str = m_playList.GetNext(); 
 
		if (++patience>1000) return FALSE; 
		if (!m_bContinue) return FALSE; 
	} 
									 /* If play list is finished */ 
	if ( str == _T("END_OF_LIST") ) { 
		if (m_bExitAfter) { 
			KillTimer(); 
			PostThreadMessage(m_boss,WM_SOUND_DONE,1,0); 
			 
		} else { 
						   /* Playback ended, so set a timer to  */ 
			SetTimer();				/* restart playing last menu */ 
		}									   /* in an interval */ 
		return TRUE; 
	} else if ( str==_T("DELAY")) { 
		if (!m_bContinue) return FALSE; 
		Sleep(500); 
		str = _T(""); 
		patience = 0; 
		goto retry; 
	} 
 
		  /* We are going to play, so tell the timer to shut up! */ 
	KillTimer(); 
 
											   /* Start playback */ 
	if (!Play( m_dwDevId, str )) { 
		if (!m_bContinue) return FALSE; 
		else { 
			TRACE(L"Cant play  [%s] \n",str); 
								 /* Skip this file if can't play */ 
			if (++retryP>10) { 
				retryP = 0; 
				str=_T(""); 
			} 
//			Sleep(100); 
			goto retry; 
		} 
	} 
	return TRUE; 
} 
 /***************************************************************** 
 * We need a window to start or kill a timer. I'm too lazy writing  
 * my own class! 
 ****************************************************************** 
 * 
 * WARNING: If you need a timer, rhis function MUST be called 
 *			In a class that has a pointer to a dialog, or any 
 *			type of a window, & the window MUST process Timer 
 *			events. Look at DaliaDlg here. 
 * 
 *****************************************************************/ 
void CHSound::SetTimerWindow(CWnd *wndTimer) 
{ 
	m_wndTimer = wndTimer; 
} 
 
 /***************************************************************** 
 * Sets a timer 
 *****************************************************************/ 
void CHSound::SetTimer() 
{ 
	if (m_wndTimer) { 
		if (m_wndTimer->GetSafeHwnd()) 
			m_wndTimer->SendMessage( 
				WM_S_TIMER, SET,0); 
	} 
} 
 
 /***************************************************************** 
 * Kills the timer, though No more notifications 
 *****************************************************************/ 
void CHSound::KillTimer() 
{ 
	if (m_wndTimer) { 
		if (m_wndTimer->GetSafeHwnd()) 
			m_wndTimer->SendMessage( 
				WM_S_TIMER, KILL,0); 
	} 
} 
 
 /***************************************************************** 
 * Start playing 'play list' 
 *****************************************************************/ 
BOOL CHSound::PlayList(DWORD dwDeviceId, BOOL bExitAfter, BOOL bDisableForceExit) 
{ 
	if (m_bForceClose && (!bDisableForceExit) ) return FALSE; 
 
	if (bDisableForceExit) { 
		m_bForceClose  = FALSE; 
	} 
 
	m_dwDevId	 = dwDeviceId; 
	m_bExitAfter = bExitAfter; 
	m_bContinue  = TRUE; 
								   /* Now start playing new list */ 
	return PlayNextFile(); 
} 
 
 /***************************************************************** 
 * Adds a file to our 'play list' 
 *****************************************************************/ 
void CHSound::AddToPlayList(CString szWaveFile) 
{ 
	m_playList.Add(szWaveFile); 
} 
 
 /***************************************************************** 
 * Removes all files from the 'play list' 
 *****************************************************************/ 
void CHSound::CleanPlayList() 
{ 
	m_playList.Reset(); 
} 
 
 /***************************************************************** 
 * Makes sound class ready to get new inputs 
 *****************************************************************/ 
BOOL CHSound::Reset() 
{ 
	EnterCriticalSection(&m_externalResetCrtcSec); 
 
	if (GetStatus()==STT_EXTRNRS ||  
		!m_bContinue) { 
		LeaveCriticalSection(&m_externalResetCrtcSec); 
		return FALSE; 
	} 
 
	LeaveCriticalSection(&m_externalResetCrtcSec); 
 
	m_bContinue = FALSE; 
	SetStatus(STT_EXTRNRS); 
	MMRESULT mmRes = waveOutReset(m_hWaveOut); 
 
	int	ptcn = 0; 
						/* If we were playng */ 
	if (mmRes==0) { 
		while(GetStatus()!=STT_ASLEEP) { 
			WaitForSingleObject(m_hStatEvent,100); 
			if (ptcn++>10) break; 
			ResetEvent(m_hStatEvent); 
		} 
//		return TRUE; 
	} 
 
	KillTimer(); 
	CleanPlayList(); 
	SetStatus(STT_ASLEEP); 
 
	return TRUE; 
} 
 
 /***************************************************************** 
 * Set a thread ID and use it to talk to parent manager 
 *****************************************************************/ 
void CHSound::SetManager(unsigned long mngrThread) 
{ 
	m_boss = mngrThread; 
}