This is a sample application that captures the groups and switches in the mixer.
For information about using this utility, see mix_ctl in the QNX Neutrino Utilities Reference.
/* * $QNXLicenseC: * Copyright 2016, QNX Software Systems. All Rights Reserved. * * You must obtain a written license from and pay applicable license fees to QNX * Software Systems before you may reproduce, modify or distribute this software, * or any work that includes all or part of this software. Free development * licenses are available for evaluation and non-commercial purposes. For more * information visit http://licensing.qnx.com or email licensing@qnx.com. * * This file may contain contributions from others. Please review this entire * file for other proprietary rights or license notices, as well as the QNX * Development Suite License Guide at http://licensing.qnx.com/license-guide/ * for other information. * $ */ #include <errno.h> #include <fcntl.h> #include <fnmatch.h> #include <gulliver.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/ioctl.h> #include <sys/select.h> #include <sys/stat.h> #include <sys/termio.h> #include <sys/types.h> #include <unistd.h> #include <ctype.h> #include <sys/asoundlib.h> //***************************************************************************** /* *INDENT-OFF* */ #ifdef __USAGE %C [Options] Cmds Options: -a[card#:]<dev#> the card & mixer device number to access OR -a[name] the AFM card name (e.g. voice, icc) to access Cmds: groups [-d] [-c] [-p] [pattern] -d will print the group details -c will show only groups effecting capture -p will show only groups effecting playback group name [mute[Y]=off|on] [capture[Y]=off|on] [volume[Y]=x|x%] ... - name is the group name quoted if it contains white space - the voice number Y (zero-based) is optional and restricts the change to the specified voice Y (if possible) switches switch name [value] - name is the switch name quoted if it contains white space #endif /* *INDENT-ON* */ //***************************************************************************** static void display_group (snd_mixer_t * mixer_handle, snd_mixer_gid_t * gid, snd_mixer_group_t * group) { int j; int dB_scale_factor = 100; double percent, dB; printf ("\"%s\",%d - %s \n", gid->name, gid->index, group->caps & SND_MIXER_GRPCAP_PLAY_GRP ? "Playback Group" : "Capture Group"); printf ("\tCapabilities - "); if (group->caps & SND_MIXER_GRPCAP_JOINTLY_VOLUME) printf (" Jointly-Volume"); else if (group->caps & SND_MIXER_GRPCAP_VOLUME) printf (" Volume"); if (group->caps & SND_MIXER_GRPCAP_JOINTLY_MUTE) printf (" Jointly-Mute"); else if (group->caps & SND_MIXER_GRPCAP_MUTE) printf (" Mute"); if (group->caps & SND_MIXER_GRPCAP_BALANCE) printf (" Balance"); if (group->caps & SND_MIXER_GRPCAP_FADE) printf (" Fade"); if (group->caps & SND_MIXER_GRPCAP_JOINTLY_CAPTURE) printf (" Jointly-Capture"); if (group->caps & SND_MIXER_GRPCAP_EXCL_CAPTURE) printf (" Exclusive-Capture"); else if (group->caps & SND_MIXER_GRPCAP_CAPTURE) printf (" Capture"); printf ("\n"); printf ("\tChannels - "); if (group->channels) { int chn_cnt = 0; for (j = 0; j <= SND_MIXER_CHN_LAST; j++) { if (!(group->channels & (1 << j))) continue; if ( (chn_cnt!=0) && ((chn_cnt%8) == 0) ) /* Display 8 channel names per line */ printf("\n\t\t "); printf ("%s ", snd_mixer_channel_name (j)); chn_cnt++; } } else { printf("None"); } printf ("\n"); if (group->dB_scale_factor > 0) dB_scale_factor = group->dB_scale_factor; printf ("\tVolume Range - minimum=%i (%.3fdB), maximum=%i (%.3fdB)\n", group->min, (double)group->min_dB/dB_scale_factor, group->max, (double)group->max_dB/dB_scale_factor); for (j = 0; j <= SND_MIXER_CHN_LAST; j++) { if (!(group->channels & (1 << j))) continue; percent = (group->max - group->min) <= 0 ? 0.0 : 100.0 * (double)(group->volume.values[j] - group->min) / (double)(group->max - group->min); dB = (((percent / 100.0) * (double)(group->max_dB - group->min_dB)) + (double)group->min_dB) / dB_scale_factor; printf ("\tChannel %2d %-22.22s - %3d (%3d%%) (%.3fdB) %s %s\n", j, snd_mixer_channel_name (j), group->volume.values[j], (int)percent, dB, group->mute & (1 << j) ? "Muted" : "", group->capture & (1 << j) ? "Capture" : ""); } if (group->caps & (SND_MIXER_GRPCAP_BALANCE|SND_MIXER_GRPCAP_FADE)) { printf("\tBalance/Fade Range - minimum=%i, maximum=%i\n", group->balance_min, group->balance_max); if (group->caps & SND_MIXER_GRPCAP_BALANCE) { percent = (group->balance_max - group->balance_min) <= 0 ? 0.0 : 100.0 * (double)(group->balance_level - group->balance_min) / (double)(group->balance_max - group->balance_min); printf("\t\tBalance Level - %3d (%3d%%)\n", group->balance_level, (int)percent); } if (group->caps & SND_MIXER_GRPCAP_FADE) { percent = (group->balance_max - group->balance_min) <= 0 ? 0.0 : 100.0 * (double)(group->fade_level - group->balance_min) / (double)(group->balance_max - group->balance_min); printf("\t\tFade Level - %3d (%3d%%)\n", group->fade_level, (int)percent); } } } static void display_groups (snd_mixer_t * mixer_handle, int argc, char *argv[]) { char details = 0; char playback_only = 0, capture_only = 0; char *pattern; snd_mixer_groups_t groups; int i; int rtn; snd_mixer_group_t group; optind = 1; while ((i = getopt (argc, argv, "cdp")) != EOF) { switch (i) { case 'c': capture_only = 1; playback_only = 0; break; case 'd': details = 1; break; case 'p': capture_only = 0; playback_only = 1; break; } } pattern = (optind >= argc) ? "*" : argv[optind]; while (1) { memset (&groups, 0, sizeof (groups)); if ((rtn = snd_mixer_groups (mixer_handle, &groups)) < 0) { fprintf (stderr, "snd_mixer_groups failed: %s\n", snd_strerror (rtn)); } else if (groups.groups == 0) { fprintf (stderr, "--> No mixer groups to list <-- \n"); break; } if (groups.groups_over > 0) { groups.pgroups = (snd_mixer_gid_t *) malloc (sizeof (snd_mixer_gid_t) * groups.groups); if (groups.pgroups == NULL) { fprintf (stderr, "Unable to malloc group array - %s\n", strerror (errno)); groups.groups = 0; break; } groups.groups_size = groups.groups; groups.groups_over = 0; groups.groups = 0; if ((rtn = snd_mixer_groups (mixer_handle, &groups)) < 0) { fprintf (stderr, "snd_mixer_groups failed: %s\n", snd_strerror (rtn)); groups.groups = 0; break; } if (groups.groups_over > 0) /* Mixer controls have changed since call above, try again */ { free (groups.pgroups); continue; } snd_mixer_sort_gid_table (groups.pgroups, groups.groups, snd_mixer_default_weights); break; } } for (i = 0; i < groups.groups; i++) { if (fnmatch (pattern, groups.pgroups[i].name, 0) == 0) { memset (&group, 0, sizeof (group)); memcpy (&group.gid, &groups.pgroups[i], sizeof (snd_mixer_gid_t)); if ((rtn = snd_mixer_group_read (mixer_handle, &group)) < 0) { fprintf (stderr, "snd_mixer_group_read of group %d failed: %s\n", i, snd_strerror (rtn)); continue; } if (playback_only && (group.caps & SND_MIXER_GRPCAP_CAP_GRP)) continue; if (capture_only && (group.caps & SND_MIXER_GRPCAP_PLAY_GRP)) continue; if (details) { display_group (mixer_handle, &groups.pgroups[i], &group); } else { printf ("\"%s\",%d%*c - %s \n", groups.pgroups[i].name, groups.pgroups[i].index, (int)(2 + sizeof (groups.pgroups[i].name) - strlen (groups.pgroups[i].name)), ' ', group.caps & SND_MIXER_GRPCAP_PLAY_GRP ? "Playback Group" : "Capture Group"); } } } } static int find_group_best_match (snd_mixer_t * mixer_handle, snd_mixer_gid_t * gid, snd_mixer_group_t * group) { snd_mixer_groups_t groups; int i; int rtn; while (1) { memset (&groups, 0, sizeof (groups)); if ((rtn = snd_mixer_groups (mixer_handle, &groups)) < 0) { fprintf (stderr, "snd_mixer_groups failed: %s\n", snd_strerror (rtn)); } else if (groups.groups == 0) { break; } if (groups.groups_over > 0) { groups.pgroups = (snd_mixer_gid_t *) malloc (sizeof (snd_mixer_gid_t) * groups.groups); if (groups.pgroups == NULL) { fprintf (stderr, "Unable to malloc group array - %s\n", strerror (errno)); groups.groups = 0; break; } groups.groups_size = groups.groups; groups.groups_over = 0; groups.groups = 0; if ((rtn = snd_mixer_groups (mixer_handle, &groups)) < 0) { fprintf (stderr, "snd_mixer_groups failed: %s\n", snd_strerror (rtn)); groups.groups = 0; break; } if (groups.groups_over > 0) /* Mixer controls have changed since call above, try again */ { free (groups.pgroups); continue; } break; } } for (i = 0; i < groups.groups; i++) { if (strcasecmp (gid->name, groups.pgroups[i].name) == 0 && gid->index == groups.pgroups[i].index) { memset (group, 0, sizeof (*group)); memcpy (gid, &groups.pgroups[i], sizeof (snd_mixer_gid_t)); memcpy (&group->gid, &groups.pgroups[i], sizeof (snd_mixer_gid_t)); if ((snd_mixer_group_read (mixer_handle, group)) < 0) return ENOENT; else return EOK; } } return ENOENT; } static int group_option_value (char *option) { char *ptr; int value; if ((ptr = strrchr (option, '=')) != NULL) { if (*(ptr + 1) == 0) value = -2; else if (strcasecmp (ptr + 1, "off") == 0) value = 0; else if (strcasecmp (ptr + 1, "on") == 0) value = 1; else value = atoi (ptr + 1); } else value = -1; return (value); } static void modify_group (snd_mixer_t * mixer_handle, int argc, char *argv[]) { int optind = 1; snd_mixer_gid_t gid; char *ptr; int rtn; snd_mixer_group_t group; long channel = 0, j; int value; char modified = 0; if (optind >= argc) { fprintf (stderr, "No Group specified \n"); return; } memset (&gid, 0, sizeof (gid)); ptr = strtok (argv[optind++], ","); if (ptr != NULL) { strlcpy (gid.name, ptr, sizeof (gid.name)); ptr = strtok (NULL, " "); if (ptr != NULL) gid.index = atoi (ptr); } memset (&group, 0, sizeof (group)); memcpy (&group.gid, &gid, sizeof (snd_mixer_gid_t)); if ((rtn = snd_mixer_group_read (mixer_handle, &group)) < 0) { if (rtn == -ENXIO) rtn = find_group_best_match (mixer_handle, &gid, &group); if (rtn != EOK) { fprintf (stderr, "snd_mixer_group_read failed: %s\n", snd_strerror (rtn)); return; } } while (optind < argc) { modified = 1; if ((value = group_option_value (argv[optind])) < 0) printf ("\n\t>>>> Unrecognized option [%s] <<<<\n\n", argv[optind]); else if (strncasecmp (argv[optind], "mute", 4) == 0) { if (argv[optind][4] == '=') channel = LONG_MAX; else { channel = atoi (&argv[optind][4]); if (group.caps & SND_MIXER_GRPCAP_JOINTLY_MUTE) channel = LONG_MAX; } if (channel == LONG_MAX) group.mute = value ? group.channels : 0; else if (group.channels & (1<<channel)) { group.mute = value ? group.mute | (1 << channel) : group.mute & ~(1 << channel); } } else if (strncasecmp (argv[optind], "capture", 7) == 0) { if (argv[optind][7] == '=') channel = LONG_MAX; else { channel = atoi (&argv[optind][7]); if (group.caps & SND_MIXER_GRPCAP_JOINTLY_CAPTURE) channel = LONG_MAX; } if (channel == LONG_MAX) group.capture = value ? group.channels : 0; else if (group.channels & (1<<channel)) { group.capture = value ? group.capture | (1 << channel) : group.capture & ~(1 << channel); } } else if (strncasecmp (argv[optind], "volume", 6) == 0) { if (argv[optind][6] == '=') channel = LONG_MAX; else { channel = atoi (&argv[optind][6]); if ((group.caps & SND_MIXER_GRPCAP_JOINTLY_VOLUME) && (group.channels & (1<<channel))) channel = LONG_MAX; } if (argv[optind][strlen (argv[optind]) - 1] == '%' && (group.max - group.min) >= 0) value = (value * (group.max - group.min)) / 100 + group.min; if (value > group.max) value = group.max; if (value < group.min) value = group.min; for (j = 0; j <= SND_MIXER_CHN_LAST; j++) { if (!(group.channels & (1 << j))) continue; if (channel == LONG_MAX || channel == j) group.volume.values[j] = value; } } else if ((strncasecmp (argv[optind], "balance", 7) == 0) && (group.caps & SND_MIXER_GRPCAP_BALANCE)) { if (argv[optind][strlen (argv[optind]) - 1] == '%' && (group.balance_max - group.balance_min) >= 0) value = (value * (group.balance_max - group.balance_min)) / 100 + group.balance_min; if (value > group.balance_max) value = group.balance_max; if (value < group.balance_min) value = group.balance_min; group.balance_level = value; } else if ((strncasecmp (argv[optind], "fade", 4) == 0) && (group.caps & SND_MIXER_GRPCAP_FADE)) { if (argv[optind][strlen (argv[optind]) - 1] == '%' && (group.balance_max - group.balance_min) >= 0) value = (value * (group.balance_max - group.balance_min)) / 100 + group.balance_min; if (value > group.balance_max) value = group.balance_max; if (value < group.balance_min) value = group.balance_min; group.fade_level = value; } else if (strncasecmp (argv[optind], "delay", 5) == 0) { if (argv[optind][5] == '=') group.change_duration = value; else group.change_duration = 50000; } else printf ("\n\t>>>> Unrecognized option [%s] <<<<\n\n", argv[optind]); if (channel != LONG_MAX && !(group.channels & (1 << channel))) printf ("\n\t>>>> Channel specified [%ld] Not in group <<<<\n\n", channel); optind++; } /* if we have a value option set the group, write and reread it (to get true driver state) */ /* some things like capture (MUX) can't be turned off but can only be set on another group */ if (modified) { if ((rtn = snd_mixer_group_write (mixer_handle, &group)) < 0) fprintf (stderr, "snd_mixer_group_write failed: %s\n", snd_strerror (rtn)); if ((rtn = snd_mixer_group_read (mixer_handle, &group)) < 0) fprintf (stderr, "snd_mixer_group_read failed: %s\n", snd_strerror (rtn)); } /* display the current group state */ display_group (mixer_handle, &gid, &group); } static void display_switch (snd_switch_t * sw, char table_formated) { printf ("\"%s\"%*c ", sw->name, (int)(table_formated ? sizeof (sw->name) - strlen (sw->name) : 1), ' '); switch (sw->type) { case SND_SW_TYPE_BOOLEAN: printf ("%s %s \n", "BOOLEAN", sw->value.enable ? "on" : "off"); break; case SND_SW_TYPE_BYTE: printf ("%s %d \n", "BYTE ", sw->value.byte.data); break; case SND_SW_TYPE_WORD: printf ("%s %d \n", "WORD ", sw->value.word.data); break; case SND_SW_TYPE_DWORD: printf ("%s %d \n", "DWORD ", sw->value.dword.data); break; case SND_SW_TYPE_LIST: { int i; if (sw->subtype == SND_SW_SUBTYPE_HEXA) { printf ("%s 0x%x ", "LIST ", sw->value.list.data); for (i=0; i < sw->value.list.items_cnt; i ++) { printf(" 0x%x", sw->value.list.items[i]); } } else { printf ("%s %d ", "LIST ", sw->value.list.data); for (i=0; i < sw->value.list.items_cnt; i ++) { printf(" %d", sw->value.list.items[i]); } } printf("\n"); } break; case SND_SW_TYPE_STRING_11: printf ("%s \"%s\" \n", "STRING ", sw->value.string_11.strings[sw->value.string_11.selection]); break; default: printf ("%s %d \n", "? ", 0); } } static void display_switches (snd_ctl_t * ctl_handle, int mixer_dev, int argc, char *argv[]) { int i; snd_switch_list_t list; snd_switch_t sw; int rtn; while (1) { memset (&list, 0, sizeof (list)); if ((rtn = snd_ctl_mixer_switch_list (ctl_handle, mixer_dev, &list)) < 0) { fprintf (stderr, "snd_ctl_mixer_switch_list failed: %s\n", snd_strerror (rtn)); } else if (list.switches == 0) { fprintf (stderr, "--> No mixer switches to list <-- \n"); break; } if (list.switches_over > 0) { list.pswitches = malloc (sizeof (snd_switch_list_item_t) * list.switches); if (list.pswitches == NULL) { fprintf (stderr, "Unable to malloc switch array - %s\n", strerror (errno)); list.switches = 0; break; } list.switches_size = list.switches; list.switches_over = 0; list.switches = 0; if ((rtn = snd_ctl_mixer_switch_list (ctl_handle, mixer_dev, &list)) < 0) { fprintf (stderr, "snd_ctl_mixer_switch_list failed: %s\n", snd_strerror (rtn)); list.switches = 0; break; } if (list.switches_over > 0) /* Mixer controls have changed since call above, try again */ { free (list.pswitches); continue; } break; } } for (i = 0; i < list.switches; i++) { memset (&sw, 0, sizeof (sw)); strlcpy (sw.name, (&list.pswitches[i])->name, sizeof (sw.name)); if ((rtn = snd_ctl_mixer_switch_read (ctl_handle, mixer_dev, &sw)) < 0) { fprintf (stderr, "snd_ctl_mixer_switch_read of switch %d failed: %s\n", i, snd_strerror (rtn)); continue; } display_switch (&sw, 1); } } static void modify_switch (snd_ctl_t * ctl_handle, int mixer_dev, int argc, char *argv[]) { int optind = 1; snd_switch_t sw; int rtn; int value = 0; char *string = NULL; if (optind >= argc) { fprintf (stderr, "No Switch specified \n"); return; } memset (&sw, 0, sizeof (sw)); strlcpy (sw.name, argv[optind++], sizeof (sw.name)); if ((rtn = snd_ctl_mixer_switch_read (ctl_handle, mixer_dev, &sw)) < 0) { fprintf (stderr, "snd_ctl_mixer_switch_read failed: %s\n", snd_strerror (rtn)); return; } /* if we have a value option set the sw, write and reread it (to get true driver state) */ if (optind < argc) { if (strcasecmp (argv[optind], "off") == 0) value = 0; else if (strcasecmp (argv[optind], "on") == 0) value = 1; else if (strncasecmp (argv[optind], "0x", 2) == 0) value = strtol (argv[optind], NULL, 16); else { value = atoi (argv[optind]); string = argv[optind]; } optind++; if (sw.type == SND_SW_TYPE_BOOLEAN) sw.value.enable = value; else if (sw.type == SND_SW_TYPE_BYTE) sw.value.byte.data = value; else if (sw.type == SND_SW_TYPE_WORD) sw.value.word.data = value; else if (sw.type == SND_SW_TYPE_DWORD) sw.value.dword.data = value; else if (sw.type == SND_SW_TYPE_LIST) { int i; sw.value.list.data = value; for (i = optind; i < argc; i ++ ) { sw.value.list.items[i-optind] = atoi(argv[i]); } sw.value.list.items_cnt = argc - optind; } else if (sw.type == SND_SW_TYPE_STRING_11) { if (!string) { fprintf (stderr, "string required for switch type"); } else { for (rtn = 0; rtn < sw.value.string_11.strings_cnt; rtn++) { if (strcasecmp (string, sw.value.string_11.strings[rtn]) == 0) { sw.value.string_11.selection = rtn; break; } } if (rtn == sw.value.string_11.strings_cnt) { fprintf (stderr, "ERROR string \"%s\" NOT IN LIST \n", string); snd_ctl_mixer_switch_read (ctl_handle, mixer_dev, &sw); } } } if ((rtn = snd_ctl_mixer_switch_write (ctl_handle, mixer_dev, &sw)) < 0) fprintf (stderr, "snd_ctl_mixer_switch_write failed: %s\n", snd_strerror (rtn)); if ((rtn = snd_ctl_mixer_switch_read (ctl_handle, mixer_dev, &sw)) < 0) fprintf (stderr, "snd_ctl_mixer_switch_read failed: %s\n", snd_strerror (rtn)); } /* display the current switch state */ display_switch (&sw, 0); } int main (int argc, char *argv[]) { int c; int card = 0; int dev = 0; int rtn; snd_ctl_t *ctl_handle; snd_mixer_t *mixer_handle; snd_mixer_info_t info = {0}; char name[_POSIX_PATH_MAX] = { 0 }; optind = 1; while ((c = getopt (argc, argv, "a:")) != EOF) { switch (c) { case 'a': if (strchr (optarg, ':')) { card = atoi (optarg); dev = atoi (strchr (optarg, ':') + 1); } else if (isalpha (optarg[0])) strlcpy (name, optarg, sizeof(name)); else dev = atoi (optarg); break; default: return 1; } } if (name[0] != '\0') { card = snd_card_name( name ); } if ((rtn = snd_ctl_open (&ctl_handle, card)) < 0) { fprintf (stderr, "snd_ctl_open failed: %s\n", snd_strerror (rtn)); return -1; } if ((rtn = snd_mixer_open (&mixer_handle, card, dev)) < 0) { fprintf (stderr, "snd_mixer_open failed: %s\n", snd_strerror (rtn)); snd_ctl_close (ctl_handle); return -1; } snd_mixer_info(mixer_handle, &info); if (name[0] != '\0') printf ("Using card %s (%d:%d), Mixer %s\n", name, card, dev, info.name); else printf ("Using card %d:%d, Mixer %s\n", card, dev, info.name); if (optind >= argc) display_groups (mixer_handle, argc - optind, argv + optind); else if (strcasecmp (argv[optind], "groups") == 0) display_groups (mixer_handle, argc - optind, argv + optind); else if (strcasecmp (argv[optind], "group") == 0) modify_group (mixer_handle, argc - optind, argv + optind); else if (strcasecmp (argv[optind], "switches") == 0) display_switches (ctl_handle, dev, argc - optind, argv + optind); else if (strcasecmp (argv[optind], "switch") == 0) modify_switch (ctl_handle, dev, argc - optind, argv + optind); else fprintf (stderr, "Unknown command specified \n"); snd_mixer_close (mixer_handle); snd_ctl_close (ctl_handle); return (0); } #if defined(__QNXNTO__) && defined(__USESRCVERSION) #include <sys/srcversion.h> __SRCVERSION("$URL$ $Rev$") #endif