?? amixer.c
字號:
/* * ALSA command line mixer utility * Copyright (c) 1999-2000 by Jaroslav Kysela <perex@suse.cz> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */#include <stdio.h>#include <stdlib.h>#include <string.h>#include <getopt.h>#include <stdarg.h>#include <ctype.h>#include <math.h>#include <errno.h>#include <assert.h>#include <alsa/asoundlib.h>#include <sys/poll.h>#include "amixer.h"#define LEVEL_BASIC (1<<0)#define LEVEL_INACTIVE (1<<1)#define LEVEL_ID (1<<2)static int quiet = 0;static int debugflag = 0;static int no_check = 0;static int smixer_level = 0;static int ignore_error = 0;static struct snd_mixer_selem_regopt smixer_options;static char card[64] = "default";static void error(const char *fmt,...){ va_list va; va_start(va, fmt); fprintf(stderr, "amixer: "); vfprintf(stderr, fmt, va); fprintf(stderr, "\n"); va_end(va);}static int help(void){ printf("Usage: amixer <options> [command]\n"); printf("\nAvailable options:\n"); printf(" -h,--help this help\n"); printf(" -c,--card N select the card\n"); printf(" -D,--device N select the device, default '%s'\n", card); printf(" -d,--debug debug mode\n"); printf(" -n,--nocheck do not perform range checking\n"); printf(" -v,--version print version of this program\n"); printf(" -q,--quiet be quiet\n"); printf(" -i,--inactive show also inactive controls\n"); printf(" -a,--abstract L select abstraction level (none or basic)\n"); printf(" -s,--stdin Read and execute commands from stdin sequentially\n"); printf("\nAvailable commands:\n"); printf(" scontrols show all mixer simple controls\n"); printf(" scontents show contents of all mixer simple controls (default command)\n"); printf(" sset sID P set contents for one mixer simple control\n"); printf(" sget sID get contents for one mixer simple control\n"); printf(" controls show all controls for given card\n"); printf(" contents show contents of all controls for given card\n"); printf(" cset cID P set control contents for one control\n"); printf(" cget cID get control contents for one control\n"); return 0;}static int info(void){ int err; snd_ctl_t *handle; snd_mixer_t *mhandle; snd_ctl_card_info_t *info; snd_ctl_elem_list_t *clist; snd_ctl_card_info_alloca(&info); snd_ctl_elem_list_alloca(&clist); if ((err = snd_ctl_open(&handle, card, 0)) < 0) { error("Control device %s open error: %s", card, snd_strerror(err)); return err; } if ((err = snd_ctl_card_info(handle, info)) < 0) { error("Control device %s hw info error: %s", card, snd_strerror(err)); return err; } printf("Card %s '%s'/'%s'\n", card, snd_ctl_card_info_get_id(info), snd_ctl_card_info_get_longname(info)); printf(" Mixer name : '%s'\n", snd_ctl_card_info_get_mixername(info)); printf(" Components : '%s'\n", snd_ctl_card_info_get_components(info)); if ((err = snd_ctl_elem_list(handle, clist)) < 0) { error("snd_ctl_elem_list failure: %s", snd_strerror(err)); } else { printf(" Controls : %i\n", snd_ctl_elem_list_get_count(clist)); } snd_ctl_close(handle); if ((err = snd_mixer_open(&mhandle, 0)) < 0) { error("Mixer open error: %s", snd_strerror(err)); return err; } if (smixer_level == 0 && (err = snd_mixer_attach(mhandle, card)) < 0) { error("Mixer attach %s error: %s", card, snd_strerror(err)); snd_mixer_close(mhandle); return err; } if ((err = snd_mixer_selem_register(mhandle, smixer_level > 0 ? &smixer_options : NULL, NULL)) < 0) { error("Mixer register error: %s", snd_strerror(err)); snd_mixer_close(mhandle); return err; } err = snd_mixer_load(mhandle); if (err < 0) { error("Mixer load %s error: %s", card, snd_strerror(err)); snd_mixer_close(mhandle); return err; } printf(" Simple ctrls : %i\n", snd_mixer_get_count(mhandle)); snd_mixer_close(mhandle); return 0;}static const char *control_iface(snd_ctl_elem_id_t *id){ return snd_ctl_elem_iface_name(snd_ctl_elem_id_get_interface(id));}static const char *control_type(snd_ctl_elem_info_t *info){ return snd_ctl_elem_type_name(snd_ctl_elem_info_get_type(info));}static const char *control_access(snd_ctl_elem_info_t *info){ static char result[10]; char *res = result; *res++ = snd_ctl_elem_info_is_readable(info) ? 'r' : '-'; *res++ = snd_ctl_elem_info_is_writable(info) ? 'w' : '-'; *res++ = snd_ctl_elem_info_is_inactive(info) ? 'i' : '-'; *res++ = snd_ctl_elem_info_is_volatile(info) ? 'v' : '-'; *res++ = snd_ctl_elem_info_is_locked(info) ? 'l' : '-'; *res++ = snd_ctl_elem_info_is_tlv_readable(info) ? 'R' : '-'; *res++ = snd_ctl_elem_info_is_tlv_writable(info) ? 'W' : '-'; *res++ = snd_ctl_elem_info_is_tlv_commandable(info) ? 'C' : '-'; *res++ = '\0'; return result;}#define check_range(val, min, max) \ (no_check ? (val) : ((val < min) ? (min) : (val > max) ? (max) : (val))) #if 0static int convert_range(int val, int omin, int omax, int nmin, int nmax){ int orange = omax - omin, nrange = nmax - nmin; if (orange == 0) return 0; return rint((((double)nrange * ((double)val - (double)omin)) + ((double)orange / 2.0)) / ((double)orange + (double)nmin));}#endif#if 0static int convert_db_range(int val, int omin, int omax, int nmin, int nmax){ int orange = omax - omin, nrange = nmax - nmin; if (orange == 0) return 0; return rint((((double)nrange * ((double)val - (double)omin)) + ((double)orange / 2.0)) / (double)orange + (double)nmin);}#endif/* Fuction to convert from volume to percentage. val = volume */static int convert_prange(int val, int min, int max){ int range = max - min; int tmp; if (range == 0) return 0; val -= min; tmp = rint((double)val/(double)range * 100); return tmp;}/* Function to convert from percentage to volume. val = percentage */#define convert_prange1(val, min, max) \ ceil((val) * ((max) - (min)) * 0.01 + (min))static const char *get_percent(int val, int min, int max){ static char str[32]; int p; p = convert_prange(val, min, max); sprintf(str, "%i [%i%%]", val, p); return str;}#if 0static const char *get_percent1(int val, int min, int max, int min_dB, int max_dB){ static char str[32]; int p, db; p = convert_prange(val, min, max); db = convert_db_range(val, min, max, min_dB, max_dB); sprintf(str, "%i [%i%%] [%i.%02idB]", val, p, db / 100, abs(db % 100)); return str;}#endifstatic long get_integer(char **ptr, long min, long max){ long val = min; char *p = *ptr, *s; if (*p == ':') p++; if (*p == '\0' || (!isdigit(*p) && *p != '-')) goto out; s = p; val = strtol(s, &p, 10); if (*p == '.') { p++; strtol(p, &p, 10); } if (*p == '%') { val = (long)convert_prange1(strtod(s, NULL), min, max); p++; } val = check_range(val, min, max); if (*p == ',') p++; out: *ptr = p; return val;}static long get_integer64(char **ptr, long long min, long long max){ long long val = min; char *p = *ptr, *s; if (*p == ':') p++; if (*p == '\0' || (!isdigit(*p) && *p != '-')) goto out; s = p; val = strtol(s, &p, 10); if (*p == '.') { p++; strtol(p, &p, 10); } if (*p == '%') { val = (long long)convert_prange1(strtod(s, NULL), min, max); p++; } val = check_range(val, min, max); if (*p == ',') p++; out: *ptr = p; return val;}struct volume_ops { int (*get_range)(snd_mixer_elem_t *elem, long *min, long *max); int (*get)(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t c, long *value); int (*set)(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t c, long value);}; enum { VOL_RAW, VOL_DB };struct volume_ops_set { int (*has_volume)(snd_mixer_elem_t *elem); struct volume_ops v[2];};static int set_playback_dB(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t c, long value){ return snd_mixer_selem_set_playback_dB(elem, c, value, 0);}static int set_capture_dB(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t c, long value){ return snd_mixer_selem_set_capture_dB(elem, c, value, 0);}static struct volume_ops_set vol_ops[2] = { { .has_volume = snd_mixer_selem_has_playback_volume, .v = {{ snd_mixer_selem_get_playback_volume_range, snd_mixer_selem_get_playback_volume, snd_mixer_selem_set_playback_volume }, { snd_mixer_selem_get_playback_dB_range, snd_mixer_selem_get_playback_dB, set_playback_dB }}, }, { .has_volume = snd_mixer_selem_has_capture_volume, .v = {{ snd_mixer_selem_get_capture_volume_range, snd_mixer_selem_get_capture_volume, snd_mixer_selem_set_capture_volume }, { snd_mixer_selem_get_capture_dB_range, snd_mixer_selem_get_capture_dB, set_capture_dB }}, },};static int set_volume_simple(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t chn, char **ptr, int dir){ long val, orig, pmin, pmax; char *p = *ptr, *s; int invalid = 0, err = 0, vol_type = VOL_RAW; if (! vol_ops[dir].has_volume(elem)) invalid = 1; if (*p == ':') p++; if (*p == '\0' || (!isdigit(*p) && *p != '-')) goto skip; if (! invalid && vol_ops[dir].v[VOL_RAW].get_range(elem, &pmin, &pmax) < 0) invalid = 1; s = p; val = strtol(s, &p, 10); if (*p == '.') { p++; strtol(p, &p, 10); } if (*p == '%') { if (! invalid) val = (long)convert_prange1(strtod(s, NULL), pmin, pmax); p++; } else if (p[0] == 'd' && p[1] == 'B') { if (! invalid) { val = (long)(strtod(s, NULL) * 100.0); vol_type = VOL_DB; if (vol_ops[dir].v[vol_type].get_range(elem, &pmin, &pmax) < 0) invalid = 1; } p += 2; } if (*p == '+' || *p == '-') { if (! invalid) { if (vol_ops[dir].v[vol_type].get(elem, chn, &orig) < 0) invalid = 1; if (*p == '+') val = orig + val; else val = orig - val; } p++; } if (! invalid) { val = check_range(val, pmin, pmax); err = vol_ops[dir].v[vol_type].set(elem, chn, val); } skip: if (*p == ',') p++; *ptr = p; return err ? err : (invalid ? -ENOENT : 0);}static int get_bool_simple(char **ptr, char *str, int invert, int orig){ if (**ptr == ':') (*ptr)++; if (!strncasecmp(*ptr, str, strlen(str))) { orig = 1 ^ (invert ? 1 : 0); while (**ptr != '\0' && **ptr != ',' && **ptr != ':') (*ptr)++; } if (**ptr == ',' || **ptr == ':') (*ptr)++; return orig;} static int simple_skip_word(char **ptr, char *str){ char *xptr = *ptr; if (*xptr == ':') xptr++; if (!strncasecmp(xptr, str, strlen(str))) { while (*xptr != '\0' && *xptr != ',' && *xptr != ':') xptr++; if (*xptr == ',' || *xptr == ':') xptr++; *ptr = xptr; return 1; } return 0;} static void show_control_id(snd_ctl_elem_id_t *id){ unsigned int index, device, subdevice; printf("numid=%u,iface=%s,name='%s'", snd_ctl_elem_id_get_numid(id), control_iface(id), snd_ctl_elem_id_get_name(id)); index = snd_ctl_elem_id_get_index(id); device = snd_ctl_elem_id_get_device(id); subdevice = snd_ctl_elem_id_get_subdevice(id); if (index) printf(",index=%i", index); if (device) printf(",device=%i", device); if (subdevice) printf(",subdevice=%i", subdevice);}static void print_spaces(unsigned int spaces){ while (spaces-- > 0) putc(' ', stdout);}static void print_dB(long dB){ printf("%li.%02lidB", dB / 100, (dB < 0 ? -dB : dB) % 100);}static void decode_tlv(unsigned int spaces, unsigned int *tlv, unsigned int tlv_size){ unsigned int type = tlv[0]; unsigned int size; unsigned int idx = 0; if (tlv_size < 2 * sizeof(unsigned int)) { printf("TLV size error!\n"); return; } print_spaces(spaces); printf("| "); type = tlv[idx++]; size = tlv[idx++]; tlv_size -= 2 * sizeof(unsigned int); if (size > tlv_size) { printf("TLV size error (%i, %i, %i)!\n", type, size, tlv_size); return; } switch (type) { case SND_CTL_TLVT_CONTAINER: size += sizeof(unsigned int) -1; size /= sizeof(unsigned int); while (idx < size) { if (tlv[idx+1] > (size - idx) * sizeof(unsigned int)) { printf("TLV size error in compound!\n"); return; } decode_tlv(spaces + 2, tlv + idx, tlv[idx+1]); idx += 2 + (tlv[1] + sizeof(unsigned int) - 1) / sizeof(unsigned int); } break; case SND_CTL_TLVT_DB_SCALE: printf("dBscale-"); if (size != 2 * sizeof(unsigned int)) {
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -