/* * ALSA command line mixer utility * Copyright (c) 1999-2000 by Jaroslav Kysela * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include #include #include #include #include #include #include #include "amixer.h" #define HELPID_HELP 1000 #define HELPID_CARD 1001 #define HELPID_QUIET 1002 #define HELPID_DEBUG 1003 #define HELPID_VERSION 1004 int quiet = 0; int debugflag = 0; int card; int scontrol_contents_is_on = 0; 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 void help(void) { printf("Usage: amixer command\n"); printf("\nAvailable options:\n"); printf(" -h,--help this help\n"); printf(" -c,--card # use a card number (0-%i) or the card name, default %i\n", snd_cards() - 1, card); printf(" -D,--debug debug mode\n"); printf(" -v,--version print version of this program\n"); printf("\nAvailable commands:\n"); printf(" groups show all mixer groups\n"); printf(" gcontents show contents of all mixer groups (default command)\n"); printf(" set G P set group contents for one mixer group\n"); printf(" get G P get group contents for one mixer group\n"); printf(" elements show all mixer elements\n"); printf(" contents show contents of all mixer elements\n"); printf(" eset E P set extended contents for one mixer element\n"); printf(" eget E P get extended contents for one mixer element\n"); } int info(void) { int err; snd_ctl_t *handle; snd_mixer_t *mhandle; snd_ctl_hw_info_t info; snd_control_list_t clist; snd_mixer_simple_control_list_t slist; if ((err = snd_ctl_open(&handle, card)) < 0) { error("Control device %i open error: %s", card, snd_strerror(err)); return -1; } if ((err = snd_ctl_hw_info(handle, &info)) < 0) { error("Control device %i hw info error: %s", card, snd_strerror(err)); return -1; } printf("Card '%s/%s':\n", info.id, info.longname); printf(" Mixer ID : '%s'\n", info.mixerid); printf(" Mixer name : '%s'\n", info.mixername); memset(&clist, 0, sizeof(clist)); if ((err = snd_ctl_clist(handle, &clist)) < 0) { error("snd_ctl_clist failure: %s", snd_strerror(err)); } else { printf(" Controls : %i\n", clist.controls); } snd_ctl_close(handle); if ((err = snd_mixer_open(&mhandle, card)) < 0) { error("Mixer device %i open error: %s", card, snd_strerror(err)); return -1; } if ((err = snd_mixer_simple_control_list(mhandle, &slist)) < 0) { error("snd_mixer_simple_control_list failure: %s\n", snd_strerror(err)); } else { printf(" Simple ctrls : %i\n", clist.controls); } snd_mixer_close(mhandle); return 0; } static const char *element_name(const char *name) { static char res[25]; strncpy(res, name, 24); res[24] = '\0'; return res; } static int check_range(int val, int min, int max) { if (val < min) return min; if (val > max) return max; return val; } static 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)); } static int convert_db_range(int val, int omin, int omax, int nmin, int nmax) { int orange = omax - omin, nrange = nmax - nmin; int tmp; if (orange == 0) return 0; tmp = rint((((double)nrange * ((double)val - (double)omin)) + ((double)orange / 2.0)) / (double)orange + (double)nmin); return tmp; } /* 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 */ static int convert_prange1(int val, int min, int max) { int range = max - min; int tmp; if (range == 0) return 0; tmp = rint((double)range * ((double)val*.01)); tmp += min; #if 0 printf("%i %i %i %i", val, max, min, tmp); #endif return tmp; } 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; } static 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; } static int get_volume(char **ptr, int min, int max, int min_dB, int max_dB) { int tmp, tmp1, tmp2; if (**ptr == ':') (*ptr)++; if (**ptr == '\0' || (!isdigit(**ptr) && **ptr != '-')) return min; tmp = atoi(*ptr); if (**ptr == '-') (*ptr)++; while (isdigit(**ptr)) (*ptr)++; tmp1 = tmp; tmp2 = 0; if (**ptr == '.') { (*ptr)++; tmp2 = atoi(*ptr); while (isdigit(**ptr)) (*ptr)++; } if (**ptr == '%') { tmp1 = convert_prange1(tmp, min, max); (*ptr)++; } else if (**ptr == 'd') { tmp1 *= 100; tmp1 += tmp2 % 100; tmp1 = convert_range(tmp1, min_dB, max_dB, min, max); (*ptr)++; } tmp1 = check_range(tmp1, min, max); if (**ptr == ',') (*ptr)++; return tmp1; } static int get_volume_simple(char **ptr, int min, int max, int orig) { int tmp, tmp1, tmp2; if (**ptr == ':') (*ptr)++; if (**ptr == '\0' || (!isdigit(**ptr) && **ptr != '-')) return min; tmp = atoi(*ptr); if (**ptr == '-') (*ptr)++; while (isdigit(**ptr)) (*ptr)++; tmp1 = tmp; tmp2 = 0; if (**ptr == '.') { (*ptr)++; tmp2 = atoi(*ptr); while (isdigit(**ptr)) (*ptr)++; } if (**ptr == '%') { tmp1 = convert_prange1(tmp, min, max); (*ptr)++; } if (**ptr == '+') { tmp1 = orig + tmp1; (*ptr)++; } else if (**ptr == '-') { tmp1 = orig - tmp1; (*ptr)++; } tmp1 = check_range(tmp1, min, max); if (**ptr == ',') (*ptr)++; return tmp1; } int controls(void) { #if 0 int err, idx; snd_mixer_t *handle; snd_mixer_elements_t elements; snd_mixer_eid_t *element; if ((err = snd_mixer_open(&handle, card, device)) < 0) { error("Mixer %i/%i open error: %s", card, device, snd_strerror(err)); return -1; } bzero(&elements, sizeof(elements)); if ((err = snd_mixer_elements(handle, &elements)) < 0) { error("Mixer %i/%i elements error: %s", card, device, snd_strerror(err)); return -1; } elements.pelements = (snd_mixer_eid_t *)malloc(elements.elements_over * sizeof(snd_mixer_eid_t)); if (!elements.pelements) { error("Not enough memory"); return -1; } elements.elements_size = elements.elements_over; elements.elements_over = elements.elements = 0; if ((err = snd_mixer_elements(handle, &elements)) < 0) { error("Mixer %i/%i elements (2) error: %s", card, device, snd_strerror(err)); return -1; } for (idx = 0; idx < elements.elements; idx++) { element = &elements.pelements[idx]; printf("Element '%s',%i,%s\n", element_name(element->name), element->index, element_type(element->type)); show_element(handle, element, " "); show_element_info(handle, element, " "); } free(elements.pelements); snd_mixer_close(handle); #endif return 0; } int controls_contents(void) { #if 0 int err, idx; snd_mixer_t *handle; snd_mixer_elements_t elements; snd_mixer_eid_t *element; if ((err = snd_mixer_open(&handle, card, device)) < 0) { error("Mixer %i/%i open error: %s", card, device, snd_strerror(err)); return -1; } bzero(&elements, sizeof(elements)); if ((err = snd_mixer_elements(handle, &elements)) < 0) { error("Mixer %i/%i elements error: %s", card, device, snd_strerror(err)); return -1; } elements.pelements = (snd_mixer_eid_t *)malloc(elements.elements_over * sizeof(snd_mixer_eid_t)); if (!elements.pelements) { error("Not enough memory"); return -1; } elements.elements_size = elements.elements_over; elements.elements_over = elements.elements = 0; if ((err = snd_mixer_elements(handle, &elements)) < 0) { error("Mixer %i/%i elements (2) error: %s", card, device, snd_strerror(err)); return -1; } for (idx = 0; idx < elements.elements; idx++) { element = &elements.pelements[idx]; printf("Element '%s',%i,%s\n", element_name(element->name), element->index, element_type(element->type)); show_element_info(handle, element, " "); show_element_contents(handle, element, " "); } free(elements.pelements); snd_mixer_close(handle); #endif return 0; } static const char *group_name(const char *name) { static char res[25]; strncpy(res, name, 24); res[24] = '\0'; return res; } int show_group(void *handle, snd_mixer_sid_t *sid, const char *space) { int err, chn; snd_mixer_simple_control_t scontrol; bzero(&scontrol, sizeof(scontrol)); scontrol.sid = *sid; if ((err = snd_mixer_simple_control_read(handle, &scontrol)) < 0) { error("Mixer %i simple_control error: %s", card, snd_strerror(err)); return -1; } if (scontrol_contents_is_on && scontrol.channels) { printf("%sCapabilities:", space); if (scontrol.caps & SND_MIXER_SCTCAP_VOLUME) printf(" volume"); if (scontrol.caps & SND_MIXER_SCTCAP_MUTE) printf(" mute"); if (scontrol.caps & SND_MIXER_SCTCAP_JOINTLY_MUTE) printf(" jointly-mute"); if (scontrol.caps & SND_MIXER_SCTCAP_CAPTURE) { printf(" capture"); } else { scontrol.capture = 0; } if (scontrol.caps & SND_MIXER_SCTCAP_JOINTLY_CAPTURE) printf(" jointly-capture"); if (scontrol.caps & SND_MIXER_SCTCAP_EXCL_CAPTURE) printf(" exclusive-capture"); printf("\n"); if ((scontrol.caps & SND_MIXER_SCTCAP_CAPTURE) && (scontrol.caps & SND_MIXER_SCTCAP_EXCL_CAPTURE)) printf("%sCapture exclusive scontrol: %i\n", space, scontrol.capture_group); printf("%sChannels: ", space); if (scontrol.channels == SND_MIXER_CHN_MASK_MONO) { printf("Mono"); } else { for (chn = 0; chn <= SND_MIXER_CHN_LAST; chn++) { if (!(scontrol.channels & (1<name), scontrol->index); show_scontrol(handle, scontrol, " "); } free(scontrols.pscontrols); snd_mixer_close(handle); #endif return 0; } int scontrols_contents(void) { #if 0 int err; scontrol_contents_is_on = 1; err = scontrols(); scontrol_contents_is_on = 0; return err; #else return 0; #endif } static int parse_sid(const char *str, snd_mixer_sid_t *sid) { int c, size; char *ptr; while (*str == ' ' || *str == '\t') str++; if (!(*str)) return 1; bzero(sid, sizeof(*sid)); ptr = sid->name; size = 0; if (*str != '"' && *str != '\'') { while (*str && *str != ',') { if (size < sizeof(sid->name)) { *ptr++ = *str; size++; } str++; } } else { c = *str++; while (*str && *str != c) { if (size < sizeof(sid->name)) { *ptr++ = *str; size++; } str++; } if (*str == c) str++; } if (*str == '\0') return 0; if (*str != ',') return 1; str++; if (!isdigit(*str)) return 1; sid->index = atoi(str); return 0; } int cset(int argc, char *argv[]) { #if 0 int err; snd_mixer_t *handle; snd_mixer_eid_t eid; if (argc < 1) { fprintf(stderr, "Specify a full element identifier: 'name',index,type\n"); return 1; } if (parse_eid(argv[0], &eid)) { fprintf(stderr, "Wrong element identifier: %s\n", argv[0]); return 1; } if ((err = snd_mixer_open(&handle, card, device)) < 0) { error("Mixer %i/%i open error: %s\n", card, device, snd_strerror(err)); return -1; } if (!quiet) { printf("Element '%s',%i,%s\n", element_name(eid.name), eid.index, element_type(eid.type)); } switch (eid.type) { case SND_MIXER_ETYPE_SWITCH1: if (eset_switch1(argc - 1, argv + 1, handle, &eid)) goto __end; break; case SND_MIXER_ETYPE_SWITCH2: if (eset_switch2(argc - 1, argv + 1, handle, &eid)) goto __end; break; case SND_MIXER_ETYPE_VOLUME1: if (eset_volume1(argc - 1, argv + 1, handle, &eid)) goto __end; break; case SND_MIXER_ETYPE_ACCU3: if (eset_accu3(argc - 1, argv + 1, handle, &eid)) goto __end; break; case SND_MIXER_ETYPE_MUX1: if (eset_mux1(argc - 1, argv + 1, handle, &eid)) goto __end; break; case SND_MIXER_ETYPE_MUX2: if (eset_mux2(argc - 1, argv + 1, handle, &eid)) goto __end; break; } if (!quiet) { if (snd_mixer_element_has_info(&eid)) { show_element_info(handle, &eid, " "); } if (snd_mixer_element_has_control(&eid)) { show_element_contents(handle, &eid, " "); } } __end: snd_mixer_close(handle); #endif return 0; } int cget(int argc, char *argv[]) { #if 0 int err; snd_mixer_t *handle; snd_mixer_eid_t eid; if (argc < 1) { fprintf(stderr, "Specify a full element identifier: 'name',index,type\n"); return 1; } if (parse_eid(argv[0], &eid)) { fprintf(stderr, "Wrong element identifier: %s\n", argv[0]); return 1; } if ((err = snd_mixer_open(&handle, card, device)) < 0) { error("Mixer %i/%i open error: %s\n", card, device, snd_strerror(err)); return -1; } printf("Element '%s',%i,%s\n", element_name(eid.name), eid.index, element_type(eid.type)); if (show_element(handle, &eid, " ") >= 0) { if (snd_mixer_element_has_info(&eid)) { show_element_info(handle, &eid, " "); } if (snd_mixer_element_has_control(&eid)) { show_element_contents(handle, &eid, " "); } } snd_mixer_close(handle); #endif return 0; } #if 0 typedef struct channel_mask { char *name; unsigned int mask; } channel_mask_t; static channel_mask_t chanmask[] = { {"frontleft", SND_MIXER_CHN_MASK_FRONT_LEFT}, {"frontright", SND_MIXER_CHN_MASK_FRONT_RIGHT}, {"frontcenter", SND_MIXER_CHN_MASK_FRONT_CENTER}, {"front", SND_MIXER_CHN_MASK_FRONT_LEFT|SND_MIXER_CHN_MASK_FRONT_RIGHT}, {"center", SND_MIXER_CHN_MASK_FRONT_CENTER}, {"rearleft", SND_MIXER_CHN_MASK_REAR_LEFT}, {"rearright", SND_MIXER_CHN_MASK_REAR_RIGHT}, {"rear", SND_MIXER_CHN_MASK_REAR_LEFT|SND_MIXER_CHN_MASK_REAR_RIGHT}, {"woofer", SND_MIXER_CHN_MASK_WOOFER}, {NULL} }; static int check_channels(char *arg, unsigned int mask, unsigned int *mask_return) { channel_mask_t *c; for (c = chanmask; c->name; c++) { if (! strncmp(arg, c->name, strlen(c->name))) { *mask_return = c->mask & mask; return 1; } } return 0; } #endif int sset(int argc, char *argv[]) { #if 0 int err, idx, chn; unsigned int channels; snd_mixer_t *handle; snd_mixer_sid_t sid; snd_mixer_scontrol_t scontrol; if (argc < 1) { fprintf(stderr, "Specify a scontrol identifier: 'name',index\n"); return 1; } if (parse_sid(argv[0], &sid)) { fprintf(stderr, "Wrong scontrol identifier: %s\n", argv[0]); return 1; } if (argc < 2) { fprintf(stderr, "Specify what you want to set...\n"); return 1; } if ((err = snd_mixer_open(&handle, card, device)) < 0) { error("Mixer %i/%i open error: %s\n", card, device, snd_strerror(err)); return -1; } bzero(&scontrol, sizeof(scontrol)); scontrol.sid = sid; if (snd_mixer_scontrol_read(handle, &scontrol)<0) { error("Unable to read scontrol '%s',%i: %s\n", scontrol_name(sid.name), sid.index, snd_strerror(err)); snd_mixer_close(handle); return -1; } channels = scontrol.channels; /* all channels */ for (idx = 1; idx < argc; idx++) { if (!strncmp(argv[idx], "mute", 4) || !strncmp(argv[idx], "off", 3)) { scontrol.mute = scontrol.channels; continue; } else if (!strncmp(argv[idx], "unmute", 6) || !strncmp(argv[idx], "on", 2)) { scontrol.mute = 0; continue; } else if (!strncmp(argv[idx], "cap", 3) || !strncmp(argv[idx], "rec", 3)) { scontrol.capture = scontrol.channels; continue; } else if (!strncmp(argv[idx], "nocap", 5) || !strncmp(argv[idx], "norec", 5)) { scontrol.capture = 0; continue; } if (check_channels(argv[idx], scontrol.channels, &channels)) continue; if (isdigit(argv[idx][0]) || argv[idx][0] == '+' || argv[idx][0] == '-') { char *ptr; int multi; multi = (strchr(argv[idx], ',') != NULL); ptr = argv[idx]; for (chn = 0; chn <= SND_MIXER_CHN_LAST; chn++) { if (!(scontrol.channels & (1< 1 ? argv + optind + 1 : NULL) ? 1 : 0; } else if (!strcmp(argv[optind], "sget")) { return sget(argc - optind - 1, argc - optind > 1 ? argv + optind + 1 : NULL) ? 1 : 0; } else if (!strcmp(argv[optind], "cset")) { return cset(argc - optind - 1, argc - optind > 1 ? argv + optind + 1 : NULL) ? 1 : 0; } else if (!strcmp(argv[optind], "cget")) { return cget(argc - optind - 1, argc - optind > 1 ? argv + optind + 1 : NULL) ? 1 : 0; } else { fprintf(stderr, "amixer: Unknown command '%s'...\n", argv[optind]); } return 0; }