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);
}