網(wǎng)上有很多方法從麥克風(fēng)讀取PCM數(shù)據(jù),不想一一舉例。只是在這里發(fā)布一個(gè)我自己寫的directsound的麥克風(fēng)PCM數(shù)據(jù)采集類,通過它,可以很方便的利用directsound技術(shù)把麥克風(fēng)的數(shù)據(jù)采集到,而且本身,開發(fā)者不必太在意自己會不會directsound編程,可以很方便的讓開發(fā)者的主要精力集中于程序本身,而不是細(xì)節(jié)。 這個(gè)是頭文件: #pragma once #ifndef _CAPTURE_SOUND_H_ #define _CAPTURE_SOUND_H_ #include <mmsystem.h> #include <dsound.h> #define NUM_REC_NOTIFICATIONS? 16 class CAdoFrameHandler { public: ?virtual void AdoFrameData(BYTE* pBuffer, long lBufferSize) = 0 ; }; class CCaptureAudio { public: ?BOOL??????? m_bRecording ;? //recording now ? also used by event recv thread protected: ?LPDIRECTSOUNDCAPTURE8??? m_pCapDev ;?? //capture device ptr ?LPDIRECTSOUNDCAPTUREBUFFER m_pCapBuf ;?? //capture loop buffer ptr ?LPDIRECTSOUNDNOTIFY8??? m_pNotify ;?? //capture auto-notify event callback handler ptr GUID??????? m_guidCapDevId ;? //capture device id ?WAVEFORMATEX????? m_wfxInput;?? //input wave format description struct DSBPOSITIONNOTIFY???? m_aPosNotify[NUM_REC_NOTIFICATIONS + 1]; //notify flag array ?HANDLE??????? m_hNotifyEvent;?? //notify event ?BOOL??????? m_abInputFmtSupported[20]; ?DWORD??????? m_dwCapBufSize;? //capture loop buffer size ?DWORD??????? m_dwNextCapOffset;//offset in loop buffer ?DWORD??????? m_dwNotifySize;? //notify pos when loop buffer need to emit the event CAdoFrameHandler*???? m_frame_handler ; // outer frame data dealer ptr public: // callback func to add enum devices string name ?static BOOL CALLBACK enum_dev_proc(LPGUID lpGUID, LPCTSTR lpszDesc, ??????????? LPCTSTR lpszDrvName, LPVOID lpContext ) ; static UINT notify_capture_thd(LPVOID data) ; protected: ?HRESULT InitDirectSound(GUID dev_id = GUID_NULL) ; ?HRESULT FreeDirectSound() ; ?HRESULT?InitNotifications() ; ?HRESULT CreateCaptureBuffer(WAVEFORMATEX * wfx) ; ?HRESULT?StartOrStopRecord(BOOL bStartRec) ; ?HRESULT RecordCapturedData() ; ?void??? SetWavFormat(WAVEFORMATEX * wfx) ; public: ?CCaptureAudio(void); ?~CCaptureAudio(void); ?BOOL EnumDevices(HWND hList) ; ?BOOL Open(void) ; ?BOOL Close() ; ?void GrabAudioFrames(BOOL bGrabAudioFrames, CAdoFrameHandler* frame_handler) ; }; #endif 下面這個(gè)是cpp文件: #include "StdAfx.h" #include "./captureaudio.h" #include <mmsystem.h> #include <dsound.h> #ifndef SAFE_DELETE #define SAFE_DELETE(p)? { if(p) { delete (p);???? (p)=NULL; } } #endif #ifndef SAFE_RELEASE #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } } #endif #ifndef MAX #define MAX(a,b)??????? ( (a) > (b) ? (a) : (b) ) #endif CCaptureAudio::CCaptureAudio(void) { ?if(FAILED(CoInitialize(NULL))) /*, COINIT_APARTMENTTHREADED)))*/ ?{ ??AfxMessageBox("CCaptureAudio CoInitialize Failed!/r/n"); ??return; ?} ?m_pCapDev = NULL ; ?m_pCapBuf = NULL ; ?m_pNotify = NULL ; // set default wave format PCM ?ZeroMemory( &m_wfxInput, sizeof(m_wfxInput)); ?m_wfxInput.wFormatTag = WAVE_FORMAT_PCM; m_guidCapDevId = GUID_NULL ; ?m_bRecording = FALSE ; ?m_hNotifyEvent = NULL ; } CCaptureAudio::~CCaptureAudio(void) { ?CoUninitialize() ; } BOOL CALLBACK CCaptureAudio::enum_dev_proc(LPGUID lpGUID, LPCTSTR lpszDesc, ???????????? LPCTSTR lpszDrvName, LPVOID lpContext) { ?HWND hList = (HWND)lpContext; ?if(!hList) return FALSE ; ?LPGUID lpTemp = NULL; if (lpGUID != NULL) { ??// NULL only for "Primary Sound Driver". ??if ((lpTemp = (LPGUID)malloc(sizeof(GUID))) == NULL) return(TRUE); ??memcpy(lpTemp, lpGUID, sizeof(GUID)); ?} ?::SendMessage(hList, CB_ADDSTRING, 0,(LPARAM)lpszDesc); ?::SendMessage(hList, LB_SETITEMDATA, 0, (LPARAM)lpTemp) ; ?free(lpTemp); ?return(TRUE); } UINT CCaptureAudio::notify_capture_thd(LPVOID data) { ?CCaptureAudio * pado = static_cast<CCaptureAudio *>(data) ; ?MSG?? msg; ?HRESULT hr ; ?DWORD?dwResult ; ?while(pado->m_bRecording) { ??dwResult = MsgWaitForMultipleObjects( 1, &(pado->m_hNotifyEvent), FALSE, INFINITE, QS_ALLEVENTS ); ??switch( dwResult ) { ??case WAIT_OBJECT_0 + 0: ???// g_hNotificationEvents[0] is signaled // This means that DirectSound just finished playing ???// a piece of the buffer, so we need to fill the circular ???// buffer with new sound from the wav file if( FAILED( hr = pado->RecordCapturedData() ) ) { ????AfxMessageBox("Error handling DirectSound notifications.") ; ????pado->m_bRecording = FALSE ; ???} ???break; ??case WAIT_OBJECT_0 + 1: ???// Windows messages are available ???while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) { ?????TranslateMessage( &msg ); ?????DispatchMessage( &msg ); ????if( msg.message == WM_QUIT ) pado->m_bRecording = FALSE ; ???} ???break; ??} ?} ?AfxEndThread(0, TRUE) ; ?return 0 ; } BOOL CCaptureAudio::EnumDevices(HWND hList) { ?if (FAILED(DirectSoundCaptureEnumerate ( ???(LPDSENUMCALLBACK)(CCaptureAudio::enum_dev_proc), ???(VOID*)&hList))) ?{ ??return(FALSE); ?} ?return (TRUE) ; } BOOL CCaptureAudio::Open(void) { ?HRESULT hr ; ?if(!m_bRecording) { ??hr = InitDirectSound() ; ?} ?return (FAILED(hr)) ? FALSE : TRUE ; } BOOL CCaptureAudio::Close() { ?HRESULT hr ; ?hr = FreeDirectSound() ; ?CloseHandle(m_hNotifyEvent) ; ?return (FAILED(hr)) ? FALSE : TRUE ; } HRESULT CCaptureAudio::InitDirectSound(GUID dev_id) { ?HRESULT hr ; ?m_guidCapDevId = dev_id ; ZeroMemory( &m_aPosNotify, sizeof(DSBPOSITIONNOTIFY) * (NUM_REC_NOTIFICATIONS + 1) ) ; ?m_dwCapBufSize = 0 ; ?m_dwNotifySize = 0 ; // Create IDirectSoundCapture using the preferred capture device ?hr = DirectSoundCaptureCreate(&m_guidCapDevId, &m_pCapDev, NULL ) ; // init wave format ?SetWavFormat(&m_wfxInput) ; return (FAILED(hr)) ? S_FALSE : S_OK ; } HRESULT CCaptureAudio::FreeDirectSound() { ?// Release DirectSound interfaces ?SAFE_RELEASE( m_pNotify ) ; ?SAFE_RELEASE( m_pCapBuf ) ; ?SAFE_RELEASE( m_pCapDev ) ; ?return S_OK; } HRESULT CCaptureAudio::CreateCaptureBuffer(WAVEFORMATEX * wfx) { ?HRESULT hr; ?DSCBUFFERDESC dscbd; SAFE_RELEASE( m_pNotify ); ?SAFE_RELEASE( m_pCapBuf ); // Set the notification size ?m_dwNotifySize = MAX( 1024, wfx->nAvgBytesPerSec / 8 ) ; ?m_dwNotifySize -= m_dwNotifySize % wfx->nBlockAlign ; // Set the buffer sizes ?m_dwCapBufSize = m_dwNotifySize * NUM_REC_NOTIFICATIONS; SAFE_RELEASE( m_pNotify ); ?SAFE_RELEASE( m_pCapBuf ); // Create the capture buffer ?ZeroMemory( &dscbd, sizeof(dscbd) ); ?dscbd.dwSize??????? = sizeof(dscbd); ?dscbd.dwBufferBytes = m_dwCapBufSize; ?dscbd.lpwfxFormat?? = wfx ; // Set the format during creatation if( FAILED( hr = m_pCapDev->CreateCaptureBuffer( &dscbd, &m_pCapBuf, NULL ) ) ) ??return S_FALSE ; m_dwNextCapOffset = 0; if( FAILED( hr = InitNotifications() ) ) ??return S_FALSE ; return S_OK; } HRESULT CCaptureAudio::InitNotifications() { ?HRESULT hr; ?int i ; ?if( NULL == m_pCapBuf ) ??return S_FALSE; // create auto notify event ?m_hNotifyEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); // Create a notification event, for when the sound stops playing ?if( FAILED( hr = m_pCapBuf->QueryInterface( IID_IDirectSoundNotify,?(VOID**)&m_pNotify ) ) ) ??return S_FALSE ; // Setup the notification positions ?for( i = 0; i < NUM_REC_NOTIFICATIONS; i++ ) { ??m_aPosNotify[i].dwOffset = (m_dwNotifySize * i) + m_dwNotifySize - 1; ??m_aPosNotify[i].hEventNotify = m_hNotifyEvent;???????????? ?} // Tell DirectSound when to notify us. the notification will come in the from ?// of signaled events that are handled in WinMain() ?if( FAILED( hr = m_pNotify->SetNotificationPositions( NUM_REC_NOTIFICATIONS, m_aPosNotify ) ) ) ??return S_FALSE ; return S_OK; } HRESULT?CCaptureAudio::StartOrStopRecord(BOOL bStartRec) { ?HRESULT hr; if( bStartRec ) { ??// Create a capture buffer, and tell the capture ??// buffer to start recording?? ??if( FAILED( hr = CreateCaptureBuffer( &m_wfxInput ) ) ) ???return S_FALSE ; if( FAILED( hr = m_pCapBuf->Start( DSCBSTART_LOOPING ) ) ) ???return S_FALSE ; // create notify event recv thread ??AfxBeginThread(CCaptureAudio::notify_capture_thd, (LPVOID)(this)) ; ?} else { ??// Stop the capture and read any data that ??// was not caught by a notification ??if( NULL == m_pCapBuf ) ???return S_OK; ??// wait until the notify_event_thd thread exit and release the resources. ??Sleep(500) ; // Stop the buffer, and read any data that was not ??// caught by a notification ??if( FAILED( hr = m_pCapBuf->Stop() ) ) ???return S_OK ; if( FAILED( hr = RecordCapturedData() ) ) ???return S_FALSE ; ?} ?return S_OK; } HRESULT CCaptureAudio::RecordCapturedData() { ?HRESULT hr; ?VOID*?? pbCaptureData??? = NULL; ?DWORD?? dwCaptureLength; ?VOID*?? pbCaptureData2?? = NULL; ?DWORD?? dwCaptureLength2; ?DWORD?? dwReadPos; ?DWORD?? dwCapturePos; ?LONG lLockSize; if( NULL == m_pCapBuf ) ??return S_FALSE; ? ?if( FAILED( hr = m_pCapBuf->GetCurrentPosition( &dwCapturePos, &dwReadPos ) ) ) ??return S_FALSE; lLockSize = dwReadPos - m_dwNextCapOffset; ?if( lLockSize < 0 ) ??lLockSize += m_dwCapBufSize; // Block align lock size so that we are always write on a boundary ?lLockSize -= (lLockSize % m_dwNotifySize); if( lLockSize == 0 ) ??return S_FALSE; // Lock the capture buffer down ?if( FAILED( hr = m_pCapBuf->Lock( m_dwNextCapOffset, lLockSize, ?????????? &pbCaptureData, &dwCaptureLength, ?????????? &pbCaptureData2, &dwCaptureLength2, 0L ) ) ) ??return S_FALSE ; // call the outer data handler ?if(m_frame_handler) { ??m_frame_handler->AdoFrameData((BYTE*)pbCaptureData, dwCaptureLength) ; ?} ? ?// Move the capture offset along ?m_dwNextCapOffset += dwCaptureLength; ?m_dwNextCapOffset %= m_dwCapBufSize; // Circular buffer if( pbCaptureData2 != NULL ) { ??// call the outer data handler ??if(m_frame_handler) { ???m_frame_handler->AdoFrameData((BYTE*)pbCaptureData, dwCaptureLength) ; ??} // Move the capture offset along ??m_dwNextCapOffset += dwCaptureLength2; ??m_dwNextCapOffset %= m_dwCapBufSize; // Circular buffer ?} // Unlock the capture buffer ?m_pCapBuf->Unlock( pbCaptureData,? dwCaptureLength, pbCaptureData2, dwCaptureLength2 ); return S_OK; } void CCaptureAudio::SetWavFormat(WAVEFORMATEX * wfx) { ?// get the default capture wave formate ?ZeroMemory(wfx, sizeof(WAVEFORMATEX)) ; ?wfx->wFormatTag = WAVE_FORMAT_PCM; // 8KHz, 16 bits PCM, Mono ?wfx->nSamplesPerSec = 8000 ; ?wfx->wBitsPerSample = 16 ; ?wfx->nChannels??= 1 ; wfx->nBlockAlign = wfx->nChannels * ( wfx->wBitsPerSample / 8 ) ; ?wfx->nAvgBytesPerSec = wfx->nBlockAlign * wfx->nSamplesPerSec; } void CCaptureAudio::GrabAudioFrames(BOOL bGrabAudioFrames, CAdoFrameHandler* frame_handler) { ?m_frame_handler = frame_handler ; ?m_bRecording = bGrabAudioFrames ; ?StartOrStopRecord(m_bRecording) ; } 使用的時(shí)候,也很簡單,我這里聲明了一個(gè)純虛類CAdoFrameHandler,這個(gè)類專門是用來讓使用者重載它的純虛函數(shù)的,只要重載了以后,設(shè)置正確,就可以自動開始采集音頻數(shù)據(jù)了。注意,在這個(gè)類里面,我用的是8KHz,16Bits,Mono單聲道的PCM數(shù)據(jù)采集。 使用的時(shí)候,首先: #include "CaptureAudio.h" 然后: class CMyClass : public CAdoFrameHandler { ... public: // override the CAdoFrameHandler ?void AdoFrameData(BYTE* pBuffer, long lBufferSize) ;? // 這個(gè)類重載一下,就可以采集了 protected: CCaptureAudio?? m_cap_ado ; // 這個(gè)對象就是用來采集音頻數(shù)據(jù)的 } ; 在OnInitDialog類中,我們可以使用如下初始化方法: 打開mic,同時(shí)初始化directsound: m_cap_ado.Open() ; 開始采集聲音就是: m_cap_ado.GrabAudioFrames(TRUE, this) ; 調(diào)用它以后,只要你重載了上面的那個(gè)函數(shù),directsound就會周期性的從麥克采集數(shù)據(jù),然后調(diào)用該函數(shù)。 停止聲音采集是: m_cap_ado.GrabAudioFrames(FALSE, NULL) ; 關(guān)閉mic,同時(shí)釋放directsound: m_cap_ado.Close() ; 就這么簡單的幾步,就可以完成麥克風(fēng)的音頻數(shù)據(jù)采集。 有問題歡迎大家多多交流。 | |