ALSA:PCM 定義名

PCM 定義名
PCM を開く場合は、name 引数で、PCM の定義名を指定する必要があります。

デフォルトの PCM を使う場合は "default" を指定しますが、ここでは、この名前に関する詳細を説明していきます。
ALSA の定義
ALSA の定義ファイルは、/usr/share/alsa/ ディレクトリにあり、メインとなる alsa.conf で、各インターフェイスの定義などが行われています。
PCM インターフェイス
alsa.conf 内の、PCM インターフェイスの定義部分を見てみます。

最初にあるのは、"pcm.hw" の定義です。
pcm.hw は、PCM ハードウェアを意味し、サンプルの変換などを行わず、ALSA カーネルドライバと直接通信するためのものです。

pcm.plughw の場合は、変換が有効な hw です。
pcm.null は、無音状態を再生したり、無音を録音する時に使います。
pcm.hw の定義
pcm.hw {
    # 引数
    @args [ CARD DEV SUBDEV ]
    # CARD 引数
    @args.CARD {
        type string
        # デフォルト値
        default {
            # 環境変数から取得
            @func getenv
            vars [
                ALSA_PCM_CARD
                ALSA_CARD
            ]
            default {
                # コピー
                @func refer
                name defaults.pcm.card
            }
        }
    }
    # DEV 引数
    @args.DEV {
        type integer
        default {
            @func igetenv
            vars [
                ALSA_PCM_DEVICE
            ]
            default {
                @func refer
                name defaults.pcm.device
            }
        }
    }
    # SUBDEV 引数
    @args.SUBDEV {
        type integer
        default {
            @func refer
            name defaults.pcm.subdevice
        }
    }        
    type hw
    card $CARD
    device $DEV
    subdevice $SUBDEV
    # 名前ヒント
    hint {
        show {
            @func refer
            name defaults.namehint.extended
        }
        description "Direct hardware device without any conversions"
    }
}

見ていると、何となくわかるように、@args で引数の指定があり、各引数では、デフォルトの値が指定されています。

この定義により、hw には、CARD, DEV, SUBDEV の引数があるのがわかります。

ALSA で PCM インターフェイスを開く際に、この pcm.hw を開きたい場合、"pcm." はインターフェイスを指定する部分なので省略し、"hw" の文字列を指定します。

さらに hw の引数を指定したい場合は、"hw" の後に ":" を付けて、その後、複数の値を、カンマで区切って指定します。
この場合、値の順番は、@args で定義されている通り、CARD, DEV, SUBDEV の順番となります。

なお、引数の名前と値を指定したい場合は、NAME=VAL の形式で、"hw:CARD=0,DEV=0" というように指定します。

デフォルトの値
省略された引数は、定義で指定されている通り、デフォルトの値が使用されます。

alsa.conf では、以下のように、デフォルトの値が指定されているので、"hw" とだけ指定した場合、CARD, DEV, SUBDEV の値は、0, 0, -1 (任意) というようになります。

defaults.pcm.card 0
defaults.pcm.device 0
defaults.pcm.subdevice -1

ユーザーが、ALSA のデフォルトのカードやデバイスを指定したい場合は、ここを編集します。
PCM を開く時の名前の例
hw    # hw:0,0,-1
hw:0
hw:0,0
hw:0,0,0
hw:CARD=PCH  # カードの ID 名
hw:CARD=0,DEV=0,SUBDEV=0

CARD で指定するのは、カードのインデックス番号か、ID (snd_ctl_card_info_get_id()) の名前です。

特定のカードのデバイス番号を列挙したい場合は、コントロールインターフェイスの snd_ctl_pcm_next_device() を使います。

int snd_ctl_pcm_next_device(snd_ctl_t *ctl, int *device);

サブデバイス番号は、PCM 情報から、有効なサブデバイス番号の数を取得できます。
デフォルト
PCM 関連のデフォルトは、以下のように指定されています。
cards は、/usr/share/alsa/cards/ で、各サウンドカードごとの定義ファイルがあります。

pcm.default cards.pcm.default
pcm.sysdefault cards.pcm.default
pcm.front cards.pcm.front
pcm.rear cards.pcm.rear
pcm.center_lfe cards.pcm.center_lfe
pcm.side cards.pcm.side
pcm.surround21 cards.pcm.surround21
pcm.surround40 cards.pcm.surround40
pcm.surround41 cards.pcm.surround41
pcm.surround50 cards.pcm.surround50
pcm.surround51 cards.pcm.surround51
pcm.surround71 cards.pcm.surround71
pcm.iec958 cards.pcm.iec958
pcm.spdif iec958
pcm.hdmi cards.pcm.hdmi
pcm.dmix cards.pcm.dmix
pcm.dsnoop cards.pcm.dsnoop
pcm.modem cards.pcm.modem
pcm.phoneline cards.pcm.phoneline

PCM で "default" を開いた場合は、実際には pcm.default、つまり cards.pcm.default (カードごとのデフォルト) が使われることになります。

"default" は、ユーザー設定でのデフォルト。
"sysdefault" は、システム設定でのデフォルトとなります。

HDMI に出力したい場合は、"hdmi" で指定できます。
名前ヒント
名前ヒントは、コントロールや PCM など、ALSA の各インターフェイスを開く時に指定する、定義名のことです。

一般的に使用可能な定義名を、一覧で取得することができます。
名前ヒントのセットを取得
int snd_device_name_hint(int card, const char *iface, void ***hints);

指定したカードの、名前ヒントのセットを取得します。

cardカードのインデックス番号。-1 ですべて
ifaceALSA のインターフェイス名。
"ctl" や "pcm" など。
hints名前ヒントの配列のポインタが返る。
配列の値が NULL で、データの終端。
解放
int snd_device_name_free_hint(void **hints);

snd_device_name_hint() で取得したポインタを解放します。
情報の取得
char *snd_device_name_get_hint(const void *hint, const char *id);

各ヒントのポインタから、値を取得します。

id取得する値の定義名。

"NAME" : デバイスの名前
"DESC" : デバイスの詳細説明
"IOID" : 入力または出力か ("Input" または "Output" が返る)。NULL が返ると、両方。
戻り値確保された ASCII 文字列。
free() で解放する。
NULL が返る場合もある。
プログラム
引数で指定したインターフェイス (省略で "pcm") の、すべての名前ヒントを出力します。

$ cc -o 09-namehint 09-namehint.c -lasound

#include <stdlib.h>
#include <stdio.h>
#include <alsa/asoundlib.h>

int main(int argc,char **argv)
{
    void **hints;
    const char *id[] = {"NAME", "DESC", "IOID"};
    int i,j;
    char *str;

    if(snd_device_name_hint(-1, (argc >= 2)? argv[1]: "pcm", &hints))
        return 0;

    for(i = 0; hints[i]; i++)
    {
        printf("-- [%d] --\n", i);
        
        for(j = 0; j < 3; j++)
        {
            str = snd_device_name_get_hint(hints[i], id[j]);

            printf("%s: ", id[j]);

            if(str)
                printf("'%s'\n", str);
            else
                printf("<null>\n");

            if(str) free(str);
        }
    }

    snd_device_name_free_hint(hints);

    return 0;
}
実行結果
-- [0] --
NAME: 'null'
DESC: 'Discard all samples (playback) or generate zero samples (capture)'
IOID: <null>
-- [1] --
NAME: 'default:CARD=PCH'
DESC: 'HDA Intel PCH, ALC892 Analog
Default Audio Device'
IOID: <null>
-- [2] --
NAME: 'sysdefault:CARD=PCH'
DESC: 'HDA Intel PCH, ALC892 Analog
Default Audio Device'
IOID: <null>
-- [3] --
NAME: 'front:CARD=PCH,DEV=0'
DESC: 'HDA Intel PCH, ALC892 Analog
Front output / input'
IOID: <null>
-- [4] --
NAME: 'surround21:CARD=PCH,DEV=0'
DESC: 'HDA Intel PCH, ALC892 Analog
2.1 Surround output to Front and Subwoofer speakers'
IOID: 'Output'
-- [5] --
NAME: 'surround40:CARD=PCH,DEV=0'
DESC: 'HDA Intel PCH, ALC892 Analog
4.0 Surround output to Front and Rear speakers'
IOID: 'Output'
-- [6] --
NAME: 'surround41:CARD=PCH,DEV=0'
DESC: 'HDA Intel PCH, ALC892 Analog
4.1 Surround output to Front, Rear and Subwoofer speakers'
IOID: 'Output'
-- [7] --
NAME: 'surround50:CARD=PCH,DEV=0'
DESC: 'HDA Intel PCH, ALC892 Analog
5.0 Surround output to Front, Center and Rear speakers'
IOID: 'Output'
-- [8] --
NAME: 'surround51:CARD=PCH,DEV=0'
DESC: 'HDA Intel PCH, ALC892 Analog
5.1 Surround output to Front, Center, Rear and Subwoofer speakers'
IOID: 'Output'
-- [9] --
NAME: 'surround71:CARD=PCH,DEV=0'
DESC: 'HDA Intel PCH, ALC892 Analog
7.1 Surround output to Front, Center, Side, Rear and Woofer speakers'
IOID: 'Output'
-- [10] --
NAME: 'hdmi:CARD=PCH,DEV=0'
DESC: 'HDA Intel PCH, PHL 224E5
HDMI Audio Output'
IOID: 'Output'
-- [11] --
NAME: 'hdmi:CARD=PCH,DEV=1'
DESC: 'HDA Intel PCH, HDMI 1
HDMI Audio Output'
IOID: 'Output'
-- [12] --
NAME: 'hdmi:CARD=PCH,DEV=2'
DESC: 'HDA Intel PCH, HDMI 2
HDMI Audio Output'
IOID: 'Output'

"null" は、カードに関係なく存在します。
IOID が null の場合は、再生/録音どちらでも使えます。

CARD=PCH は、実際に存在するサウンドカードです。

DEV の指定がない場合は、デフォルトのデバイス (ユーザーが設定で選択可能) となります。

PCM を開く場合、NAME の文字列を指定することができます。
HDMI
"hdmi" を指定した場合、デバイス番号が 0,1,2 となっていますが、PCM 情報で "hdmi" を指定した時、実際のデバイス番号は3でした。

ALSA 定義における pcm.hdmi は、別途異なる形で定義されているので、hdmi の引数 DEV は、カードの実際のデバイス番号とは異なります。

例えば、カードが HDA-Intel の場合は、hdmi のデバイス 0 は、DEVICE=3 として定義されています。

HDA-Intel.pcm.hdmi.0 {
    @args [ CARD AES0 AES1 AES2 AES3 ]
    @args.CARD { type string }
    ...
    @func refer
    name {
        @func concat
        strings [
            "cards.HDA-Intel.pcm.hdmi.common:"
            "CARD=" $CARD ","
            "DEVICE=3,"
            "CTLINDEX=0,"
            ...
        ]
    }
}
コントロールの名前ヒント
名前ヒントで "ctl" を指定すると、コントロールインターフェイスを開く時の名前を取得できます。

$ ./09-namehint ctl

-- [0] --
NAME: 'default'
DESC: 'Default Control Device'
IOID: <null>
-- [1] --
NAME: 'sysdefault'
DESC: 'Default Control Device'
IOID: <null>
-- [2] --
NAME: 'hw'
DESC: 'Direct control device'
IOID: <null>
-- [3] --
NAME: 'default:CARD=PCH'
DESC: 'HDA Intel PCH
Default Control Device'
IOID: <null>
-- [4] --
NAME: 'sysdefault:CARD=PCH'
DESC: 'HDA Intel PCH
Default Control Device'
IOID: <null>
-- [5] --
NAME: 'hw:CARD=PCH'
DESC: 'HDA Intel PCH
Direct control device'
IOID: <null>