Middleware, development tools, realtime operating system
software and services for superior embedded design


Home
QNX Community Resources
Developer Support
QNX Documentation Library
QNX Developer Support

QNX Developer Support

QNX Software Systems
Developer Resources
Blogs
Board support packages
Foundry27 projects
Forums
Hardware support listing
Online video tutorials
Product documentation
Technical Articles

[Previous] [Contents] [Index] [Next]

Appendix: mixer_ctl.c example

This is a sample application that captures the groups and switches in the mixer:

/*
 * $QNXLicenseC:  
 * Copyright 2005, QNX Software Systems. All Rights Reserved.
 *
 * This source code may contain confidential information of QNX Software 
 * Systems (QSS) and its licensors.  Any use, reproduction, modification, 
 * disclosure, distribution or transfer of this software, or any software 
 * that includes or is based upon any of this code, is prohibited unless 
 * expressly authorized by QSS by written agreement.  For more information 
 * (including whether this source code file has been published) please
 * email licensing@qnx.com. $
*/



#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;

	if (optind >= argc)
	{
		fprintf (stderr, "No Group secified \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)
	{
		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
			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 ((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_formated)
{
	printf ("\"%s\"%*c ", sw->name,
		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:
		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 secified \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);
}

[Previous] [Contents] [Index] [Next]