zoukankan      html  css  js  c++  java
  • 音频捕捉(directshow)

    现在的所谓多媒体电脑一般都会有声卡(软声卡或硬声卡),有声卡就能进行音频的捕捉。大家一定熟悉Windows自带的附件“录音机”程序,可以通过麦克风进行录音,最终生成一个Wave文件。读完本文之后,你就会发现,自己使用DirectShow写一个音频捕捉的应用程序,原来也是这么的容易!

      大家知道,DirectShow对硬件的支持是通过特定的包装Filter来实现的。声卡使用的是Audio Capture Filter,Filter内部使用以waveIn开头的一套API实现(如waveInOpen等)。运行GraphEdit,插入Filter时,在“Audio Capture Sources”目录下,我们就能看到所有代表本地机器上的声卡的各个Filter(有的机器装了几张声卡,这里就会有几个Filter)。在Filter Graph中加入这个Filter,我们发现这个Filter有很多Input pin,如Line In、CD Audio、Microphone、Stereo Mix等等;有一个Capture output pin。需要说明的是,在Filter Graph中,这些Input pin并没有真正的数据流入,它们只是声卡的各个输入端子的象征性表示;所以这些Input pin永远也不用连接。

      下面我们来看一下如何创建一个音频捕捉程序。首先,当然是加入一个Audio Capture Filter。大家知道,DirectShow加入一个硬件Filter,都是要靠“枚举”;声卡Filter也不例外。代表声卡的Filter都注册在CLSID_AudioInputDeviceCategory目录下,使用系统设备枚举器枚举这个目录,就能发现我们想要创建的声卡对象。(如何枚举这里就不再赘述了。)当成功加入声卡Filter后,接下去的问题就是要将这个Filter与其他Filter相连。比如,我们想捕捉生成一个Wave文件,那么我们还需加入一个Wave Dest Filter和一个File Writer Filter,然后依次将它们相连。需要说明的是,Wave Dest Filter是微软DirectX SDK带的一个例子,在samples\Multimedia\DirectShow\Filters\WavDest目录下,我们必须首先编译这个例子并且注册这个Filter;这个Filter的功能是,当我们结束捕捉时,往Wave文件中写入一个文件头信息。下图是在GraphEdit中的Filter连接图:

    音频捕捉(directshow) - hackbin - 一个人的天空 
    点击查看大图

      下面是一段创建音频捕捉程序的框架代码,可供参考:

    void BuildAudioCaptureGraph(void) // Warning! No error checking here.
    {
     IBaseFilter *pSrc = NULL, *pWaveDest = NULL, *pWriter = NULL;
     IFileSinkFilter *pSink= NULL;
     IGraphBuilder *pGraph;
     // Create the Filter Graph Manager.
     CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&pGraph);
     // Add the audio capture filter.
     FindAudioCapture(&pSrc); // Assume that this function enumerates
     // audio capture devices and picks one.
     pGraph->AddFilter(pSrc, L"Capture");
     // Add the WavDest and the File Writer.
     AddFilterByClsid(pGraph, L"WavDest", CLSID_WavDest, &pWavDest);
     AddFilterByClsid(pGraph, L"File Writer", CLSID_FileWriter, &pWriter);
     // Set the file name.
     pWriter->QueryInterface(IID_IFileSinkFilter, (void**)&pSink);
     pSink->SetFileName(L"C:\\MyWackyWav.wav", NULL);
     // Hook everything up.
     ConnectTwoFilters(pGraph, pSrc, pWavDest);
     ConnectTwoFilters(pGraph, pWavDest, pWriter);
    }
      当然,在进行音频捕捉的同时,我们还可以实时监听音频源的输入。如下示意图:

    音频捕捉(directshow) - hackbin - 一个人的天空

      我们在Audio Capture Filter后面接了一个Infinite Pin Tee,这个Filter能够将一个Input pin输入的数据,复制成多份,分别通过各个Output pin发送出去。(这个Filter也是微软DirectX SDK带的一个例子,在samples\Multimedia\DirectShow\Filters\ InfTee目录下。)我们看到Tee Filter的一支连到了DirectSound Renderer,可以将声音放在声卡上输出。

      创建音频捕捉的应用程序很简单吧!下面,我们还要来讨论一下音频捕捉前可能用到的一些参数设置。在声卡Filter的每个Input pin上,我们都可以得到IAMAudioInputMixer这个接口。通过这个接口,我们可以设置各个输入端子的音频属性,如进行音频合成时是否允许某个输入端子的音频参与混合、音频输入的音量,还有Treble、Bass等等。另外,在Filter上也可以得到IAMAudioInputMixer接口,这时调用接口方法就可以统一控制各个输入端子的属性。音频捕捉,还可以设置的是音频的采样频率以及声音的具体格式(8Bits或16Bits,单声道或双声道)。我们可以通过Capture output pin的IAMStreamConfig来完成。下面的代码可供参考:
    HRESULT hr = pCapturePin->QueryInterface(IID_IAMStreamConfig, (void **)&pCfg);
    // Read current media type/format
    AM_MEDIA_TYPE *pmt={0};
    hr = pCfg->GetFormat(&pmt);
    if (SUCCEEDED(hr))
    {
     // Fill in values for the new format
     WAVEFORMATEX *pWF = (WAVEFORMATEX *) pmt->pbFormat;
     pWF->nChannels = (Word) nChannels;
     pWF->nSamplesPerSec = nFrequency;
     pWF->nAvgBytesPerSec = lBytesPerSecond;
     pWF->wBitsPerSample = (WORD) (nBytesPerSample * 8);
     pWF->nBlockAlign = (WORD) (nBytesPerSample * nChannels);
     // Set the new formattype for the output pin
     hr = pCfg->SetFormat(pmt);
     DeleteMediaType(pmt);
    }
    // Release interfaces
    pCfg->Release();
      最后,还要提到的一点,也是音频捕捉比较特殊的地方:我们可以通过Capture output pin上的IAMBufferNegotiation接口,改变音频捕捉缓冲的大小,以减少声音播放的延迟。默认情况下,Audio Capture Filter使用0.5秒钟的缓冲。对于一些特殊的应用,这么大的缓冲是没有必要的,带来的延迟也比较大。一般,缓冲设置成能够容纳80毫秒的数据已经很可靠;甚至30-40毫秒也已经足够了。但是也不能太小,否则会影响到音频捕捉的效率,使音质受到损害。下面的代码设置音频捕捉的缓冲大小,可供参考:

    pCapturePin->QueryInterface(IID_IAMBufferNegotiation, (void **)&pNeg);
    // Set the buffer size based on selected settings
    ALLOCATOR_PROPERTIES prop={0};
    prop.cbBuffer = lBufferSize;
    prop.cBuffers = 6;
    prop.cbAlign = nBytesPerSample * nChannels;
    hr = pNeg->SuggestAllocatorProperties(&prop);
    pNeg->Release();
      以上,我们讲述了音频捕捉程序的创建过程,以及一些捕捉参数的设置方法。相信大家对于如何写音频捕捉程序已经有了自己的认识。音频捕捉直接得到的是PCM数据,根据需要,我们还可以对其进行压缩,比如用mp3格式(微软提供了一个免费的Mp3 Encoder)、AC3格式等等;压缩后数据量更少,可以符合很多场合的应用。 
  • 相关阅读:
    Touchpad rocks
    KTorrent for KDE 4 已抵达
    小技巧: 怎样将 GNOME applets 添加到 Xfce 面板
    小本事:遵守令行发布 Twitter 新闻
    小才略: 如何将 Gmail 设置为默许的邮件客户端
    小伎俩: 若安在 Compiz 中设置菜单透明
    Firefox 使用手腕四则
    办理 VMware 在 2.6.24 内核下的成绩
    小手段:开启 GNOME 的窗口分组效果
    不喜爱 KDE 4.0 的新启动菜单? 换掉它!
  • 原文地址:https://www.cnblogs.com/qq78292959/p/2077064.html
Copyright © 2011-2022 走看看