1st step to understand DirectShow (DirectShow 入門 その2 )

 


 1.DirectShowの使用を、早くも、あきらめかけた。

      AUDIO_STREAM_CONFIG_CAPS なるものを使って、調べたのですが、

      この中の MaximumSampleFrequency    44100Hz

             を、見て、がっくり。

      これでは、48KHz, 96KHz sampling できません。

      他の方法を探します。 早とちりでした、涙....


      もしかしたら、出来るかも知れないと思い、プログラムを組んでみました。
    
    .............



   プログラムエラーは出ないのですが、 結果は、

   出力された 48KHz sampled wavファイルの、ファイルサイズが ゼロ byte... こら、あかん。



 
  2. あきらめないで、よかった..... 

      実は、できたようだ...

     

      

     できたようだ」
  とは、私のプログラムでは、まだ、それが確認でいないからです。



     ほら、44.1KHz samplingみたいになってしまいますので。

     できているようなのを、偉大なる Rockyで、確かめたからです。

     

     

     48KHz samplingした、ファイルを、Rockyで再生すると、帯域の端は、しっかりと、 24KHzになっているではありませんか...

     私のプログラムで、それを再現できないのは、

     ファイルをmedia playerで、先ず.再生して、

     それを、Prodigy7.1XTのドライバで、ASIOと繋いでいるからだと、思います。

3.どうやって、うまく行ったか?

   前節のプログラムを発展させたのです。

    以下の部分は、前節と全く同じです。

#include "stdafx.h"
#include "Dshow.h"
#include "mmreg.h"

HRESULT GetUnconnectedPin(
    IBaseFilter *pFilter,   // フィルタへのポインタ。
    PIN_DIRECTION PinDir,   // 検索するピンの方向。
    IPin **ppPin)           // ピンへのポインタを受け取る。
{
    *ppPin = 0;
    IEnumPins *pEnum = 0;
    IPin *pPin = 0;
    HRESULT hr = pFilter->EnumPins(&pEnum);
    if (FAILED(hr))
    {
        return hr;
    }
    while (pEnum->Next(1, &pPin, NULL) == S_OK)
    {
        PIN_DIRECTION ThisPinDir;
        pPin->QueryDirection(&ThisPinDir);
        if (ThisPinDir == PinDir)
        {
            IPin *pTmp = 0;
            hr = pPin->ConnectedTo(&pTmp);
            if (SUCCEEDED(hr))  // 既に接続済み、必要なピンではない。
            {
                pTmp->Release();
            }
            else  // 未接続、これが必要なピンである。
            {
                pEnum->Release();
                *ppPin = pPin;
                return S_OK;
            }
        }
        pPin->Release();
    }
    pEnum->Release();
    // 一致するピンが見つからなかった。
    return E_FAIL;
}

 


HRESULT AddFilterByCLSID(
    IGraphBuilder *pGraph,  // フィルタ グラフ マネージャへのポインタ。
    const GUID& clsid,      // 作成するフィルタの CLSID。
    LPCWSTR wszName,        // フィルタの名前。
    IBaseFilter **ppF)      // フィルタへのポインタを受け取る。
{
    if (!pGraph || ! ppF) return E_POINTER;
    *ppF = 0;
    IBaseFilter *pF = 0;
    HRESULT hr = CoCreateInstance(clsid, 0, CLSCTX_INPROC_SERVER,
        IID_IBaseFilter, reinterpret_cast<void**>(&pF));
    if (SUCCEEDED(hr))
    {
        hr = pGraph->AddFilter(pF, wszName);
        if (SUCCEEDED(hr))
            *ppF = pF;
        else
            pF->Release();
    }
    return hr;
}

HRESULT ConnectFilters(
    IGraphBuilder *pGraph, // フィルタ グラフ マネージャ。
    IPin *pOut,            // アップストリーム フィルタの出力ピン。
    IBaseFilter *pDest)    // ダウンストリーム フィルタ。
{
    if ((pGraph == NULL) || (pOut == NULL) || (pDest == NULL))
    {
        return E_POINTER;
    }
#ifdef debug
        PIN_DIRECTION PinDir;
        pOut->QueryDirection(&PinDir);
        _ASSERTE(PinDir == PINDIR_OUTPUT);
#endif

    // ダウンストリーム フィルタの入力ピンを検索する。
    IPin *pIn = 0;
    HRESULT hr = GetUnconnectedPin(pDest, PINDIR_INPUT, &pIn);
    if (FAILED(hr))
    {
        return hr;
    }
    // 接続を試す。
    hr = pGraph->Connect(pOut, pIn);
    pIn->Release();
    return hr;
}

HRESULT ConnectFilters(
    IGraphBuilder *pGraph,
    IBaseFilter *pSrc,
    IBaseFilter *pDest)
{
    if ((pGraph == NULL) || (pSrc == NULL) || (pDest == NULL))
    {
        return E_POINTER;
    }

    // 最初のフィルタの出力ピンを検索する。
    IPin *pOut = 0;
    HRESULT hr = GetUnconnectedPin(pSrc, PINDIR_OUTPUT, &pOut);
    if (FAILED(hr))
    {
        return hr;
    }
    hr = ConnectFilters(pGraph, pOut, pDest);
    pOut->Release();
    return hr;
}

///////////////////////////////////////////////////////////////////////////////////////
int _tmain(int argc, _TCHAR* argv[])
{
    HRESULT hr;

/*typedef struct _GUID {  //guiddef.h
    unsigned long  Data1;
    unsigned short Data2;
    unsigned short Data3;
    unsigned char  Data4[ 8 ];
} GUID;
*/
//3C78B8E2-6C4D-11D1-ADE2-0000F8754B99
 GUID CLSID_WavDest={
  0x3C78B8E2, 0x6C4D, 0x11D1, 0xAD, 0xE2, 0x00, 0x00, 0xF8, 0x75, 0x4B, 0x99};
//CLSID_WAVEParser {D51BD5A1-7548-11cf-A520-0080C77EF58A}

  GUID CLSID_WAVEParser={0xD51BD5A1,0x7548,0x11cf,0xA5,0x20,0x00,0x80,0xC7,0x7E,0xF5,0x8A};


    // COMを初期化
    CoInitialize(NULL);

IBaseFilter *pSrc = NULL, *pWaveDest = NULL, *pWriter = NULL;
IFileSinkFilter *pSink= NULL;
IGraphBuilder *pGraph;
// フィルタ グラフ マネージャを作成する。
CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
    IID_IGraphBuilder, (void**)&pGraph);

// 省略 : システム デバイス列挙子を使ってオーディオ キャプチャ
// フィルタを作成する。

ICreateDevEnum *pSysDevEnum = NULL;
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
    IID_ICreateDevEnum, (void **)&pSysDevEnum);
if (FAILED(hr))
{
    return hr;
}


IEnumMoniker *pEnumCat = NULL;
hr = pSysDevEnum->CreateClassEnumerator(CLSID_AudioInputDeviceCategory, &pEnumCat, 0);

if (hr == S_OK)
{
    // モニカを列挙する。
    IMoniker *pMoniker = NULL;
    ULONG cFetched;
   // while(pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK)
 //pEnumCat->Next(1, &pMoniker, &cFetched);
    if(pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK)//最初のデバイスを入力とする
    {
        IPropertyBag *pPropBag;
        hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag,
            (void **)&pPropBag);
        if (SUCCEEDED(hr))
        {
            // フィルタのフレンドリ名を取得するには、次の処理を行う。
            VARIANT varName;
            VariantInit(&varName);
            hr = pPropBag->Read(L"FriendlyName", &varName, 0);
            if (SUCCEEDED(hr))
            {
                // なんらかの方法で UI に名前を表示する。
    printf("%S\n",varName.pcVal);
            }
            VariantClear(&varName);

            // フィルタのインスタンスを作成するには、次の処理を行う。
          //  IBaseFilter *pFilter;
            hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter,
                (void**)&pSrc);
            // ここでグラフにフィルタを追加する。
            // 後で忘れずに pFilter を解放すること。
            pPropBag->Release();
        }
        pMoniker->Release();
    }
    pEnumCat->Release();
}
pSysDevEnum->Release();

// フィルタ グラフにオーディオ キャプチャ フィルタを追加する。
pGraph->AddFilter(pSrc, L"Capture");

     そして、ここから、オーディオ キャプチャ フィルタの出力ピンの状態を調べて、

     WaveformatEXを書き換えてしまうのです。

//接続する前に 出力pinを、調べる
IPin *pin;
hr=GetUnconnectedPin(pSrc,PINDIR_OUTPUT,&pin);
////////////////////////////////////////////////////////////////////////
IAMStreamConfig *pConfig = NULL;

 AM_MEDIA_TYPE *pmt=0;
 AUDIO_STREAM_CONFIG_CAPS scc;

//先ず、このpinで、IAMStreamConfig を取得する
//QueryInterfaceは、こんな方法もある。
 hr=pin->QueryInterface((IAMStreamConfig**)&pConfig);

if (hr==S_OK)
{
//このIAMStreamConfig から、AM_MEDIA_TYPE 、AUDIO_STREAM_CONFIG_CAPS を得ておく
  pConfig->GetFormat(&pmt);
  hr = pConfig->GetStreamCaps(0, &pmt, reinterpret_cast<BYTE*>(&scc));

//ここで、WAVEFORMATEX構造体を書き換えてしまう。 
WAVEFORMATEX *ex=reinterpret_cast<WAVEFORMATEX*>(pmt->pbFormat);

  ex->wFormatTag= WAVE_FORMAT_IEEE_FLOAT;    //WAVE_FORMAT_PCM ;//WAVE_FORMAT_IEEE_FLOAT
  ex->nChannels=2;
  ex->wBitsPerSample=32;
  ex->nSamplesPerSec=48000;
  ex->nBlockAlign=(ex->nChannels*ex->wBitsPerSample)/8;
  ex->nAvgBytesPerSec=ex->nSamplesPerSec*ex->nBlockAlign;
  ex->cbSize=0;

  hr=pConfig->SetFormat(pmt);

  if(hr!=S_OK)
  {
    printf("%s\n","fail to change sample rate");
    ::getchar();
    return 1;
  } 

}

     後は、前節のプログラムの続きを書きます。


// WavDest とファイル ライタを追加する。
AddFilterByCLSID(pGraph, CLSID_WavDest, L"WavDest", &pWaveDest);
AddFilterByCLSID(pGraph, CLSID_FileWriter, L"File Writer", &pWriter);

// ファイル名を設定する。
pWriter->QueryInterface(IID_IFileSinkFilter, (void**)&pSink);
pSink->SetFileName(L"MyWavFile.wav", NULL);

// フィルタを接続する。
ConnectFilters(pGraph, pSrc, pWaveDest);
ConnectFilters(pGraph, pWaveDest, pWriter);


    // MediaControlインターフェース取得
    IMediaControl *pMediaControl = NULL;
    pGraph->QueryInterface(IID_IMediaControl,(LPVOID *)&pMediaControl);
 
    // 録音開始
    pMediaControl->Run();

 ::getchar();
 

    // 録音終了
    pMediaControl->Stop();

    // 終了処理
    pMediaControl->Release();

pSrc->Release();
pWaveDest->Release();
pWriter->Release() ;
pSink->Release();
pGraph->Release();

CoUninitialize();

    return 0;

     次は、 私のプログラムと、waveファイルを、プログラム上で繋ぐ事を考えないと、なりませんね、超難しそう....


   H.20.2.25