zoukankan      html  css  js  c++  java
  • [转]directsound抓取麦克风PCM数据封装类

    网上有很多方法从麦克风读取PCM数据,不想一一举例。只是在这里发布一个我自己写的directsound的麦克风PCM数据采集类,通过它,可以很方便的利用directsound技术把麦克风的数据采集到,而且本身,开发者不必太在意自己会不会directsound编程,可以很方便的让开发者的主要精力集中于程序本身,而不是细节。就是需要安装directx9b的sdk,然后在编译的时候加入以下lib: strmiids.lib Quartz.lib winmm.lib dsound.lib dxguid.lib

    这个是头文件:

    #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!
    "); 
      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() ;

    就这么简单的几步,就可以完成麦克风的音频数据采集。

    转自:http://blog.chinaunix.net/uid-8272118-id-2033248.html

  • 相关阅读:
    快速幂模板
    部分有关素数的题
    POJ 3624 Charm Bracelet (01背包)
    51Nod 1085 背包问题 (01背包)
    POJ 1789 Truck History (Kruskal 最小生成树)
    HDU 1996 汉诺塔VI
    HDU 2511 汉诺塔X
    HDU 2175 汉诺塔IX (递推)
    HDU 2077 汉诺塔IV (递推)
    HDU 2064 汉诺塔III (递推)
  • 原文地址:https://www.cnblogs.com/ydxt/p/3445551.html
Copyright © 2011-2022 走看看