音量の変更
音量の値を取得できたので、今回は、要素の値を変更することで、音量を変更してみます。
前回は TLV データを読み込んで、そこから音量の dB 情報を取得しました。
しかし、音量を変更する場合、TLV で書き込むことはしません。
音量が持っている TLV データは、あくまで dB 値と生の音量値を変換するための補足情報であって、読み込みのみが可能になっているので、書き込むことはできません。
音量を変更する場合は、要素の生の数値で直接指定するか、TLV データで dB 値から生の音量値に変換した値を使います。
それを、要素の新しい値として書き込みます。
要素の値を変更する場合、snd_ctl_elem_value_t に、変更したい要素の識別子と、要素の値をセットして、snd_ctl_elem_write() を実行します。
戻り値は、0 で成功。
正の値で、値が変更された時に成功。
負の値で、エラーコードです。
要素の値をセットする関数は、要素:値 を参照してください。
前回は TLV データを読み込んで、そこから音量の dB 情報を取得しました。
しかし、音量を変更する場合、TLV で書き込むことはしません。
音量が持っている TLV データは、あくまで dB 値と生の音量値を変換するための補足情報であって、読み込みのみが可能になっているので、書き込むことはできません。
音量を変更する場合は、要素の生の数値で直接指定するか、TLV データで dB 値から生の音量値に変換した値を使います。
それを、要素の新しい値として書き込みます。
要素に値を書き込む
int snd_ctl_elem_write(snd_ctl_t *ctl, snd_ctl_elem_value_t *data);
要素の値を変更する場合、snd_ctl_elem_value_t に、変更したい要素の識別子と、要素の値をセットして、snd_ctl_elem_write() を実行します。
戻り値は、0 で成功。
正の値で、値が変更された時に成功。
負の値で、エラーコードです。
要素の値をセットする関数は、要素:値 を参照してください。
dB 関連関数
なお、生の音量値と dB 値を変換するのに、いちいち TLV の読み込みを行うのは面倒なので、内部で TLV の読み込みと変換を行う、ラッパー関数が存在します。
ただし、dB の範囲を取得した上で、値の変換が必要な場合は、複数回同じ TLV の読み込みを行うことになるので、多少非効率にはなります。
//dB の最小値と最大値を取得 int snd_ctl_get_dB_range(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id, long *min, long *max); //生の音量値を dB 値に変換 int snd_ctl_convert_to_dB(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id, long volume, long *db_gain); //dB 値を生の音量値に変換 int snd_ctl_convert_from_dB(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id, long db_gain, long *value, int xdir);
ただし、dB の範囲を取得した上で、値の変換が必要な場合は、複数回同じ TLV の読み込みを行うことになるので、多少非効率にはなります。
プログラム
"Master Playback Volume" の音量を、dB 範囲の中間位置に変更します。
$ cc -o 06-volume 06-volume.c -lasound
#include <stdio.h> #include <string.h> #include <alsa/asoundlib.h> int main(int argc,char **argv) { int ret; snd_ctl_t *ctl; snd_ctl_elem_list_t *list; snd_ctl_elem_value_t *value; snd_ctl_elem_id_t *eid; uint32_t i,cnt; long vol,min,max; if(snd_ctl_open(&ctl, (argc >= 2)? argv[1]: "default", 0)) return 1; //開く snprintf(m, 16, "hw:%d", card); if(snd_ctl_open(&ctl, m, 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_value_malloc(&value); snd_ctl_elem_id_malloc(&eid); for(i = 0; i < cnt; i++) { snd_ctl_elem_list_get_id(list, i, eid); if(strcmp(snd_ctl_elem_id_get_name(eid), "Master Playback Volume") == 0) { //dB 範囲の中間位置 snd_ctl_get_dB_range(ctl, eid, &min, &max); snd_ctl_convert_from_dB(ctl, eid, (max - min) / 2 + min, &vol, 0); //書き込み snd_ctl_elem_value_set_id(value, eid); snd_ctl_elem_value_set_integer(value, 0, vol); ret = snd_ctl_elem_write(ctl, value); printf("value:%ld, ret:%d\n", vol, ret); } } snd_ctl_elem_id_free(eid); snd_ctl_elem_value_free(value); // snd_ctl_elem_list_free_space(list); snd_ctl_elem_list_free(list); snd_ctl_close(ctl); return 0; }
解説
value:32, ret:0
dB 範囲が min = -6400, max = 0 の場合、中間位置は -3200 です。
※dB 値は、0.01 dB 単位の整数。
snd_ctl_elem_value_t に、要素の識別子 (snd_ctl_elem_id_t) と、数値の値をセットして、snd_ctl_elem_write() で書き込みます。
alsamixer で確認してみると、dB は -32.0 で、音量値 (0〜100) は 23 でした。
dB は、単純な線形グラフにはならないので、dB 範囲の中間位置は、実際は 50% より下の位置になります。
書き込まれた生の値は、0〜64 の範囲の中での 32 なので、中間位置となっています。
dB 範囲と生の値は、線形の関係にあるようです。