Appendix: mixer_ctl.c example

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