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

Muting All Microphones on Vista/Win7

4.89/5 (9 votes)
12 Aug 2012CPOL2 min read 52K   1.9K  
How to enumerate and mute all audio capture endpoints using Core Audio API

Introduction

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

Background

My target platforms for this project were XP, Vista and Win7. In order to do what I wanted, I had two options. Either use XP/Legacy Audio APIs (e.g. Mixer API) that should still work, alas not that efficiently, on all three Windows versions but may be phased out in upcoming Win8 and later or use the new and lower level Core Audio APIs introduced with Vista. As XP is not supported by these APIs, the latter approach would require a separate implementation for XP, which in my case was Microsoft's Audio Mixer APIs (<mmsystem.h>, part of <Windows.h>). This article will present the Vista/Win7 version of the console application.

Using the Code

The console app is based on the EndpointVolume Microsoft sample that comes with SDK 7.1 under multimedia\audio. I have modified this sample significantly mainly because it was written as a general purpose console application that received a number of command line options that were irrelevant to my purpose but also because the code only dealt with output endpoints (like Speakers) and not input endpoints which was what I was interested in.

Most of the work is done in the console app's wmain() function which you can see below. Code includes explanatory inline comments.

C++
int wmain(int argc, wchar_t* argv[])
{
    //
    //  A GUI application should use COINIT_APARTMENTTHREADED instead of COINIT_MULTITHREADED.
    //
    HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    if (FAILED(hr))
    {
        printf("Unable to initialize COM: %x\n", hr);
        goto Exit;
    }
 
    IMMDeviceEnumerator *deviceEnumerator = NULL;
 
    //We initialize the device enumerator here
    hr = CoCreateInstance( __uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, 
                           IID_PPV_ARGS(&deviceEnumerator) );
    if (FAILED(hr))
    {
        printf("Unable to instantiate device enumerator: %x\n", hr);
        goto Exit;
    }

    IMMDeviceCollection *deviceCollection = NULL;
    
    //Here we enumerate the audio endpoints of interest (in this case audio capture endpoints)
    //into our device collection. We use "eCapture"
    //for audio capture endpoints, "eRender" for 
    //audio output endpoints and "eAll" for all audio endpoints 
    hr = deviceEnumerator->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, 
                                               &deviceCollection );
    if (FAILED(hr))
    {
        printf("Unable to retrieve device collection: %x\n", hr);
        goto Exit;
    }

    UINT deviceCount;
 
    hr = deviceCollection->GetCount(&deviceCount);
    if (FAILED(hr))
    {
        printf("Unable to get device collection length: %x\n", hr);
        goto Exit;
    }

    IMMDevice *device = NULL;
    
    //
    //This loop goes over each audio endpoint in our device collection,
    //gets and diplays its friendly name and then tries to mute it
    //
    for (UINT i = 0 ; i < deviceCount ; i += 1)
    {
        LPWSTR deviceName;
 
        //Here we use the GetDeviceName() function provided with the sample 
        //(see source code zip)
        deviceName = GetDeviceName(deviceCollection, i); //Get device friendly name

        if (deviceName == NULL) goto Exit;
        
        printf("Device to be muted has index: %d and name: %S\n", i, deviceName);

        //this needs to be done because name is stored in a heap allocated buffer
        free(deviceName);

        device = NULL;

        //Put device ref into device var
        hr = deviceCollection->Item(i, &device);
        if (FAILED(hr))
        {
            printf("Unable to retrieve device %d: %x\n", i, hr);
            goto Exit;
        }

        //This is the Core Audio interface of interest
        IAudioEndpointVolume *endpointVolume = NULL;
 
        //We activate it here
        hr = device->Activate( __uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, 
                               reinterpret_cast<void **>(&endpointVolume) );
        if (FAILED(hr))
        {
            printf("Unable to activate endpoint volume on output device: %x\n", hr);
            goto Exit;
        }

        hr = endpointVolume->SetMute(TRUE, NULL); //Try to mute endpoint here
        if (FAILED(hr))
        {
            printf("Unable to set mute state on endpoint: %x\n", hr);
            goto Exit;
        }
        else
            printf("Endpoint muted successfully!\n");
    }
 
Exit: //Core Audio and COM clean up here
    SafeRelease(&deviceCollection);
    SafeRelease(&deviceEnumerator);
    SafeRelease(&device);
    CoUninitialize();
    return 0;
}

Points of Interest

Core Audio is a powerful low level API that provides direct access to kernel mode components of the audio subsystem. It is therefore a good choice for an application that wishes to have robust control over audio devices/endpoints.

The Core Audio interface most relevant to controlling volume level and mute status on an audio endpoint (be it a output or input one) at a low level is IAudioEndpointVolume. This interface provides methods like SetMute() and SetMasterVolumeLevel(). SetMute() works great in muting the microphones of a system.

The code above will mute all microphones and any other active/enabled audio capture device on a system. It does so by enumerating all available audio capture endpoints thus:

C++
hr = deviceEnumerator->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, 
                                               &deviceCollection );

Notice here how we call the EnumAudioEndpoints() function using the eCapture and DEVICE_STATE_ACTIVE constants. The possible values for the former are:

  • eRender
  • eCapture
  • eAll

while the possible values for the latter are:

  • DEVICE_STATE_ACTIVE
  • DEVICE_STATE_DISABLED
  • DEVICE_STATE_NOTPRESENT
  • DEVICE_STATE_UNPLUGGED

Running the Console App

Once you build the attached Visual Studio project (Visual Studio 2010 required), you can run the console app by invoking the executable in your build directory. The console app will display the name of each audio capture endpoint it attempts to mute along with the result (success/failure) of the mute operation.

History

  • V.1.1

License

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