音频采集(VC++ SDK实现)

来源:互联网 发布:美林数据 知乎 编辑:程序博客网 时间:2024/06/05 20:31

当根据DirectShow程序包中AudioCap例子写了自己的MyAudioMFC程序后,虽然程序本身可以很好的运行,也可以实现“边录边听”,并实现播放;但感觉还是很有必要把思路理清一下;将其MFC改写为普通函数API的SDK方式后,一般理解起来就更容易些。且程序界面也简单的改写为只包含四个按钮的录音程序(封装了Input Device、InputPins、Graph Filters、InputPins和OutPins):

                                                                                                

在DialogProc中的WM_INITDIALOAG消息中初始化COM库:    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);为使用一系列的COM接口函数做好准备;

在初始化一系列要用到的变量,默认都为0值,SetDefaults();

初始化准备做好后在写COM接口函数

HRESULT GetInterfaces()

 HRESULT hr;

    // Create the filter graph.
    hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC,
                          IID_IGraphBuilder, (void **)&m_pGB);
    if (FAILED(hr) || !m_pGB)
        return E_NOINTERFACE;

    // Create the capture graph builder.
    hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC,
                          IID_ICaptureGraphBuilder2, (void **)&m_pCapture);
    if (FAILED(hr) || !m_pCapture)
        return E_NOINTERFACE;

    // Associate the filter graph with the capture graph builder
    hr = m_pCapture->SetFiltergraph(m_pGB);   
    if (FAILED(hr))
        return hr;

    // Get useful interfaces from the graph builder
    JIF(hr = m_pGB->QueryInterface(IID_IMediaControl, (void **)&m_pMC));
    JIF(hr = m_pGB->QueryInterface(IID_IMediaEventEx, (void **)&m_pME));

    // Have the graph signal events via window callbacks
    hr = m_pME->SetNotifyWindow((OAHWND)m_hWnd, WM_GRAPHNOTIFY, RECORD_EVENT);

CLEANUP:
    return hr;

先初始化Filter Graph的变量IGraphBuilder *m_pGB;再初始化辅助组件Capture Graph Builder(此组件可以简化Filter Graph的构建)ICaptureGraphBuilder2 *m_pCapture的变量;在通过hr = m_pCapture->SetFiltergraph(m_pGB); 设置Filter Graph Manager对象指针;则在下面的应用中就可以方便的使用ICaptureBuilder2接口方法(如RenderStream、FindPin、FindInterface等)来构建Filter Graph。

 

枚举音频采集设备

创建系统枚举器组件对象,获得ICreateDevEnum接口:  

 hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL,
                              CLSCTX_INPROC, IID_ICreateDevEnum,
                              (void **)&pSysDevEnum););
为指定的Filtre注册类型目录创建一个枚举器,获得IEnumMoniker接口:

hr = pSysDevEnum->CreateClassEnumerator(*clsid, &pEnumCat, 0);

枚举指定类型目录下所有设备标识(Device Moniker):

    while(pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK){}

 

访问设备标识的属性集,相关属性值(如Friendly Name)可以保存在IPropertyBag接口对象中:hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag,
                                    (void **)&pPropBag);

读出Friendly Name的属性值,可调用IPropertyBag接口的成员函数Read():

        hr = pPropBag->Read(L"FriendlyName", &varName, 0);

        CString str(varName.bstrVal);
其中varName是一个VARIANT类型的结构体变量

由于实在SDK中实现的音频采集,界面很简单(只有四个按钮),没有AudioCap中的复杂界面;则就要在此while循环中判断设备标识中是否含有输入Pin,有则选定并跳出,没有则继续循环,知道Next()值为S_FALSE:

hr = EnumPinsOnFilter(m_pInputDevice, PINDIR_INPUT);
        if (FAILED(hr))
          continue;
     else
    break;

 

其中枚举输入Pin时:

    hr = m_pInputDevice->EnumPins(&pEnum);
    while((hr = pEnum->Next(1, &pPin, 0)) == S_OK){}

在循环中,        hr = pPin->QueryDirection(&PinDirThis);  可查询Pin的方向,结果返回到PinDirThis中

 if (PINDIR_INPUT== PinDirThis){  PIN_INFO pininfo={0};

 hr = pPin->QueryPinInfo(&pininfo);
            if (SUCCEEDED(hr))
            {
                CString str(pininfo.achName);
               // Listbox.AddString(str);
            }

调用QueryPinInfo()可将Pin的属性集值返回到PIN_INFO类型的变量中;Pin的名字则保存与PIN_INFO.achName中;

要选定音频采集的输入端子,则需使用IAMAudioInputMixer接口:

 hr = pPin->QueryInterface(IID_IAMAudioInputMixer, (void **)&pPinMixer);

然后将选定的输入Pin设置为:

hr = pPinMixer->put_Enable(TRUE);

break;

跳出循环;

当这前两不都准备好后,开始录音,实现边听边录,点击“录音”按钮即可;

一般的Audio Caputre Fitler只有一个Capture输出Pin,要实现录音音频流的话,可以使用ICaptureGraphBuilder2::RenderStream来实现,它可根据需要自动插入一个Smart Tee Filter(类似与Infinite Pin Tee的标准Filter):

HRESULT RenderPreviewStream(){}

其中也可以设置采集音频的参数:

 int nChannels =  2;  //双声道
 int nBytesPerSample =  2;//16位(量化精度)

 nFrequency = 44100;//44kHz

  long lBytesPerSecond = (long) (nBytesPerSample * nFrequency * nChannels);//一秒传送的字节数

  long lBufferSize = (long) ((float) lBytesPerSecond * DEFAULT_BUFFER_TIME);//设置数据的缓存大小

//得到这几个变量值后就可以使用IID_IAMBufferNegotiation、IAMStreamConfig来分别设置采集的缓冲大小和音频的声道数、量化精度、采样频率

 for (int i=0; i<2; i++)
   {
        hr = GetPin(m_pInputDevice, PINDIR_OUTPUT, i, &pPin);
        if (SUCCEEDED(hr))
        {
            // Get buffer negotiation interface
            hr = pPin->QueryInterface(IID_IAMBufferNegotiation, (void **)&pNeg);
            if (FAILED(hr))
            {
                pPin->Release();
                break;
            }

            hr = pNeg->SuggestAllocatorProperties(&prop);

其中prop是ALLOCATOR_PROPERTIES的结构体变量,保存了音频采集的缓冲大小:

 prop.cbBuffer = lBufferSize;
 prop.cBuffers = 6;
 prop.cbAlign = nBytesPerSample * nChannels;

设置IAMStreamConfig接口对象:

            hr = pPin->QueryInterface(IID_IAMStreamConfig, (void **)&pCfg);

获取当前输出Pin的数据媒体类型 

AM_MEDIA_TYPE *pmt={0};
hr = pCfg->GetFormat(&pmt);

设置当前输出Pin的数据媒体类型 

hr = pCfg->SetFormat(pmt);
 AM_MEDIA_TYPE *pmt={0};
            hr = pCfg->GetFormat(&pmt); AM_MEDIA_TYPE *pmt={0};
            hr = pCfg->GetFormat(&pmt);    long lBytesPerSecond = (long) (nBytesPerSample * nFrequency * nChannels);

至此“边听边录”的效果已经实现,若还想实现回路播放则需写

HRESULT RenderCaptureStream(){}函数:

创建Wave Dest Filter和File Write Filter,并加入Filter Grpah中,经过必要的设置后,取得声卡Filter或Smart Tee的输出Pin,在调用Render进行剩余部分的Filter连接:

 hr = CoCreateInstance(CLSID_WavDest, NULL, CLSCTX_INPROC,
                          IID_IBaseFilter, (void **)&m_pWAVDest);

 hr = CoCreateInstance(CLSID_FileWriter, NULL, CLSCTX_INPROC,
                          IID_IFileSinkFilter2, (void **)&pFileSink);

hr = pFileSink->QueryInterface(IID_IBaseFilter, (void **)&m_pFileWriter);   

hr = m_pGB->AddFilter(m_pWAVDest, L"WAV Dest");   

hr = m_pGB->AddFilter(m_pFileWriter, L"File Writer");   

hr = pFileSink->SetMode(AM_FILE_OVERWRITE);//设置生成的文件总是覆盖原有文件 

wcscpy(wszFilename, T2W(TEXT("C:\\test.wav\0")));  

hr = pFileSink->SetFileName(wszFilename, NULL);//设置数据流与文件磁盘中

获得Smart Tee Fitler上的IPin接口

hr = GetPin(m_pSplitter, PINDIR_OUTPUT, 0, &pPin);

进行“智能连接”:     

hr = m_pGB->Render(pPin);

到了这里一条完整的采集Filter Graph链路便连接完成了,在Edit Graph中自己电脑中的链路如下图


 

按下“录音”按钮:

hr = m_pMC->Run();

按下“停止”按钮: 

if (m_pMCPlayback)
            hr = m_pMCPlayback->Stop();

 if (m_pMCPlayback)
            hr = m_pMCPlayback->Stop();
 if (m_pMC)
            hr = m_pMC->StopWhenReady();

按下“播放”按钮: 

if (SUCCEEDED(GetPlaybackInterfaces()))
    {
         HRESULT hr = m_pGBPlayback->RenderFile(T2W(TEXT("C:\\test.wav")), NULL);
        if (SUCCEEDED(hr))
        {
            hr = m_pMCPlayback->Run();
        }
    }

按下“暂停”按钮:

 if (m_pMCPlayback)
    {
        hr = m_pMCPlayback->Pause();       
    }
    else
    {
        if (m_pMC)
        {
            hr = m_pMC->Pause();
        }
    }

OK了,这就是一个音频采集的SDK程序了,完整的SDK程序可到资源分享中下载

 

 

 

·

 

原创粉丝点击