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.
int wmain(int argc, wchar_t* argv[])
{
HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if (FAILED(hr))
{
printf("Unable to initialize COM: %x\n", hr);
goto Exit;
}
IMMDeviceEnumerator *deviceEnumerator = NULL;
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;
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;
for (UINT i = 0 ; i < deviceCount ; i += 1)
{
LPWSTR deviceName;
deviceName = GetDeviceName(deviceCollection, i);
if (deviceName == NULL) goto Exit;
printf("Device to be muted has index: %d and name: %S\n", i, deviceName);
free(deviceName);
device = NULL;
hr = deviceCollection->Item(i, &device);
if (FAILED(hr))
{
printf("Unable to retrieve device %d: %x\n", i, hr);
goto Exit;
}
IAudioEndpointVolume *endpointVolume = NULL;
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); if (FAILED(hr))
{
printf("Unable to set mute state on endpoint: %x\n", hr);
goto Exit;
}
else
printf("Endpoint muted successfully!\n");
}
Exit: 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:
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:
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