65.9K
CodeProject 正在变化。 阅读更多。
Home

静音 Vista/Win7 上的所有麦克风

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (9投票s)

2012年8月9日

CPOL

2分钟阅读

viewsIcon

52881

downloadIcon

1907

如何使用 Core Audio API 枚举和静音所有音频捕获终结点

引言

我最近需要编写一个应用程序,其中一项功能是确保所有活动/启用的音频捕获设备端点(通常是麦克风)在其运行时都处于静音状态。为了实现这一点,我编写了两个软件:一个实现计时器的系统服务,以及本文中介绍的控制台应用程序,计时器的回调函数会在每次设定的时间间隔到期时调用该应用程序。

背景

我这个项目的目标平台是 XP、Vista 和 Win7。 为了实现我想要的功能,我有两种选择。 要么使用 XP/Legacy Audio API(例如 Mixer API),它仍然可以在所有三个 Windows 版本上工作,尽管效率不高,但可能会在即将推出的 Win8 及更高版本中被淘汰;要么使用 Vista 中引入的新的、更低级别的 Core Audio API。 由于这些 API 不支持 XP,因此后一种方法需要为 XP 单独实现,在我的例子中,它是 Microsoft 的 Audio Mixer API (<mmsystem.h>,是 <Windows.h> 的一部分)。 本文将介绍控制台应用程序的 Vista/Win7 版本。

Using the Code

控制台应用程序基于 Microsoft 的 EndpointVolume 示例,该示例随 SDK 7.1 一起提供,位于multimedia\audio下。 我对这个示例进行了重大修改,主要是因为它被编写为一个通用的控制台应用程序,它接收许多与我的目的无关的命令行选项,而且还因为该代码只处理输出端点(如扬声器)而不处理我感兴趣的输入端点。

大部分工作都在控制台应用程序的 wmain() 函数中完成,您可以在下面看到。 代码包括解释性的内联注释。

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;
}

关注点

Core Audio 是一个强大的低级 API,可以直接访问音频子系统的内核模式组件。 因此,对于希望对音频设备/端点进行强大控制的应用程序来说,这是一个不错的选择。

与低级别控制音频端点(无论是输出端点还是输入端点)的音量级别和静音状态最相关的 Core Audio 接口是 IAudioEndpointVolume。 该接口提供诸如 SetMute()SetMasterVolumeLevel() 之类的方法。 SetMute() 在使系统麦克风静音方面效果很好。

上面的代码将使系统上的所有麦克风和任何其他活动/启用的音频捕获设备静音。 它通过枚举所有可用的音频捕获端点来实现,如下所示

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

请注意,这里我们如何使用 eCaptureDEVICE_STATE_ACTIVE 常量调用 EnumAudioEndpoints() 函数。 前者的可能值为

  • eRender (渲染)
  • eCapture (捕获)
  • eAll (全部)

而后者的可能值为

  • DEVICE_STATE_ACTIVE (设备状态_活动)
  • DEVICE_STATE_DISABLED (设备状态_禁用)
  • DEVICE_STATE_NOTPRESENT (设备状态_不存在)
  • DEVICE_STATE_UNPLUGGED (设备状态_已拔出)

运行控制台应用程序

构建附加的 Visual Studio 项目(需要 Visual Studio 2010)后,您可以通过在构建目录中调用可执行文件来运行控制台应用程序。 控制台应用程序将显示它尝试静音的每个音频捕获端点的名称以及静音操作的结果(成功/失败)。

历史

  • V.1.1
© . All rights reserved.