Using an Audio Delay DMO Within a DirectShow Application
Before we explore the implementation specifics of a DMO, it ll be useful to examine how a DMO is instantiated, connected, and executed within a DirectShow filter graph. In this chapter, we ll be working with a very simple audio DMO, SimpleDelay, which adds a measured audio delay to an audio stream passed through it. The duration of the delay, 2000 milliseconds, is hard-coded into the DMO, as is the wet/dry mix (the mix ratio between the delayed signal and the original signal), which is set at 25 percent. A full-featured DMO would have COM interfaces to allow you to change these parameters in real time, but SimpleDelay doesn t have them. (At this point, you should compile the SimpleDelay sample DMO, along with the DShowDemo application that s supplied with it.)
When you launch the DShowDemo (which uses the DMO), the application will allow you to select a valid WAV file. After a WAV file has been located, the Play button will be enabled, as shown in Figure 13-1.
Figure 13-1. The DShowDemo application that uses the SimpleDelay DMO
During WAV playback, you ll be able to hear the delayed audio signal, mixed in with the main audio signal. It ll be somewhat faint because the wet/dry mix is set at 25 percent. You can change that value by opening the Delay.h file and recompiling the SimpleDelay DMO. You can also do the same thing with the delay time. It s not real-time adjustment, but it s better than nothing.
The source code for DShowDemo is very brief. It does little more than manage the dialog box and build the filter graph. The filter graph building takes place in the LoadWavFile function, as shown here:
HRESULT LoadWavFile(HWND hwnd, LPTSTR szFileName) { IBaseFilter *pDmoFilter = NULL; HRESULT hr; CleanUp(); // Restore to original state // Create the Filter Graph Manager and query for interfaces. hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&g_pGraph); if (FAILED(hr)) { return hr; } g_pGraph->QueryInterface(IID_IMediaControl, (void **)&g_pMediaControl); g_pGraph->QueryInterface(IID_IMediaEventEx, (void **)&g_pEvent); g_pEvent->SetNotifyWindow((OAHWND)hwnd, WM_GRAPHNOTIFY, 0); // Create the DMO Wrapper filter. hr = CoCreateInstance(CLSID_DMOWrapperFilter, NULL, CLSCTX_INPROC, IID_IBaseFilter, (void **)&pDmoFilter); if (FAILED(hr)) { return hr; } IDMOWrapperFilter *pWrap; pDmoFilter->QueryInterface(IID_IDMOWrapperFilter, (void **)&pWrap); hr = pWrap->Init(CLSID_Delay, DMOCATEGORY_AUDIO_EFFECT); pWrap->Release(); if (SUCCEEDED(hr)) { hr = g_pGraph->AddFilter(pDmoFilter, L"SimpleDelay DMO ); #ifdef _UNICODE hr = g_pGraph->RenderFile(szFileName, NULL); #else WCHAR wszFile[MAX_PATH]; int result = MultiByteToWideChar(CP_ACP, 0, szFileName, -1, wszFile, MAX_PATH); if (result == 0) { hr = E_FAIL; } else { hr = g_pGraph->RenderFile(wszFile, NULL); } #endif } pDmoFilter->Release(); return hr; }
In this function, a filter graph builder object is instantiated, and its media control and event interfaces are queried. Once that overhead is complete, the function makes a CoCreateInstance call with a class ID of CLSID_DMO WrapperFilter. This call instantiates a DMO wrapper filter, the generic wrapper for all DMO objects. Once the function has queried for the IDMOWrapperFilter interface, it invokes the interface s Init method, passing the GUID of the requested DMO. This operation assigns the appropriate DMO to the DirectShow wrapper filter. The second parameter on the Init call is a category identifier used by DirectShow when it enumerates the DMOs looking for one that matches the passed GUID.
At this point, the DMO is treated like any other DirectShow filter. It s added to the filter graph with a call to AddFilter, and it s put into the render path as a transform filter when the RenderFile method is executed, resulting in a filter graph that looks like Figure 13-2.
Figure 13-2. The SimpleDelay DMO, which looks like any other DirectShow filter when invisibly wrapped inside the DirectShow Wrapper filter
Although this is a very simple exploration of an audio DMO inside a filter graph, this code contains everything you ll need to use DMOs within your own DirectShow applications. If you need to learn which DMOs are available for use, you can enumerate them using the System Device Enumerator, which was explored in Chapter 4. (There s also a DMOEnum sample program in the DirectX SDK that does much the same thing.) Instead of passing the GUID for classes of DirectShow filters in the call, pass a DMO enumerator. The DMO audio effects can be enumerated with the value DMOCATEGORY_AUDIO_EFFECT, while video effects can be enumerated with the value DMOCATEGORY_ VIDEO_EFFECT. Table 13-1 lists the DMO enumerators.
GUID | Description |
DMOCATEGORY_AUDIO_DECODER | Audio decoder |
DMOCATEGORY_AUDIO_EFFECT | Audio effect |
DMOCATEGORY_AUDIO_ENCODER | Audio encoder |
DMOCATEGORY_VIDEO_DECODER | Video decoder |
DMOCATEGORY_VIDEO_EFFECT | Video effect |
DMOCATEGORY_VIDEO_ENCODER | Video encoder |
DMOCATEGORY_AUDIO_CAPTURE_EFFECT | Audio capture effect |