| | 網上有很多方法從麥克風讀取PCM數據,不想一一舉例。只是在這里發布一個我自己寫的directsound的麥克風PCM數據采集類,通過它,可以很方便的利用directsound技術把麥克風的數據采集到,而且本身,開發者不必太在意自己會不會directsound編程,可以很方便的讓開發者的主要精力集中于程序本身,而不是細節。 這個是頭文件: #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  下面這個是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) ;
 }  使用的時候,也很簡單,我這里聲明了一個純虛類CAdoFrameHandler,這個類專門是用來讓使用者重載它的純虛函數的,只要重載了以后,設置正確,就可以自動開始采集音頻數據了。注意,在這個類里面,我用的是8KHz,16Bits,Mono單聲道的PCM數據采集。 使用的時候,首先: #include "CaptureAudio.h" 然后: class CMyClass : public CAdoFrameHandler { ... public: // override the CAdoFrameHandler
 ?void AdoFrameData(BYTE* pBuffer, long lBufferSize) ;? // 這個類重載一下,就可以采集了  protected: CCaptureAudio?? m_cap_ado ; // 這個對象就是用來采集音頻數據的 } ;   在OnInitDialog類中,我們可以使用如下初始化方法:  打開mic,同時初始化directsound: m_cap_ado.Open() ;   開始采集聲音就是: m_cap_ado.GrabAudioFrames(TRUE, this) ;  調用它以后,只要你重載了上面的那個函數,directsound就會周期性的從麥克采集數據,然后調用該函數。  停止聲音采集是: m_cap_ado.GrabAudioFrames(FALSE, NULL) ;  關閉mic,同時釋放directsound: m_cap_ado.Close() ;   就這么簡單的幾步,就可以完成麥克風的音頻數據采集。 有問題歡迎大家多多交流。
 | 
 |