Пример: работа с микшером (mixer_ctl.c)

Исходный код утилиты mix_ctl

Приложение захватывает группы и переключатели микшера:

/*
* (c) 2014, SWD Embedded Systems Limited, http://www.kpda.ru
*/
/*
* $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>
//*****************************************************************************
#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
//*****************************************************************************
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->nam) : 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);
}




Предыдущий раздел: Библиотека libasound