Microsoft DirectX 8.1 (C++)

Creating an Audio Capture Graph

The first step for an audio capture application is to build a filter graph. The configuration of the graph depends on the type of file that you want to create.

The WavDest filter is provided as an SDK sample. To use it, you must build and register the filter.

To use the ASF Writer filter, you must install the Windows Media SDK and obtain a software key to unlock the filter. For more information, see Windows Media Applications.

You can build the filter graph using the Capture Graph Builder object, or you can build the graph "manually"; that is, have the application programmatically add and connect each filter. This article describes the manual approach. For more information about using the Capture Graph Builder, see How to Write a Capture Application. Much of the information in that article applies to audio-only graphs.

Adding the Audio Capture Device

Because the Audio Capture Filter communicates with a specific hardware device, you cannot simply call CoCreateInstance to create the filter. Instead, use the System Device Enumerator to enumerate all of the devices in the "Audio Capture Sources" category, which is identified by the class identifier CLSID_AudioInputDeviceCategory.

The System Device Enumerator returns a list of monikers for the devices; each moniker's friendly name corresponds to the name of the device. Choose one of the returned monikers and use it to create an instance of the Audio Capture Filter for that device. Add the filter to the filter graph. The user's preferred audio recording device appears first in the moniker list. (The user selects a preferred device by clicking Sounds and Multimedia in Control Panel.)

For more information, see Using the System Device Enumerator.

Adding the Multiplexer and the File Writer

An audio capture graph must contain a multiplexer and a file writer.

A multiplexer is a filter that combines one or more streams into a single stream with a particular format. For example, the AVI Mux filter combines audio and video streams into an interleaved AVI stream. For audio capture, there is typically only a single audio stream, but the audio data must still be packaged into a format that can be saved to disk, which requires a multiplexer. The choice of multiplexer depends on the target format:

A file writer is a filter that writes incoming data to a file. For AVI or WAV files, use the File Writer Filter. For WMA files, the ASF Writer acts as both multiplexer and file writer.

After you create the filters and add them to the graph, connect the Audio Capture Filter's output pin to the multiplexer's input pin, and connect the multiplexer's output pin to the filter writer's input pin (assuming these are separate filters). To specify the file name, query the file writer for the IFileSinkFilter interface and call the IFileSinkFilter::SetFileName method.

Example Code

The following example shows how to build an audio capture graph using the WavDest filter. The same principles apply for the other file types.

IPin *GetPin(IBaseFilter *pFilter, PIN_DIRECTION PinDir);
// The GetPin function is shown in the article "Enumerating Pins."

// ConnectTwoFilters: Connects two filters using the first pin on each.
HRESULT ConnectTwoFilters(IGraphBuilder *pGraph, IBaseFilter *pSrc,
    IBaseFilter *pDest)
{
    IPin *pOut = GetPin(pSrc, PINDIR_OUTPUT); 
    if (!pOut) return E_FAIL;
    IPin *pIn = GetPin(pDest, PINDIR_INPUT);
    if (!pOut) {
        pIn->Release();
        return E_FAIL;
    }
    HRESULT hr = pGraph->ConnectDirect(pOut, pIn, NULL);
    pIn->Release();
    pOut->Release();
    return hr;
}

// AddFilterByClsid: Given a CLSID, adds a filter to the graph. 
// Returns a pointer to the filter in ppF.
HRESULT AddFilterByClsid(IGraphBuilder *pGraph, LPCWSTR wszName, 
    const GUID& clsid, IBaseFilter **ppF)
{
    *ppF = NULL;
    HRESULT hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER,
       IID_IBaseFilter, (void**)ppF);
    if (SUCCEEDED(hr))
    {
        hr = pGraph->AddFilter((*ppF), wszName);
    }
    return hr;
}

void BuildAudioCaptureGraph()  // 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);
}

This example takes advantage of the fact that each filter involved has exactly one pin to connect in each direction. Therefore, for each pair of filters, it simply finds the first output pin on the upstream filter and the first input pin on the downstream filter, and connects them. The GetPin function used here is shown in the article Enumerating Pins.