Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / Win32

Muting all microphones on WinXP

4.76/5 (6 votes)
12 Aug 2012CPOL4 min read 26.9K   888  
How to enumerate and mute all microphone components under all mixer devices using the Audio Mixer API

Introduction  

This article is the Windows XP counterpart of my other article on how to mute all microphones on Windows Vista/7 systems.

I recently had to write a application that among other things makes sure all active/enabled audio capture device endpoints (usually microphones), on Windows XP/Vista/7, are guaranteed muted while it runs. To accomplish this I wrote two pieces of software: a system service that implements a timer and a console application that the timer's callback invokes every time a set interval elapses.

The console application has two versions. The main version, presented in my previous article, uses the Core Audio API, introduced in Windows Vista, and works on all Windows OS versions supporting the Core Audio API (Windows Vista and later). The other version, presented in this article, works on Windows XP, and maybe earlier Windows versions (untested!), using the Windows Multimedia APIs (Mixer API).

Background  

What is now referred to as "Legacy Audio and Video" APIs have a long history on the Windows platform starting off on some of the earliest version of Windows OS, e.g. an early implementation of DirectSound was available on Windows 95.

What I found most convenient to work with, in programmatically muting microphones on Windows XP, is the Audio Mixers API which is part of the Multimedia Audio API, itself part of the Windows Multimedia legacy APIs. 

The Audio Mixer Reference page documents a number of functions (e.g. mixerOpen(), mixerGetLineControls(), mixerGetControlDetails(), mixerSetControlDetails(), mixerGetLineInfo()) that provide ways for the programmer to access and manipulate the mixer controls representing the state of various sound system components like speakers and microphones. 

One obvious conceptual problem that this API has (and this could be part of the reason it got superseded on Vista by the CoreAudio API) is that it is actually controlling the state of the sound system components (e.g. volume, mute state) by controlling the graphical widgets/controls (shown on audio mixer type windows) that represent them, instead of accessing structures at a lower level. However, it does seem to be doing its job reliably.

Please note that although the Audio Mixer API is still available on later Windows versions (e.g. Vista/7), it is not the recommended way to control audio components although it may, in some cases, actually work as expected if the executables accessing it are run in XP compatibility mode. The reason for this is explained on this thread by Microsoft's own Larry Osterman: the Audio Mixer API has been virtualized in post-XP Windows versions and therefore it may not work as expected even when run in compatibility mode.

I have actually run the executable produced by this code in XP compatibility mode on a Win7 x64 installation and it did work properly in some (but not all) variations of the code presented below (variations that all worked properly on XP).

Using the code  

I did not start my work on this on a clean slate. My starting point was Audio Mixer API code posted by Microsoft on this KB article. There are three functions listed there (UnMute(), SetVolume() and SelectMic()). The function most relevant to what I wanted to do is UnMute().

The original version of the UnMute() function is listed below:

C++
void UnMute()
{
   // Open the mixer device
   HMIXER hmx;
   mixerOpen(&hmx, 0, 0, 0, 0);
 
   // Get the line info for the wave in destination line
   MIXERLINE mxl;
  mxl.cbStruct = sizeof(mxl);
  mxl.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
  mixerGetLineInfo((HMIXEROBJ)hmx, &mxl, MIXER_GETLINEINFOF_COMPONENTTYPE);
 
   // Now find the microphone source line connected to this wave in
   // destination
   DWORD cConnections = mxl.cConnections;
   for(DWORD j=0; j<cConnections; j++){
      mxl.dwSource = j;
      mixerGetLineInfo((HMIXEROBJ)hmx, &mxl, MIXER_GETLINEINFOF_SOURCE);
      if (MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE == mxl.dwComponentType)
         break;
   }
   // Find a mute control, if any, of the microphone line
   LPMIXERCONTROL pmxctrl = (LPMIXERCONTROL)malloc(sizeof MIXERCONTROL);
   MIXERLINECONTROLS mxlctrl = {sizeof mxlctrl, mxl.dwLineID,
      MIXERCONTROL_CONTROLTYPE_MUTE, 1, sizeof MIXERCONTROL, pmxctrl};
   if(!mixerGetLineControls((HMIXEROBJ) hmx, &mxlctrl,
MIXER_GETLINECONTROLSF_ONEBYTYPE)){
      // Found, so proceed
      DWORD cChannels = mxl.cChannels;
      if (MIXERCONTROL_CONTROLF_UNIFORM & pmxctrl->fdwControl)
         cChannels = 1;
 
      LPMIXERCONTROLDETAILS_BOOLEAN pbool =
         (LPMIXERCONTROLDETAILS_BOOLEAN) malloc(cChannels * sizeof
MIXERCONTROLDETAILS_BOOLEAN);
      MIXERCONTROLDETAILS mxcd = {sizeof(mxcd), pmxctrl->dwControlID,
cChannels, (HWND)0,
         sizeof MIXERCONTROLDETAILS_BOOLEAN, (LPVOID) pbool};
      mixerGetControlDetails((HMIXEROBJ)hmx, &mxcd,
MIXER_SETCONTROLDETAILSF_VALUE);
      // Unmute the microphone line (for both channels)
      pbool[0].fValue = pbool[cChannels - 1].fValue = 0;
      mixerSetControlDetails((HMIXEROBJ)hmx, &mxcd,
MIXER_SETCONTROLDETAILSF_VALUE);
 
    free(pmxctrl);
    free(pbool);
   }
  else
    free(pmxctrl);
 
   mixerClose(hmx);
}

I started off by reversing the functionality of this function so that it actually mutes the discovered microphone instead of un-muting it. This is pretty simple to do; just change a '0' to a '1'.

C++
// Unmute the microphone line (for both channels)
pbool[0].fValue = pbool[cChannels - 1].fValue = 1;

As you will notice, the Microsoft logic above only cares to discover the first microphone type component on the first mixer device. We can see this in the following two snippets:

C++
if (MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE == mxl.dwComponentType)
    break;

In the code snippet right above they just break out of the loop once the first microphone has been found.  Granted, this will suffice on most systems but what is there's more microphones?

C++
mixerOpen(&hmx, 0, 0, 0, 0);

In the second code snippet right above they only care to open the mixer device at index 0. What if there's more soundcards or some other type of connected sound device? 

To address these issues and make my code mute all microphones, whether on the same or different mixer devices, I modified the code as follows: 

C++
void MuteAllMixerMics()
{
    HMIXER hmx;

    UINT i = 0;
    while (mixerOpen(&hmx, i++, 0, 0, 0) == MMSYSERR_NOERROR)
    {
        // Get the line info for the wave in destination line
        MIXERLINE mxl;
        mxl.cbStruct = sizeof(mxl);
        mxl.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
        mixerGetLineInfo((HMIXEROBJ)hmx, &mxl, MIXER_GETLINEINFOF_COMPONENTTYPE);

        // Now find the microphone source line connected to this wave in
        // destination
        DWORD cConnections = mxl.cConnections;
        for(DWORD j = 0; j < cConnections; j++)
        {
            mxl.dwSource = j;
            mixerGetLineInfo((HMIXEROBJ)hmx, &mxl, MIXER_GETLINEINFOF_SOURCE);
            if (MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE == mxl.dwComponentType)
            {
                //Find a mute control, if any, of the microphone line
                LPMIXERCONTROL pmxctrl = (LPMIXERCONTROL)malloc(sizeof MIXERCONTROL);
                MIXERLINECONTROLS mxlctrl = {sizeof mxlctrl, mxl.dwLineID, 
                MIXERCONTROL_CONTROLTYPE_MUTE, 1, sizeof MIXERCONTROL, pmxctrl};
                
                if(!mixerGetLineControls((HMIXEROBJ) hmx, &mxlctrl,
                                         MIXER_GETLINECONTROLSF_ONEBYTYPE))
                {
                    //Found, so proceed
                    DWORD cChannels = mxl.cChannels;
                    if (MIXERCONTROL_CONTROLF_UNIFORM & pmxctrl->fdwControl)
                        cChannels = 1;
                    
                    LPMIXERCONTROLDETAILS_BOOLEAN pbool = (LPMIXERCONTROLDETAILS_BOOLEAN) 
                                                          malloc(cChannels * 
                                                          sizeof MIXERCONTROLDETAILS_BOOLEAN);
                    
                    MIXERCONTROLDETAILS mxcd = {sizeof(mxcd), pmxctrl->dwControlID, cChannels, (HWND)0,
                                                sizeof MIXERCONTROLDETAILS_BOOLEAN, (LPVOID) pbool};
                    
                    mixerGetControlDetails((HMIXEROBJ)hmx, &mxcd, MIXER_SETCONTROLDETAILSF_VALUE);
                    
                    //Mute the microphone line (for both channels)
                    pbool[0].fValue = pbool[cChannels - 1].fValue = 1;
                    	
                    mixerSetControlDetails((HMIXEROBJ)hmx, &mxcd, MIXER_SETCONTROLDETAILSF_VALUE);
                    
                    free(pmxctrl);
                    free(pbool);
                }
                else
                    free(pmxctrl);
            }
		}
        
        mixerClose(hmx);
    }
}

Points of Interest

The code above repeats the microphone discovery and muting operations for each mixer device known by the system. It iterates over available mixer devices like this:

C++
while (mixerOpen(&hmx, i++, 0, 0, 0) == MMSYSERR_NOERROR)

According to its reference, mixerOpen() will return MMSYSERR_NOERROR everytime it finds a mixer device under the incremented UINT index i. So we loop until mixerOpen() returns something else (i.e. an error) and search each found mixer for possibly more than one microphones like this:

C++
DWORD cConnections = mxl.cConnections;
for(DWORD j = 0; j < cConnections; j++)
{
    mxl.dwSource = j;
    mixerGetLineInfo((HMIXEROBJ)hmx, &mxl, MIXER_GETLINEINFOF_SOURCE);
    if (MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE == mxl.dwComponentType)
    {
        ...
    }
    ...
}

How to compile and run 

I created the attached full project using Visual Studio 2010; so you will need to either have VS 2010 installed on create a new project with your own version of Visual Studion using the provided source code files.

Just compile and run the executable produced in the build directory either on XP's command line or by double clicking it on Windows Explorer.

History 

This is V 1.0 of the article.

License

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