directsound抓取麥克風PCM資料封裝類

來源:互聯網
上載者:User
directsound抓取麥克風PCM資料封裝類
網上有很多方法從麥克風讀取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  16class 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() ;  就這麼簡單的幾步,就可以完成麥克風的音頻資料擷取。有問題歡迎大家多多交流。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.