ALSA:カード情報

ALSA
ALSA のインクルードファイルは、/usr/include/alsa ディレクトリにあります。

alsa/asoundlib.h をインクルードすると、すべてのファイルをインクルードできます。

#include <alsa/asoundlib.h>

ライブラリは、libasound です。
-lasound でリンクします。
ALSA の情報
ALSA の情報は、/proc/asound/ ディレクトリ内に、ファイルとして出力されています。

$ ls /proc/asound
PCH  card0  cards  devices  hwdep  modules  oss  pcm  seq  timers  version
cards
/proc/asound/cards のテキストファイルを見てみると、物理的なサウンドカードの情報がリスト化されています。

$ cat /proc/asound/cards
 0 [PCH            ]: HDA-Intel - HDA Intel PCH
                      HDA Intel PCH at 0xdf140000 irq 130

この情報は、以下のような形式で出力されています。

index [ID]: Driver - name
            longname
カードの情報
まずは、ALSA で、サウンドカードの情報を取得してみたいと思います。

ALSA でサウンドカードの制御を行うには、コントロールインターフェイスを使って、サウンドカードを開き、snd_ctl_t のハンドラを取得する必要があります。

そのためには、カードのインデックス番号か、名前が必要になります。
サウンドカードの列挙
snd_card_next() を使って、物理的なサウンドカードのインデックス番号を列挙することができます。

int snd_card_next(int *card);

card にカードのインデックスをセットして実行すると、次のカードインデックスを、card に返します。
戻り値は、0 で成功。負の値でエラーコードです。

card に -1 を指定すると、最初のカードのインデックスが返ります。
card に -1 が返った場合は、終端です。

カードが2つ (index = 0, 1) ある場合、card に指定する値と、card に返る値は、以下のようになります。

-1 -> 0
 0 -> 1
 1 -> -1
インデックスから名前を取得
カードのインデックスから、カードの名前を取得する場合は、以下の関数を使います。

int snd_card_get_name(int card, char **name);
int snd_card_get_longname(int card, char **name);

name には、strdup で複製された文字列のポインタが返るので、free() で解放する必要があります。

name は、"HDA Intel PCH"
longname は、"HDA Intel PCH at 0xdf140000 irq 130" というような文字列になります。
エラー
ALSA の関数の戻り値は、基本的に、0 で成功、負の値でエラーコードになります。

エラーコードは、errno を負にした値になります (-EAGAIN など)。

取得した値を返すような関数以外で、戻り値を明記していない場合は、このルールとなります。
エラーコードから文字列取得
const char *snd_strerror(int errnum);

エラーコードから、説明用の文字列を取得します。
エラーハンドラ
int snd_lib_error_set_handler(snd_lib_error_handler_t handler);

typedef void (*snd_lib_error_handler_t)(const char *file, int line, const char *function,
    int err, const char *fmt, ...);

エラー時に実行されるハンドラをセットします。
デフォルト (handler = NULL) の場合、stderr にエラーメッセージが出力されます。
snd_ctl_t の取得
カードのインデックスを元に取得できるのは、名前の情報だけです。
カードのより詳細な情報を取得したい場合は、コントロールインターフェイスを使います。

まずは、カードのインデックスを元に、コントロール用に、サウンドカードを開きます。
開く
int snd_ctl_open(snd_ctl_t **ctl, const char *name, int mode);

サウンドカードを開き、*ctl に snd_ctl_t ハンドラを返します。

name開くサウンドカードの定義名
mode開くモード。
0 でブロックモードです。

SND_CTL_NONBLOCK : 非ブロッキングモード
SND_CTL_ASYNC : 非同期通知

カードの名前
ALSA 設定ファイルで定義されている、デフォルトのカードを指定する場合は、"default" を指定します。

カードのインデックス番号を指定して開きたい場合は、"hw:<index>"
カードの ID 文字列を指定して開きたい場合は、"hw:CARD=<ID>" という形式で指定します。
例: "hw:0", "hw:CARD=PCH"

"hw" は、ハードウェアを意味します。
閉じる
int snd_ctl_close(snd_ctl_t *ctl);

snd_ctl_t を閉じて、解放します。
カードの情報取得
snd_ctl_t が取得できたら、カードの情報を取得します。
カードの情報取得
int snd_ctl_card_info(snd_ctl_t *ctl, snd_ctl_card_info_t *info);

カードの情報を、snd_ctl_card_info_t に取得します。

snd_ctl_card_info_t は、メンバが非公開な形になっているので、構造体として直接使用することができません。
この関数を実行する前に、snd_ctl_card_info_t のメモリを確保する必要があります。
snd_ctl_card_info_t の確保
ALSA で取得できる情報は、基本的にメンバが非公開なものが多いので、そのような情報では、データを取得する前に、メモリを確保する必要があります。

サイズを取得して自分で確保するか、専用の確保関数と解放関数を使います。

//サイズの取得
size_t snd_ctl_card_info_sizeof(void);

//確保
int snd_ctl_card_info_malloc(snd_ctl_card_info_t **ptr);

//解放
void snd_ctl_card_info_free(snd_ctl_card_info_t *obj);
各値の取得
snd_ctl_card_info() で情報を取得したら、関数を使って、snd_ctl_card_info_t から各値を取得します。

//カードインデックス
int snd_ctl_card_info_get_card(const snd_ctl_card_info_t *obj);

//ID 文字列
const char *snd_ctl_card_info_get_id(const snd_ctl_card_info_t *obj);

//ドライバ名
const char *snd_ctl_card_info_get_driver(const snd_ctl_card_info_t *obj);

//名前
const char *snd_ctl_card_info_get_name(const snd_ctl_card_info_t *obj);

//長い名前
const char *snd_ctl_card_info_get_longname(const snd_ctl_card_info_t *obj);

//ミキサー名
const char *snd_ctl_card_info_get_mixername(const snd_ctl_card_info_t *obj);

//コンポーネントプロパティ
const char *snd_ctl_card_info_get_components(const snd_ctl_card_info_t *obj);
プログラム
snd_ctl_card_info_t のサイズと、各サウンドカードの情報を出力するプログラムです。

$ cc -o 01_cardinfo 01_cardinfo.c -lasound

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

int main(void)
{
    int card;
    snd_ctl_t *ctl;
    snd_ctl_card_info_t *info;
    char m[16];

    printf("size: %d\n", (int)snd_ctl_card_info_sizeof());

    card = -1;

    while(snd_card_next(&card) == 0)
    {
        if(card == -1) break;

        printf("--- [%d] ---\n", card);

        //開く

        snprintf(m, 16, "hw:%d", card);

        if(snd_ctl_open(&ctl, m, 0))
            break;

        //情報取得

        snd_ctl_card_info_malloc(&info);

        snd_ctl_card_info(ctl, info);

        //

        printf("ID: %s\n", snd_ctl_card_info_get_id(info));
        printf("driver: %s\n", snd_ctl_card_info_get_driver(info));
        printf("name: %s\n", snd_ctl_card_info_get_name(info));
        printf("longname: %s\n", snd_ctl_card_info_get_longname(info));
        printf("mixername: %s\n", snd_ctl_card_info_get_mixername(info));
        printf("components: %s\n", snd_ctl_card_info_get_components(info));

        //閉じる

        snd_ctl_card_info_free(info);

        snd_ctl_close(ctl);
    }

    return 0;
}
出力結果
size: 376
--- [0] ---
ID: PCH
driver: HDA-Intel
name: HDA Intel PCH
longname: HDA Intel PCH at 0xdf140000 irq 130
mixername: Realtek ALC892
components: HDA:10ec0892,18495892,00100302 HDA:80862809,80860101,00100000