公开多声道节点

在 Windows XP 之前的 Microsoft Windows 版本中,WDM 音频驱动程序没有简化的方式公开以下类型的多声道节点:

KSNODETYPE_VOLUME

KSNODETYPE_MUTE

KSNODETYPE_TONE

具体而言,不存在用于显式查询节点以获取其支持的声道数的机制。 尽管存在针对此问题的解决方法,但它们有缺点。 例如,客户端可以使用 KSPROPERTY_AUDIO_VOLUMELEVEL 属性来迭代查询音量节点 (KSNODETYPE_VOLUME),以获取每个声道(0、1 等)的音量级别,直到请求返回指示不存在更多声道的错误。 但是,此方法需要多个查询,并且处理较新的多声道音频设备效率太低。 在 Windows XP 和更高版本的操作系统中,通过定义 KSPROPERTY_MEMBERSHEADER 结构的 Flags 成员中的两个附加标志位可解决此限制,属性处理程序在响应基本支持查询时输出这些标志位:

  • KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_MULTICHANNEL

    在节点上的基本支持属性请求期间,处理程序将设置此标志位,以指示 KSPROPERTY_MEMBERSHEADER 的 MembersCount 成员包含该节点支持的声道数。 对于 Windows Vista 和更高版本的 Windows 操作系统,必须为每个声道属性设置此标志。

  • KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_UNIFORM

    处理程序会在此标志位和 KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_MULTICHANNEL 标志位之间执行位或运算,以指示单个属性值统一应用到节点中的所有声道中。 例如,如果硬件仅为所有声道提供单个音量级控制,则音量节点的基本支持处理程序将设置 KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_UNIFORM 标志来指示此限制。 如果未设置此标志,则可以独立于其他声道的音量级别控制每个声道的音量级别。

    请注意,Windows Vista 操作系统不使用 KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_UNIFORM 标志。

在 Windows XP 及更高版本的微型端口驱动程序中,多声道音量节点的属性处理程序应设置 KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_MULTICHANNEL 位以响应 KSPROPERTY_AUDIO_VOLUMELEVEL 基本支持查询。 处理程序会返回 KSPROPERTY_STEPPING_LONG 结构的数组,即节点公开的每个声道的各一个,并将 MembersSize 设置为 sizeof (KSPROPERTY_STEPPING_LONG)。 每个数组元素描述声道的最小和最大音量级别以及范围中连续值之间的增量。 可以为每个单独的声道指定不同的范围,以便正确公开具有非统一范围的声道。 例如,低音炮声道的范围可能与其他声道的范围不同。

下面的代码示例演示如何使用非统一属性值处理音频属性的基本支持查询。 下面第一行代码中的变量 pDescription 指向处理程序将基本支持信息写入到的数据缓冲区开头的 KSPROPERTY_DESCRIPTION 结构:

  //
  // Fill in the members header.
  //
  PKSPROPERTY_MEMBERSHEADER pMembers = PKSPROPERTY_MEMBERSHEADER(pDescription + 1);

  pMembers->MembersFlags = KSPROPERTY_MEMBER_STEPPEDRANGES;
  pMembers->MembersSize = sizeof(KSPROPERTY_STEPPING_LONG);
  pMembers->MembersCount = ulNumChannels;
  pMembers->Flags = KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_MULTICHANNEL;

  //
  // Fill in the stepped range with the driver default.
  //
  PKSPROPERTY_STEPPING_LONG pRange = PKSPROPERTY_STEPPING_LONG(pMembers + 1);
  pRange->Reserved = 0;

  for (ULONG i=0; i<ulNumChannels; i++)
  {
      pRange[i].Bounds.SignedMinimum = ulChannelMin[i];
      pRange[i].Bounds.SignedMaximum = ulChannelMax[i];
      pRange[i].SteppingDelta = ChannelStepping[i];
  }

  pPropertyRequest->ValueSize = sizeof(KSPROPERTY_DESCRIPTION) +
                                sizeof(KSPROPERTY_MEMBERSHEADER) + 
                                ulNumChannels * sizeof(KSPROPERTY_STEPPING_LONG);

下图显示了此示例的数据缓冲区的布局。 显示的 pDescription、pMembers 和 pRange 指针指向缓冲区内的相应偏移量。

Diagram illustrating the layout of a data buffer for a basic-support query with pDescription, pMembers, and pRange pointers.

对于此示例,处理程序将 MembersCount 设置为 ulNumChannels,即声道数。 范围数组的大小(以字节为单位)为

MembersSize * MembersCount

请注意,如果此示例中设置了 KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_UNIFORM 标志,处理程序会将数组中的所有 KSPROPERTY_STEPPING_LONG 结构设置为同一范围。

音调节点 KSPROPERTY_AUDIO_BASSKSPROPERTY_AUDIO_TREBLEKSPROPERTY_AUDIO_MID 属性的基本支持处理程序以类似的方式运行。

如果多声道节点的某个属性具有 BOOL 类型的每声道属性值,则基本支持处理程序必须填写单步执行范围数组的值。 在这种情况下,处理程序会将成员设置为以下代码示例中显示的值。 此类属性的两个示例是静音节点的 KSPROPERTY_AUDIO_MUTE 属性和音调节点的 KSPROPERTY_AUDIO_BASS_BOOST 属性。

下面的代码示例演示了如何处理多声道节点的基本支持请求(对于具有 BOOL 类型的每声道属性值的属性):

  //
  // Fill in the members header.
  //
  PKSPROPERTY_MEMBERSHEADER pMembers = PKSPROPERTY_MEMBERSHEADER(pDescription + 1);

  pMembers->MembersFlags = KSPROPERTY_MEMBER_STEPPEDRANGES;
  pMembers->MembersSize = sizeof (KSPROPERTY_STEPPING_LONG);
  pMembers->MembersCount = ulNumChannels;
  pMembers->Flags = KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_MULTICHANNEL;

  pPropertyRequest->ValueSize = sizeof(KSPROPERTY_DESCRIPTION) +
                                sizeof(KSPROPERTY_MEMBERSHEADER) + 
                                ulNumChannels * sizeof(KSPROPERTY_STEPPING_LONG);

  //
  // Fill in the stepped range with values in FOR loop.
  //
  PKSPROPERTY_STEPPING_LONG pRange = PKSPROPERTY_STEPPING_LONG(pMembers + 1);
  pRange->Reserved = 0;

  for (ULONG i=0; i<ulNumChannels; i++)
  {
      pRange[i].Bounds.SignedMinimum = 0;
      pRange[i].Bounds.SignedMaximum = 1;
      pRange[i].SteppingDelta = 1;
  }

请注意,在前面的代码示例中,FOR 循环使用零 (0) 和一个 (1) 来设置每声道范围的最小值和最大值。 这是因为,我们正在配置具有 BOOL 类型的每声道属性值的多声道节点。

如果声道属性是统一的,则可以在 KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_UNIFORM 标志和 KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_MULTICHANNEL 标志之间执行位或运算,结果将分配给 pMembers->Flags 成员。 此值用于指示硬件在节点中的所有声道上统一应用同一属性值。

使用 KSPROPERTY_MEMBER_FLAG_UNIFORM and KSPROPERTY_MEMBER_FLAG_MULTICHANNEL 标志无需将声道分组成对,并为每个声道对公开单独的立体声音量节点,就像在 Windows 驱动程序工具包 (WDK) 中的 Ac97 示例驱动程序内所做的那样。 由于早于 Windows XP 的 Windows 版本不支持这些标志,因此驱动程序的基本支持处理程序必须使用 IPortClsVersion 接口来查询 Portcls.sys 版本,以确定是否使用这些标志。

拓扑分析器(在内核模式 WDMAud 系统驱动程序中,Wdmaud.sys)从其 WDM 音频驱动程序获取音频设备的拓扑。 分析器通过旧版 Windows 多媒体混音器 API 将该设备公开为传统混音器设备。 在 Windows XP 及更高版本中,WDMAud 使用 KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_MULTICHANNEL 标志来确定在 MIXERLINE 结构的 cChannels 成员中报告的声道数。 此外,如果节点的基本支持处理程序指定 KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_UNIFORM 标志,WDMAud 会在相应的 MIXERCONTROL 结构中设置 MIXERCONTROL_CONTROLF_UNIFORM 标志。 通过此标志,应用程序可以确定是否可以通过主控件单独调整每个声道或所有声道。 有关 MIXERCONTROL、MIXERLINE 和混音器 API 的详细信息,请参阅 Microsoft Windows SDK 文档。

在 Windows XP 及更高版本中,SndVol32 音量控制程序(请参阅 SysTray 和 SndVol32)会显示多声道设备的控件,如下图所示。

Screenshot of the SndVol32 volume-control dialog box displaying controls for multichannel devices.

如果 SndVol32 检测到具有不止两个声道的行,则会将普通平移控件替换为标记为“扬声器音量”的按钮,该按钮显示在上图的主音量滑块上方。 点击“扬声器音量”按钮将显示一个对话框,其中显示特定行的所有声道的控件,如下图所示。

Screenshot of the speaker-volume dialog box displaying controls for all channels and advanced audio properties.

由于混音器 API 按编号公开声道,因此它会根据当前在 Windows 多媒体控制面板 (Mmsys.cpl) 的“高级音频属性”对话框中选择的扬声器配置推断出声道名称。

例如,如果设备在一行中公开四个声道,并且用户已选择“四声道扬声器”,则声道名称将为“左”(声道 0)、“右”(声道 1)、“左后”(声道 2)和“右后”(声道 3),如上图所示。 将扬声器配置更改为“环绕声音扬声器”会产生声道映射“左”(声道 0)、“右”(声道 1)、“前中”(声道 2)和“后中”(声道 3)。

在驱动程序级别,KSPROPERTY_AUDIO_CHANNEL_CONFIG 属性使用掩码值 KSAUDIO_SPEAKER_QUAD 或 KSAUDIO_SPEAKER_SURROUND 分别表示四声或环绕扬声器配置。 头文件 Ksmedia.h 定义以下值:

  #define KSAUDIO_SPEAKER_QUAD      (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \
                                     SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT)

  #define KSAUDIO_SPEAKER_SURROUND  (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \
                                     SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER)

任何一个掩码都包含四位,用于指定四个声道的扬声器位置。 在任一情况下,KSPROPERTY_AUDIO_VOLUMELEVEL 属性都将这四个声道分别标识为声道 0、1、2 和 3。

如果节点的基本支持处理程序设置 KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_UNIFORM 标志位,则“扬声器音量”对话框中显示的滑块将随对任何单个滑块所做的更改一起移动。