www.pudn.com > direct_mp3.rar > AudioPlayRec.cpp


// AudioPlayRec.cpp : implementation file 
// 
 
#include "stdafx.h" 
#include "hwaudiorec.h" 
#include "AudioPlayRec.h" 
#include  
 
#ifdef _DEBUG 
#define new DEBUG_NEW 
#undef THIS_FILE 
static char THIS_FILE[] = __FILE__; 
#endif 
 
BOOL CALLBACK InputLineProc ( UINT uLineIndex, MIXERLINE* pLineInfo, DWORD dwUserValue ) 
{ 
	CAudioPlayRec *pAudioPlayRec = reinterpret_cast(dwUserValue); 
	ASSERT ( pAudioPlayRec ); 
	CString csShortName = pLineInfo->szShortName; 
	csShortName.MakeLower (); 
 
	if ( csShortName.Find ( "microphone" ) >= 0 || pLineInfo->dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE ) 
	{ 
		pAudioPlayRec->m_uLineIndex_Microphone = pLineInfo->dwSource; 
	} 
	else if ( csShortName.Find ( "line in" ) >= 0 || pLineInfo->dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_LINE ) 
	{ 
		pAudioPlayRec->m_uLineIndex_LineIn = pLineInfo->dwSource; 
	} 
 
	TRACE ( "csShortName = %s, dwComponentType = %d\n", csShortName, pLineInfo->dwComponentType ); 
	return TRUE; 
} 
 
///////////////////////////////////////////////////////////////////////////// 
// CAudioPlayRec 
CAudioPlayRec::CAudioPlayRec () 
: m_eStatus ( ENUM_STATUS_INVALID ) 
, m_hRecord ( NULL ) 
, m_hPlay ( NULL ) 
, m_dwQueuBufferSize ( 1024 ) 
, m_nDataQueueNum ( 0 ) 
, m_bRecording ( FALSE ) 
, m_eRecChannel ( ENUM_REC_CHANNEL_MONO ) 
, m_szAryInData ( NULL ) 
, m_szLeftInData ( NULL ) 
, m_szRightInData ( NULL ) 
, m_pAryHdr ( NULL ) 
, m_bAlwaysDrawTowChannel ( FALSE ) 
, m_uLineIndex_Microphone ( 0 ) 
, m_uLineIndex_LineIn ( 0 ) 
{ 
	memset ( &m_Format, 0, sizeof(WAVEFORMATEX) ); 
	ZeroMemory(&m_MMCKInfoParent,sizeof(m_MMCKInfoParent)); 
	ZeroMemory(&m_MMCKInfoChild,sizeof(m_MMCKInfoChild)); 
	memset ( m_hWaveFile, 0, sizeof(m_hWaveFile) ); 
	ResetMp3EncodeVar (); 
	 
	m_clrBK = RGB ( 0,0,0 ); 
	SetBkColor ( m_clrBK ); 
	 
} 
 
CAudioPlayRec::~CAudioPlayRec() 
{ 
	StopAndFreeAll (); 
	if ( m_brsBkGnd.GetSafeHandle() ) 
	{ 
		m_brsBkGnd.DeleteTempMap(); 
		m_brsBkGnd.DeleteObject(); 
	} 
	 
	if ( m_PenB.GetSafeHandle() ) 
		m_PenB.DeleteObject(); 
	if ( m_PenG.GetSafeHandle() ) 
		m_PenG.DeleteObject(); 
	if ( m_PenPartLine.GetSafeHandle() ) 
		m_PenPartLine.DeleteObject(); 
	if ( m_fntChannelText.GetSafeHandle() ) 
		m_fntChannelText.DeleteObject(); 
	if ( m_fntDeviceNameText.GetSafeHandle() ) 
		m_fntDeviceNameText.DeleteObject(); 
} 
 
 
BEGIN_MESSAGE_MAP(CAudioPlayRec, CWnd) 
//{{AFX_MSG_MAP(CAudioPlayRec) 
ON_WM_ERASEBKGND() 
ON_WM_SETCURSOR() 
	ON_WM_TIMER() 
	//}}AFX_MSG_MAP 
END_MESSAGE_MAP() 
 
///////////////////////////////////////////////////////////////////////////// 
// CAudioPlayRec message handlers 
 
void CAudioPlayRec::SetWaveFormat ( ENUM_REC_CHANNEL eRecChannel, DWORD nSamplesPerSec, WORD wBitsPerSample ) 
{ 
	m_eRecChannel = eRecChannel; 
	memset ( &m_Format, 0, sizeof(WAVEFORMATEX) ); 
	m_Format.cbSize				= 0; 
	m_Format.wFormatTag			= WAVE_FORMAT_PCM; 
	m_Format.wBitsPerSample		= wBitsPerSample; 
	m_Format.nChannels			= ( (eRecChannel==ENUM_REC_CHANNEL_MONO) ? 1 : 2 ); 
	m_Format.nSamplesPerSec		= nSamplesPerSec; 
	m_Format.nAvgBytesPerSec	= m_Format.nSamplesPerSec * (m_Format.wBitsPerSample/8); 
	m_Format.nBlockAlign		= m_Format.nChannels * (m_Format.wBitsPerSample/8); 
} 
 
BOOL CAudioPlayRec::SetRelateParaAfterGetWaveFormat () 
{ 
	FreeBuffer (); 
	m_wInQueu = (WORD) ( m_Format.nChannels + m_Format.wBitsPerSample/8 + m_Format.nSamplesPerSec/11025 ); 
	if ( !AllocateBuffer ( m_dwQueuBufferSize ) ) 
		return FALSE; 
	return TRUE; 
} 
 
BOOL CAudioPlayRec::Create ( HWND hwndParent, LPRECT lpRect/*=NULL*/ ) 
{ 
	LPCTSTR lpszClassName = AfxRegisterWndClass( 
		0, 
		LoadCursor(AfxGetInstanceHandle(), IDC_ARROW), 
		NULL, NULL ); 
	 
	CRect rc ( 0,0,0,0 ); 
	if ( lpRect ) rc = *lpRect; 
	 
	if ( !CreateEx ( 0, lpszClassName, "", 
		WS_CHILD | WS_TABSTOP, 
		rc.left, rc.top, rc.Width(), rc.Height(), 
		hwndParent, NULL, NULL) ) 
	{ 
		AfxMessageBox ( "Create window failed" ); 
		return FALSE; 
	} 
	 
	if ( lpRect ) 
		ShowWindow ( SW_SHOW ); 
	else 
		ShowWindow ( SW_HIDE ); 
 
	GetClientRect ( &m_rcClient ); 
	 
	CClientDC dc(this); 
	m_fntChannelText.CreatePointFont ( 100, "Impact", &dc ); 
	m_fntDeviceNameText.CreateFont ( 14, 0, 0, 0, 0, TRUE, TRUE, FALSE, 0, 0, 
		0, 0, 0, "MS Sans Serif" ); 
	return TRUE; 
} 
 
BOOL CAudioPlayRec::OnEraseBkgnd(CDC* pDC)  
{ 
	DrawBackground ( pDC ); 
	return CWnd::OnEraseBkgnd(pDC); 
} 
 
void CAudioPlayRec::DrawBackground ( CDC *pDC ) 
{ 
	pDC->SetBkMode ( TRANSPARENT ); 
	DrawBackground ( pDC, TRUE ); 
	DrawBackground ( pDC, FALSE ); 
	// 画分隔线 
	CPen *pOldPen = NULL; 
	if ( m_PenPartLine.GetSafeHandle() ) 
		pOldPen = pDC->SelectObject ( &m_PenPartLine ); 
	pDC->MoveTo ( 0, m_rcClient.Height()/2 ); 
	pDC->LineTo ( m_rcClient.right, m_rcClient.Height()/2 ); 
	if ( pOldPen ) pDC->SelectObject ( pOldPen ); 
	// 画声卡名文字 
	CFont *pOldFnt = NULL; 
	CRect rcText = m_rcClient; 
	rcText.DeflateRect ( 4, 4 ); 
	if ( m_fntDeviceNameText.GetSafeHandle() ) 
	{ 
		pOldFnt = pDC->SelectObject ( &m_fntDeviceNameText ); 
	} 
	pDC->SetTextColor ( RGB(128,255,255) ); 
	pDC->DrawText ( m_csDeviceName, &rcText, DT_RIGHT | DT_BOTTOM | DT_SINGLELINE ); 
	if ( pOldFnt ) pDC->SelectObject ( pOldFnt ); 
 
	// 画外框 
	pDC->Draw3dRect ( &m_rcClient, COLOR_FRAME, COLOR_FRAME ); 
} 
 
CRect CAudioPlayRec::GetRectByChannel ( BOOL bLeftChannel ) 
{ 
	CRect rcBK = m_rcClient; 
	if ( bLeftChannel ) 
	{ 
		rcBK.bottom = (m_rcClient.Height() - PARTLINE_HEIGHT) / 2; 
	} 
	else 
	{ 
		rcBK.top = (m_rcClient.Height() + PARTLINE_HEIGHT) / 2; 
	} 
	return rcBK; 
} 
 
void CAudioPlayRec::DrawBackground ( CDC *pDC, BOOL bLeftChannel ) 
{ 
	ASSERT ( pDC ); 
	if ( !m_brsBkGnd.GetSafeHandle() ) return; 
 
	// 画背景 
	CString csCaption; 
	CRect rcBK = GetRectByChannel ( bLeftChannel ); 
	if ( bLeftChannel ) 
	{ 
		csCaption = "Left"; 
	} 
	else 
	{ 
		csCaption = "Right"; 
	} 
	pDC->FillRect ( &rcBK, &m_brsBkGnd ); 
 
	// 画文字 
	CFont *pOldFnt = NULL; 
	if ( m_fntChannelText.GetSafeHandle() ) 
	{ 
		pOldFnt = pDC->SelectObject ( &m_fntChannelText ); 
	} 
	CRect rcText = rcBK; 
	rcText.DeflateRect ( 2, 2 ); 
	pDC->SetTextColor ( RGB(255,255,0) ); 
	pDC->DrawText ( csCaption, &rcText, DT_LEFT | DT_TOP | DT_SINGLELINE ); 
	if ( pOldFnt ) pDC->SelectObject ( pOldFnt ); 
} 
 
void CAudioPlayRec::DrawWave ( DWORD dwChannelBytes ) 
{ 
	CClientDC dc(this); 
 
	BOOL bRecLeft = TRUE, bRecRight = TRUE; 
	if ( !m_bAlwaysDrawTowChannel && m_eStatus == ENUM_STATUS_RECORDING && m_eRecChannel == ENUM_REC_CHANNEL_ALONE ) 
	{ 
		if ( !m_hWaveFile[ENUM_FILE_CHANNEL_LEFT] && !m_pFileMp3[ENUM_FILE_CHANNEL_LEFT] ) 
			bRecLeft = FALSE; 
		if ( !m_hWaveFile[ENUM_FILE_CHANNEL_RIGHT] && !m_pFileMp3[ENUM_FILE_CHANNEL_RIGHT] ) 
			bRecRight = FALSE; 
	} 
 
	// 先将上次画的擦掉 
	DrawBackground ( &dc ); 
	// 画波形图 
	CPen *pOndPen = dc.SelectObject ( &m_PenG ); 
 
	if ( m_Format.wBitsPerSample == 8 ) 
	{ 
		if ( bRecLeft ) DrwaWaveChar ( dc, dwChannelBytes/2, (BYTE*)m_szLeftInData, TRUE ); 
		if ( bRecRight ) DrwaWaveChar ( dc, dwChannelBytes/2, (BYTE*)m_szRightInData, FALSE ); 
	} 
	else 
	{ 
		if ( bRecLeft ) DrwaWaveShort ( dc, dwChannelBytes/2, (SHORT*)m_szLeftInData, TRUE ); 
		if ( bRecRight ) DrwaWaveShort ( dc, dwChannelBytes/2, (SHORT*)m_szRightInData, FALSE ); 
	} 
 
	if ( pOndPen ) 
		dc.SelectObject ( pOndPen ); 
} 
 
void CAudioPlayRec::DrwaWaveShort ( CClientDC &dc, DWORD dwDrawBytes, SHORT *pShortData, BOOL bLeftChannel ) 
{ 
	CRect rcBK = GetRectByChannel ( bLeftChannel ); 
	int nCenterY = rcBK.CenterPoint().y; 
	int y = nCenterY + (int) ( pShortData[0] * rcBK.Height() / 0xffff ); 
	dc.MoveTo ( 0, y ); 
	float fStep = (float)rcBK.Width() / (float)(dwDrawBytes); 
	float fLineX = 0; 
	for ( DWORD i=1; i GetWaveInCount() || m_uDeviceID > GetWaveOutCount()) ) 
	{ 
		AfxMessageBox ( "Error audio device" ); 
		return FALSE; 
	} 
	m_dwQueuBufferSize = dwBufferSize; 
	m_Proc_CallbackNotify = Proc_CallbackNotify; 
	m_wParam = wParam; 
 
	// 创建窗口用来接收消息 
	if ( !Create ( hwndParent, lpRect ) ) return FALSE; 
	if ( hwndParent && lpRect ) 
	{ 
		m_PenG.CreatePen ( PS_SOLID, 0, RGB(0, 255, 0) ); 
		m_PenPartLine.CreatePen ( PS_SOLID, PARTLINE_HEIGHT, COLOR_FRAME ); 
	} 
	 
	m_eStatus = ENUM_STATUS_READY; 
	return TRUE; 
} 
 
void CAudioPlayRec::SetDeviceID ( UINT uDeviceID ) 
{ 
	StopAndFreeAll (); 
	m_uDeviceID = uDeviceID; 
	m_csDeviceName = GetWaveInName ( m_uDeviceID ); 
	// 找录音线路的序号 
	CVolumeInXXX::EnumerateInputLines ( m_uDeviceID, InputLineProc, (DWORD)this ); 
	 
	if ( ::IsWindow ( m_hWnd ) ) 
	{ 
		CRect rcBK = GetRectByChannel ( FALSE ); 
		rcBK.DeflateRect ( 4, 4 ); 
		InvalidateRect ( &rcBK ); 
	} 
} 
 
BOOL CAudioPlayRec::Record ( ENUM_REC_CHANNEL eRecChannel, DWORD nSamplesPerSec, WORD wBitsPerSample ) 
{ 
	// 录音设备正在录音 
	if ( m_eStatus == ENUM_STATUS_RECORDING ) 
	{ 
		TRACE ( "录音设备正在录音 ...\n" ); 
		return TRUE; 
	} 
 
	m_bRecording = TRUE; 
	MMRESULT mmReturn = 0; 
	ResetMp3EncodeVar (); 
	ASSERT ( (wBitsPerSample%8) == 0 ); 
	if ( wBitsPerSample > 16 ) 
		wBitsPerSample = 16; 
	 
	if ( m_eStatus != ENUM_STATUS_READY ) 
	{ 
		AfxMessageBox ( "Class status error" ); 
		return FALSE; 
	} 
	 
	SetWaveFormat ( eRecChannel, nSamplesPerSec, wBitsPerSample ); 
	if ( !SetRelateParaAfterGetWaveFormat () ) 
	{ 
		return FALSE; 
	} 
 
	// open wavein device 
	mmReturn = ::waveInOpen ( &m_hRecord, m_uDeviceID, &m_Format, (DWORD)GetSafeHwnd(), NULL, CALLBACK_WINDOW ); 
	if ( mmReturn ) 
	{ 
		waveErrorMsg ( mmReturn, "waveInOpen()"); 
		goto failed; 
	} 
	else 
	{ 
		// make several input buffers and add them to the input queue 
		for(int i=0; i 0 ); 
	CString csFileName = lpszAudioFileName; 
	csFileName.MakeLower (); 
	// 需要保存到mp3文件中 
	if ( csFileName.Find ( ".mp3" ) == csFileName.GetLength() - 4 ) 
	{ 
		if ( m_Format.wBitsPerSample == 8 ) 
		{ 
			AfxMessageBox ( "Cannot record 8 bits mp3" ); 
			goto failed; 
		} 
		 
		if ( !LoadMp3DllFunc () ) 
		{ 
			goto failed; 
		} 
 
		if ( !PrepareEncodeMp3 ( csFileName, eFileChannel ) ) 
		{ 
			goto failed; 
		} 
	} 
	else 
	{ 
		// 创建一个wave文件 
		if ( !CreateWaveFile ( csFileName, eFileChannel ) ) 
		{ 
			goto failed; 
		} 
	} 
 
	return TRUE; 
 
failed: 
	FreeBuffer (); 
	return FALSE; 
} 
 
void CAudioPlayRec::StopRecordAudioFile ( ENUM_FILE_CHANNEL eFileChannel, CString csStopFileType/*="mp3"*/ ) 
{ 
	csStopFileType.MakeLower (); 
	if ( csStopFileType=="wav" && m_hWaveFile[eFileChannel] ) 
	{ 
		::mmioAscend ( m_hWaveFile[eFileChannel], &m_MMCKInfoChild[eFileChannel], 0 ); 
		::mmioAscend ( m_hWaveFile[eFileChannel], &m_MMCKInfoParent[eFileChannel], 0 ); 
		::mmioClose ( m_hWaveFile[eFileChannel], 0 ); 
		m_hWaveFile[eFileChannel] = NULL; 
	} 
 
	if ( csStopFileType=="mp3" && m_ForMp3_hDLL_LameEnc ) 
	{ 
		EndEncodeMp3 ( eFileChannel ); 
	} 
} 
 
BOOL CAudioPlayRec::AddInputBufferToQueue ( int nIndex ) 
{ 
	ASSERT ( nIndex >= 0 && nIndex < m_wInQueu ); 
	ASSERT ( m_szAryInData[nIndex] ); 
	MMRESULT mmReturn = 0; 
	 
	LPWAVEHDR pHdr = m_pAryHdr[nIndex]; 
	ZeroMemory ( pHdr, sizeof(WAVEHDR) ); 
	pHdr->lpData = (char*)m_szAryInData[nIndex]; 
	pHdr->dwBufferLength = m_dwQueuBufferSize; 
	 
	// prepare it 
	mmReturn = ::waveInPrepareHeader ( m_hRecord, pHdr, sizeof(WAVEHDR) ); 
	if ( mmReturn ) 
	{ 
		waveErrorMsg ( mmReturn, "AddInputBufferToQueue Failed"); 
		return FALSE; 
	} 
	 
	// add the input buffer to the queue 
	mmReturn = ::waveInAddBuffer ( m_hRecord, pHdr, sizeof(WAVEHDR) ); 
	if ( mmReturn ) 
	{ 
		waveErrorMsg ( mmReturn, "waveInAddBuffer() failed"); 
		return FALSE; 
	} 
	 
	m_nDataQueueNum ++; 
	// no error 
	return TRUE; 
	 
} 
 
void CAudioPlayRec::waveErrorMsg ( MMRESULT result, LPCTSTR addstr ) 
{ 
	// say error message 
	char errorbuffer[100]; 
	if ( m_bRecording ) 
		waveInGetErrorText ( result, errorbuffer, 100 ); 
	else 
		waveOutGetErrorText ( result, errorbuffer, 100 ); 
	CString csMsg; 
	csMsg.Format ( "WAVEIN:%x:%s %s", result, errorbuffer, addstr ); 
	AfxMessageBox ( csMsg ); 
} 
 
LRESULT CAudioPlayRec::OnMM_WIM_DATA ( WPARAM wParam, LPARAM lParam ) 
{ 
	MMRESULT mmReturn = 0; 
	 
	LPWAVEHDR pHdr = (LPWAVEHDR) lParam; 
	ASSERT ( pHdr ); 
 
	mmReturn = ::waveInUnprepareHeader ( m_hRecord, pHdr, sizeof(WAVEHDR)); 
	if ( mmReturn ) 
	{ 
		waveErrorMsg ( mmReturn, "waveInUnprepareHeader() failed" ); 
		return -1L; 
	} 
	 
	if( m_eStatus == ENUM_STATUS_RECORDING ) 
	{ 
		// 提取单声道PCM数据 
		int nBytesPickup = PickupMonoData ( m_Format.wBitsPerSample, pHdr->lpData, pHdr->dwBytesRecorded ); 
		// 根据需要保存的通道文件类型选择PCM数据和数据长度 
		char *pRecData[ENUM_FILE_CHANNEL_NUM] = { pHdr->lpData, pHdr->lpData }; 
		int nRecBytes[ENUM_FILE_CHANNEL_NUM] = { pHdr->dwBytesRecorded, pHdr->dwBytesRecorded }; 
		if ( m_eRecChannel == ENUM_REC_CHANNEL_ALONE ) 
		{ 
			pRecData[ENUM_FILE_CHANNEL_LEFT] = m_szLeftInData; 
			nRecBytes[ENUM_FILE_CHANNEL_LEFT] = nBytesPickup; 
			pRecData[ENUM_FILE_CHANNEL_RIGHT] = m_szRightInData; 
			nRecBytes[ENUM_FILE_CHANNEL_RIGHT] = nBytesPickup; 
		} 
 
		// 保存到wave文件中 
		for ( int eFileChannel=ENUM_FILE_CHANNEL_COMMON; eFileChannel dwSamplesBytes; m_ForMp3_dwWaveDataBytes[eFileChannel] -= dwSamplesBytes ) 
					{ 
						WaveBufferMp3Encode ( (char*)m_ForMp3_pWaveBuffer[eFileChannel], (int)dwSamplesBytes, (ENUM_FILE_CHANNEL)eFileChannel ); 
						memmove ( m_ForMp3_pWaveBuffer[eFileChannel], m_ForMp3_pWaveBuffer[eFileChannel]+dwSamplesBytes, m_ForMp3_dwWaveDataBytes[eFileChannel]-dwSamplesBytes ); 
					} 
					 
					char *pUnCopyData = pRecData[eFileChannel] + nCopyBytes; 
					int nUnCopyDataBytes = nRecBytes[eFileChannel] - nCopyBytes; 
					nRemainSize = m_ForMp3_dwWaveBufferSize - m_ForMp3_dwWaveDataBytes[eFileChannel]; 
					nCopyBytes = (nRemainSize < nUnCopyDataBytes) ? nRemainSize : nUnCopyDataBytes; 
					memcpy ( m_ForMp3_pWaveBuffer[eFileChannel]+m_ForMp3_dwWaveDataBytes[eFileChannel], pUnCopyData, nCopyBytes ); 
				} 
			} 
		} 
		 
		// reuse the buffer: 
		// prepare it again 
		mmReturn = ::waveInPrepareHeader ( m_hRecord, pHdr, sizeof(WAVEHDR) ); 
		if ( mmReturn ) 
		{ 
			waveErrorMsg ( mmReturn, "waveInPrepareHeader() failed" ); 
		} 
		else // no error 
		{ 
			// add the input buffer to the queue again 
			mmReturn = ::waveInAddBuffer ( m_hRecord, pHdr, sizeof(WAVEHDR) ); 
			if ( mmReturn ) 
			{ 
				waveErrorMsg ( mmReturn, "waveInAddBuffer() failed"); 
			} 
			else 
			{ 
				DrawWave( (DWORD)nBytesPickup ); 
				return 0L;  // no error 
			} 
		} 
	} 
	// 停止录音 
	else 
	{ 
		if ( m_nDataQueueNum == 1 ) 
		{ 
			StopRec (); 
		} 
		else 
		{ 
			m_nDataQueueNum --; 
		} 
	} 
 
	return 0L; 
} 
 
LRESULT CAudioPlayRec::OnMM_WOM_DONE(WPARAM wParam, LPARAM lParam) 
{ 
	MMRESULT mmReturn = 0; 
	 
	LPWAVEHDR pHdr = (LPWAVEHDR) lParam; 
	mmReturn = ::waveOutUnprepareHeader ( m_hPlay, pHdr, sizeof(WAVEHDR) ); 
	if ( mmReturn ) 
	{ 
		waveErrorMsg ( mmReturn, "waveOutUnprepareHeader() failed" ); 
		return -1L; 
	} 
	 
	m_nDataQueueNum--; 
	 
	if ( m_eStatus == ENUM_STATUS_PLAYING ) 
	{		 
		 
		int nSize = m_dwQueuBufferSize; 
		if ( ReadSoundDataFromFile ( pHdr->lpData, nSize ) ) 
		{ 
			AddOutputBufferToQueue ( (int)pHdr->dwUser, nSize );			 
			return 0L; 
		} 
		else 
		{ 
			Stop(); 
		} 
	} 
	 
	// we are closing the waveOut handle,  
	// all data must be deleted 
	// this buffer was allocated in Start()	 
	if ( m_nDataQueueNum == 0 && m_eStatus != ENUM_STATUS_PLAYING ) 
	{ 
		StopPlay (); 
	} 
	 
	return 0L; 
} 
 
// 
// 创建一个wave文件 
// 
BOOL CAudioPlayRec::CreateWaveFile ( LPCTSTR lpszWaveFileName, ENUM_FILE_CHANNEL eFileChannel ) 
{ 
	ASSERT ( eFileChannel >= ENUM_FILE_CHANNEL_COMMON && eFileChannel < ENUM_FILE_CHANNEL_NUM ); 
	if ( m_hWaveFile[eFileChannel] ) 
		return TRUE; 
 
	ASSERT ( lpszWaveFileName && strlen(lpszWaveFileName) > 0 ); 
	::mmioOpen ( (LPTSTR)lpszWaveFileName, NULL, MMIO_DELETE );  
	// check if file is already open 
	if ( m_hWaveFile[eFileChannel] ) 
		return TRUE; 
	 
	WAVEFORMATEX wfx = m_Format; 
	if ( m_eRecChannel==ENUM_REC_CHANNEL_ALONE ) 
		wfx.nChannels = 1; 
	// open file 
	m_hWaveFile[eFileChannel] = ::mmioOpen ( (LPTSTR)lpszWaveFileName, NULL, MMIO_CREATE|MMIO_WRITE|MMIO_EXCLUSIVE|MMIO_ALLOCBUF ); 
	if ( m_hWaveFile[eFileChannel] == NULL )  
	{ 
		AfxMessageBox ( "Open wave file failed" ); 
		return FALSE; 
	} 
	 
	ZeroMemory ( &m_MMCKInfoParent[eFileChannel], sizeof(MMCKINFO) ); 
	m_MMCKInfoParent[eFileChannel].fccType = mmioFOURCC('W','A','V','E'); 
	 
	MMRESULT mmResult = ::mmioCreateChunk( m_hWaveFile[eFileChannel],&m_MMCKInfoParent[eFileChannel], MMIO_CREATERIFF); 
	 
	ZeroMemory ( &m_MMCKInfoChild[eFileChannel], sizeof(MMCKINFO) ); 
	m_MMCKInfoChild[eFileChannel].ckid = mmioFOURCC('f','m','t',' '); 
	m_MMCKInfoChild[eFileChannel].cksize = sizeof(WAVEFORMATEX) + wfx.cbSize; 
	mmResult = ::mmioCreateChunk(m_hWaveFile[eFileChannel], &m_MMCKInfoChild[eFileChannel], 0); 
	mmResult = ::mmioWrite(m_hWaveFile[eFileChannel], (char*)&wfx, sizeof(WAVEFORMATEX) + wfx.cbSize);  
	mmResult = ::mmioAscend(m_hWaveFile[eFileChannel], &m_MMCKInfoChild[eFileChannel], 0); 
	m_MMCKInfoChild[eFileChannel].ckid = mmioFOURCC('d', 'a', 't', 'a'); 
	mmResult = ::mmioCreateChunk ( m_hWaveFile[eFileChannel], &m_MMCKInfoChild[eFileChannel], 0 ); 
	 
	return TRUE; 
	 
} 
 
void CAudioPlayRec::Stop() 
{ 
	if ( m_eStatus != ENUM_STATUS_PLAYING && m_eStatus != ENUM_STATUS_RECORDING ) 
		return; 
 
	MMRESULT mmReturn = 0; 
	if ( m_eStatus == ENUM_STATUS_PLAYING ) 
	{ 
		if ( ::waveOutReset(m_hPlay) ) waveErrorMsg ( mmReturn, "waveOutReset() failed"); 
		SetTimer ( TIMER_EVENT_STOPPLAY, 1000, NULL ); 
	} 
	else if ( m_eStatus == ENUM_STATUS_RECORDING ) 
	{ 
		SetTimer ( TIMER_EVENT_STOPREC, 1000, NULL ); 
	} 
	 
	Invalidate ( TRUE ); 
	m_eStatus = ENUM_STATUS_STOPING; 
} 
 
// 
// 打开一个wave文件 
// 
BOOL CAudioPlayRec::OpenWaveFile(LPCTSTR lpszWaveFileName) 
{ 
	ASSERT ( lpszWaveFileName && strlen(lpszWaveFileName) > 0 ); 
	// check if file is already open 
	if ( m_hWaveFile[ENUM_FILE_CHANNEL_COMMON] ) return FALSE;  
	 
	m_hWaveFile[ENUM_FILE_CHANNEL_COMMON] = ::mmioOpen ( (LPTSTR)lpszWaveFileName,NULL,MMIO_READ ); 
	if ( m_hWaveFile[ENUM_FILE_CHANNEL_COMMON] == NULL )  
	{ 
		AfxMessageBox ( "Open wave file failed" ); 
		return FALSE; 
	} 
	 
	m_MMCKInfoParent[ENUM_FILE_CHANNEL_COMMON].fccType = mmioFOURCC('W','A','V','E'); 
	MMRESULT mmResult = ::mmioDescend(m_hWaveFile[ENUM_FILE_CHANNEL_COMMON], &m_MMCKInfoParent[ENUM_FILE_CHANNEL_COMMON],NULL,MMIO_FINDRIFF); 
	if(mmResult) 
	{ 
		AfxMessageBox("Error descending into file"); 
		::mmioClose(m_hWaveFile[ENUM_FILE_CHANNEL_COMMON],0); 
		m_hWaveFile[ENUM_FILE_CHANNEL_COMMON] = NULL; 
		return FALSE; 
	} 
	m_MMCKInfoChild[ENUM_FILE_CHANNEL_COMMON].ckid = mmioFOURCC('f','m','t',' '); 
	mmResult = mmioDescend(m_hWaveFile[ENUM_FILE_CHANNEL_COMMON],&m_MMCKInfoChild[ENUM_FILE_CHANNEL_COMMON],&m_MMCKInfoParent[ENUM_FILE_CHANNEL_COMMON],MMIO_FINDCHUNK); 
	if(mmResult) 
	{ 
		AfxMessageBox("Error descending in wave file"); 
		mmioClose(m_hWaveFile[ENUM_FILE_CHANNEL_COMMON],0); 
		m_hWaveFile[ENUM_FILE_CHANNEL_COMMON] = NULL; 
		return FALSE; 
	} 
	 
	DWORD bytesRead = mmioRead ( m_hWaveFile[ENUM_FILE_CHANNEL_COMMON],(LPSTR)&m_Format, m_MMCKInfoChild[ENUM_FILE_CHANNEL_COMMON].cksize ); 
	if ( bytesRead < 0 ) 
	{ 
		AfxMessageBox ( "Error reading PCM wave format record" ); 
		mmioClose ( m_hWaveFile[ENUM_FILE_CHANNEL_COMMON], 0 ); 
		return FALSE; 
	} 
	if ( !SetRelateParaAfterGetWaveFormat () ) 
		return FALSE; 
	 
	// open output sound file 
	mmResult = mmioAscend ( m_hWaveFile[ENUM_FILE_CHANNEL_COMMON], &m_MMCKInfoChild[ENUM_FILE_CHANNEL_COMMON], 0 ); 
	if ( mmResult ) 
	{ 
		AfxMessageBox ( "Error ascending in File" ); 
		mmioClose ( m_hWaveFile[ENUM_FILE_CHANNEL_COMMON], 0 ); 
		m_hWaveFile[ENUM_FILE_CHANNEL_COMMON] = NULL; 
		return FALSE; 
	} 
	m_MMCKInfoChild[ENUM_FILE_CHANNEL_COMMON].ckid = mmioFOURCC('d','a','t','a'); 
	mmResult = mmioDescend ( m_hWaveFile[ENUM_FILE_CHANNEL_COMMON], &m_MMCKInfoChild[ENUM_FILE_CHANNEL_COMMON], &m_MMCKInfoParent[ENUM_FILE_CHANNEL_COMMON], MMIO_FINDCHUNK ); 
	if ( mmResult ) 
	{ 
		AfxMessageBox("error reading data chunk"); 
		mmioClose(m_hWaveFile[ENUM_FILE_CHANNEL_COMMON],0); 
		m_hWaveFile[ENUM_FILE_CHANNEL_COMMON] = NULL; 
		return FALSE; 
	} 
	 
	return TRUE; 
	 
} 
 
BOOL CAudioPlayRec::Play(LPCTSTR lpszWaveFileName) 
{ 
	m_bRecording = FALSE; 
	if ( m_eStatus != ENUM_STATUS_READY ) 
	{ 
		AfxMessageBox ( "Class status error" ); 
		return FALSE; 
	} 
	 
	if ( !OpenWaveFile ( lpszWaveFileName ) ) 
		return FALSE; 
	 
	MMRESULT mmReturn = 0; 
	 
	// open wavein device 
	mmReturn = ::waveOutOpen ( &m_hPlay, m_uDeviceID, &m_Format, (DWORD)GetSafeHwnd(), NULL, CALLBACK_WINDOW ); 
	if ( mmReturn ) 
	{ 
		waveErrorMsg ( mmReturn, "waveOutOpen() failed"); 
		return FALSE; 
	} 
	else 
	{ 
		// make several input buffers and add them to the input queue 
		for(int i=0; i 0 ); 
	ASSERT ( m_hWaveFile[ENUM_FILE_CHANNEL_COMMON] ); 
	return ( ( size = ::mmioRead ( m_hWaveFile[ENUM_FILE_CHANNEL_COMMON], (char*)data, size) ) > 0 ); 
} 
 
BOOL CAudioPlayRec::AddOutputBufferToQueue ( int nIndex, int nSize ) 
{ 
	ASSERT ( nIndex >= 0 && nIndex < m_wInQueu ); 
	ASSERT ( m_szAryInData[nIndex] ); 
	 
	MMRESULT mmReturn = 0; 
	 
	// create the header 
	LPWAVEHDR pHdr = m_pAryHdr[nIndex]; 
	memset ( pHdr, 0, sizeof(WAVEHDR) ); 
	 
	// new a buffer 
	pHdr->lpData = (char*)m_szAryInData[nIndex]; 
	pHdr->dwBufferLength = m_dwQueuBufferSize; 
	pHdr->dwBytesRecorded = nSize; 
	pHdr->dwFlags = 0; 
	pHdr->dwUser = nIndex; 
	 
	// prepare it 
	mmReturn = ::waveOutPrepareHeader ( m_hPlay, pHdr, sizeof(WAVEHDR) ); 
	if ( mmReturn ) 
	{ 
		waveErrorMsg ( mmReturn, "waveOutPrepareHeader() failed"); 
		return FALSE; 
	} 
	// write the buffer to output queue 
	mmReturn = ::waveOutWrite ( m_hPlay, pHdr, sizeof(WAVEHDR) ); 
	if ( mmReturn ) waveErrorMsg ( mmReturn, "waveOutWrite() failed"); 
	// increment the number of waiting buffers 
	m_nDataQueueNum++; 
 
	int nBytesPickup = PickupMonoData ( m_Format.wBitsPerSample, pHdr->lpData, pHdr->dwBytesRecorded ); 
	DrawWave( (DWORD)nBytesPickup ); 
	return TRUE; 
} 
 
void CAudioPlayRec::StopRec () 
{ 
	if ( !m_hRecord ) return; 
	if ( m_eStatus != ENUM_STATUS_RECORDING && m_eStatus != ENUM_STATUS_STOPING ) 
		return; 
 
	MMRESULT mmReturn = 0; 
	mmReturn = ::waveInReset ( m_hRecord ); 
	if ( mmReturn ) waveErrorMsg ( mmReturn, "waveInReset() failed" );	 
	::Sleep ( 10 ); 
	for ( int eFileChannel=ENUM_FILE_CHANNEL_COMMON; eFileChannel 0 ); 
	m_szAryInData = new char*[m_wInQueu]; 
	m_szLeftInData = new char[m_dwQueuBufferSize]; 
	m_szRightInData = new char[m_dwQueuBufferSize]; 
	m_pAryHdr = new WAVEHDR*[m_wInQueu]; 
	if ( !m_szAryInData || !m_szLeftInData || !m_szRightInData || !m_pAryHdr ) 
	{ 
		::AfxThrowMemoryException (); 
		return FALSE; 
	} 
	memset ( m_szAryInData, 0, sizeof(char*)*m_wInQueu ); 
	memset ( m_szLeftInData, 0, sizeof(char)*m_dwQueuBufferSize ); 
	memset ( m_szRightInData, 0, sizeof(char)*m_dwQueuBufferSize ); 
	memset ( m_pAryHdr, 0, sizeof(WAVEHDR*)*m_wInQueu ); 
 
	for ( int i=0; i= (DWORD)nOrgSize ); 
		memcpy ( m_szLeftInData, szOrgData, nOrgSize ); 
		memcpy ( m_szRightInData, szOrgData, nOrgSize ); 
		return nOrgSize; 
	} 
 
	ASSERT ( szOrgData!=NULL && AfxIsValidAddress(szOrgData,nOrgSize,TRUE)); 
	ASSERT ( m_szLeftInData!=NULL && AfxIsValidAddress(szOrgData,nOrgSize,TRUE)); 
	ASSERT ( m_szRightInData!=NULL && AfxIsValidAddress(szOrgData,nOrgSize,TRUE)); 
	DWORD dwBytesPerSample = wBitsPerSample/8; 
	int nDestBytes_Left = 0, nDestBytes_Right = 0; 
	for ( int i=0; i= ENUM_FILE_CHANNEL_COMMON && eFileChannel < ENUM_FILE_CHANNEL_NUM ); 
	if ( !m_ForMp3_hDLL_LameEnc ) return FALSE; 
	if ( m_pFileMp3[eFileChannel] )		// 已经开始纪录mp3文件了 
		return TRUE; 
 
	ASSERT ( lpszMp3FileName && strlen(lpszMp3FileName) > 0 ); 
	m_csMp3FileName[eFileChannel] = lpszMp3FileName; 
	BE_ERR err = 0; 
 
	BE_CONFIG beConfig = {0}; 
	memset ( &beConfig, 0, sizeof(beConfig) );					// clear all fields 
	 
	// use the LAME config structure 
	beConfig.dwConfig = BE_CONFIG_LAME; 
	 
	WAVEFORMATEX wfx = m_Format; 
	if ( m_eRecChannel == ENUM_REC_CHANNEL_ALONE ) 
		wfx.nChannels = 1; 
	// this are the default settings for testcase.wav 
	int nMode = (wfx.nChannels==1)?BE_MP3_MODE_MONO:BE_MP3_MODE_JSTEREO; 
	beConfig.format.LHV1.dwStructVersion	= 1; 
	beConfig.format.LHV1.dwStructSize		= sizeof(beConfig);		 
	beConfig.format.LHV1.dwSampleRate		= wfx.nSamplesPerSec;		// INPUT FREQUENCY 
	beConfig.format.LHV1.dwReSampleRate		= 0;						// DON"T RESAMPLE 
	beConfig.format.LHV1.nMode				= nMode;					// OUTPUT IN STREO 
	beConfig.format.LHV1.dwBitrate			= 128;						// MINIMUM BIT RATE 
	beConfig.format.LHV1.nPreset			= LQP_R3MIX;				// QUALITY PRESET SETTING 
	beConfig.format.LHV1.dwMpegVersion		= MPEG1;					// MPEG VERSION (I or II) 
	beConfig.format.LHV1.dwPsyModel			= 0;						// USE DEFAULT PSYCHOACOUSTIC MODEL  
	beConfig.format.LHV1.dwEmphasis			= 0;						// NO EMPHASIS TURNED ON 
	beConfig.format.LHV1.bOriginal			= TRUE;						// SET ORIGINAL FLAG 
	beConfig.format.LHV1.bWriteVBRHeader	= TRUE;						// Write INFO tag 
	 
	//	beConfig.format.LHV1.dwMaxBitrate		= 320;					// MAXIMUM BIT RATE 
	//	beConfig.format.LHV1.bCRC				= TRUE;					// INSERT CRC 
	//	beConfig.format.LHV1.bCopyright			= TRUE;					// SET COPYRIGHT FLAG	 
	//	beConfig.format.LHV1.bPrivate			= TRUE;					// SET PRIVATE FLAG 
	//	beConfig.format.LHV1.bWriteVBRHeader	= TRUE;					// YES, WRITE THE XING VBR HEADER 
	//	beConfig.format.LHV1.bEnableVBR			= TRUE;					// USE VBR 
	//	beConfig.format.LHV1.nVBRQuality		= 5;					// SET VBR QUALITY 
	beConfig.format.LHV1.bNoRes				= TRUE;					// No Bit resorvoir 
	 
	// Preset Test 
	//	beConfig.format.LHV1.nPreset			= LQP_PHONE; 
	 
	// Init the MP3 Stream 
	DWORD dwMP3Buffer = 0; 
	err = m_ForMp3_Proc_hInitStream ( &beConfig, &m_dwSamplesEncodeMp3Block, &dwMP3Buffer, &m_ForMp3_hStream[eFileChannel] ); 
	m_ForMp3_dwWaveBufferSize = m_wInQueu*m_dwQueuBufferSize; 
	 
	// Check result 
	if ( err != BE_ERR_SUCCESSFUL ) 
	{ 
		AfxMessageBox ( "Error opening encoding stream (%lu)", err); 
		return FALSE; 
	} 
 
	 
	// Open MP3 file 
	::DeleteFile ( lpszMp3FileName ); 
	m_pFileMp3[eFileChannel] = fopen ( lpszMp3FileName, "wb+" ); 
	if ( m_pFileMp3[eFileChannel] == NULL ) 
	{ 
		CString csMsg; 
		csMsg.Format ( "Error creating file %s", lpszMp3FileName ); 
		AfxMessageBox ( csMsg ); 
		return FALSE; 
	}	 
	 
	// Allocate buffer 
	if ( m_ForMp3_pMP3Buffer[eFileChannel] ) delete[] m_ForMp3_pMP3Buffer[eFileChannel]; 
	m_ForMp3_pMP3Buffer[eFileChannel] = new BYTE[dwMP3Buffer]; 
	if ( m_ForMp3_pWaveBuffer[eFileChannel] ) delete[] m_ForMp3_pWaveBuffer[eFileChannel]; 
	m_ForMp3_pWaveBuffer[eFileChannel] = new BYTE[m_ForMp3_dwWaveBufferSize]; 
	 
	// Check if Buffer are allocated properly 
	if( !m_ForMp3_pMP3Buffer[eFileChannel] || !m_ForMp3_pWaveBuffer[eFileChannel] ) 
	{ 
		AfxMessageBox ( "Out of memory" ); 
		return FALSE; 
	} 
 
	return TRUE; 
} 
 
BOOL CAudioPlayRec::WaveBufferMp3Encode ( char *szWavData, int nWavSize, ENUM_FILE_CHANNEL eFileChannel) 
{ 
	ASSERT ( eFileChannel >= ENUM_FILE_CHANNEL_COMMON && eFileChannel < ENUM_FILE_CHANNEL_NUM ); 
 
	DWORD		dwWrite			=0; 
	BE_ERR		err				=0; 
	 
	// Encode samples 
	err = m_ForMp3_Proc_hEncodeChunk ( m_ForMp3_hStream[eFileChannel], nWavSize/sizeof(SHORT), 
		(SHORT*)szWavData, m_ForMp3_pMP3Buffer[eFileChannel], &dwWrite ); 
	 
	// Check result 
	if ( err != BE_ERR_SUCCESSFUL ) 
	{ 
		m_ForMp3_Proc_hCloseStream ( m_ForMp3_hStream[eFileChannel] ); 
		CString csMsg; 
		csMsg.Format ( "m_ForMp3_Proc_hEncodeChunk() failed (%lu)", err ); 
		AfxMessageBox ( csMsg ); 
		return FALSE; 
	} 
	 
	// write dwWrite bytes that are returned in tehe m_ForMp3_pMP3Buffer to disk 
	if(fwrite(m_ForMp3_pMP3Buffer[eFileChannel],1,dwWrite,m_pFileMp3[eFileChannel]) != dwWrite) 
	{ 
		AfxMessageBox ( "Output file write error" ); 
		return FALSE; 
	} 
 
	return TRUE; 
	 
} 
 
// 
// 结束mp3编码 
// 
void CAudioPlayRec::EndEncodeMp3 ( ENUM_FILE_CHANNEL eFileChannel ) 
{ 
	if ( !m_pFileMp3[eFileChannel] ) return; 
 
	ASSERT ( eFileChannel >= ENUM_FILE_CHANNEL_COMMON && eFileChannel < ENUM_FILE_CHANNEL_NUM ); 
	DWORD		dwWrite			=0; 
	BE_ERR		err				=0; 
 
	if ( !m_ForMp3_Proc_hDeinitStream ) return; 
 
	// Deinit the stream 
	err = m_ForMp3_Proc_hDeinitStream ( m_ForMp3_hStream[eFileChannel], m_ForMp3_pMP3Buffer[eFileChannel], &dwWrite ); 
	 
	// Check result 
	if ( err != BE_ERR_SUCCESSFUL ) 
	{ 
		 
		m_ForMp3_Proc_hCloseStream ( m_ForMp3_hStream[eFileChannel] ); 
		CString csMsg; 
		csMsg.Format ( "beExitStream failed (%lu)", err ); 
		AfxMessageBox ( csMsg ); 
		return; 
	} 
	 
	// Are there any bytes returned from the DeInit call? 
	// If so, write them to disk 
	if ( dwWrite ) 
	{ 
		if( fwrite ( m_ForMp3_pMP3Buffer[eFileChannel], 1, dwWrite, m_pFileMp3[eFileChannel] ) != dwWrite ) 
		{ 
			AfxMessageBox ( "Output file write error" ); 
			return; 
		} 
	} 
	 
	// close the MP3 Stream 
	ASSERT ( m_ForMp3_Proc_hCloseStream ); 
	m_ForMp3_Proc_hCloseStream( m_ForMp3_hStream[eFileChannel] ); 
	 
	// Delete Buffer 
	if ( m_ForMp3_pMP3Buffer[eFileChannel] ) delete [] m_ForMp3_pMP3Buffer[eFileChannel]; 
	m_ForMp3_pMP3Buffer[eFileChannel] = NULL; 
	if ( m_ForMp3_pWaveBuffer[eFileChannel] ) delete [] m_ForMp3_pWaveBuffer[eFileChannel]; 
	m_ForMp3_pWaveBuffer[eFileChannel] = NULL; 
	 
	// Close output file 
	fclose( m_pFileMp3[eFileChannel] ); 
	m_pFileMp3[eFileChannel] = NULL; 
	 
	if ( m_ForMp3_Proc_hWriteInfoTag ) 
	{ 
		// Write the INFO Tag 
		m_ForMp3_Proc_hWriteInfoTag ( m_ForMp3_hStream[eFileChannel], m_csMp3FileName[eFileChannel] ); 
	} 
	else 
	{ 
		m_ForMp3_Proc_hWriteVBRHeader( m_csMp3FileName[eFileChannel] ); 
	} 
} 
 
// 
// 释放mp3编码模块所申请的资源 
// 
void CAudioPlayRec::FreeMp3Encode () 
{ 
	if ( m_ForMp3_hDLL_LameEnc ) 
		::FreeLibrary ( m_ForMp3_hDLL_LameEnc ); 
	m_ForMp3_hDLL_LameEnc = NULL; 
	ResetMp3EncodeVar (); 
} 
 
void CAudioPlayRec::ResetMp3EncodeVar() 
{ 
	for ( int eFileChannel=ENUM_FILE_CHANNEL_COMMON; eFileChannelmessage >= MM_WOM_OPEN && pMsg->message <= MM_MOM_DONE ) 
	{ 
		switch ( pMsg->message ) 
		{ 
		case MM_WIM_DATA: 
			OnMM_WIM_DATA ( pMsg->wParam, pMsg->lParam ); 
			break; 
		case MM_WIM_CLOSE: 
			m_eStatus = ENUM_STATUS_READY; 
			m_nDataQueueNum = 0; 
			m_bRecording = FALSE; 
			break; 
		case MM_WOM_DONE: 
			OnMM_WOM_DONE ( pMsg->wParam, pMsg->lParam ); 
			break; 
		case MM_WOM_CLOSE: 
			m_eStatus = ENUM_STATUS_READY; 
			m_bRecording = FALSE; 
			break; 
		case MM_WIM_OPEN: 
			break; 
		case MM_WOM_OPEN: 
			break; 
		} 
		 
		if ( m_Proc_CallbackNotify ) 
		{ 
			m_Proc_CallbackNotify ( pMsg->message, m_wParam ); 
		} 
	} 
 
	return CWnd::PreTranslateMessage(pMsg); 
} 
 
void CAudioPlayRec::OnTimer(UINT nIDEvent)  
{ 
	switch ( nIDEvent ) 
	{ 
	case TIMER_EVENT_STOPREC: 
		KillTimer ( nIDEvent ); 
		StopRec (); 
		break; 
	case TIMER_EVENT_STOPPLAY: 
		KillTimer ( nIDEvent ); 
		StopPlay (); 
		break; 
	} 
	 
	CWnd::OnTimer(nIDEvent); 
} 
 
void CAudioPlayRec::StopAndFreeAll () 
{ 
	if ( m_hRecord ) 
	{ 
		StopRec (); 
	} 
	 
	if ( m_hPlay ) 
	{ 
		StopPlay (); 
	} 
 
	FreeBuffer (); 
} 
 
// 
// 获取系统中有多少个可以录音的声卡 
// 
UINT CAudioPlayRec::GetWaveInCount() 
{ 
	return waveInGetNumDevs(); 
} 
 
// 
// 获取某录音声卡的名字 
// 
CString CAudioPlayRec::GetWaveInName(UINT uDeviceID) 
{ 
	ASSERT ( uDeviceID < GetWaveInCount() ); 
	WAVEINCAPS tagCaps; 
	switch ( waveInGetDevCaps(uDeviceID, &tagCaps, sizeof(tagCaps)) ) 
	{ 
	case MMSYSERR_NOERROR: 
		return tagCaps.szPname; 
		break; 
	default: 
		return ""; 
	} 
	 
} 
 
// 
// 获取系统中有多少个可以放音的声卡 
// 
UINT CAudioPlayRec::GetWaveOutCount() 
{ 
	return waveOutGetNumDevs(); 
} 
 
// 
// 获取某放音声卡的名字 
// 
CString CAudioPlayRec::GetWaveOutName(UINT uDeviceID) 
{ 
	ASSERT ( uDeviceID < GetWaveOutCount() ); 
	WAVEOUTCAPS tagCaps; 
	switch (waveOutGetDevCaps(uDeviceID, &tagCaps, sizeof(tagCaps))) 
	{ 
	case MMSYSERR_NOERROR: 
		return tagCaps.szPname; 
		break; 
	default: 
		return ""; 
	} 
} 
 
// 
// 设置录音设备的来源和音量 
// 
void CAudioPlayRec::SetWaveInDevice ( BOOL bLineIn, DWORD dwVolume/*=ULONG_MAX*/ ) 
{ 
	CVolumeInXXX VolumeInXXX ( bLineIn?m_uLineIndex_LineIn:m_uLineIndex_Microphone, m_uDeviceID ); 
	VolumeInXXX.SetCurrentVolume ( ( dwVolume == ULONG_MAX ) ? VolumeInXXX.GetMaximalVolume() : dwVolume ); 
	VolumeInXXX.Enable (); 
} 
 
// 
// 获取录音设备音量 
// 
DWORD CAudioPlayRec::GetWaveInVolume ( BOOL bLineIn, DWORD *pdwMaxVolume/*=NULL*/, DWORD *pdwMinVolume/*=NULL*/ ) 
{ 
	CVolumeInXXX VolumeInXXX ( bLineIn?m_uLineIndex_LineIn:m_uLineIndex_Microphone, m_uDeviceID ); 
	if ( pdwMaxVolume ) *pdwMaxVolume = VolumeInXXX.GetMaximalVolume (); 
	if ( pdwMinVolume ) *pdwMinVolume = VolumeInXXX.GetMinimalVolume (); 
 
	return VolumeInXXX.GetCurrentVolume (); 
} 
 
// 
// 获取当前系统中 WAVE_MAPPER 在使用的声卡序号 
// 
int CAudioPlayRec::GetAvailableDeviceIndex() 
{ 
	for ( DWORD i=0; i