1. 程式人生 > >音訊採集(VC++ SDK實現)

音訊採集(VC++ SDK實現)

當根據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程式了

相關推薦

音訊採集VC++ SDK實現

當根據DirectShow程式包中AudioCap例子寫了自己的MyAudioMFC程式後,雖然程式本身可以很好的執行,也可以實現“邊錄邊聽”,並實現播放;但感覺還是很有必要把思路理清一下;將其MFC改寫為普通函式API的SDK方式後,一般理解起來就更容易些。且程式介面也

Android音視訊-視訊採集Camera2功能實現

這一篇文章我們要實現Camera實現的等一些功能。熟悉Camera2API的使用,著重瞭解我們前面沒有深入瞭解的視訊錄製相關的內容。 基本功能實現 切換攝像頭 這個的實現和Camera API的步驟一摸一樣。只是換了一個API而已。Camera是通

C#音訊採集 筆記

using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Threading; using Microsoft.

二十四進制編碼串轉換為32位無符號整數C語言實現

bool while open 參數錯誤 hint div 第一個字符 bsp opened typedef int BOOL; #define TRUE 1; #define FALSE 0; #define UINT_MAX 0xffffffff

UEditor上傳圖片到七牛C#後端實現

nco nag manage ora 個人 finall code 七牛存儲 json 由於個人網站空間存儲有所以選擇將圖片統一存儲到七牛上,理由很簡單 1 免費10G 的容量 ,對個人網站足夠用 2 規範的開發者文檔 和完善的sdk(幾乎所有熱門語言sdk)

day1作業二:多級菜單操作函數實現

數據庫 語句 會有 是否 臺灣 inpu return .com {} 作業二:多級菜單 (1)三級菜單 (2)可以次選擇進入各子菜單 (3)所需新知識點:列表、字典 要求:輸入back返回上一層,輸入quit退出整個程序 本示例的三級菜單是一個yaml文件格式,格式如下:

IOS 自定義按鈕代碼實現+九宮格

uifont 排列 end uiview height iyu void rec name 在一些下載應用裏整個頁面都是按鈕,有好多好多,但是仔細觀察不難發現他們很有規律。就像下面一樣?? 很有規律的排列在屏幕上,那麽這需要我們怎麽去做能。 正如標題,我們需要了解兩個知

深思 PHP 數組遍歷的差異array_diff 的實現

函數 沒有 但是 iss 組織 func 找到 china 速度 function array_diff($array_1, $array_2) { $diff = array(); foreach ($array_1 as $k => $v1) {

堆排序C語言實現

names 博客 鏈接 c語言實現 建立 ron 要求 clas [1] 之前的博客介紹介紹了數組的兩種排序算法:插入排序和歸並排序(採用遞歸),見鏈接http://blog.csdn.net/u013165521/article/detai

算法 - 棧與隊列C 語言實現

元素 語言 訪問規則 並且 下標 出棧 數據結構 規則 算法 目標: 理解 棧 與 隊列 這兩種數據結構, 並且知道如何應用。 算法 + 數據結構 = 程序 一、堆棧 堆棧是一組元素的集合,類似於數組,但數組可以按下標訪問,堆棧的訪問規則只能為push 與

碼海拾遺:二叉樹的遍歷遞歸實現

code out pos 高度 tor 個數 htc alt include   二叉樹是一種特殊的樹結構:每個節點最多有兩個子節點。   二叉樹的性質:   (1)二叉樹第i層的節點數目最多為 2{i-1} (i≥1)。   (2)深度為k的二叉樹至多有2{k}-1個結點

列車調度隊列實現

ini == stream int base clu pre new ges 思路:要實現輸出很簡單,關鍵是時間的限制,當時提交了很多次,一直是超時,後來改用二分法,才交上去。 #include<stdio.h> #include<iostream&g

關於樹如laytree,ztree節點數據的組裝遞歸實現

又是 就是 輸出 遍歷 arc urn -1 pan == 在一些項目中需要用到樹形結構來表示一些層級關系時候,可用如在layui框架中的laytree或者ztree來完成效果如圖 往往在獲取節點數值時候所需要的數據的結構比較復雜,比如laytree和ztree的節點數據

歸並排序C語言實現

ngs 基本 merge 兩個 它的 efi 分別是 void rec 合並排序(MERGE SORT)是又一類不同的排序方法,合並的含義就是將兩個或兩個以上的有序數據序列合並成一個新的有序數據序列,因此它又叫歸並算法。 它的基本思想就是假

Java基礎------楊輝三角遞歸實現

orm pad 兩個 ref 合數 概述 bsp http round 第一列 第二列 第三列 第四列 第五列 第六列 第七列 第八列 第九列 第十列 第十一列 ...... 第一行 第一個空格 第二個空格 第三個空格 第四個空格 第五個空格 1

發布一個麻將作弊器Qt 繪圖實現

ref logs 小工具 href post uri gpo 算法 密碼 麻將作弊器是通過應用麻將胡牌算法和聽牌算法的編寫的一個小工具,使用Qt繪制實現。 https://pan.baidu.com/s/1eTDXeIA 鏈接:https://pan.baidu.com/s

數據結構-線性表的鏈式存儲相關算法C語言實現

存儲位置 lib 方法 lis 逆序 順序 程序 查詢 c語言 鏈表的簡單介紹 為什麽需要線性鏈表 當然是為了克服順序表的缺點,在順序表中,做插入和刪除操作時,需要大量的移動元素,導致效率下降。 線性鏈表的分類 按照鏈接方式: 按照實現角度: 線性鏈表的創建和簡單遍歷

下壓堆棧鏈表實現

list true urn div AC 元素 next 添加 定義 import java.util.Iterator; public class Stack<Item> { private Node first; //棧頂 privat

【HTML+CSS+JavaScript】實現待辦事項純DOM實現

todolist ive relative spa java set 釣魚 scrip input 需求:實現待辦事項 <!DOCTYPE html> <html lang="en"> <head> <meta chars

排序C語言實現

內部排序 利用 int 分治 arr 個數 size quic 外部排序 讀數據結構與算法分析 插入排序 核心:利用的是從位置0到位置P都是已排序的 所以從位置1開始排序,如果當前位置不對,則和前面元素反復交換重新排序 實現 void InsertionSort