This is a sample application that captures the groups and switches in the mixer:
/* * $QNXLicenseC: * Copyright 2007, 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 <sys/asoundlib.h> //***************************************************************************** /* *INDENT-OFF* */ #ifdef __USAGE %C [Options] Cmds Options: -a[card#:]<dev#> the card & mixer device number 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 Y is a option the restricts the change to only one voice (if possible) switches [pattern] switch name [value] - name is the switch name quoted if it contains white space #endif /* *INDENT-ON* */ //***************************************************************************** void display_group (snd_mixer_t * mixer_handle, snd_mixer_gid_t * gid, snd_mixer_group_t * group) { int j; 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_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_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 - "); for (j = 0; j <= SND_MIXER_CHN_LAST; j++) { if (!(group->channels & (1 << j))) continue; printf ("%s ", snd_mixer_channel_name (j)); } printf ("\n"); printf ("\tVolume Range - minimum=%i, maximum=%i\n", group->min, group->max); for (j = 0; j <= SND_MIXER_CHN_LAST; j++) { if (!(group->channels & (1 << j))) continue; printf ("\tChannel %d %-12.12s - %3d (%3d%%) %s %s\n", j, snd_mixer_channel_name (j), group->volume.values[j], (group->max - group->min) <= 0 ? 0 : 100 * (group->volume.values[j] - group->min) / (group->max - group->min), group->mute & (1 << j) ? "Muted" : "", group->capture & (1 << j) ? "Capture" : ""); } } 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 (snd_mixer_groups (mixer_handle, &groups) < 0) { fprintf (stderr, "snd_mixer_groups API call - %s", strerror (errno)); } else if (groups.groups == 0) { fprintf (stderr, "--> No mixer groups to list <-- \n"); break; } if (groups.groups_over > 0) { groups.groups_size = groups.groups_over; groups.pgroups = (snd_mixer_gid_t *) malloc (sizeof (snd_mixer_gid_t) * groups.groups_size); if (groups.pgroups == NULL) fprintf (stderr, "Unable to malloc group array - %s", strerror (errno)); groups.groups_over = 0; groups.groups = 0; if (snd_mixer_groups (mixer_handle, &groups) < 0) fprintf (stderr, "No Mixer Groups "); if (groups.groups_over > 0) { free (groups.pgroups); continue; } else { snd_mixer_sort_gid_table (groups.pgroups, groups.groups_size, 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 failed: %s\n", snd_strerror (rtn)); 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, 2 + sizeof (groups.pgroups[i].name) - strlen (groups.pgroups[i].name), ' ', group.caps & SND_MIXER_GRPCAP_PLAY_GRP ? "Playback Group" : "Capture Group"); } } } } 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; while (1) { memset (&groups, 0, sizeof (groups)); if (snd_mixer_groups (mixer_handle, &groups) < 0) { fprintf (stderr, "snd_mixer_groups API call - %s", strerror (errno)); } if (groups.groups_over > 0) { groups.groups_size = groups.groups_over; groups.pgroups = (snd_mixer_gid_t *) malloc (sizeof (snd_mixer_gid_t) * groups.groups_size); if (groups.pgroups == NULL) fprintf (stderr, "Unable to malloc group array - %s", strerror (errno)); groups.groups_over = 0; groups.groups = 0; if (snd_mixer_groups (mixer_handle, &groups) < 0) fprintf (stderr, "No Mixer Groups "); if (groups.groups_over > 0) { free (groups.pgroups); continue; } else break; } } for (i = 0; i < groups.groups; i++) { if (stricmp (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; } int group_option_value (char *option) { char *ptr; int value; if ((ptr = strrchr (option, '=')) != NULL) { if (*(ptr + 1) == 0) value = -2; else if (stricmp (ptr + 1, "off") == 0) value = 0; else if (stricmp (ptr + 1, "on") == 0) value = 1; else value = atoi (ptr + 1); } else value = -1; return (value); } 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; uint32_t channel = 0, j; int32_t value; char modified = 0; if (optind >= argc) { fprintf (stderr, "No Group specified \n"); return; } memset (&gid, 0, sizeof (gid)); ptr = strtok (argv[optind++], ","); strncpy (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; } } /* 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 */ 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 (strnicmp (argv[optind], "mute", 4) == 0) { if (argv[optind][4] == '=') channel = LONG_MAX; else channel = atoi (&argv[optind][4]); if (channel == LONG_MAX) group.mute = value ? LONG_MAX : 0; else { group.mute = value ? group.mute | (1 << channel) : group.mute & ~(1 << channel); } } else if (strnicmp (argv[optind], "capture", 7) == 0) { if (argv[optind][7] == '=') channel = LONG_MAX; else channel = atoi (&argv[optind][7]); if (channel == LONG_MAX) group.capture = value ? LONG_MAX : 0; else group.capture = value ? group.capture | (1 << channel) : group.capture & ~(1 << channel); } else if (strnicmp (argv[optind], "volume", 6) == 0) { if (argv[optind][6] == '=') channel = LONG_MAX; else channel = atoi (&argv[optind][6]); 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 (strnicmp (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 [%d] Not in group <<<<\n\n", channel); optind++; } 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); } void display_switch (snd_switch_t * sw, char table_formatted) { printf ("\"%s\"%*c ", sw->name, table_formatted ? 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: if (sw->subtype == SND_SW_SUBTYPE_HEXA) printf ("%s 0x%x \n", "LIST ", sw->value.list.data); else printf ("%s %d \n", "LIST ", sw->value.list.data); 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); } } void display_switches (snd_ctl_t * ctl_handle, int mixer_dev, int argc, char *argv[]) { int i; char *pattern; snd_switch_list_t list; snd_switch_t sw; int rtn; optind = 1; while ((i = getopt (argc, argv, "d")) != EOF) { switch (i) { } } pattern = (optind >= argc) ? "*" : argv[optind]; while (1) { memset (&list, 0, sizeof (list)); if (snd_ctl_mixer_switch_list (ctl_handle, mixer_dev, &list) < 0) { fprintf (stderr, "snd_ctl_mixer_switch_list API call - %s", strerror (errno)); } else if (list.switches == 0) { fprintf (stderr, "--> No mixer switches to list <-- \n"); break; } if (list.switches_over > 0) { list.switches_size = list.switches_over; list.pswitches = malloc (sizeof (snd_switch_list_item_t) * list.switches_size); if (list.pswitches == NULL) fprintf (stderr, "Unable to malloc switch array - %s", strerror (errno)); list.switches_over = 0; list.switches = 0; if (snd_ctl_mixer_switch_list (ctl_handle, mixer_dev, &list) < 0) fprintf (stderr, "No Switches "); if (list.switches_over > 0) { free (list.pswitches); continue; } else break; } } for (i = 0; i < list.switches_size; i++) { memset (&sw, 0, sizeof (sw)); strncpy (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 failed: %s\n", snd_strerror (rtn)); display_switch (&sw, 1); } } 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)); strncpy (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 (stricmp (argv[optind], "off") == 0) value = 0; else if (stricmp (argv[optind], "on") == 0) value = 1; else if (strnicmp (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) sw.value.list.data = value; else if (sw.type == SND_SW_TYPE_STRING_11) { for (rtn = 0; rtn < sw.value.string_11.strings_cnt; rtn++) { if (stricmp (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; 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 dev = atoi (optarg); printf ("Using card %d device %d \n", card, dev); break; default: return 1; } } if ((rtn = snd_ctl_open (&ctl_handle, card)) < 0) { fprintf (stderr, "snd_ctlr_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)); return -1; } if (optind >= argc) display_groups (mixer_handle, argc - optind, argv + optind); else if (stricmp (argv[optind], "groups") == 0) display_groups (mixer_handle, argc - optind, argv + optind); else if (stricmp (argv[optind], "group") == 0) modify_group (mixer_handle, argc - optind, argv + optind); else if (stricmp (argv[optind], "switches") == 0) display_switches (ctl_handle, dev, argc - optind, argv + optind); else if (stricmp (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); }