问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

如何利用DirectSound实现声卡录音

发布网友 发布时间:2022-04-26 09:27

我来回答

1个回答

热心网友 时间:2022-06-26 17:54

详细讲述了如何利用DirectSound对经过声卡和麦克风的数据进行捕获,进行录音,并保存为wave格式的文件。

  曾经学习过Directshow的开发,对于Dsound一直没有仔细的学习,以前只是知道Dsound是做音频开发的,我一直以为它和Dshow的结构体系差不多,经过仔细学习后,发现,其实他们完全两码事。DirectSound虽然也基于COM,但不象Dshow那样多个的filter组成链表。
闲话少说,下面我们看看DirectSound到底能帮我们做些什么。

  1、播放WAVE格式的音频文件或者资源。

  2、可以同时播放多个音频。

  3、Assign high-priority sounds to hardware-controlled buffers

  4、播放3D立体声音

  5、在声音中添加特技效果,比如回声,动态的改变特技的参数等

  6、将麦克风或者其他音频输入设备的声音录制成wave格式的文件

  DirectSound就能做这么多事情,到这里,我都有点怀疑DirectSound是不是就是封装了mmio系列和wav系列的函数。因为这些底层的API也能够完成这些事情。这里我们主要讨论一下,如果使用Directsound进行录音,并保存成wave格式的文件。

  在开始工作之前,要先介绍DirectSound录音用到的三个非常重要的对象:

  ·IDirectSoundCapture8 ,设备对象,根据你录音的设备创建的设备对象,利用该对象可以获取设备的属性。

  ·IDirectSoundCaptureBuffer8,缓冲区对象,该对象由设备对象创建,主要用来操作音频数据

  ·IDirectSoundNotify8 ,事件通知对象,该对象用来通知应用程序从缓冲区中将数据取走,写入文件保存起来。

  利用DirectSound录音的主要思路,就是先根据选择的录音设备创建设备对象,然后通过设备对象创建辅助缓冲区对象,开始录音的时候,设备将数据写入缓冲区,应用程序主动的从缓冲区将数据读出来写文件即可,就实现了录音功能。这里简单介绍一下dsound的通知功能,应用程序会创建一个通知对象,然后将通知对象邦定,然后设定通知位置(position),什么是通知位置呢,比如缓冲区的大小为4000字节,如果你想当数据达到缓冲区一半的时候能得到通知开始copy数据,那么此时你就可以将通知位置设定为2000,通知位置可以任意的设定,当缓冲区的数据达到你设定的位置时,就会通知应用程序将缓冲区的数据copy到文件中,缓冲区是循环利用的,当缓冲区填充满了以后,就会从头开始充填数据,所以,缓冲区就是一边读,一边写的过程。

  下面我讲一下录音的主要步骤,可以使大家的思路更清晰一些

  1、枚举录音的设备

  2、根据选择的设备创建设备对象

  3、利用设备对象创建缓冲区对象

  4、设置通知机制

  5、创建工作线程,用来将缓冲区的数据写入文件。

  先来定义一下用到的数据

LPDIRECTSOUNDCAPTURE8 g_pDSCapture = NULL;//设备对象指针
LPDIRECTSOUNDCAPTUREBUFFER g_pDSBCapture = NULL;//缓冲区对象指针
LPDIRECTSOUNDNOTIFY8 g_pDSNotify = NULL;//用来设置通知的对象接口

GUID g_guidCaptureDevice = GUID_NULL; //设备id
BOOL g_bRecording = FALSE; //是否正在录音
WAVEFORMATEX g_wfxInput; //输入的音频格式

DSBPOSITIONNOTIFY g_aPosNotify[ NUM_REC_NOTIFICATIONS + 1 ]; //设置通知标志的数组
HANDLE g_hNotificationEvent; //通知事件
BOOL g_abInputFormatSupported[20];
DWORD g_dwCaptureBufferSize; //录音用缓冲区的大小
DWORD g_dwNextCaptureOffset;//偏移位置
DWORD g_dwNotifySize;// 通知位置
CWaveFile* g_pWaveFile;//

  枚举录音的设备

  如果你的程序只是想从用户缺省的设备上进行声音的录制,那么就没有必要来枚举出系统中的所有录音的设备,当你调用DirectSoundCaptureCreate8 或者另外一个函数DirectSoundFullDuplexCreate8的时候,其实就默认指定了一个缺省的录音设备。

  当然,在下面的情况下,你就必须要枚举系统中所有的设备,例如,你的应用程序并不支持所有的输出设备,或者你的应用程需要两个或者多个设备,或者你希望用户自己来选择输出设备。

  枚举设备,你首先要定义一个回调函数,这个回调函数可以被系统中的每个设备来调用,你可以在各函数做任何事情,这个函数的命名也没有任何的*,但是函数应该以DSEnumCallback为原型,如果枚举没有结束,这个回调函数就返回TRUE,如果枚举结束,例如你找到合适的设备,这个函数就要返回FALSE。

  下面是回调函数的一个例子,这个函数将枚举的每一个设备都添加到一个combox中,将设备的GUID保存到一个item 中,这个函数的前三个参数由设备的驱动程序提供,第四个参数有DirectSoundCaptureEnumerate函数提供,这个参数可以是任意的32位值,这个例子里是combox的句柄,

BOOL CALLBACK DSEnumProc(LPGUID lpGUID,
LPCTSTR lpszDesc,
LPCTSTR lpszDrvName,
LPVOID lpContext )
{
HWND hCombo = (HWND)lpContext;
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));
}
//下面的代码主要主要是将设备添加到CComboBox,其实你完全直接将CComboBox指针传递过来,直接的添加,这里采用的是给combox窗口发送消息的方法,
ComboBox_AddString(hCombo, lpszDesc);
ComboBox_SetItemData(hCombo,
ComboBox_FindString(hCombo, 0, lpszDesc),
lpTemp );
free(lpTemp);
return(TRUE);
}

  枚举设备通常都是在对话框初始化的时候才进行的,我们假设hCombo就是combox句柄,hDlg就对话框的句柄,看看我们怎么来枚举设备的吧。

if (FAILED(DirectSoundCaptureEnumerate ((LPDSENUMCALLBACK)DSEnumProc,
(VOID*)&hCombo)))
{
EndDialog(hDlg, TRUE);
return(TRUE);
}

  在这个例子中,combox的句柄作为参数传递到DirectSoundEnumerate函数中,然后又被传递到回调函数中,这个参数你可以是你想传递的任意的32位值。
注:第一个被枚举的设备通常称为Primary sound driver,并且回调函数的lpGUID为NULL,这个设备就是用户通过控制面板设置的缺省的录音声音设备,
创建设备对象

  你可以通过DirectSoundCaptureCreate8或者DirectSoundFullDuplexCreate8函数直接创建设备对象,该函数返回一个指向IDirectSoundCapture8接口的指针

if( FAILED( hr = CoInitialize(NULL) ) )
return hr;
if(pDeviceGuid)
{
 if(FAILED( hr = DirectSoundCaptureCreate(pDeviceGuid,&g_pDSCapture,NULL)))
  return hr;
}
else
{
 if(FAILED(hr= DirectSoundCaptureCreate(&DSDEVID_DefaultCapture ,&g_pDSCapture,NULL)))
  return hr
}

  其中pDeviceGuid是从枚举的combox中选择的设备的ID。

  现在创建了设备对象你可以通过IDirectSoundCapture8::GetCaps方法来获取录音设备的性能,这个函数的参数是一个DSCCAPS类型的结构,在传递这个参数之前,一定要初始化该结构的dwSize成员变量。同时,你可以通过这个结构返回设备支持的声道数,以及类似WAVEINCAPS结构的其他设备属性

  创建录音的缓冲区对象

  我们可以通过IDirectSoundCapture8::CreateCaptureBuffer来创建一个录音的buffer对象,这个函数的一个参数采用DSCBUFFERDESC类型的结构来说明buffer的一些特性,这个结构的最后一个成员变量是一个WAVEFORMATEX结构,这个结构一定要初始化成泥需要的wav格式。
说明一下,如果你的应用程序一边播放的同时进行录制,如果你录制的buffer格式和你的主缓冲buffer不一样,那么你创建录制buffer对象就会失败,原因在于,有些声卡只支持一种时钟,不能同时支持录音和播放同时以两种不同的格式进行。

  下面的函数,演示了如何创建一个录音的buffer对象,这个buffer对象能够处理1秒的数据,注意,这里传递的录音设备对象参数一定要通过DirectSoundCaptureCreate8来创建,而不是早期的DirectSoundCaptureCreate接口,否则,buffer对象不支持IDirectSoundCaptureBuffer8接口。

HRESULT CreateCaptureBuffer(LPDIRECTSOUNDCAPTURE8 pDSC,
LPDIRECTSOUNDCAPTUREBUFFER8* ppDSCB8)
{
 HRESULT hr;
 DSCBUFFERDESC dscbd;
 LPDIRECTSOUNDCAPTUREBUFFER pDSCB;
 WAVEFORMATEX wfx ={WAVE_FORMAT_PCM, 2, 44100, 176400, 4, 16, 0};
 // wFormatTag, nChannels, nSamplesPerSec, mAvgBytesPerSec,
 // nBlockAlign, wBitsPerSample, cbSize

 if ((NULL == pDSC) || (NULL == ppDSCB8)) return E_INVALIDARG;
 dscbd.dwSize = sizeof(DSCBUFFERDESC);
 dscbd.dwFlags = 0;
 dscbd.dwBufferBytes = wfx.nAvgBytesPerSec;
 dscbd.dwReserved = 0;
 dscbd.lpwfxFormat = &wfx; //设置录音用的wave格式
 dscbd.dwFXCount = 0;
 dscbd.lpDSCFXDesc = NULL;

 if (SUCCEEDED(hr = pDSC->CreateCaptureBuffer(&dscbd, &pDSCB, NULL)))
 {
  hr = pDSCB->QueryInterface(IID_IDirectSoundCaptureBuffer8, (LPVOID*)ppDSCB8);
  pDSCB->Release();
 }
 return hr;
}

  你可以通过IDirectSoundCaptureBuffer8::GetCaps方法来获取录音buffer的大小,但一定要记得初始化DSCBCAPS结构类型参数的dwSize成员变量。

  为了获取buffer中数据的格式,你可以通过IDirectSoundCaptureBuffer8::GetFormat.方法来获取buffer中的数据格式,这个函数通过WAVEFORMATEX结构返回音频数据的信息,如果我们想知道一个录音buffer目前的状态如何,可以通过IDirectSoundCaptureBuffer8::GetStatus来获取,这个函数通过一个DWORD类型的参数来表示该buffer是否正在录音,
IDirectSoundCaptureBuffer8::GetCurrentPosition方法可以获取buffer中read指针和录制指针的偏差。Read指针指向填充到该buffer中的数据的最末端,capture指针则指向复制到硬件的数据的末端,你read指针指向的前段数据都是安全数据,你都可以安全的复制。

  录音buffer对象通知机制

  为了安全的定期的从录音buffer中copy数据,你的应用程序就要知道,什么时候read指针指向了特定的位置,一个方法是通过IDirectSoundCaptureBuffer8::GetCurrentPosition.方法来获取read指针的位置,另外一个更有效的方法采用通知机制,通过IDirectSoundNotify8::SetNotificationPositions方法,你可以设置任何一个小于buffer的位置来触发一个事件,切记,当buffer正在running的时候,不要设置。

  如何来设置一个触发事件呢,首先要得到IDirectSoundNotify8接口指针,你可以通过buffer对象的QuerInterface来获取这个指针接口,对于你指定的任何一个position,你都要通过CreateEvent方法,创建一个win32内核对象, 然后将内核对象的句柄赋给DSBPOSITIONNOTIFY结构的hEventNotify成员,通过该结构的dwOffset来设置需要通知的位置在buffer中的偏移量。

  最后将这个结构或者结构数组,传递给SetNotificationPositions函数,下面的例子设置了NUM_REC_NOTIFICATIONS个通知,当position达到g_dwNotifySize时会触发一个通知,依次类推。

HRESULT InitNotifications()
{
 HRESULT hr ;
 g_hNotificationEvent = CreateEvent(NULL,FALSE,FALSE,NULL); //创建事件
 if(g_pDSBCapture == NULL)
  return E_FAIL;
 if(FAILED(hr = g_pDSBCapture ->QueryInterface(IID_IDirectSoundNotify,(VOID**)&g_pDSNotify)))
  return hr;

 for( INT i = 0; i < NUM_REC_NOTIFICATIONS; i++ )
 {
  g_aPosNotify[i].dwOffset = (g_dwNotifySize * i) + g_dwNotifySize - 1;
  g_aPosNotify[i].hEventNotify = g_hNotificationEvent;
 }

 if(FAILED( hr =g_pDSNotify->SetNotificationPositions( NUM_REC_NOTIFICATIONS, g_aPosNotify ) ) )
  return hr;
 return S_OK;
}
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
填地方专项志愿会影响本科二批录取吗? 地方专项计划成绩不好的可以报吗 国家专项地方专项影响本科填报吗 地方专项计划滑档影响本科录取吗 报地方专项计划会影响高考录取吗 地方专项计划影响后续录取吗 电脑最高配组装机配置组装电脑什么配置比较好 台式组装游戏电脑配置组装电脑什么配置比较好 打游戏主机电脑配置台式电脑主要是玩游戏什么配置最合适 组装高配置台式电脑组装电脑什么配置比较好 袁心_十年五冠,为什么军衔还只是中尉? 2个独立的pm文件怎样合成一个pm文件? 清朝为何坐视沙俄吞并西伯利亚呢? 李沁演的葡萄是什么电视剧? 我的洛克人x8怎么突然玩不了了!显示directsound8 乱码 以PM为后缀名的文件,是什么脚本语言 合金装备2directsound8错误 袁心_为啥之前被叫做“小苹果”,现在又被喊成“老果子”? PM格式是什么 出发-西伯利亚 歌词 DirectX里DirectSoundCreate8和DirectSoundCreate什么区别? pm文件是什么 山东省有哪些名特蔬菜? 袁心_到底多高,为何她总是说自己1.99米? DirectX11 sdk中IDirectSound8接口不能使用? 西伯利USB声卡驱动问题 pl 和 pm分别是什么文件 守望的天空中小女孩袁心的扮演者? 魔法少女武斗祭打开时,说“directsound8程序生成失效” 如何打开后缀名.pm的文件? 山东出什么蔬菜 请问一下pm文件可以转pdf吗?急! 守望的天空中,一个是小葡萄(袁心,被抛弃的那个)还有一个是小袁心,请问她生活中的名字叫什么? 愤怒的小鸟玩不了!提示DirectSoundcreate8 failed 常见青菜的种类图片和名字大全? 纬地道路设计软件主线平面设计完成以后,不能进行设计向导,提示说.pm文件中包含无效的路径,请问是什么原因? 在婚姻关系存续期间,女方有权享有男方的房子居住权吗? C#里边怎么用directsound混音? 我下载了一个类型PM的文件要用哪一种播放器才播放 绿叶菜大量入市,绿叶菜的价格究竟如何? 作为军人球员,“小苹果”袁心_是什么级别呢? 愤怒的小鸟玩不了,提示directsoundcreate8 failed,为什么?怎么解决?高手请帮忙 PM文件破坏是否能修复 守望的天空袁望是孤独症,小女孩袁心是什么病? 不能初始化DirectSound 上午am下午pm格式吗? 《守望的天空》葡萄怎么和大志结婚了啊 directsound 错误 请问如何把PM格式的转成JPG格式呢 寿光蔬菜的介绍