diff options
author | dvdli <70133153+dvdli@users.noreply.github.com> | 2020-12-09 15:04:44 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-12-09 15:04:44 +0800 |
commit | 69b9891f995450be8dd8f24dbd39cf1d417fac61 (patch) | |
tree | 708078ee3400b6cebfaa1a6a2976160c6aa6ea7b | |
parent | 3cfea802956d0f1852239592e9b6cdc76386c75e (diff) | |
parent | 4acbc58a193b39fb13c093d966d43a33613f8020 (diff) |
Merge pull request #194 from dvdli/tinymix-dev
Fix tinymix bugs and support setting negative values
-rw-r--r-- | utils/tinymix.c | 247 |
1 files changed, 154 insertions, 93 deletions
diff --git a/utils/tinymix.c b/utils/tinymix.c index 41ca269..5c0378c 100644 --- a/utils/tinymix.c +++ b/utils/tinymix.c @@ -37,27 +37,33 @@ #define OPTPARSE_IMPLEMENTATION #include "optparse.h" -static void tinymix_list_controls(struct mixer *mixer, int print_all); +static void list_controls(struct mixer *mixer, int print_all); -static void tinymix_detail_control(struct mixer *mixer, const char *control); +static void print_control_values(struct mixer_ctl *control); +static void print_control_values_by_name_or_id(struct mixer *mixer, const char *name_or_id); -static int tinymix_set_value(struct mixer *mixer, const char *control, +static int set_values(struct mixer *mixer, const char *control, char **values, unsigned int num_values); -static void tinymix_print_enum(struct mixer_ctl *ctl); +static void print_enum(struct mixer_ctl *ctl); void usage(void) { printf("usage: tinymix [options] <command>\n"); printf("options:\n"); - printf("\t-h, --help : prints this help message and exits\n"); - printf("\t-v, --version : prints this version of tinymix and exits\n"); - printf("\t-D, --card NUMBER : specifies the card number of the mixer\n"); + printf("\t-h, --help : prints this help message and exits\n"); + printf("\t-v, --version : prints this version of tinymix and exits\n"); + printf("\t-D, --card NUMBER : specifies the card number of the mixer\n"); + printf("\n"); printf("commands:\n"); - printf("\tget NAME|ID : prints the values of a control\n"); - printf("\tset NAME|ID VALUE : sets the value of a control\n"); - printf("\tcontrols : lists controls of the mixer\n"); - printf("\tcontents : lists controls of the mixer and their contents\n"); + printf("\tget NAME|ID : prints the values of a control\n"); + printf("\tset NAME|ID VALUE(S) ... : sets the value of a control\n"); + printf("\t\tVALUE(S): integers, percents, and relative values\n"); + printf("\t\t\tIntegers: 0, 100, -100 ...\n"); + printf("\t\t\tPercents: 0%%, 100%% ...\n"); + printf("\t\t\tRelative values: 1+, 1-, 1%%+, 2%%+ ...\n"); + printf("\tcontrols : lists controls of the mixer\n"); + printf("\tcontents : lists controls of the mixer and their contents\n"); } void version(void) @@ -65,11 +71,24 @@ void version(void) printf("tinymix version 2.0 (tinyalsa version %s)\n", TINYALSA_VERSION_STRING); } +static int is_command(char *arg) { + return strcmp(arg, "get") == 0 || strcmp(arg, "set") == 0 || + strcmp(arg, "controls") == 0 || strcmp(arg, "contents") == 0; +} + +static int find_command_position(int argc, char **argv) +{ + for (int i = 0; i < argc; ++i) { + if (is_command(argv[i])) { + return i; + } + } + return -1; +} + int main(int argc, char **argv) { - struct mixer *mixer; - int card = 0, c; - char *cmd; + int card = 0; struct optparse opts; static struct optparse_long long_options[] = { { "card", 'D', OPTPARSE_REQUIRED }, @@ -78,8 +97,20 @@ int main(int argc, char **argv) { 0, 0, 0 } }; - optparse_init(&opts, argv); + // optparse_long may change the order of the argv list. Duplicate one for parsing the options. + char **argv_options_list = (char **) calloc(argc + 1, sizeof(char *)); + if (!argv_options_list) { + fprintf(stderr, "Failed to allocate options list\n"); + return EXIT_FAILURE; + } + + for (int i = 0; i < argc; ++i) { + argv_options_list[i] = argv[i]; + } + + optparse_init(&opts, argv_options_list); /* Detect the end of the options. */ + int c; while ((c = optparse_long(&opts, long_options, NULL)) != -1) { switch (c) { case 'D': @@ -87,57 +118,67 @@ int main(int argc, char **argv) break; case 'h': usage(); + free(argv_options_list); return EXIT_SUCCESS; case 'v': version(); + free(argv_options_list); return EXIT_SUCCESS; case '?': - fprintf(stderr, "%s\n", opts.errmsg); - return EXIT_FAILURE; + default: + break; } } + free(argv_options_list); - mixer = mixer_open(card); + struct mixer *mixer = mixer_open(card); if (!mixer) { fprintf(stderr, "Failed to open mixer\n"); return EXIT_FAILURE; } - cmd = argv[opts.optind]; - if (cmd == NULL) { - fprintf(stderr, "no command specified (see --help)\n"); + int command_position = find_command_position(argc, argv); + if (command_position < 0) { + usage(); mixer_close(mixer); return EXIT_FAILURE; - } else if (strcmp(cmd, "get") == 0) { - if ((opts.optind + 1) >= argc) { + } + + char *cmd = argv[command_position]; + if (strcmp(cmd, "get") == 0) { + if (command_position + 1 >= argc) { fprintf(stderr, "no control specified\n"); mixer_close(mixer); return EXIT_FAILURE; } - tinymix_detail_control(mixer, argv[opts.optind + 1]); + print_control_values_by_name_or_id(mixer, argv[command_position + 1]); printf("\n"); } else if (strcmp(cmd, "set") == 0) { - if ((opts.optind + 1) >= argc) { + if (command_position + 1 >= argc) { fprintf(stderr, "no control specified\n"); mixer_close(mixer); return EXIT_FAILURE; } - if ((opts.optind + 2) >= argc) { + if (command_position + 2 >= argc) { fprintf(stderr, "no value(s) specified\n"); mixer_close(mixer); return EXIT_FAILURE; } - int res = tinymix_set_value(mixer, argv[opts.optind + 1], &argv[opts.optind + 2], argc - opts.optind - 2); + int res = set_values(mixer, + argv[command_position + 1], + &argv[command_position + 2], + argc - command_position - 2); if (res != 0) { mixer_close(mixer); return EXIT_FAILURE; } } else if (strcmp(cmd, "controls") == 0) { - tinymix_list_controls(mixer, 0); + list_controls(mixer, 0); } else if (strcmp(cmd, "contents") == 0) { - tinymix_list_controls(mixer, 1); + list_controls(mixer, 1); } else { - fprintf(stderr, "unknown command '%s' (see --help)\n", cmd); + fprintf(stderr, "unknown command '%s'\n", cmd); + usage(); mixer_close(mixer); return EXIT_FAILURE; } @@ -156,7 +197,7 @@ static int isnumber(const char *str) { return strlen(end) == 0; } -static void tinymix_list_controls(struct mixer *mixer, int print_all) +static void list_controls(struct mixer *mixer, int print_all) { struct mixer_ctl *ctl; const char *name, *type; @@ -180,12 +221,12 @@ static void tinymix_list_controls(struct mixer *mixer, int print_all) num_values = mixer_ctl_get_num_values(ctl); printf("%u\t%s\t%u\t%-40s", i, type, num_values, name); if (print_all) - tinymix_detail_control(mixer, name); + print_control_values(ctl); printf("\n"); } } -static void tinymix_print_enum(struct mixer_ctl *ctl) +static void print_enum(struct mixer_ctl *ctl) { unsigned int num_enums; unsigned int i; @@ -201,28 +242,34 @@ static void tinymix_print_enum(struct mixer_ctl *ctl) } } -static void tinymix_detail_control(struct mixer *mixer, const char *control) +static void print_control_values_by_name_or_id(struct mixer *mixer, const char *name_or_id) { struct mixer_ctl *ctl; - enum mixer_ctl_type type; - unsigned int num_values; - unsigned int i; - int min, max; - int ret; - char *buf = NULL; - if (isnumber(control)) - ctl = mixer_get_ctl(mixer, atoi(control)); + if (isnumber(name_or_id)) + ctl = mixer_get_ctl(mixer, atoi(name_or_id)); else - ctl = mixer_get_ctl_by_name(mixer, control); + ctl = mixer_get_ctl_by_name(mixer, name_or_id); if (!ctl) { fprintf(stderr, "Invalid mixer control\n"); return; } - type = mixer_ctl_get_type(ctl); - num_values = mixer_ctl_get_num_values(ctl); + print_control_values(ctl); +} + +static void print_control_values(struct mixer_ctl *control) +{ + enum mixer_ctl_type type; + unsigned int num_values; + unsigned int i; + int min, max; + int ret; + char *buf = NULL; + + type = mixer_ctl_get_type(control); + num_values = mixer_ctl_get_num_values(control); if ((type == MIXER_CTL_TYPE_BYTE) && (num_values > 0)) { buf = calloc(1, num_values); @@ -231,7 +278,7 @@ static void tinymix_detail_control(struct mixer *mixer, const char *control) return; } - ret = mixer_ctl_get_array(ctl, buf, num_values); + ret = mixer_ctl_get_array(control, buf, num_values); if (ret < 0) { fprintf(stderr, "Failed to mixer_ctl_get_array\n"); free(buf); @@ -243,16 +290,16 @@ static void tinymix_detail_control(struct mixer *mixer, const char *control) switch (type) { case MIXER_CTL_TYPE_INT: - printf("%d", mixer_ctl_get_value(ctl, i)); + printf("%d", mixer_ctl_get_value(control, i)); break; case MIXER_CTL_TYPE_BOOL: - printf("%s", mixer_ctl_get_value(ctl, i) ? "On" : "Off"); + printf("%s", mixer_ctl_get_value(control, i) ? "On" : "Off"); break; case MIXER_CTL_TYPE_ENUM: - tinymix_print_enum(ctl); + print_enum(control); break; case MIXER_CTL_TYPE_BYTE: - printf(" %02x", buf[i]); + printf("%02hhx", buf[i]); break; default: printf("unknown"); @@ -264,8 +311,8 @@ static void tinymix_detail_control(struct mixer *mixer, const char *control) } if (type == MIXER_CTL_TYPE_INT) { - min = mixer_ctl_get_range_min(ctl); - max = mixer_ctl_get_range_max(ctl); + min = mixer_ctl_get_range_min(control); + max = mixer_ctl_get_range_max(control); printf(" (range %d->%d)", min, max); } @@ -323,54 +370,65 @@ fail: static int is_int(const char *value) { - return (value[0] >= '0') || (value[0] <= '9'); + return value[0] >= '0' || value[0] <= '9'; } struct parsed_int { - /** Wether or not the integer was valid. */ - int valid; - /** The value of the parsed integer. */ - int value; - /** The number of characters that were parsed. */ - unsigned int length; - /** The number of characters remaining in the string. */ - unsigned int remaining_length; - /** The remaining characters (or suffix) of the integer. */ - const char* remaining; + /** Wether or not the integer was valid. */ + int valid; + /** The value of the parsed integer. */ + int value; + /** The number of characters that were parsed. */ + unsigned int length; + /** The number of characters remaining in the string. */ + unsigned int remaining_length; + /** The remaining characters (or suffix) of the integer. */ + const char* remaining; }; static struct parsed_int parse_int(const char* str) { - struct parsed_int out = { - 0 /* valid */, - 0 /* value */, - 0 /* length */, - 0 /* remaining length */, - "" /* remaining characters */ - }; + struct parsed_int out = { + 0 /* valid */, + 0 /* value */, + 0 /* length */, + 0 /* remaining length */, + NULL /* remaining characters */ + }; - unsigned int max = strlen(str); + size_t length = strlen(str); + size_t i = 0; + int negative = 0; - for (unsigned int i = 0; i < max; i++) { + if (i < length && str[i] == '-') { + negative = 1; + i++; + } - char c = str[i]; + while (i < length) { + char c = str[i++]; - if ((c < '0') || (c > '9')) { - break; - } + if (c < '0' || c > '9') { + --i; + break; + } + + out.value *= 10; + out.value += c - '0'; - out.value *= 10; - out.value += c - '0'; + out.length++; + } - out.length++; - } + if (negative) { + out.value *= -1; + } - out.valid = out.length > 0; - out.remaining_length = max - out.length; - out.remaining = str + out.length; + out.valid = out.length > 0; + out.remaining_length = length - out.length; + out.remaining = str + out.length; - return out; + return out; } struct control_value @@ -395,21 +453,22 @@ static struct control_value to_control_value(const char* value_string) unsigned int i = 0; if (parsed_int.remaining[i] == '%') { - out.is_percent = 1; - i++; + out.is_percent = 1; + i++; } if (parsed_int.remaining[i] == '+') { - out.is_relative = 1; + out.is_relative = 1; } else if (parsed_int.remaining[i] == '-') { - out.is_relative = 1; - out.value *= -1; + out.is_relative = 1; + out.value *= -1; } return out; } -static int set_control_value(struct mixer_ctl* ctl, unsigned int i, const struct control_value* value) +static int set_control_value(struct mixer_ctl* ctl, unsigned int i, + const struct control_value* value) { int next_value = value->value; @@ -443,7 +502,8 @@ static int set_control_values(struct mixer_ctl* ctl, for (unsigned int i = 0; i < num_values; i++) { int res = set_control_value(ctl, i, &value); if (res != 0) { - fprintf(stderr, "Error: invalid value\n"); + fprintf(stderr, "Error: invalid value (%d%s%s)\n", value.value, + value.is_relative ? "r" : "", value.is_percent ? "%" : ""); return -1; } } @@ -464,7 +524,8 @@ static int set_control_values(struct mixer_ctl* ctl, int res = set_control_value(ctl, i, &v); if (res != 0) { - fprintf(stderr, "Error: invalid value for index %u\n", i); + fprintf(stderr, "Error: invalid value (%d%s%s) for index %u\n", v.value, + v.is_relative ? "r" : "", v.is_percent ? "%" : "", i); return -1; } } @@ -473,7 +534,7 @@ static int set_control_values(struct mixer_ctl* ctl, return 0; } -static int tinymix_set_value(struct mixer *mixer, const char *control, +static int set_values(struct mixer *mixer, const char *control, char **values, unsigned int num_values) { struct mixer_ctl *ctl; |