Windows音频设备管理工具开发

在开发Windows应用程序时,经常需要对音频设备进行管理,例如确保在程序运行期间所有激活的音频输入设备(通常是麦克风)都被静音。为了实现这一功能,编写了两个软件组件:一个系统服务,它实现了一个定时器;以及一个控制台应用程序,定时器的回调函数会在设定的时间间隔到达时调用这个程序。

本文主要介绍的是控制台应用程序的开发,它是基于Vista和Win7操作系统的。在XP、Vista和Win7这三个目标平台上,有两种选择:一是使用XP/传统音频API(例如Mixer API),这些API在所有三个Windows版本上应该仍然可以工作,尽管效率不高,并且可能在即将到来的Win8及以后的版本中被淘汰;二是使用Vista引入的新的核心音频API。由于XP不支持这些API,后者的方法将需要为XP单独实现,这在本文中使用的是微软的音频混合器API(<mmsystem.h>,是<Windows.h>的一部分)。

控制台应用程序基于EndpointVolume Microsoft示例,这个示例随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("\nUnable 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("\nUnable 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("\nUnable to retrieve device collection: %x\n", hr); goto Exit; } UINT deviceCount; hr = deviceCollection->GetCount(&deviceCount); if (FAILED(hr)) { printf("\nUnable 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("\nDevice 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("\nUnable 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(&endpointVolume)); if (FAILED(hr)) { printf("\nUnable 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("\nUnable to set mute state on endpoint: %x\n", hr); goto Exit; } else printf("\nEndpoint muted successfully!\n"); } Exit: //Core Audio and COM clean up here SafeRelease(&deviceCollection); SafeRelease(&deviceEnumerator); SafeRelease(&device); CoUninitialize(); return 0; }

核心音频是一个功能强大的低级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),可以通过在构建目录中调用可执行文件来运行控制台应用程序。控制台应用程序将显示它尝试静音的每个音频捕获端点的名称,以及静音操作的结果(成功/失败)。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485