Click here to Skip to main content
13,050,617 members (84,251 online)
Click here to Skip to main content
Add your own
alternative version


73 bookmarked
Posted 7 Dec 2008

Audio Capture with DirectShow - Part 1

, 11 Feb 2009
Rate this:
Please Sign up or sign in to vote.
A console program that captures audio but does not save it.


This is the first part of a two part tutorial on capturing audio via DirectShow. The first part simply shows how to connect an input device with an output device. The first part does not save the data coming through the input device; the saving portion is being left out for the second part of the tutorial.

What You Should Know First

I have written two small tutorials on how to read audio/video devices installed on a system. For those who do not know how to get hold of such devices, please take a look at these articles:

This would make it easier for you to understand this tutorial, which uses a function that is explained in the previous two tutorials, and which I will not be explaining here. Plus, a bit of understanding of COM programming is always helpful.

How to Use the Program

This is not a program that you can just download and compile and run! Rather, this tutorial will take you through the steps of how to compile the program, and at the end, you will understand how and why GraphEdit and the properties of the filters shown in GraphEdit are being used in the program. The most important step would be changing the values of some variables. This step is very, very important, and must be followed in order to run the program correctly. And, in the end, you will know how to connect input pins to output pins.

First Open GraphEdit!

Step 1: Add an Audio Capture Source

I wrote this in a rather highly un-professional way of building the program. The very simple reason being that I wanted readers to have a real "look-and-feel" of what GrapEdit.exe shows when filters are accessed, and what properties are being used during a call to the various devices. What this means is that when you place a filter, such as an Audio Capture Source, what GraphEdit shows is a filter along with its properties. Therefore, the first step is to open GraphEdit and place a filter from the Audio Capture Source category. Shown below is a snapshot of the Audio Capture Sources on my system. Look at the red square that is around one of the filters. This is the mic that resides on the front panel of my system, and is shown as "Front Mic (IDT High Definition" in GraphEdit.


Step 2: Change of Variable Values

As you can see, my system has a filter (or a device) with the "FriendlyName" of "Front Mic (IDT High Definition". Read carefully as I have deliberately written the two terms in double quoted marks. The FriendlyName of the second input device in the list is "Front Mic (IDT High Definition" which happens to be on my system. For your system, you would probably, if not definitely, see something else. I am using this device as the audio input device in my code. A line which resides in the code is as follows:

bstrDeviceName =(L"Front Mic (IDT High Definition ");
// device name as seen in Graphedit.exe

The above line of code will allow the program to use the front mic of my system for audio input. What you need to do is replace this string "Front Mic (IDT High Definition " with the FriendlyName that is being "shown" by GraphEdit on your system. Yes, include the space at the end if it is there, like I have done. After placing an audio capture source that GraphEdit has on offer, the next step would be to look at what is the capturing pin in that source. Shown below is what is available on my system:


You can see the red square and the exact term "Capture" that is also being used in the code:

hr = pInputDevice->FindPin(L"Capture",&pIn);
//Get hold of the pin "Capture", as seen in GraphEdit

It's a jump in the code from main(), but hold on, here the "Capture" property shown by GraphEdit is actually a 'pin', and needs to be enumerated and initialized in order to use it. Whatever be shown by GraphEdit on your system, you should change the value of the variable:


Step 3: Add Audio Renderers

Now, you need to add an audio rendering source which, in my case, is going to be the one shown by the red square.


Step 4: Change of Variable Values

Just like in step 1, here, you would need to change the value available in GraphEdit and modify the value in the following line of code; this is the head-phone (HP) on my system:

bstrDeviceName = (L"Speakers/HP (IDT High Definition");
// device name as seen in Graphedit.exe

Now, just like step 3, you would change the value in the following line of code:

hr = pOutputDevice->FindPin(L"Audio Input pin (rendered)",&pOut);

A peek into GraphEdit shows the following:


After making a change to the code in your program, it should be ready to compile, but first, try connecting the two sources, Audio capture and Audio renderer, in GraphEdit, and hit the Play button to test the graph. You should hear whatever is being said on the mic in the speaker.


Finally, the Rest of the Code!

Well, up to this point, you may have wondered how it is quite a puzzle to fix the variable values. But, this is because I want the coders to have a real taste of GraphEdit, the filters and their properties, and how to use these filter properties inside the code.

I am not going to discuss the code which enumerates the audio sources. The code which I am going to explain are of these functions:

//Function to initialize Input/Output devices
IBaseFilter* Device_Init(IMoniker*,IBaseFilter*);
//Function to add device to graph
void Device_Addition(IGraphBuilder*,IBaseFilter*,BSTR);
//Function to connect the two devices, in this case input and output
void Device_Connect(IBaseFilter*,IBaseFilter*);
//Function to run the graph
void Run_Graph(IMediaControl*);

The function Device_Init takes a single pointer variable which is a reference to IBaseFilter. The IBaseFilter interface allows exclusive access to a specific device represented by the pDeviceMonik pointer. In this case, the IBaseFilter references are pInputDevice and pOutputDevice. These two variables retain the address of the input and output devices that were accessed by the Device_Read(...) function.

IBaseFilter* Device_Init(IBaseFilter* pDevice)
 //Instantiate the device
 hr = pDeviceMonik->BindToObject(NULL, NULL,
 if (SUCCEEDED(hr))
  cout<<"Device initiation successful..."<<endl;
 else HR_Failed();
  hr = pGraph->AddFilter(pDevice,bstrDeviceName);// add to Graph
 if (SUCCEEDED(hr))
  cout<<"Device addition to graph successful..."<<endl;
 else HR_Failed();
 return pDevice;

The function Device_Addition(...); takes pointers to the IBuilderGraph, IBaseFilter, and BSTR types to actually add the audio devices to the graph. Here is a call to add the input device to the graph:

//add device to graph
void Device_Addition(IGraphBuilder* pGraph,IBaseFilter* pDevice,BSTR bstrName)
    HRESULT hr;
    hr = pGraph->AddFilter(pDevice,bstrName);
     wcout<<"Addition of "<<bstrName<<" successful..."<<endl;
     else HR_Failed(hr);

After making calls for output devices similar to those for input devices, we can now connect the two. In GraphEdit, we connect the two filters (devices) through their pins. Here, we do it manually in the Device_Connect(...) function.

//Connect input to output

The function Device_Connect(...) takes the two input devices and connects them. Here, we first need to enumerate the input and output device pins:

IEnumPins *pInputPin = NULL,*pOutputPin  = NULL;
// Pin enumeration

IPin *pIn = NULL, *pOut = NULL;// Pins

These pins need to be accessed, and this is done by an enumeration:

hr = pInputDevice->EnumPins(&pInputPin);// Enumerate the pin

The above is also repeated for pOutputDevice. The next step is accessing the enumerated pins, which is done by a call to FindPin(...). Remember when I said "It's a jump in the code from main()"? This is the point where the jump has to be made. The pins that are exposed by the device drivers are shown in GraphEdit, and that is why we had to change the values so that we correctly access the pins of the specific device. This method takes a string argument, and searches whether there is a pin by the name of either "Capture" or "Audio Input pin (rendered)" for the code working on my system. After the changes you make, the string values would certainly be different for your system.

//Get hold of the pin "Capture", as seen in GraphEdit
hr = pInputDevice->FindPin(L"Capture",&pIn);

Now, the two pins are ready to be connected, and are connected by the following code:

//Connect the input pin to output pin
hr = pIn->Connect(pOut,NULL);

The last and final step is to run the graph. This is done by the reference pointer variable pControl of type IMediaControl. This interface basically allows the running, stopping, and various other control functionalities of the graph.

void Run_Graph(IMediaControl* pControl)
    HRESULT hr;
    hr = pControl->Run();// Now run the graph, i.e. start listening!
      cout<<"You must be listening to something!!!"<<endl;
    else HR_Failed(hr);


The last step completes the code, and hopefully there will be a successful run!

The code is not professional by any means, and should be optimized whenever this is a need for a professional approach. The task was to explain the code in the simplest possible way, and to cater for very limited errors. The error handling part is very basic, and should not be used for professional applications as it just displays an error text. I expect bugs to appear, and will highly appreciate changes to the code itself!

A Note on bool Bstr_Compare(BSTR,BSTR)

I had to update the previous code. There was a programming error which was pointed out by Igorka, which I highly appreciate, and I have fixed the error.

if(Bstr_Compare(varName.bstrVal,bstrDeviceName) == true)
//make a comparison

This calls the function Bstr_Compare(...) and checks for the returned boolean value. If 'true' was returned, it means the required device was found.

Rest of the code for Bstr_Compare:

bool Bstr_Compare(BSTR bstrFilter,BSTR bstrDevice)
     bool flag = true;
     int strlenFilter = SysStringLen(bstrFilter);//set string length
     int strlenDevice = SysStringLen(bstrDevice);//set string length
     char* chrFilter = (char*)malloc(strlenFilter+1);// allocate memory
     char* chrDevice = (char*)malloc(strlenDevice+1);// allocate memory
     int j = 0;

     if (strlenFilter!=strlenDevice)
     //if the strings are of not the same length, 
     //means they totall different strings
          flag = false;
          //sety flag to false to indicate "not-same" strings
          for(; j < strlenFilter;j++)
          //now, copy 1 by 1 each char 
          //to chrFilter and chrDevice respectively
               chrFilter[j] = (char)bstrFilter[j];//copy
               chrDevice[j] = (char)bstrDevice[j];//copy
          chrFilter[strlenFilter] = '\0';//add terminating character
          chrDevice[strlenDevice] = '\0';//add terminating character
          for(j=0; j < strlenFilter;j++)//check loop
               if(chrFilter[j] != chrDevice[j])
               //check if there are chars that are not samne
                    flag = false;
                    //if chars are not same, set flag 
                    //to false to indicate "not-same" strings

          if(flag == true && j == strlenFilter-1)
          //see if we went through the 'check loop' 
               flag = true;//means strings are same
     return flag;

The Code is Built with the Following

  • Windows Server 2008
  • Microsoft Visual Studio 2008
  • DirectX 10.1
  • Microsoft Windows SDK 6.1



  • 7th December, 2008: Initial post.
  • 13th December, 2008: Article updated.
    • Added new function Bstr_Compare(...) that correctly compares two BSTR type strings.
    • Added the functions SysAllocString(...) and SysFreeString(...) to correctly allocate string values to BSTR type variables and free them, respectively.
  • 8th February, 2008: Aritcle updated.
    • Fixed Bstr_Compare(...) that correctly compares two BSTR type strings.


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Engineer -
Pakistan Pakistan
-I am a die hard fan of C/C++ programming. It is poetry and I love the poems of C++.
-I tell people that C# is "NOTHING" compared to C++ and I always say that!

My interest lie in Signal Processing, Image Processing,Video Processing, Pattern Recognition, DSP, Communications. Though I have no ONE stop in terms of interests.

You may also be interested in...

Comments and Discussions

QuestionThanks its really great tutorial.. But i'm getting an error Pin
anchal_scorpio17-Nov-14 23:06
memberanchal_scorpio17-Nov-14 23:06 
QuestionThanks it really help a lot,but I still have some problem Pin
Shane_Wayne15-Jul-12 20:38
memberShane_Wayne15-Jul-12 20:38 
AnswerRe: Thanks it really help a lot,but I still have some problem Pin
wajih_ullah15-Sep-12 0:13
memberwajih_ullah15-Sep-12 0:13 
QuestionBstr_Compare Pin
igorka3-Feb-09 5:08
memberigorka3-Feb-09 5:08 
AnswerRe: Bstr_Compare Pin
wajih_VCP3-Feb-09 8:10
memberwajih_VCP3-Feb-09 8:10 
Thanks for the post. I must admit you pointed out another programming error. I must regard the fact that I had to change the code too. But I had a problem wcscmp. Whenever I tried to use it, either I got junk or I got a 'cannot read memory' error, reason? Well click on this link to find out[^]

According to MSDN the function is not compatible with the current OS version I use. i.e. 32bit Windows Server. So that is why, may be, I kept getting errors.

Anyway the strings were not corrupted as you had mention, but I did not add a termination character, therefore I changed the code is working well. I am posting the code for your review too which I highly appreciate and once I update the code, I am going to mention your name and your post.

char* chrFilter = (char*)malloc(strlenFilter+1);// allocate memory
char* chrDevice = (char*)malloc(strlenDevice+1);// allocate memory

for(; j < strlenFilter;j++)//now, copy 1 by 1 each char to chrFilter and chrDevice respectively
chrFilter[j] = (char)bstrFilter[j];//copy
chrDevice[j] = (char)bstrDevice[j];//copy

chrFilter[strlenFilter] = '\0';//add terminating character
chrDevice[strlenDevice] = '\0';//add terminating character

Above is the change code,

So when the article is updated, your post will be there too.



GeneralWindows Server dev Pin
Member 145512523-Dec-08 4:53
memberMember 145512523-Dec-08 4:53 
GeneralRe: Windows Server dev Pin
wajih_VCP23-Dec-08 6:38
memberwajih_VCP23-Dec-08 6:38 
GeneralNice Pin
Just someone else9-Dec-08 2:06
memberJust someone else9-Dec-08 2:06 
GeneralRe: Nice Pin
wajih_VCP9-Dec-08 5:43
memberwajih_VCP9-Dec-08 5:43 
GeneralAwesome! Pin
Rene Pilon7-Dec-08 14:54
memberRene Pilon7-Dec-08 14:54 
GeneralRe: Awesome! Pin
wajih_VCP7-Dec-08 20:10
memberwajih_VCP7-Dec-08 20:10 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.170713.1 | Last Updated 11 Feb 2009
Article Copyright 2008 by wajih_ullah
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid