[转]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) = ;
};
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 + ]; //notify flag array
HANDLE m_hNotifyEvent; //notify event
BOOL m_abInputFmtSupported[];
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, ,(LPARAM)lpszDesc);
::SendMessage(hList, LB_SETITEMDATA, , (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( , &(pado->m_hNotifyEvent), FALSE, INFINITE, QS_ALLEVENTS );
switch( dwResult ) {
case WAIT_OBJECT_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 + :
// Windows messages are available
while( PeekMessage( &msg, NULL, , , PM_REMOVE ) ) {
TranslateMessage( &msg );
DispatchMessage( &msg );
if( msg.message == WM_QUIT ) pado->m_bRecording = FALSE ;
}
break;
}
}
AfxEndThread(, TRUE) ;
return ;
}
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 + ) ) ;
m_dwCapBufSize = ;
m_dwNotifySize = ;
// 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( , wfx->nAvgBytesPerSec / ) ;
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 = ;
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 = ; i < NUM_REC_NOTIFICATIONS; i++ ) {
m_aPosNotify[i].dwOffset = (m_dwNotifySize * i) + m_dwNotifySize - ;
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() ;
// 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 < )
lLockSize += m_dwCapBufSize;
// Block align lock size so that we are always write on a boundary
lLockSize -= (lLockSize % m_dwNotifySize);
if( lLockSize == )
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 = ;
wfx->wBitsPerSample = ;
wfx->nChannels = ;
wfx->nBlockAlign = wfx->nChannels * ( wfx->wBitsPerSample / ) ;
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类中,我们可以使用如下初始化方法:
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
[转]directsound抓取麦克风PCM数据封装类的更多相关文章
- 使用Office 365抓取PM2.5数据
近日微软发布了Microsoft Flow,一个类似IFTTT自动化任务触发工具.例如,我们可以设置这样一个触发事件和对应的处理过程:当有人在微博上@我的时候,发一封邮件通知我:当我关注的博主有新文章 ...
- Fiddler捕获抓取 App端数据包
最近项目设计到App抓包,所以采用Fiddler工具来采集获取APP数据包,但是fiddler对有些app是无法捕获到数据包的,以下是我的处理方法: 1. 我默认代理端口使用的是自定义的端口而不是默认 ...
- python requests抓取NBA球员数据,pandas进行数据分析,echarts进行可视化 (前言)
python requests抓取NBA球员数据,pandas进行数据分析,echarts进行可视化 (前言) 感觉要总结总结了,希望这次能写个系列文章分享分享心得,和大神们交流交流,提升提升. 因为 ...
- 通过WireShark抓取iPhone联网数据方法
通过WireShark抓取iPhone联网数据方法 命令行 rvictl -s <UDID> 然后再wireshark选择rvi0进行抓包即可 抓包完后,移除用命令 rvictl -x & ...
- 转 PHP的CURL方法curl_setopt()函数案例介绍(抓取网页,POST数据)
PHP的CURL方法curl_setopt()函数案例介绍(抓取网页,POST数据) 通过curl_setopt()函数可以方便快捷的抓取网页(采集很方便),curl_setopt 是php的一个 ...
- Fiddler 详尽教程与抓取移动端数据包
转载自:http://blog.csdn.net/qq_21445563/article/details/51017605 阅读目录 1. Fiddler 抓包简介 1). 字段说明 2). Stat ...
- selenium抓取动态网页数据
1.selenium抓取动态网页数据基础介绍 1.1 什么是AJAX AJAX(Asynchronouse JavaScript And XML:异步JavaScript和XML)通过在后台与服务器进 ...
- 使用Fiddler抓取手机APP数据包--360WIFI
使用Fiddler抓取手机APP流量--360WIFI 操作步骤:1.打开Fiddler,Tools-Fiddler Options-Connections,勾选Allow remote comput ...
- 使用Fiddler抓包抓取不了数据包的问题
一:(我)抓包遇到的问题. ①:抓包一直出现这个问题 解决办法: 如果你遇到上面的问题,就可能是证书的问题(我的本地证书是用系统生成证书的一个软件生成的个人证书,所以出现了问题,我抓的所有数据都出现 ...
随机推荐
- IIS Web负载均衡的几种方式
Web负载均衡的几种实现方式 摘要:负载均衡(Load Balance)是集群技术(Cluster)的一种应用.负载均衡可以将工作任务分摊到多个处理单元,从而提高并发处理能力.目前最常见的负载均衡应用 ...
- UVA 11374 Airport Express(最短路)
最短路. 把题目抽象一下:已知一张图,边上的权值表示长度.现在又有一些边,只能从其中选一条加入原图,使起点->终点的距离最小. 当加上一条边a->b,如果这条边更新了最短路,那么起点st- ...
- 在VC中显示和处理图片的方法
落鹤生 发布于 2011-10-21 09:12 点击:344次 来自:blog.csdn.net/mengaim_cn 几种用GDI画图的方法介绍. TAG: GDI 法1:这个方法其实用的是 ...
- Spring学习之Ioc
Ioc原理讲解:http://www.cnblogs.com/xdp-gacl/p/4249939.html Ioc IoC是一种编程思想,由主动编程变为被动接收. 也就是说,所有的组件都是被动的(p ...
- 【转】Windows环境下Android Studio v1.0安装教程
原文网址:http://ask.android-studio.org/?/article/9 http://android-studio.org/index.php/docs/experience/1 ...
- Spring4整合Hibernate4
首先,要明确Spring整合Hibernate可以做什么? 答案是: 1.由IOC容器来管理Hibernate的SessionFactory 2.让Hibernate使用上Spring的声明式事务 整 ...
- hdu 4300(kmp)
题意:说实话这个题的题意还真的挺难懂的,我开始看了好久都没看懂,后来百度了下题意才弄懂了,这题的意思就是首先有一个字母的转换表,就是输入的第一行的字符串,就是'a'转成第一个字母,'b'转成转换表的第 ...
- Oracle 存储过程动态建表
动态sql,顾名思义就是动态执行的sql,也就是说在没执行之前是动态的拼接的. 任务 传入参数:新建的表名hd+当前的年和月,例如hd_201105表结构是:字段1:id ,类型是number,可以自 ...
- java中的getClass()函数
Java反射学习 所谓反射,可以理解为在运行时期获取对象类型信息的操作.传统的编程方法要求程序员在编译阶段决定使用的类型,但是在反射的帮助下,编程人员可以动态获取这些信息,从而编写更加具有可移植性的代 ...
- 《C++ primer》--第7章
删除指针后,该指针就变成了悬垂指针.悬垂指针指向曾经存放对象的内存,但该对象已经不再存在了. 习题7.8 举一个例子说明什么时候应该将形参定义为引用类型.再举一个例子说明什么时候不应该将形参定义为引用 ...