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 输出时会应用错误的通道,您会突然从后方听到奇怪的声音。或者,有些设备私下认为中置/LFE 是第三/第四通道,而另一些设备则认为 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 状态下允许。在其他状态下调用时,应返回错误。