音量のデータ
ここまでで、要素の情報と、要素の値が取得できました。
音量やチャンネルマップ、ミュートの設定、各端子が接続されているかなどの情報があります。
要素の値を変更すると、これらの音量の値などを変更することができます。
音量やチャンネルマップ、ミュートの設定、各端子が接続されているかなどの情報があります。
要素の値を変更すると、これらの音量の値などを変更することができます。
マスター音量
一例として、再生時のマスター音量の情報と値を見てみます。
マスター音量の名前は、"Master Playback Volume" です。
マスター音量の要素は、INTEGER (数値) タイプで、読み書き可、TLV での読み込みが可となっています。
値の範囲は、0〜64 です。
値の個数は一つで、現在の値は 27 になっています。
つまり、音量は、0〜64 の範囲でしか指定できないということです。
マスター音量の名前は、"Master Playback Volume" です。
# 要素の情報 -- [28] -- <name> Master Playback Volume type: (2) INTEGER readable: 1 writable: 1 volatile: 0 inactive: 0 locked: 0 tlv_readable: 1 tlv_writable: 0 tlv_commandable: 0 owner: 0 user: 0 count: 1 (int) min:0, max:64, step:0 # 要素の値 -- [28] -- <name> Master Playback Volume type: (2) INTEGER 27,
マスター音量の要素は、INTEGER (数値) タイプで、読み書き可、TLV での読み込みが可となっています。
値の範囲は、0〜64 です。
値の個数は一つで、現在の値は 27 になっています。
つまり、音量は、0〜64 の範囲でしか指定できないということです。
dB
ALSA では、TLV 機能を使うことで、dB (デシベル) 単位で音量を指定することができます。
(正確には、dB 値を、生の音量値に変換する)
(正確には、dB 値を、生の音量値に変換する)
TLV
要素の値を操作する時、TLV 機能を使うことで、値に関する補足情報を読み書きしたり、要素に対してコマンドを実行することができます。
音量の場合は、要素の値とは別に、TLV で dB の情報が付加されているので、それを読み込むことで、指定できる dB 値の範囲を取得することができます。
なお、各要素で TLV 操作ができるかは、要素情報の is_tlv_readable, is_tlv_writable, is_tlv_commandable で判断できるので、そちらで確認してから使用してください。
マスター音量の場合は、TLV の読み込みのみが可能となっています。
要素の識別子 (snd_ctl_elem_id_t) を指定して、指定した要素の TLV 値を読み込みます。
tlv_size は、データ全体が読み込めるサイズである必要があります。
TLV データの実際のサイズは取得できないので、大きめに用意しておくといいでしょう。
dB 情報 (コンテナは除く) の場合は、最大で 8 * sizeof(int) です。
正しく読み込めなかった場合は、tlv[0] = -1, tlv[1] = 0 になります。
TLV タイプは、配列の先頭で指定される値です。
現在は、dB とチャンネルマップ関連のタイプしかありません。
コンテナタイプは、値の中に、複数の TLV データが含まれる形になります。
音量は、基本的に SND_CTL_TLVT_DB_SCALE が使われます。
音量の場合は、要素の値とは別に、TLV で dB の情報が付加されているので、それを読み込むことで、指定できる dB 値の範囲を取得することができます。
なお、各要素で TLV 操作ができるかは、要素情報の is_tlv_readable, is_tlv_writable, is_tlv_commandable で判断できるので、そちらで確認してから使用してください。
マスター音量の場合は、TLV の読み込みのみが可能となっています。
TLV 読み込み
int snd_ctl_elem_tlv_read(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id, unsigned int *tlv, unsigned int tlv_size);
要素の識別子 (snd_ctl_elem_id_t) を指定して、指定した要素の TLV 値を読み込みます。
tlv | 値が格納される配列 |
---|---|
tlv_size | tlv 配列の全体のバイト数 |
tlv_size は、データ全体が読み込めるサイズである必要があります。
TLV データの実際のサイズは取得できないので、大きめに用意しておくといいでしょう。
dB 情報 (コンテナは除く) の場合は、最大で 8 * sizeof(int) です。
正しく読み込めなかった場合は、tlv[0] = -1, tlv[1] = 0 になります。
TLV データ
TLV データには、「タイプ・サイズ・値」の3つの要素があり、整数の配列に格納されます。
先頭にタイプ、2番目に値部分のサイズ、3番目以降に値が入ります。
音量の dB 関連値の場合は、直接この値を使うことはせずに、対応する関数を使います。
tlv[0] | TLV タイプ |
---|---|
tlv[1] | 値全体のバイト数 (個数ではない) |
tlv[2..] | 値。タイプによって、数や意味は異なる。 |
先頭にタイプ、2番目に値部分のサイズ、3番目以降に値が入ります。
音量の dB 関連値の場合は、直接この値を使うことはせずに、対応する関数を使います。
TLV タイプ
#define SND_CTL_TLVT_CONTAINER 0x0000 //コンテナ #define SND_CTL_TLVT_DB_SCALE 0x0001 //dB スケール #define SND_CTL_TLVT_DB_LINEAR 0x0002 //線形音量 (0.0〜1.0) #define SND_CTL_TLVT_DB_RANGE 0x0003 //dB 範囲 #define SND_CTL_TLVT_DB_MINMAX 0x0004 //dB 最小値/最大値 #define SND_CTL_TLVT_DB_MINMAX_MUTE 0x0005 //dB 最小値/最大値(ミュート) //チャンネルマップ #define SND_CTL_TLVT_CHMAP_FIXED 0x00101 #define SND_CTL_TLVT_CHMAP_VAR 0x00102 #define SND_CTL_TLVT_CHMAP_PAIRED 0x00103
TLV タイプは、配列の先頭で指定される値です。
現在は、dB とチャンネルマップ関連のタイプしかありません。
コンテナタイプは、値の中に、複数の TLV データが含まれる形になります。
音量は、基本的に SND_CTL_TLVT_DB_SCALE が使われます。
dB 情報
読み込んだ TLV データから、dB の情報を取得したい場合は、まず、snd_tlv_parse_dB_info() で、TLV データ内から、dB 情報の位置を取得します。
その後、snd_tlv_get_dB_range() などを使って、dB の範囲を取得したり、値を変換したりします。
指定された TLV データから、dB 情報 (SND_CTL_TLVT_DB_*) を検索し、先頭位置のポインタを db_tlvp に返します。
コンテナタイプであれば、その中のデータを見つけます。
成功した場合は、0 より大きい値になるので、注意してください。
snd_tlv_parse_dB_info() で取得したポインタを渡して、音量に指定できる dB 値の、最小値と最大値を取得します。
rangemin と rangemax は、要素の本来の数値の、最小値と最大値を指定します。
要素情報の、snd_ctl_elem_info_get_min() と snd_ctl_elem_info_get_max() で取得できる値です。
min, max に、dB 値の最小値と最大値が返ります。
この場合、0.01 dB 単位の整数となります。
snd_tlv_parse_dB_info() で取得したポインタを渡します。
そこから dB 情報を取得し、生の音量値を、dB 値に変換します。
現在の音量値から、dB 単位の値を取得する時に使います。
volume は、変換する生の音量値です。
db_gain に、変換された db 値が返ります (0.01 dB 単位)。
snd_tlv_parse_dB_info() で取得したポインタを渡します。
そこから dB 情報を取得し、dB 値を、生の音量値に変換します。
dB 単位で音量を変更したい時に使います。
その後、snd_tlv_get_dB_range() などを使って、dB の範囲を取得したり、値を変換したりします。
TLV から dB 情報を検索
int snd_tlv_parse_dB_info(unsigned int *tlv, unsigned int tlv_size, unsigned int **db_tlvp);
指定された TLV データから、dB 情報 (SND_CTL_TLVT_DB_*) を検索し、先頭位置のポインタを db_tlvp に返します。
コンテナタイプであれば、その中のデータを見つけます。
tlv_size | tlv 配列全体のバイト数 |
---|---|
戻り値 | 見つかった場合は、その TLV データの全体バイト数 (タイプやサイズのデータも含む)。 見つからない場合は -ENOENT。 エラーの場合は、負のエラーコード。 |
成功した場合は、0 より大きい値になるので、注意してください。
dB の範囲を取得
int snd_tlv_get_dB_range(unsigned int *tlv, long rangemin, long rangemax, long *min, long *max);
snd_tlv_parse_dB_info() で取得したポインタを渡して、音量に指定できる dB 値の、最小値と最大値を取得します。
rangemin と rangemax は、要素の本来の数値の、最小値と最大値を指定します。
要素情報の、snd_ctl_elem_info_get_min() と snd_ctl_elem_info_get_max() で取得できる値です。
min, max に、dB 値の最小値と最大値が返ります。
この場合、0.01 dB 単位の整数となります。
生の音量値を dB 値に変換
int snd_tlv_convert_to_dB(unsigned int *tlv, long rangemin, long rangemax, long volume, long *db_gain);
snd_tlv_parse_dB_info() で取得したポインタを渡します。
そこから dB 情報を取得し、生の音量値を、dB 値に変換します。
現在の音量値から、dB 単位の値を取得する時に使います。
volume は、変換する生の音量値です。
db_gain に、変換された db 値が返ります (0.01 dB 単位)。
dB 値を生の音量値に変換
int snd_tlv_convert_from_dB(unsigned int *tlv, long rangemin, long rangemax, long db_gain, long *value, int xdir);
snd_tlv_parse_dB_info() で取得したポインタを渡します。
そこから dB 情報を取得し、dB 値を、生の音量値に変換します。
dB 単位で音量を変更したい時に使います。
rangemin rangemax | 生の音量の最小値と最大値 |
---|---|
db_gain | 変換する dB 値 (0.01 dB 単位) |
value | 変換された、生の音量値が返る |
xdir | 整数に変換する時の、切り上げの方向。 正の値で切り上げ、負の値で切り捨て。 0 で、最も近い方向に切り上げ。 |
プログラム
TLV で読み込み可能な要素の TLV データを出力します。
TLV データに dB 情報があれば、dB 範囲と、現在の音量の dB 値を出力します。
TLV データに dB 情報があれば、dB 範囲と、現在の音量の dB 値を出力します。
$ cc -o 05-tlv 05-tlv.c -lasound
#include <stdio.h> #include <alsa/asoundlib.h> int main(int argc,char **argv) { snd_ctl_t *ctl; snd_ctl_elem_list_t *list; snd_ctl_elem_info_t *info; snd_ctl_elem_value_t *value; snd_ctl_elem_id_t *eid; uint32_t i,j,cnt,vcnt,vcnt2; unsigned int tlv[8],*ptlv; long min,max,val1,val2; 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); snd_ctl_elem_list_alloc_space(list, cnt); snd_ctl_elem_list(ctl, list); //リスト snd_ctl_elem_info_malloc(&info); snd_ctl_elem_value_malloc(&value); snd_ctl_elem_id_malloc(&eid); for(i = 0; i < cnt; i++) { snd_ctl_elem_list_get_id(list, i, eid); snd_ctl_elem_info_set_id(info, eid); snd_ctl_elem_info(ctl, info); //TLV if(snd_ctl_elem_info_is_tlv_readable(info)) { snd_ctl_elem_tlv_read(ctl, eid, tlv, 8 * sizeof(int)); printf("-- [%d] --\n", i); printf("<name> %s\n", snd_ctl_elem_list_get_name(list, i)); printf("type:%d, size:%d\n", tlv[0], tlv[1]); vcnt = tlv[1] / sizeof(int); if(tlv[0] == SND_CTL_TLVT_CONTAINER) { //コンテナ for(ptlv = tlv + 2; vcnt; ) { printf("{\n type:%d, size:%d\n [", ptlv[0], ptlv[1]); vcnt2 = ptlv[1] / sizeof(int); for(j = 0; j < vcnt2; j++) printf("%u, ", ptlv[2 + j]); ptlv += 2 + vcnt2; vcnt -= 2 + vcnt2; } printf("]\n}\n"); } else { printf("[ "); for(j = 0; j < vcnt; j++) printf("%u, ", tlv[2 + j]); printf("]\n"); } //dB if(snd_tlv_parse_dB_info(tlv, 8 * sizeof(int), &ptlv) > 0) { min = snd_ctl_elem_info_get_min(info); max = snd_ctl_elem_info_get_max(info); //range if(snd_tlv_get_dB_range(ptlv, min, max, &val1, &val2) == 0) printf("dB range: %.2f, %.2f\n", val1 * 0.01, val2 * 0.01); //raw -> dB snd_ctl_elem_value_set_id(value, eid); snd_ctl_elem_read(ctl, value); if(snd_tlv_convert_to_dB(ptlv, min, max, snd_ctl_elem_value_get_integer(value, 0), &val1) == 0) { printf("current dB: %.2f\n", val1 * 0.01); } } } } snd_ctl_elem_id_free(eid); snd_ctl_elem_value_free(value); snd_ctl_elem_info_free(info); // snd_ctl_elem_list_free_space(list); snd_ctl_elem_list_free(list); snd_ctl_close(ctl); return 0; }
実行結果
-- [28] -- <name> Master Playback Volume type:1, size:8 [ 4294960896, 100, ] dB range: -64.00, 0.00 current dB: -37.00 -- [58] -- <name> Playback Channel Map type:0, size:16 { type:258, size:8 [3, 4, ] } -- [59] -- <name> PCM Playback Volume type:1, size:8 [ 4294962196, 20, ] dB range: -51.00, 0.00 current dB: 0.00
音量
音量は、SND_CTL_TLVT_DB_SCALE タイプで、値は2つになっています。
1つ目の値は、フラグなども含めた値なので、直接ユーザーが操作することはできません。
詳細は、ALSA ライブラリのソースコードを見ればわかります。
alsamixer コマンド (alsa-utils パッケージ) で、各音量をチェックしてみてください。
dB の最小値と最大値、現在の dB 値が一致しているのが確認できると思います。

Master の 17 という値は、音量を 0〜100 で表現した時の値です。
alsamixer のソースコードを見るとわかりますが、少し複雑な計算となっているので、PulseAudio などのアプリで見た時の音量と、値が一致しない場合があります。
1つ目の値は、フラグなども含めた値なので、直接ユーザーが操作することはできません。
詳細は、ALSA ライブラリのソースコードを見ればわかります。
alsamixer コマンド (alsa-utils パッケージ) で、各音量をチェックしてみてください。
dB の最小値と最大値、現在の dB 値が一致しているのが確認できると思います。

Master の 17 という値は、音量を 0〜100 で表現した時の値です。
alsamixer のソースコードを見るとわかりますが、少し複雑な計算となっているので、PulseAudio などのアプリで見た時の音量と、値が一致しない場合があります。
音量の連動
再生のマスター音量は、PulseAudio の出力(シンク)の音量と連動しているので、ALSA のマスター音量を変更すると、PulseAudio の出力音量も変更されます。
逆も同じで、pavucontrol で PulseAudio の出力音量を変更すると、alsamixer 上の音量も変更されます。
ただし、この場合、マスター音量の他に、PCM の音量も微妙に変化するようです。
逆も同じで、pavucontrol で PulseAudio の出力音量を変更すると、alsamixer 上の音量も変更されます。
ただし、この場合、マスター音量の他に、PCM の音量も微妙に変化するようです。
チャンネルマップ
チャンネルマップは、コンテナの情報になっています。
Playback Channel Map は、SND_CTL_TLVT_CHMAP_VAR タイプで、値が2つです。
3 と 4 は、チャンネル位置を意味し、SND_CHMAP_FL (左) と SND_CHMAP_FR (右) が指定されています。
Playback Channel Map は、SND_CTL_TLVT_CHMAP_VAR タイプで、値が2つです。
3 と 4 は、チャンネル位置を意味し、SND_CHMAP_FL (左) と SND_CHMAP_FR (右) が指定されています。