ALSA PCM 通道映射 API

Takashi Iwai <tiwai@suse.de>

概述

通道映射 API 允许用户查询可能的通道映射和当前的通道映射,还可以选择修改当前流的通道映射。

通道映射是每个 PCM 通道的位置数组。通常,立体声 PCM 流的通道映射为 { front_left, front_right },而 4.0 环绕声 PCM 流的通道映射为 { front left, front right, rear left, rear right }.

到目前为止,问题在于我们没有明确的标准通道映射,应用程序也无法知道哪个通道对应哪个(扬声器)位置。因此,应用程序为 5.1 输出应用了错误的通道,你突然会听到来自后方的奇怪声音。或者,一些设备秘密地假设中心/低频效果是第三/第四通道,而其他设备则假设 C/LFE 是第五/第六通道。

此外,一些设备(如 HDMI)即使在总通道数相同的情况下,也可以配置不同的扬声器位置。但是,由于缺乏通道映射规范,无法指定这一点。这些是新的通道映射 API 的主要动机。

设计

实际上,“通道映射 API”在内核/用户空间 ABI 视角下并没有引入任何新东西。它仅使用现有的控制元素功能。

作为基础设计,每个 PCM 子流可以包含一个提供通道映射信息和配置的控制元素。此元素由以下内容指定:

  • iface = SNDRV_CTL_ELEM_IFACE_PCM

  • name = “Playback Channel Map” 或 “Capture Channel Map”

  • device = 为分配的 PCM 子流分配的相同设备编号

  • index = 为分配的 PCM 子流分配的相同索引编号

请注意,名称因 PCM 子流方向而异。

每个控制元素至少提供 TLV 读取操作和读取操作。可以选择提供写入操作,以允许用户动态更改通道映射。

TLV

TLV 操作提供可用通道映射的列表。通道映射的列表项通常是 type data-bytes ch0 ch1 ch2... 的 TLV,其中 type 是 TLV 类型值,第二个参数是通道值的总字节数(不是数字),其余是每个通道的位置值。

作为 TLV 类型,可以使用 SNDRV_CTL_TLVT_CHMAP_FIXEDSNDRV_CTL_TLV_CHMAP_VARSNDRV_CTL_TLVT_CHMAP_PAIRED_FIXED 类型用于通道位置固定的通道映射,而后两种类型用于灵活的通道位置。_VAR 类型用于所有通道都可以自由交换的通道映射,而 _PAIRED 类型用于成对通道可以交换的通道映射。例如,当您有 {FL/FR/RL/RR} 通道映射时,_PAIRED 类型将允许您仅交换 {RL/RR/FL/FR},而 _VAR 类型甚至允许交换 FL 和 RR。

这些新的 TLV 类型在 sound/tlv.h 中定义。

可用的通道位置值在 sound/asound.h 中定义,这是一个节选

/* channel positions */
enum {
      SNDRV_CHMAP_UNKNOWN = 0,
      SNDRV_CHMAP_NA,         /* N/A, silent */
      SNDRV_CHMAP_MONO,       /* mono stream */
      /* this follows the alsa-lib mixer channel value + 3 */
      SNDRV_CHMAP_FL,         /* front left */
      SNDRV_CHMAP_FR,         /* front right */
      SNDRV_CHMAP_RL,         /* rear left */
      SNDRV_CHMAP_RR,         /* rear right */
      SNDRV_CHMAP_FC,         /* front center */
      SNDRV_CHMAP_LFE,        /* LFE */
      SNDRV_CHMAP_SL,         /* side left */
      SNDRV_CHMAP_SR,         /* side right */
      SNDRV_CHMAP_RC,         /* rear center */
      /* new definitions */
      SNDRV_CHMAP_FLC,        /* front left center */
      SNDRV_CHMAP_FRC,        /* front right center */
      SNDRV_CHMAP_RLC,        /* rear left center */
      SNDRV_CHMAP_RRC,        /* rear right center */
      SNDRV_CHMAP_FLW,        /* front left wide */
      SNDRV_CHMAP_FRW,        /* front right wide */
      SNDRV_CHMAP_FLH,        /* front left high */
      SNDRV_CHMAP_FCH,        /* front center high */
      SNDRV_CHMAP_FRH,        /* front right high */
      SNDRV_CHMAP_TC,         /* top center */
      SNDRV_CHMAP_TFL,        /* top front left */
      SNDRV_CHMAP_TFR,        /* top front right */
      SNDRV_CHMAP_TFC,        /* top front center */
      SNDRV_CHMAP_TRL,        /* top rear left */
      SNDRV_CHMAP_TRR,        /* top rear right */
      SNDRV_CHMAP_TRC,        /* top rear center */
      SNDRV_CHMAP_LAST = SNDRV_CHMAP_TRC,
};

当 PCM 流可以提供多个通道映射时,您可以在 TLV 容器类型中提供多个通道映射。要返回的 TLV 数据将包含如下内容:

SNDRV_CTL_TLVT_CONTAINER 96
    SNDRV_CTL_TLVT_CHMAP_FIXED 4 SNDRV_CHMAP_FC
    SNDRV_CTL_TLVT_CHMAP_FIXED 8 SNDRV_CHMAP_FL SNDRV_CHMAP_FR
    SNDRV_CTL_TLVT_CHMAP_FIXED 16 NDRV_CHMAP_FL SNDRV_CHMAP_FR \
        SNDRV_CHMAP_RL SNDRV_CHMAP_RR

通道位置以 LSB 16 位提供。高位用于位标志。

#define SNDRV_CHMAP_POSITION_MASK       0xffff
#define SNDRV_CHMAP_PHASE_INVERSE       (0x01 << 16)
#define SNDRV_CHMAP_DRIVER_SPEC         (0x02 << 16)

SNDRV_CHMAP_PHASE_INVERSE 指示通道的相位已反转(因此,将左右通道相加几乎会导致静音)。一些数字麦克风设备具有此功能。

当设置 SNDRV_CHMAP_DRIVER_SPEC 时,所有通道位置值都不遵循上述标准定义,而是特定于驱动程序的。

读取操作

控制读取操作用于提供给定流的当前通道映射。控制元素返回一个整数数组,其中包含每个通道的位置。

当在指定通道数之前执行此操作时(即,设置了 hw_params),它应返回设置为 UNKNOWN 的所有通道。

写入操作

控制写入操作是可选的,仅适用于可以动态更改通道配置的设备,例如 HDMI。用户需要传递一个整数值,其中包含分配的 PCM 子流的所有通道的有效通道位置。

仅允许在 PCM PREPARED 状态下执行此操作。在其他状态下调用时,它应返回错误。