ALSA:要素のリスト

カードの要素のリスト
コントロールインターフェイスでは、音量などの値を取得したり、設定したりすることができます。
これらは、それぞれ「要素 (element)」として格納されています。

まずは、カードが持っている要素のリストを取得してみます。
要素のリストを取得
int snd_ctl_elem_list(snd_ctl_t *ctl, snd_ctl_elem_list_t *list);

要素のリストを取得します。

この関数を実行する前に、snd_ctl_elem_list_t のメモリを確保する必要があります。
snd_ctl_elem_list_t の確保
//サイズの取得
size_t snd_ctl_elem_list_sizeof(void);

//確保
int snd_ctl_elem_list_malloc(snd_ctl_elem_list_t **ptr);

//解放
void snd_ctl_elem_list_free(snd_ctl_elem_list_t *obj);
スペースの確保
要素のリストを取得する場合、注意しなければならない点があります。
それは、snd_ctl_elem_list_t を確保しても、実際の各要素のスペースは確保されないということです。

要素は可変個数なので、固定のメモリ領域で取得できないというのは、当然といえば当然です。

そのため、実際に要素のデータを読み込むためには、snd_ctl_elem_list_t 内に、読み込みに必要な要素数分のスペースを確保する必要があります。

//指定個数の要素スペースを確保
int snd_ctl_elem_list_alloc_space(snd_ctl_elem_list_t *obj, unsigned int entries);

//要素スペースを解放
void snd_ctl_elem_list_free_space(snd_ctl_elem_list_t *obj);

snd_ctl_elem_list_free() で snd_ctl_elem_list_t を解放しても、要素用に確保したスペースは解放されません。
snd_ctl_elem_list_free() を実行する前に、snd_ctl_elem_list_free_space() で解放する必要があります。
要素数の取得
要素スペースを確保するためには、カードが実際に持っている要素数が必要になります。
しかし、要素数だけを取得する関数は存在しません。

そこで、まずは要素スペースを確保していない状態で、一度 snd_ctl_elem_list() を実行します。
※スペースが確保されていないので、要素のデータは取得されません。

これにより、snd_ctl_elem_list_t に、カードが持っている要素数がセットされるので、その情報を取得します。

unsigned int snd_ctl_elem_list_get_count(const snd_ctl_elem_list_t *obj);

デバイスに存在する要素の数を取得します。
この情報は、snd_ctl_elem_list() の実行後にセットされます。

これで、必要な要素数が取得できるので、snd_ctl_elem_list_alloc_space() でスペースを確保した後、再度 snd_ctl_elem_list() を実行します。
確保したスペースに、実際に要素データがセットされます。
実際にセットされた個数を取得
unsigned int snd_ctl_elem_list_get_used(const snd_ctl_elem_list_t *obj);

実際にセットされた要素の個数を取得します。
スペースを確保していない状態で実行すると、0 になります。

snd_ctl_elem_list() は、スペースが足りない場合は、確保されている分のデータだけをセットし、余分なスペースがある場合は、その部分を未使用の状態にします。

snd_ctl_elem_list() を複数回実行するような場合は、同じスペースを使いまわすことができます。
要素の識別子
各要素には、その要素を識別するための ID がセットされています。

snd_ctl_elem_list() は、実際には、要素の値などは読み込まず、要素の識別子のリストだけを読み込みます。
構成
要素の識別子は、以下の複数の値で構成されています。

  • numid (数値の識別子)
    サウンドカードが接続されている間は不変の値。
  • インターフェイスタイプ
  • デバイス
  • サブデバイス
  • 名前
  • インデックス

1つの要素は、「numid」、または、「インターフェイスタイプ、デバイス、サブデバイス、名前、インデックス」の5つセットのいずれかで識別されます。

基本的には、numid を使って識別した方がいいでしょう。
snd_ctl_elem_id_t
要素の識別子は、snd_ctl_elem_id_t 型で表現されます。

ここには、上記の複数の値が格納されているので、他の情報と同じように、まずはメモリを確保する必要があります。

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

//確保
int snd_ctl_elem_id_malloc(snd_ctl_elem_id_t **ptr);

//解放
void snd_ctl_elem_id_free(snd_ctl_elem_id_t *obj);

//ASCII文字列にして返す
//戻り値: strdup で複製された文字列
char *snd_ctl_ascii_elem_id_get(snd_ctl_elem_id_t *id);

//ASCII文字列から解析して値をセット
int snd_ctl_ascii_elem_id_parse(snd_ctl_elem_id_t *dst, const char *str);

その他の関数は、要素:識別子 で確認してください。
リストから情報取得
snd_ctl_elem_list() で取得したリストは、「要素の識別子」のリストです。

snd_ctl_elem_list_get_* 関数を使うことで、リストから、ぞれぞれの要素の識別子の情報を取得することができます。

idx 引数は、要素スペース内の、項目のインデックス番号 (0〜) です。

//項目のインデックスのオフセットをセット
void snd_ctl_elem_list_set_offset(snd_ctl_elem_list_t *obj, unsigned int val);

//snd_ctl_elem_id_t の情報を取得
//(あらかじめ確保しておくこと)
void snd_ctl_elem_list_get_id(const snd_ctl_elem_list_t *obj, unsigned int idx, snd_ctl_elem_id_t *ptr);

//numid を取得
unsigned int snd_ctl_elem_list_get_numid(const snd_ctl_elem_list_t *obj, unsigned int idx);

//インターフェイスのタイプを取得
snd_ctl_elem_iface_t snd_ctl_elem_list_get_interface(const snd_ctl_elem_list_t *obj, unsigned int idx);

//デバイスを取得
unsigned int snd_ctl_elem_list_get_device(const snd_ctl_elem_list_t *obj, unsigned int idx);

//サブデバイスを取得
unsigned int snd_ctl_elem_list_get_subdevice(const snd_ctl_elem_list_t *obj, unsigned int idx);

//名前を取得
const char *snd_ctl_elem_list_get_name(const snd_ctl_elem_list_t *obj, unsigned int idx);

//インデックスを取得
//(項目のインデックスとは異なる)
unsigned int snd_ctl_elem_list_get_index(const snd_ctl_elem_list_t *obj, unsigned int idx);
プログラム
引数で指定したサウンドカード (デフォルトは "default") の、要素のリストを取得し、情報を出力するプログラムです。

$ cc -o 02_elemlist 02_elemlist.c -lasound

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

int main(int argc,char **argv)
{
    snd_ctl_t *ctl;
    char *str;
    snd_ctl_elem_list_t *list;
    snd_ctl_elem_id_t *eid;
    uint32_t i,cnt,n;

    //開く

    if(snd_ctl_open(&ctl, (argc >= 2)? argv[1]: "default", 0))
        return 1;

    //要素のリスト取得 (個数のみ)

    snd_ctl_elem_list_malloc(&list);

    snd_ctl_elem_list(ctl, list);

    //必要な個数

    cnt = snd_ctl_elem_list_get_count(list);

    printf("count:%u, used:%u\n", cnt, snd_ctl_elem_list_get_used(list));

    //個数分を確保して、取得

    snd_ctl_elem_list_alloc_space(list, cnt);
    snd_ctl_elem_list(ctl, list);

    printf("used:%u\n", snd_ctl_elem_list_get_used(list));

    //各要素の識別子

    snd_ctl_elem_id_malloc(&eid);

    for(i = 0; i < cnt; i++)
    {
        printf("--- [%d] ---\n", i);

        printf("numid: %u\n", snd_ctl_elem_list_get_numid(list, i));

        n = snd_ctl_elem_list_get_interface(list, i);
        printf("interface: (%d) %s\n", n, snd_ctl_elem_iface_name(n));

        printf("device: %u\n", snd_ctl_elem_list_get_device(list, i));
        printf("subdevice: %u\n", snd_ctl_elem_list_get_subdevice(list, i));
        printf("name: '%s'\n", snd_ctl_elem_list_get_name(list, i));
        printf("index: %u\n", snd_ctl_elem_list_get_index(list, i));

        //ASCII文字列で表現
        
        snd_ctl_elem_list_get_id(list, i, eid);
        str = snd_ctl_ascii_elem_id_get(eid);
        printf("<ascii> %s\n", str);
        free(str);
    }

    snd_ctl_elem_id_free(eid);

    //

    snd_ctl_elem_list_free_space(list);
    snd_ctl_elem_list_free(list);

    snd_ctl_close(ctl);

    return 0;
}
実行結果
count:61, used:0
used:61
--- [0] ---
numid: 1
interface: (2) MIXER
device: 0
subdevice: 0
name: 'Channel Mode'
index: 0
<ascii> numid=1,iface=MIXER,name='Channel Mode'
--- [1] ---
numid: 2
interface: (2) MIXER
device: 0
subdevice: 0
name: 'Front Playback Volume'
index: 0
<ascii> numid=2,iface=MIXER,name='Front Playback Volume'
--- [2] ---
numid: 3
interface: (2) MIXER
device: 0
subdevice: 0
name: 'Front Playback Switch'
index: 0
<ascii> numid=3,iface=MIXER,name='Front Playback Switch'
--- [3] ---
numid: 4
interface: (2) MIXER
device: 0
subdevice: 0
name: 'Surround Playback Volume'
index: 0
<ascii> numid=4,iface=MIXER,name='Surround Playback Volume'

...

--- [59] ---
numid: 60
interface: (2) MIXER
device: 0
subdevice: 0
name: 'PCM Playback Volume'
index: 0
<ascii> numid=60,iface=MIXER,name='PCM Playback Volume'
--- [60] ---
numid: 61
interface: (2) MIXER
device: 0
subdevice: 0
name: 'Digital Capture Volume'
index: 0
<ascii> numid=61,iface=MIXER,name='Digital Capture Volume'
解説
スペースの確保に必要な個数を調べるため、スペースを確保していない状態で、snd_ctl_elem_list() を実行します。

count:61, used:0

実行後に snd_ctl_elem_list_get_count() を使用すると、61 が返ります。これが要素の個数です。

snd_ctl_elem_list_get_used() を実行すると、0 が返ります。
スペースが確保されていないので、実際には読み込まれたデータがありません。

その後、61 個のスペースを確保して、再度 snd_ctl_elem_list() を行うと、used は 61 になっています。
実際に 61 個のデータが読み込まれたということです。

あとは、各関数を使って、要素の識別子の情報を取得します。

snd_ctl_elem_id_t から、ASCII 文字列形式で取得した場合、"<name>=<val>,..." という形式になります。
識別子の各値が 0 (デフォルト) の場合は、項目が省略されるようです。

終了時は、snd_ctl_elem_list_free() の前に、snd_ctl_elem_list_free_space() でスペースを解放することを忘れないでください。

snd_ctl_elem_list_free_space(list);
snd_ctl_elem_list_free(list);
インターフェイスタイプ
音量関連の要素は、インターフェイスタイプが MIXER になります。
他に、CARD や PCM のタイプがあります。