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 狀態下允許。在其他狀態下呼叫時,應返回錯誤。