Mixer v2.0 merged to the main CVS tree...

This commit is contained in:
Jaroslav Kysela 1999-05-02 16:21:40 +00:00
parent daab0cd6d0
commit 2e02eadd72
16 changed files with 3759 additions and 2048 deletions

View file

@ -2,5 +2,10 @@ sbin_PROGRAMS=alsactl
noinst_HEADERS=alsactl.h
man_MANS=alsactl.1
alsactl_SOURCES=alsactl.c alsactl_parser.y setup.c alsactl_lexer.l
alsactl_SOURCES=alsactl.c alsactl_parser.y setup.c merge.c alsactl_lexer.l
YFLAGS=-d
# lexer / parser debug
#CFLAGS=-pipe -g -DYYDEBUG
#LFLAGS=-d

View file

@ -64,28 +64,6 @@ static void help(void)
printf(" from configuration file\n");
}
static int collect_all(void)
{
int idx, err;
unsigned int card_mask;
card_mask = snd_cards_mask();
if (!card_mask) {
error("No soundcards found...");
return 1;
}
soundcard_setup_init();
for (idx = 0; idx < 32; idx++) {
if (card_mask & (1 << idx)) { /* find each installed soundcards */
if ((err = soundcard_setup_collect(idx))) {
soundcard_setup_done();
return err;
}
}
}
return 0;
}
static int store_setup(const char *cardname)
{
int err;
@ -101,12 +79,18 @@ static int store_setup(const char *cardname)
soundcard_setup_init();
for (idx = 0; idx < 32; idx++) {
if (card_mask & (1 << idx)) { /* find each installed soundcards */
if ((err = soundcard_setup_collect(idx))) {
if ((err = soundcard_setup_collect_switches(idx))) {
soundcard_setup_done();
return err;
}
if ((err = soundcard_setup_collect_data(idx))) {
soundcard_setup_done();
return err;
}
}
}
err = soundcard_setup_write(cfgfile, -1);
soundcard_setup_done();
} else {
int cardno;
@ -115,17 +99,17 @@ static int store_setup(const char *cardname)
error("Cannot find soundcard '%s'...", cardname);
return 1;
}
if ((err = collect_all()))
return err;
if ((err = soundcard_setup_load(cfgfile, 1)))
return err;
if ((err = soundcard_setup_collect(cardno))) {
if ((err = soundcard_setup_collect_switches(cardno))) {
soundcard_setup_done();
return err;
}
if ((err = soundcard_setup_collect_data(cardno))) {
soundcard_setup_done();
return err;
}
err = soundcard_setup_write(cfgfile, cardno);
soundcard_setup_done();
}
err = soundcard_setup_write(cfgfile);
soundcard_setup_done();
return err;
}
@ -140,11 +124,32 @@ static int restore_setup(const char *cardname)
return 1;
}
}
if ((err = collect_all()))
return err;
if ((err = soundcard_setup_load(cfgfile, 0)))
return err;
err = soundcard_setup_process(cardno);
if ((err = soundcard_setup_collect_switches(cardno))) {
soundcard_setup_done();
return err;
}
if ((err = soundcard_setup_merge_switches(cardno))) {
soundcard_setup_done();
return err;
}
if ((err = soundcard_setup_process_switches(cardno))) {
soundcard_setup_done();
return err;
}
if ((err = soundcard_setup_collect_data(cardno))) {
soundcard_setup_done();
return err;
}
if ((err = soundcard_setup_merge_data(cardno))) {
soundcard_setup_done();
return err;
}
if ((err = soundcard_setup_process_data(cardno))) {
soundcard_setup_done();
return err;
}
soundcard_setup_done();
return err;
}
@ -210,5 +215,3 @@ int main(int argc, char *argv[])
return 0;
}

View file

@ -48,21 +48,16 @@ struct ctl {
struct ctl_switch *switches;
};
struct mixer_channel {
int no;
int direction;
int voice;
snd_mixer_channel_info_t info;
snd_mixer_channel_t data;
snd_mixer_channel_direction_info_t dinfo[2];
snd_mixer_channel_direction_t ddata[2];
struct mixer_channel *next;
struct mixer_element {
snd_mixer_element_info_t info;
snd_mixer_element_t element;
struct mixer_element *next;
};
struct mixer {
int no;
snd_mixer_info_t info;
struct mixer_channel *channels;
struct mixer_element *elements;
struct ctl_switch *switches;
struct mixer *next;
};
@ -93,10 +88,17 @@ struct soundcard {
};
extern struct soundcard *soundcards;
extern struct soundcard *rsoundcards; /* read soundcards */
void soundcard_setup_init(void);
void soundcard_setup_done(void);
int soundcard_setup_load(const char *filename, int skip);
int soundcard_setup_write(const char *filename);
int soundcard_setup_collect(int cardno);
int soundcard_setup_process(int cardno);
int soundcard_setup_write(const char *filename, int cardno);
int soundcard_setup_collect_switches(int cardno);
int soundcard_setup_collect_data(int cardno);
int soundcard_setup_merge_switches(int cardno);
int soundcard_setup_merge_data(int cardno);
int soundcard_setup_process_switches(int cardno);
int soundcard_setup_process_data(int cardno);
char *mixer_element_id(snd_mixer_eid_t *eid);

View file

@ -22,6 +22,12 @@
%{
#include "alsactl.h"
struct bytearray {
unsigned char *data;
size_t datalen;
};
#include "alsactl_parser.h"
#define YY_NO_UNPUT
@ -41,15 +47,14 @@ int linecount;
"["|"]" return yytext[0];
")"[ \t]*"{" return L_DOUBLE1;
"," return yytext[0];
"=" return yytext[0];
/* tokens */
soundcard return L_SOUNDCARD;
control return L_CONTROL;
mixer return L_MIXER;
channel return L_CHANNEL;
stereo return L_STEREO;
mono return L_MONO;
element return L_ELEMENT;
switch return L_SWITCH;
rawdata return L_RAWDATA;
pcm return L_PCM;
@ -71,8 +76,29 @@ type return L_TYPE;
gstatus return L_GSTATUS;
enable return L_ENABLE;
disable return L_DISABLE;
mute return L_MUTE;
swap return L_SWAP;
sw return L_SW;
mono_sw return L_MONO_SW;
wide return L_WIDE;
volume return L_VOLUME;
center return L_CENTER;
space return L_SPACE;
depth return L_DEPTH;
delay return L_DELAY;
feedback return L_FEEDBACK;
bass return L_BASS;
treble return L_TREBLE;
/* element types */
Switch1 return L_SWITCH1;
Switch2 return L_SWITCH2;
Switch3 return L_SWITCH3;
Volume1 return L_VOLUME1;
Accu3 return L_ACCU3;
Mux1 return L_MUX1;
Mux2 return L_MUX2;
ToneControl1 return L_TONE_CONTROL1;
_3D_Effect1 return L_3D_EFFECT1;
/* boolean */
@ -81,9 +107,9 @@ true|on|yes return L_TRUE;
/* integers */
[0-9]+ { yylval.i_value = atoi( yytext ); return L_INTEGER; }
[0-9]+ { yylval.i_value = atoi(yytext); return L_INTEGER; }
0x[0-9a-f]+ { char *end;
yylval.i_value = strtol( yytext, &end, 0 );
yylval.i_value = strtol(yytext, &end, 0);
return L_INTEGER; }
/* byte array */
@ -92,25 +118,27 @@ true|on|yes return L_TRUE;
char *p = yytext + 1, x[3];
unsigned char *d;
int val;
yylval.a_value = d = (unsigned char *)malloc( 32 );
while ( p ) {
strncpy( x, p, 2 ); x[2] = '\0';
sscanf( x, "%02x", &val );
*d++ = val;
yylval.a_value.data = d = (unsigned char *)malloc( 32 );
yylval.a_value.datalen = 0;
while (p) {
strncpy(x, p, 2); x[2] = '\0';
sscanf(x, "%02x", &val);
*d++ = val;
++yylval.a_value.datalen;
}
return L_BYTEARRAY; }
/* strings */
\"[^\"]*\" { yytext[ strlen( yytext ) - 1 ] = 0;
yylval.s_value = strdup( &yytext[ 1 ] );
\"[^\"]*\" { yytext[strlen(yytext) - 1] = 0;
yylval.s_value = strdup(&yytext[1]);
return L_STRING; }
\'[^\']*\' { yytext[ strlen( yytext ) - 1 ] = 0;
yylval.s_value = strdup( &yytext[ 1 ] );
\'[^\']*\' { yytext[strlen(yytext) - 1] = 0;
yylval.s_value = strdup(&yytext[1]);
return L_STRING; }
[a-z0-9/\~@-Za-z_\+=:\.]+ { yylval.s_value = strdup( yytext );
[a-z0-9/\~@-Za-z_]+ { yylval.s_value = strdup(yytext);
return L_STRING; }
$[a-z0-9/\~@-Za-z_\+=:\.]+ { yylval.s_value = strdup( getenv( &yytext[ 1 ] ) );
$[a-z0-9/\~@-Za-z_]+ { yylval.s_value = strdup(getenv(&yytext[1]));
return L_STRING; }
/* comments & whitespaces */

View file

@ -32,36 +32,53 @@ extern char cfgfile[];
extern int linecount;
extern FILE *yyin;
/* structure for byte arrays */
struct bytearray {
unsigned char *data;
size_t datalen;
};
/* local functions */
static void yyerror(char *, ...);
static void select_soundcard(char *name);
static void select_mixer(char *name);
static void select_pcm(char *name);
static void select_rawmidi(char *name);
static void build_soundcard(char *name);
static void build_mixer(char *name);
static void build_pcm(char *name);
static void build_rawmidi(char *name);
static void select_mixer_channel(char *name);
static void select_mixer_direction(int direction);
static void select_mixer_voice(int voice);
static void set_mixer_volume(int volume);
static void set_mixer_flags(int flags);
static void select_mixer_channel_end(void);
static void build_mixer_element(char *name, int index, int etype);
#define SWITCH_CONTROL 0
#define SWITCH_MIXER 1
#define SWITCH_PCM 2
#define SWITCH_RAWMIDI 3
static void build_control_switch(char *name);
static void build_mixer_switch(char *name);
static void build_pcm_playback_switch(char *name);
static void build_pcm_record_switch(char *name);
static void build_rawmidi_output_switch(char *name);
static void build_rawmidi_input_switch(char *name);
static void select_control_switch(char *name);
static void select_mixer_switch(char *name);
static void select_pcm_playback_switch(char *name);
static void select_pcm_record_switch(char *name);
static void select_rawmidi_output_switch(char *name);
static void select_rawmidi_input_switch(char *name);
static void mixer_switch1(int end);
static void mixer_switch1_value(int val);
static void mixer_switch2(int end);
static void mixer_switch2_value(int val);
static void mixer_switch3(int end);
static void mixer_switch3_value(int val);
static void mixer_volume1(int end);
static void mixer_volume1_value(int val);
static void mixer_3d_effect1(int end);
static void mixer_3d_effect1_value(unsigned int effect, int val);
static void mixer_accu3(int end);
static void mixer_accu3_value(int val);
static void mixer_mux1(int end);
static void mixer_mux1_value(char *str, int index, int type);
static void mixer_mux2(int end);
static void mixer_mux2_value(char *str, int index, int type);
static void mixer_tone_control1(int end);
static void mixer_tone_control1_value(unsigned int effect, int val);
static void set_switch_boolean(int val);
static void set_switch_integer(int val);
static void set_switch_bytearray(struct bytearray val);
static void set_switch_iec958ocs_begin(int end);
static void set_switch_iec958ocs(int idx, unsigned short val, unsigned short mask);
@ -71,10 +88,8 @@ static struct soundcard *Xsoundcard = NULL;
static struct mixer *Xmixer = NULL;
static struct pcm *Xpcm = NULL;
static struct rawmidi *Xrawmidi = NULL;
static struct mixer_channel *Xchannel = NULL;
static int Xswitchtype = SWITCH_CONTROL;
static int *Xswitchchange = NULL;
static snd_switch_t *Xswitch = NULL;
static struct mixer_element *Xelement = NULL;
static struct ctl_switch *Xswitch = NULL;
static unsigned int Xswitchiec958ocs = 0;
static unsigned short Xswitchiec958ocs1[16];
@ -86,7 +101,7 @@ static unsigned short Xswitchiec958ocs1[16];
int b_value;
int i_value;
char *s_value;
unsigned char *a_value;
struct bytearray a_value;
};
%token <b_value> L_TRUE L_FALSE
@ -101,15 +116,20 @@ static unsigned short Xswitchiec958ocs1[16];
/* misc */
%token L_DOUBLE1
/* other keywords */
%token L_SOUNDCARD L_MIXER L_CHANNEL L_STEREO L_MONO L_SWITCH L_RAWDATA
%token L_CONTROL L_PCM L_RAWMIDI L_PLAYBACK L_RECORD L_OUTPUT L_INPUT
%token L_SOUNDCARD L_MIXER L_ELEMENT L_SWITCH L_RAWDATA
%token L_CONTROL L_PCM L_RAWMIDI L_PLAYBACK L_RECORD L_INPUT L_OUTPUT
%token L_SWITCH1 L_SWITCH2 L_SWITCH3 L_VOLUME1 L_3D_EFFECT1 L_ACCU3
%token L_MUX1 L_MUX2 L_TONE_CONTROL1
%token L_IEC958OCS L_3D L_RESET L_USER L_VALID L_DATA L_PROTECT L_PRE2
%token L_FSUNLOCK L_TYPE L_GSTATUS L_ENABLE L_DISABLE L_MUTE L_SWAP
%token L_FSUNLOCK L_TYPE L_GSTATUS L_ENABLE L_DISABLE
%token L_SW L_MONO_SW L_WIDE L_VOLUME L_CENTER L_SPACE L_DEPTH L_DELAY
%token L_FEEDBACK L_BASS L_TREBLE
%type <b_value> boolean
%type <i_value> integer
%type <s_value> string
%type <a_value> bytearray
%type <a_value> rawdata
%%
@ -117,8 +137,9 @@ lines : line
| lines line
;
line : L_SOUNDCARD '(' string { select_soundcard( $3 ); } L_DOUBLE1 soundcards { select_soundcard( NULL ); } '}'
| error { yyerror( "unknown keyword in top level" ); }
line : L_SOUNDCARD '(' string { build_soundcard($3); }
L_DOUBLE1 soundcards '}' { build_soundcard(NULL); }
| error { yyerror("unknown keyword in top level"); }
;
soundcards : soundcard
@ -126,85 +147,165 @@ soundcards : soundcard
;
soundcard : L_CONTROL '{' controls '}'
| L_MIXER '(' string { select_mixer( $3 ); } L_DOUBLE1 mixers { select_mixer( NULL ); } '}'
| L_PCM '(' string { select_pcm( $3 ); } L_DOUBLE1 pcms { select_pcm( NULL ); } '}'
| L_RAWMIDI '(' string { select_rawmidi( $3 ); } L_DOUBLE1 rawmidis { select_rawmidi( NULL ); } '}'
| error { yyerror( "unknown keyword in soundcard{} level" ); }
| L_MIXER '(' string { build_mixer($3); }
L_DOUBLE1 mixers '}' { build_mixer(NULL); }
| L_PCM '(' string { build_pcm($3); }
L_DOUBLE1 pcms '}' { build_pcm(NULL); }
| L_RAWMIDI '(' string { build_rawmidi($3); }
L_DOUBLE1 rawmidis '}' { build_rawmidi(NULL); }
| error { yyerror( "an unknown keyword in the soundcard{} level"); }
;
controls : control
| controls control
;
control : L_SWITCH '(' string { select_control_switch( $3 ); } ',' switches ')' { select_control_switch( NULL ); }
| error { yyerror( "unknown keyword in control{} level" ); }
control : L_SWITCH '(' string { build_control_switch($3); }
',' switches ')' { build_control_switch(NULL); }
| error { yyerror("an unknown keyword in the control{} level"); }
;
mixers : mixer
| mixers mixer
;
mixer : L_CHANNEL '(' string { select_mixer_channel( $3 ); }
',' settings ')' { select_mixer_channel_end();
select_mixer_channel( NULL ); }
| L_SWITCH '(' string { select_mixer_switch( $3 ); }
',' switches ')' { select_mixer_switch( NULL ); }
| error { yyerror( "unknown keyword in mixer level" ); }
mixer : L_ELEMENT '(' string
',' integer ',' integer { build_mixer_element($3, $5, $7); }
',' etype ')' { build_mixer_element(NULL, -1, -1); }
| L_SWITCH '(' string { build_mixer_switch($3); }
',' switches ')' { build_mixer_switch(NULL); }
| error { yyerror("an unknown keyword in the mixer level"); }
;
settings: setting
| settings ',' setting
etype : L_SWITCH1 '(' { mixer_switch1(0); }
m_switch1 ')' { mixer_switch1(1); }
| L_SWITCH2 '(' { mixer_switch2(0); }
m_switch2 ')' { mixer_switch2(1); }
| L_SWITCH3 '(' { mixer_switch3(0); }
m_switch3 ')' { mixer_switch3(1); }
| L_VOLUME1 '(' { mixer_volume1(0); }
m_volume1 ')' { mixer_volume1(1); }
| L_3D_EFFECT1 '(' { mixer_3d_effect1(0); }
m_3d_effect1 ')' { mixer_3d_effect1(1); }
| L_ACCU3 '(' { mixer_accu3(0); }
m_accu3 ')' { mixer_accu3(1); }
| L_MUX1 '(' { mixer_mux1(0); }
m_mux1 ')' { mixer_mux1(1); }
| L_MUX2 '(' { mixer_mux2(0); }
L_ELEMENT '('
string ','
integer ','
integer ')' { mixer_mux2_value($6, $8, $10); }
')' { mixer_mux2(1); }
| L_TONE_CONTROL1 '(' { mixer_tone_control1(0); }
m_tone_control1 ')' { mixer_tone_control1(1); }
| error { yyerror("an unknown keyword in the mixer element level"); }
;
setting : L_OUTPUT { select_mixer_direction(OUTPUT); }
dsetting
| L_INPUT { select_mixer_direction(INPUT); }
dsetting
| error { yyerror( "unknown keyword in mixer channel level" ); }
m_switch1 : m_switch1_0
| m_switch1 ',' m_switch1_0
;
dsetting: L_STEREO '(' { select_mixer_voice(LEFT); }
vsettings ',' { select_mixer_voice(RIGHT); }
vsettings ')'
| L_MONO '(' { select_mixer_voice(LEFT|RIGHT); }
vsettings ')'
| error { yyerror( "unknown keyword in mixer direction level" ); }
m_switch1_0 : boolean { mixer_switch1_value($1); }
| error { yyerror("an unknown keyword in the Switch1 element level"); }
;
vsettings: vsetting
| vsettings vsetting
m_switch2 : m_switch2_0
| m_switch2 ',' m_switch2_0
;
vsetting: L_INTEGER { set_mixer_volume($1); }
| L_MUTE { set_mixer_flags(SND_MIXER_DFLG_MUTE); }
| L_SWAP { set_mixer_flags(SND_MIXER_DFLG_SWAP); }
| error { yyerror( "unknown keyword in mixer voice level" ); }
m_switch2_0 : boolean { mixer_switch2_value($1); }
| error { yyerror("an unknown keyword in the Switch2 element level"); }
;
m_switch3 : m_switch3_0
| m_switch3 ',' m_switch3_0
;
m_switch3_0 : boolean { mixer_switch3_value($1); }
| error { yyerror("an unknown keyword in the Switch3 element level"); }
;
m_volume1 : m_volume1_0
| m_volume1 ',' m_volume1_0
;
m_volume1_0 : integer { mixer_volume1_value($1); }
| error { yyerror("an unknown keyword in the Volume1 element level"); }
;
m_3d_effect1 : m_3d_effect1_0
| m_3d_effect1 ',' m_3d_effect1_0
;
m_3d_effect1_0 : L_SW '=' boolean { mixer_3d_effect1_value(SND_MIXER_EFF1_SW, $3); }
| L_MONO_SW '=' boolean { mixer_3d_effect1_value(SND_MIXER_EFF1_MONO_SW, $3); }
| L_WIDE '=' integer { mixer_3d_effect1_value(SND_MIXER_EFF1_WIDE, $3); }
| L_VOLUME '=' integer { mixer_3d_effect1_value(SND_MIXER_EFF1_VOLUME, $3); }
| L_CENTER '=' integer { mixer_3d_effect1_value(SND_MIXER_EFF1_CENTER, $3); }
| L_SPACE '=' integer { mixer_3d_effect1_value(SND_MIXER_EFF1_SPACE, $3); }
| L_DEPTH '=' integer { mixer_3d_effect1_value(SND_MIXER_EFF1_DEPTH, $3); }
| L_DELAY '=' integer { mixer_3d_effect1_value(SND_MIXER_EFF1_DELAY, $3); }
| L_FEEDBACK '=' integer { mixer_3d_effect1_value(SND_MIXER_EFF1_FEEDBACK, $3); }
| error { yyerror("an unknown keyword in the 3D Effect1 element level"); }
;
m_accu3 : m_accu3_0
| m_accu3 ',' m_accu3_0
;
m_accu3_0 : integer { mixer_accu3_value($1); }
| error { yyerror("an unknown keyword in the Accu3 element level"); }
;
m_mux1 : m_mux1_0
| m_mux1 ',' m_mux1_0
;
m_mux1_0 : L_ELEMENT '(' string
',' integer ','
integer ')' { mixer_mux1_value($3, $5, $7); }
| error { yyerror("an unknown keyword in the Mux1 element level"); }
;
m_tone_control1 : m_tone_control1_0
| m_tone_control1 ',' m_tone_control1_0
;
m_tone_control1_0 : L_SW '=' boolean { mixer_tone_control1_value(SND_MIXER_TC1_SW, $3); }
| L_BASS '=' integer { mixer_tone_control1_value(SND_MIXER_TC1_BASS, $3); }
| L_TREBLE '=' integer { mixer_tone_control1_value(SND_MIXER_TC1_TREBLE, $3); }
| error { yyerror("an unknown keyword in the ToneControl1 element level"); }
;
pcms : pcm
| pcms pcm
;
pcm : L_PLAYBACK '{' playbacks '}'
| L_RECORD '{' records '}'
| error { yyerror( "unknown keyword in pcm{} section" ); }
| error { yyerror("an unknown keyword in the pcm{} section"); }
;
playbacks : playback
| playbacks playback
;
playback : L_SWITCH '(' string { select_pcm_playback_switch( $3 ); } ',' switches ')' { select_pcm_playback_switch( NULL ); }
| error { yyerror( "unknown keyword in playback{} section" ); }
playback : L_SWITCH '(' string { build_pcm_playback_switch($3); }
',' switches ')' { build_pcm_playback_switch(NULL); }
| error { yyerror("an unknown keyword in the playback{} section"); }
;
records : record
| records record
;
record : L_SWITCH '(' string { select_pcm_record_switch( $3 ); } ',' switches ')' { select_pcm_record_switch( NULL ); }
| error { yyerror( "unknown keyword in record{} section" ); }
record : L_SWITCH '(' string { build_pcm_record_switch($3); }
',' switches ')' { build_pcm_record_switch(NULL); }
| error { yyerror("an unknown keyword in the record{} section"); }
;
rawmidis : rawmidi
@ -219,31 +320,34 @@ inputs : input
| inputs input
;
input : L_SWITCH '(' string { select_rawmidi_input_switch( $3 ); } ',' switches ')' { select_rawmidi_input_switch( NULL ); }
| error { yyerror( "unknown keyword in input{} section" ); }
input : L_SWITCH '(' string { build_rawmidi_input_switch($3); }
',' switches ')' { build_rawmidi_input_switch(NULL); }
| error { yyerror( "an unknown keyword in the input{} section" ); }
;
outputs : output
| outputs output
;
output : L_SWITCH '(' string { select_rawmidi_output_switch( $3 ); } ',' switches ')' { select_rawmidi_output_switch( NULL ); }
| error { yyerror( "unknown keyword in output{} section" ); }
output : L_SWITCH '(' string { build_rawmidi_output_switch($3); }
',' switches ')' { build_rawmidi_output_switch(NULL); }
| error { yyerror( "an unknown keyword in the output{} section" ); }
;
switches : switch
| switches switch
;
switch : L_TRUE { set_switch_boolean( 1 ); }
| L_FALSE { set_switch_boolean( 0 ); }
| L_INTEGER { set_switch_integer( $1 ); }
| L_IEC958OCS '(' { set_switch_iec958ocs_begin( 0 ); } iec958ocs { set_switch_iec958ocs_begin( 1 ); } ')'
| error { yyerror( "unknown keyword in switch() data parameter" ); }
switch : boolean { set_switch_boolean($1); }
| integer { set_switch_integer($1); }
| L_IEC958OCS '(' { set_switch_iec958ocs_begin(0); }
iec958ocs ')' { set_switch_iec958ocs_begin(1); }
| rawdata { set_switch_bytearray($1); }
| error { yyerror( "an unknown keyword in the switch() data parameter" ); }
;
iec958ocs : iec958ocs1
| iec958ocs iec958ocs1
| iec958ocs ',' iec958ocs1
;
iec958ocs1 : L_ENABLE { set_switch_iec958ocs( 0, 1, 0 ); }
@ -258,24 +362,21 @@ iec958ocs1 : L_ENABLE { set_switch_iec958ocs( 0, 1, 0 ); }
| L_FSUNLOCK { set_switch_iec958ocs( 5, 0x0020, ~0x0020 ); }
| L_TYPE '(' integer ')' { set_switch_iec958ocs( 5, ($3 & 0x7f) << 6, ~(0x7f<<6) ); }
| L_GSTATUS { set_switch_iec958ocs( 5, 0x2000, ~0x2000 ); }
| error { yyerror( "unknown keyword in iec958ocs1() arguments" ); }
| error { yyerror( "an unknown keyword in the iec958ocs1() arguments" ); }
;
boolean : L_TRUE { $$ = 1; }
| L_FALSE { $$ = 0; }
| error { yyerror( "unknown boolean value" ); }
;
integer : L_INTEGER { $$ = $1; }
| error { yyerror( "unknown integer value" ); }
;
string : L_STRING { $$ = $1; }
| error { yyerror( "unknown string value" ); }
;
bytearray : L_BYTEARRAY { $$ = $1; }
| error { yyerror( "unknown byte array value" ); }
rawdata : L_RAWDATA '(' L_BYTEARRAY ')' { $$ = $3; }
| L_RAWDATA error { yyerror( "malformed rawdata value" ); }
;
%%
@ -293,7 +394,12 @@ static void yyerror(char *string,...)
exit(1);
}
static void select_soundcard(char *name)
static void error_nomem(void)
{
yyerror("No enough memory...\n");
}
static void build_soundcard(char *name)
{
struct soundcard *soundcard;
@ -301,17 +407,24 @@ static void select_soundcard(char *name)
Xsoundcard = NULL;
return;
}
for (soundcard = soundcards; soundcard; soundcard = soundcard->next)
if (!strcmp(soundcard->control.hwinfo.id, name)) {
Xsoundcard = soundcard;
free(name);
return;
}
yyerror("Cannot find soundcard '%s'...", name);
Xsoundcard = (struct soundcard *)malloc(sizeof(struct soundcard));
if (!Xsoundcard) {
free(name);
error_nomem();
return;
}
bzero(Xsoundcard, sizeof(*Xsoundcard));
for (soundcard = rsoundcards; soundcard && soundcard->next; soundcard = soundcard->next);
if (soundcard) {
soundcard->next = Xsoundcard;
} else {
rsoundcards = Xsoundcard;
}
strncpy(Xsoundcard->control.hwinfo.id, name, sizeof(Xsoundcard->control.hwinfo.id));
free(name);
}
static void select_mixer(char *name)
static void build_mixer(char *name)
{
struct mixer *mixer;
@ -319,17 +432,24 @@ static void select_mixer(char *name)
Xmixer = NULL;
return;
}
for (mixer = Xsoundcard->mixers; mixer; mixer = mixer->next)
if (!strcmp(mixer->info.name, name)) {
Xmixer = mixer;
free(name);
return;
}
yyerror("Cannot find mixer '%s' for soundcard '%s'...", name, Xsoundcard->control.hwinfo.id);
Xmixer = (struct mixer *)malloc(sizeof(struct pcm));
if (!Xmixer) {
free(name);
error_nomem();
return;
}
bzero(Xmixer, sizeof(*Xmixer));
for (mixer = Xsoundcard->mixers; mixer && mixer->next; mixer = mixer->next);
if (mixer) {
mixer->next = Xmixer;
} else {
Xsoundcard->mixers = Xmixer;
}
strncpy(Xmixer->info.name, name, sizeof(Xmixer->info.name));
free(name);
}
static void select_pcm(char *name)
static void build_pcm(char *name)
{
struct pcm *pcm;
@ -337,17 +457,24 @@ static void select_pcm(char *name)
Xpcm = NULL;
return;
}
for (pcm = Xsoundcard->pcms; pcm; pcm = pcm->next)
if (!strcmp(pcm->info.name, name)) {
Xpcm = pcm;
free(name);
return;
}
yyerror("Cannot find pcm device '%s' for soundcard '%s'...", name, Xsoundcard->control.hwinfo.id);
Xpcm = (struct pcm *)malloc(sizeof(struct pcm));
if (!Xpcm) {
free(name);
error_nomem();
return;
}
bzero(Xpcm, sizeof(*Xpcm));
for (pcm = Xsoundcard->pcms; pcm && pcm->next; pcm = pcm->next);
if (pcm) {
pcm->next = Xpcm;
} else {
Xsoundcard->pcms = Xpcm;
}
strncpy(Xpcm->info.name, name, sizeof(Xpcm->info.name));
free(name);
}
static void select_rawmidi(char *name)
static void build_rawmidi(char *name)
{
struct rawmidi *rawmidi;
@ -355,185 +482,342 @@ static void select_rawmidi(char *name)
Xrawmidi = NULL;
return;
}
for (rawmidi = Xsoundcard->rawmidis; rawmidi; rawmidi = rawmidi->next)
if (!strcmp(rawmidi->info.name, name)) {
Xrawmidi = rawmidi;
free(name);
return;
}
yyerror("Cannot find rawmidi device '%s' for soundcard '%s'...", name, Xsoundcard->control.hwinfo.id);
free(name);
}
static void select_mixer_channel(char *name)
{
struct mixer_channel *channel;
if (!name) {
Xchannel = NULL;
Xrawmidi = (struct rawmidi *)malloc(sizeof(struct rawmidi));
if (!Xrawmidi) {
free(name);
error_nomem();
return;
}
for (channel = Xmixer->channels; channel; channel = channel->next)
if (!strcmp(channel->info.name, name)) {
Xchannel = channel;
Xchannel->ddata[OUTPUT].flags = 0;
Xchannel->ddata[INPUT].flags = 0;
free(name);
return;
}
yyerror("Cannot find mixer channel '%s'...", name);
bzero(Xrawmidi, sizeof(*Xrawmidi));
for (rawmidi = Xsoundcard->rawmidis; rawmidi && rawmidi->next; rawmidi = rawmidi->next);
if (rawmidi) {
rawmidi->next = Xrawmidi;
} else {
Xsoundcard->rawmidis = Xrawmidi;
}
strncpy(Xrawmidi->info.name, name, sizeof(Xrawmidi->info.name));
free(name);
}
static void select_mixer_direction(int direction)
static void build_mixer_element(char *name, int index, int etype)
{
Xchannel->direction = direction;
}
struct mixer_element *element;
static void select_mixer_voice(int voice)
{
Xchannel->voice = voice;
}
static void set_mixer_volume(int volume)
{
snd_mixer_channel_direction_info_t *i = &Xchannel->dinfo[Xchannel->direction];
snd_mixer_channel_direction_t *d = &Xchannel->ddata[Xchannel->direction];
if (Xchannel->voice & LEFT) {
if (i->min > volume || i->max < volume)
yyerror("Value out of range (%i-%i)...", i->min, i->max);
d->left = volume;
if (!name) {
Xelement = NULL;
return;
}
if (Xchannel->voice & RIGHT) {
if (i->min > volume || i->max < volume)
yyerror("Value out of range (%i-%i)...", i->min, i->max);
d->right = volume;
Xelement = (struct mixer_element *)malloc(sizeof(struct mixer_element));
if (!Xelement) {
free(name);
error_nomem();
return;
}
bzero(Xelement, sizeof(*Xelement));
for (element = Xmixer->elements; element && element->next; element = element->next);
if (element) {
element->next = Xelement;
} else {
Xmixer->elements = Xelement;
}
strncpy(Xelement->element.eid.name, name, sizeof(Xelement->element.eid.name));
Xelement->element.eid.index = index;
Xelement->element.eid.type = etype;
Xelement->info.eid = Xelement->element.eid;
free(name);
}
static void set_mixer_flags(int flags)
static void mixer_type_check(int type)
{
snd_mixer_channel_direction_t *d = &Xchannel->ddata[Xchannel->direction];
if (Xchannel->voice & LEFT) {
if (flags & SND_MIXER_DFLG_MUTE)
d->flags |= SND_MIXER_DFLG_MUTE_LEFT;
if (flags & SND_MIXER_DFLG_SWAP)
d->flags |= SND_MIXER_DFLG_LTOR;
}
if (Xchannel->voice & RIGHT) {
if (flags & SND_MIXER_DFLG_MUTE)
d->flags |= SND_MIXER_DFLG_MUTE_RIGHT;
if (flags & SND_MIXER_DFLG_SWAP)
d->flags |= SND_MIXER_DFLG_RTOL;
}
if (Xelement->element.eid.type != type)
yyerror("The element has got the unexpected data type.");
}
static void select_mixer_channel_end(void)
static void mixer_switch1(int end)
{
mixer_type_check(SND_MIXER_ETYPE_SWITCH1);
}
static void find_switch(int xtype, struct ctl_switch *first, char *name, char *err)
static void mixer_switch1_value(int val)
{
unsigned int *ptr;
if (Xelement->element.data.switch1.sw_size <= Xelement->element.data.switch1.sw) {
Xelement->element.data.switch1.sw_size += 32;
ptr = (unsigned int *)realloc(Xelement->element.data.switch1.psw, ((Xelement->element.data.switch1.sw_size + 31) / 32) * sizeof(unsigned int));
if (ptr == NULL) {
error_nomem();
return;
}
Xelement->element.data.switch1.psw = ptr;
}
snd_mixer_set_bit(Xelement->element.data.switch1.psw, Xelement->element.data.switch1.sw++, val ? 1 : 0);
}
static void mixer_switch2(int end)
{
mixer_type_check(SND_MIXER_ETYPE_SWITCH2);
}
static void mixer_switch2_value(int val)
{
Xelement->element.data.switch2.sw = val ? 1 : 0;
}
static void mixer_switch3(int end)
{
mixer_type_check(SND_MIXER_ETYPE_SWITCH3);
}
static void mixer_switch3_value(int val)
{
unsigned int *ptr;
if (Xelement->element.data.switch3.rsw_size <= Xelement->element.data.switch3.rsw) {
Xelement->element.data.switch3.rsw_size += 32;
ptr = (unsigned int *)realloc(Xelement->element.data.switch1.psw, ((Xelement->element.data.switch3.rsw_size + 31) / 32) * sizeof(unsigned int));
if (ptr == NULL) {
error_nomem();
return;
}
Xelement->element.data.switch3.prsw = ptr;
}
snd_mixer_set_bit(Xelement->element.data.switch3.prsw, Xelement->element.data.switch3.rsw++, val ? 1 : 0);
}
static void mixer_volume1(int end)
{
mixer_type_check(SND_MIXER_ETYPE_VOLUME1);
}
static void mixer_volume1_value(int val)
{
int *ptr;
if (Xelement->element.data.volume1.voices_size <= Xelement->element.data.volume1.voices) {
Xelement->element.data.volume1.voices_size += 4;
ptr = (int *)realloc(Xelement->element.data.volume1.pvoices, Xelement->element.data.volume1.voices_size * sizeof(int));
if (ptr == NULL) {
error_nomem();
return;
}
Xelement->element.data.volume1.pvoices = ptr;
}
Xelement->element.data.volume1.pvoices[Xelement->element.data.volume1.voices++] = val;
}
static void mixer_3d_effect1(int end)
{
mixer_type_check(SND_MIXER_ETYPE_3D_EFFECT1);
}
static void mixer_3d_effect1_value(unsigned int effect, int val)
{
switch (effect) {
case SND_MIXER_EFF1_SW:
Xelement->element.data.teffect1.sw = val ? 1 : 0;
break;
case SND_MIXER_EFF1_MONO_SW:
Xelement->element.data.teffect1.mono_sw = val ? 1 : 0;
break;
case SND_MIXER_EFF1_WIDE:
Xelement->element.data.teffect1.wide = val;
break;
case SND_MIXER_EFF1_VOLUME:
Xelement->element.data.teffect1.volume = val;
break;
case SND_MIXER_EFF1_CENTER:
Xelement->element.data.teffect1.center = val;
break;
case SND_MIXER_EFF1_SPACE:
Xelement->element.data.teffect1.space = val;
break;
case SND_MIXER_EFF1_DEPTH:
Xelement->element.data.teffect1.depth = val;
break;
case SND_MIXER_EFF1_DELAY:
Xelement->element.data.teffect1.delay = val;
break;
case SND_MIXER_EFF1_FEEDBACK:
Xelement->element.data.teffect1.feedback = val;
break;
default:
yyerror("Unknown effect 0x%x\n", effect);
}
}
static void mixer_accu3(int end)
{
mixer_type_check(SND_MIXER_ETYPE_ACCU3);
}
static void mixer_accu3_value(int val)
{
int *ptr;
if (Xelement->element.data.accu3.voices_size <= Xelement->element.data.accu3.voices) {
Xelement->element.data.accu3.voices_size += 4;
ptr = (int *)realloc(Xelement->element.data.accu3.pvoices, Xelement->element.data.accu3.voices_size * sizeof(int));
if (ptr == NULL) {
error_nomem();
return;
}
Xelement->element.data.accu3.pvoices = ptr;
}
Xelement->element.data.accu3.pvoices[Xelement->element.data.accu3.voices++] = val;
}
static void mixer_mux1(int end)
{
mixer_type_check(SND_MIXER_ETYPE_MUX1);
}
static void mixer_mux1_value(char *name, int index, int type)
{
snd_mixer_eid_t *ptr;
snd_mixer_eid_t *eid;
if (Xelement->element.data.mux1.output_size <= Xelement->element.data.mux1.output) {
Xelement->element.data.mux1.output_size += 4;
ptr = (snd_mixer_eid_t *)realloc(Xelement->element.data.mux1.poutput, Xelement->element.data.mux1.output_size * sizeof(snd_mixer_eid_t));
if (ptr == NULL) {
error_nomem();
free(name);
return;
}
Xelement->element.data.mux1.poutput = ptr;
}
eid = &Xelement->element.data.mux1.poutput[Xelement->element.data.mux1.output++];
strncpy(eid->name, name, sizeof(eid->name));
eid->index = index;
eid->type = type;
free(name);
}
static void mixer_mux2(int end)
{
mixer_type_check(SND_MIXER_ETYPE_MUX2);
}
static void mixer_mux2_value(char *name, int index, int type)
{
snd_mixer_eid_t *eid;
eid = &Xelement->element.data.mux2.output;
strncpy(eid->name, name, sizeof(eid->name));
eid->index = index;
eid->type = type;
free(name);
}
static void mixer_tone_control1(int end)
{
mixer_type_check(SND_MIXER_ETYPE_TONE_CONTROL1);
}
static void mixer_tone_control1_value(unsigned int effect, int val)
{
switch (effect) {
case SND_MIXER_TC1_SW:
Xelement->element.data.tc1.sw = val ? 1 : 0;
break;
case SND_MIXER_TC1_BASS:
Xelement->element.data.tc1.bass = val;
break;
case SND_MIXER_TC1_TREBLE:
Xelement->element.data.tc1.treble = val;
break;
default:
yyerror("Unknown effect 0x%x\n", effect);
}
}
static void build_switch(struct ctl_switch **first, char *name)
{
struct ctl_switch *sw;
if (!name) {
Xswitch = NULL;
Xswitchchange = NULL;
return;
}
for (sw = first; sw; sw = sw->next) {
if (!strcmp(sw -> s.name, name)) {
Xswitchtype = xtype;
Xswitchchange = &sw->change;
Xswitch = &sw->s;
free(name);
return;
}
Xswitch = (struct ctl_switch *)malloc(sizeof(struct ctl_switch));
if (!Xswitch) {
free(name);
error_nomem();
return;
}
yyerror("Cannot find %s switch '%s'...", err, name);
bzero(Xswitch, sizeof(*Xswitch));
for (sw = *first; sw && sw->next; sw = sw->next);
if (sw) {
sw->next = Xswitch;
} else {
*first = Xswitch;
}
strncpy(Xswitch->s.name, name, sizeof(Xswitch->s.name));
free(name);
}
static void select_control_switch(char *name)
static void build_control_switch(char *name)
{
find_switch(SWITCH_CONTROL, Xsoundcard->control.switches, name, "control");
build_switch(&Xsoundcard->control.switches, name);
}
static void select_mixer_switch(char *name)
static void build_mixer_switch(char *name)
{
find_switch(SWITCH_MIXER, Xmixer->switches, name, "mixer");
build_switch(&Xmixer->switches, name);
}
static void select_pcm_playback_switch(char *name)
static void build_pcm_playback_switch(char *name)
{
find_switch(SWITCH_PCM, Xpcm->pswitches, name, "pcm playback");
build_switch(&Xpcm->pswitches, name);
}
static void select_pcm_record_switch(char *name)
{
find_switch(SWITCH_PCM, Xpcm->rswitches, name, "pcm record");
static void build_pcm_record_switch(char *name)
{
build_switch(&Xpcm->rswitches, name);
}
static void select_rawmidi_output_switch(char *name)
static void build_rawmidi_output_switch(char *name)
{
find_switch(SWITCH_RAWMIDI, Xrawmidi->oswitches, name, "rawmidi output");
build_switch(&Xrawmidi->oswitches, name);
}
static void select_rawmidi_input_switch(char *name)
static void build_rawmidi_input_switch(char *name)
{
find_switch(SWITCH_RAWMIDI, Xrawmidi->iswitches, name, "rawmidi input");
build_switch(&Xrawmidi->iswitches, name);
}
static void set_switch_boolean(int val)
{
snd_switch_t *sw = Xswitch;
unsigned int xx;
if (sw->type != SND_SW_TYPE_BOOLEAN)
yyerror("Switch '%s' isn't boolean type...", sw->name);
xx = val ? 1 : 0;
if (sw->value.enable != xx)
*Xswitchchange = 1;
sw->value.enable = xx;
#if 0
printf("name = '%s', sw->value.enable = %i\n", sw->name, xx);
#endif
Xswitch->s.type = SND_SW_TYPE_BOOLEAN;
Xswitch->s.value.enable = val ? 1 : 0;
}
static void set_switch_integer(int val)
{
snd_switch_t *sw = Xswitch;
unsigned int xx;
if (sw->type != SND_SW_TYPE_BYTE &&
sw->type != SND_SW_TYPE_WORD &&
sw->type != SND_SW_TYPE_DWORD)
yyerror("Switch '%s' isn't integer type...", sw->name);
if (val < sw->low || val > sw->high)
yyerror("Value for switch '%s' out of range (%i-%i)...\n", sw->name, sw->low, sw->high);
Xswitch->s.type = SND_SW_TYPE_DWORD;
xx = val;
if (memcmp(&sw->value, &xx, sizeof(xx)))
*Xswitchchange = 1;
memcpy(&sw->value, &xx, sizeof(xx));
memcpy(&Xswitch->s.value, &xx, sizeof(xx));
}
static void set_switch_bytearray(struct bytearray val)
{
Xswitch->s.type = SND_SW_TYPE_LAST + 1;
if (val.datalen > 32)
yyerror("Byte array too large for switch.");
memcpy(Xswitch->s.value.data8, val.data, val.datalen);
}
static void set_switch_iec958ocs_begin(int end)
{
snd_switch_t *sw = Xswitch;
if (end) {
if (Xswitchiec958ocs != sw->value.enable) {
sw->value.enable = Xswitchiec958ocs;
*Xswitchchange = 1;
}
if (Xswitchiec958ocs1[4] != sw->value.data16[4]) {
sw->value.data16[4] = Xswitchiec958ocs1[4];
*Xswitchchange = 1;
}
if (Xswitchiec958ocs1[5] != sw->value.data16[5]) {
sw->value.data16[5] = Xswitchiec958ocs1[5];
*Xswitchchange = 1;
}
Xswitch->s.value.enable = Xswitchiec958ocs;
Xswitch->s.value.data16[4] = Xswitchiec958ocs1[4];
Xswitch->s.value.data16[5] = Xswitchiec958ocs1[5];
#if 0
printf("IEC958: enable = %i, ocs1[4] = 0x%x, ocs1[5] = 0x%x\n",
sw->value.enable,
@ -542,11 +826,8 @@ static void set_switch_iec958ocs_begin(int end)
#endif
return;
}
if (Xswitchtype != SWITCH_MIXER || sw->type != SND_SW_TYPE_BOOLEAN ||
strcmp(sw->name, SND_MIXER_SW_IEC958OUT))
yyerror("Switch '%s' cannot store IEC958 information for Cirrus Logic chips...", sw->name);
if (sw->value.data32[1] != (('C' << 8) | 'S'))
yyerror("Switch '%s' doesn't have Cirrus Logic signature!!!", sw->name);
Xswitch->s.type = SND_SW_TYPE_BOOLEAN;
Xswitch->s.value.data32[1] = ('C' << 8) | 'S';
Xswitchiec958ocs = 0;
Xswitchiec958ocs1[4] = 0x0000;
Xswitchiec958ocs1[5] = 0x0004; /* copy permitted */

492
alsactl/merge.c Normal file
View file

@ -0,0 +1,492 @@
/*
* Advanced Linux Sound Architecture Control Program
* Copyright (c) 1999 by Jaroslav Kysela <perex@jcu.cz>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include "alsactl.h"
static char *sw_id(const char *name, int cardno, int devno, const char *id)
{
static char str[256];
sprintf(str, "%s %s card %i", name, id, cardno);
if (devno >= 0)
sprintf(str + strlen(str)," device %i", devno);
return str;
}
static int merge_one_sw(struct ctl_switch *csw, struct ctl_switch *usw, int cardno, int devno, const char *id)
{
switch (csw->s.type) {
case SND_SW_TYPE_BOOLEAN:
if (usw->s.type != SND_SW_TYPE_BOOLEAN) {
error("A wrong type for the switch %s. The type boolean is expected. Skipping...", sw_id(usw->s.name, cardno, devno, id));
return 1;
}
if (csw->s.value.enable != usw->s.value.enable) {
csw->change = 1;
csw->s.value.enable = usw->s.value.enable;
}
if (!strncmp(csw->s.name, SND_MIXER_SW_IEC958_OUTPUT, sizeof(csw->s.name))) {
if (usw->s.value.data32[1] == (('C' << 8) | 'S')) {
if (csw->s.value.data16[4] != usw->s.value.data16[4] ||
csw->s.value.data16[5] != usw->s.value.data16[5]) {
csw->change = 1;
csw->s.value.data16[4] = usw->s.value.data16[4];
csw->s.value.data16[5] = usw->s.value.data16[5];
}
}
}
break;
case SND_SW_TYPE_BYTE:
if (usw->s.type != SND_SW_TYPE_DWORD) {
error("A wrong type for the switch %s. The type byte is expected. Skipping...", sw_id(usw->s.name, cardno, devno, id));
return 1;
}
if (csw->s.low > usw->s.value.data32[0] ||
csw->s.high < usw->s.value.data32[0]) {
error("The value %u for the switch %s is out of range %i-%i.", usw->s.value.data32[0], sw_id(usw->s.name, cardno, devno, id), csw->s.low, csw->s.high);
return 1;
}
if (csw->s.value.data8[0] != (unsigned char)usw->s.value.data32[0]) {
csw->change = 1;
csw->s.value.data8[0] = (unsigned char)usw->s.value.data32[0];
}
break;
case SND_SW_TYPE_WORD:
if (usw->s.type != SND_SW_TYPE_DWORD) {
error("A wrong type for the switch %s. The type word is expected. Skipping...", sw_id(usw->s.name, cardno, devno, id));
return 1;
}
if (csw->s.low > usw->s.value.data32[0] ||
csw->s.high < usw->s.value.data32[0]) {
error("The value %u for the switch %s is out of range %i-%i.", usw->s.value.data32[0], sw_id(usw->s.name, cardno, devno, id), csw->s.low, csw->s.high);
return 1;
}
if (csw->s.value.data16[0] != (unsigned short)usw->s.value.data32[0]) {
csw->change = 1;
csw->s.value.data16[0] = (unsigned short)usw->s.value.data32[0];
}
break;
case SND_SW_TYPE_DWORD:
if (usw->s.type != SND_SW_TYPE_DWORD) {
error("A wrong type for the switch %s. The type dword is expected. Skipping...", sw_id(usw->s.name, cardno, devno, id));
return 1;
}
if (csw->s.low > usw->s.value.data32[0] ||
csw->s.high < usw->s.value.data32[0]) {
error("The value %u for the switch %s is out of range %i-%i.", usw->s.value.data32[0], sw_id(usw->s.name, cardno, devno, id), csw->s.low, csw->s.high);
return 1;
}
if (csw->s.value.data32[0] != usw->s.value.data32[0]) {
csw->change = 1;
csw->s.value.data32[0] = usw->s.value.data32[0];
}
break;
default:
error("The switch type %i is not known.", csw->s.type);
}
return 0;
}
static int soundcard_setup_merge_sw(struct ctl_switch *csw, struct ctl_switch *usw, int cardno, int devno, const char *id)
{
struct ctl_switch *csw1;
for ( ; usw; usw = usw->next) {
for (csw1 = csw; csw1; csw1 = csw1->next) {
if (!strncmp(csw1->s.name, usw->s.name, sizeof(csw1->s.name))) {
merge_one_sw(csw1, usw, cardno, devno, id);
break;
}
}
if (!csw1) {
error("Cannot find the switch %s...", sw_id(usw->s.name, cardno, devno, id));
}
}
return 0;
}
int soundcard_setup_merge_switches(int cardno)
{
struct soundcard *soundcard, *rsoundcard;
struct mixer *mixer, *rmixer;
struct pcm *pcm, *rpcm;
struct rawmidi *rawmidi, *rrawmidi;
for (rsoundcard = rsoundcards; rsoundcard; rsoundcard = rsoundcard->next) {
for (soundcard = soundcards; soundcard; soundcard = soundcard->next) {
if (!strncmp(soundcard->control.hwinfo.id, rsoundcard->control.hwinfo.id, sizeof(soundcard->control.hwinfo.id)))
break;
}
if (!soundcard) {
error("The soundcard '%s' was not found...\n", rsoundcard->control.hwinfo.id);
continue;
}
if (cardno >= 0 && soundcard->no != cardno)
continue;
soundcard_setup_merge_sw(soundcard->control.switches, rsoundcard->control.switches, soundcard->no, -1, "control");
for (rmixer = rsoundcard->mixers; rmixer; rmixer = rmixer->next) {
for (mixer = soundcard->mixers; mixer; mixer = rmixer->next) {
if (!strncmp(mixer->info.name, rmixer->info.name, sizeof(mixer->info.name)))
break;
}
if (!mixer) {
error("The mixer device '%s' from the soundcard %i was not found...\n", rmixer->info.name, soundcard->no);
continue;
}
soundcard_setup_merge_sw(mixer->switches, rmixer->switches, soundcard->no, mixer->no, "mixer");
}
for (rpcm = rsoundcard->pcms; rpcm; rpcm = rpcm->next) {
for (pcm = rsoundcard->pcms; pcm; pcm = pcm->next) {
if (!strncmp(pcm->info.name, rpcm->info.name, sizeof(pcm->info.name)))
break;
}
if (!rpcm) {
error("The PCM device '%s' from the soundcard %i was not found...\n", rpcm->info.name, soundcard->no);
continue;
}
soundcard_setup_merge_sw(pcm->pswitches, rpcm->pswitches, soundcard->no, pcm->no, "PCM playback");
soundcard_setup_merge_sw(pcm->rswitches, rpcm->rswitches, soundcard->no, pcm->no, "PCM record");
}
for (rrawmidi = rsoundcard->rawmidis; rrawmidi; rrawmidi = rrawmidi->next) {
for (rawmidi = soundcard->rawmidis; rawmidi; rawmidi = rawmidi->next) {
if (!strncmp(rawmidi->info.name, rrawmidi->info.name, sizeof(rawmidi->info.name)))
break;
}
if (!rrawmidi) {
error("The rawmidi device '%s' from the soundcard %i was not found...\n", rrawmidi->info.name, soundcard->no);
continue;
}
soundcard_setup_merge_sw(rawmidi->iswitches, rrawmidi->iswitches, soundcard->no, rawmidi->no, "rawmidi input");
soundcard_setup_merge_sw(rawmidi->oswitches, rrawmidi->oswitches, soundcard->no, rawmidi->no, "rawmidi output");
}
}
return 0;
}
static char *element_id(snd_mixer_eid_t *eid, int cardno, int devno, const char *id)
{
static char str[256];
sprintf(str, "%s %s card %i", mixer_element_id(eid), id, cardno);
if (devno >= 0)
sprintf(str + strlen(str)," device %i", devno);
return str;
}
static int merge_one_element(struct mixer_element *celement, struct mixer_element *uelement, int cardno, int devno, const char *id)
{
int tmp;
if (snd_mixer_element_has_control(&celement->element.eid) != 1)
return 0;
switch (celement->element.eid.type) {
case SND_MIXER_ETYPE_SWITCH1:
if (celement->element.data.switch1.sw != uelement->element.data.switch1.sw) {
error("Element %s has got a wrong count of voices.", element_id(&celement->element.eid, cardno, devno, id));
return 1;
}
tmp = ((celement->element.data.switch1.sw + 31) / 32) * sizeof(unsigned int);
memcpy(celement->element.data.switch1.psw, uelement->element.data.switch1.psw, tmp);
break;
case SND_MIXER_ETYPE_SWITCH2:
celement->element.data.switch2.sw = uelement->element.data.switch2.sw;
break;
case SND_MIXER_ETYPE_SWITCH3:
if (celement->element.data.switch3.rsw != uelement->element.data.switch3.rsw) {
error("Element %s has got a wrong count of switches.", element_id(&celement->element.eid, cardno, devno, id));
return 1;
}
tmp = ((celement->element.data.switch3.rsw + 31) / 32) * sizeof(unsigned int);
memcpy(celement->element.data.switch3.prsw, uelement->element.data.switch3.prsw, tmp);
break;
case SND_MIXER_ETYPE_VOLUME1:
if (celement->element.data.volume1.voices != uelement->element.data.volume1.voices) {
error("Element %s has got a wrong count of voices.", element_id(&celement->element.eid, cardno, devno, id));
return 1;
}
tmp = celement->element.data.volume1.voices * sizeof(int);
memcpy(celement->element.data.volume1.pvoices, uelement->element.data.volume1.pvoices, tmp);
break;
case SND_MIXER_ETYPE_VOLUME2:
if (celement->element.data.volume2.avoices != uelement->element.data.volume2.avoices) {
error("Element %s has got a wrong count of voices.", element_id(&celement->element.eid, cardno, devno, id));
return 1;
}
tmp = celement->element.data.volume2.avoices * sizeof(int);
memcpy(celement->element.data.volume2.pavoices, uelement->element.data.volume2.pavoices, tmp);
break;
case SND_MIXER_ETYPE_ACCU3:
if (celement->element.data.accu3.voices != uelement->element.data.accu3.voices) {
error("Element %s has got a wrong count of voices.", element_id(&celement->element.eid, cardno, devno, id));
return 1;
}
tmp = celement->element.data.accu3.voices * sizeof(int);
memcpy(celement->element.data.accu3.pvoices, uelement->element.data.accu3.pvoices, tmp);
break;
case SND_MIXER_ETYPE_MUX1:
if (celement->element.data.mux1.poutput)
free(celement->element.data.mux1.poutput);
celement->element.data.mux1.output_size = 0;
celement->element.data.mux1.output = 0;
celement->element.data.mux1.output_over = 0;
tmp = uelement->element.data.mux1.output * sizeof(snd_mixer_eid_t);
if (tmp > 0) {
celement->element.data.mux1.poutput = (snd_mixer_eid_t *)malloc(uelement->element.data.mux1.output_size * sizeof(snd_mixer_eid_t));
if (!celement->element.data.mux1.poutput) {
error("No enough memory...");
return 1;
}
celement->element.data.mux1.output_size = uelement->element.data.mux1.output_size;
celement->element.data.mux1.output = uelement->element.data.mux1.output;
memcpy(celement->element.data.mux1.poutput, uelement->element.data.mux1.poutput, tmp);
}
break;
case SND_MIXER_ETYPE_MUX2:
celement->element.data.mux2.output = uelement->element.data.mux2.output;
break;
case SND_MIXER_ETYPE_TONE_CONTROL1:
if ((uelement->element.data.tc1.tc & ~celement->info.data.tc1.tc) != 0) {
error("Wrong (unsupported) input for the element %s.", element_id(&celement->element.eid, cardno, devno, id));
return 1;
}
celement->element.data.tc1.tc = uelement->element.data.tc1.tc;
celement->element.data.tc1.sw = uelement->element.data.tc1.sw;
celement->element.data.tc1.bass = uelement->element.data.tc1.bass;
celement->element.data.tc1.treble = uelement->element.data.tc1.treble;
break;
case SND_MIXER_ETYPE_3D_EFFECT1:
if ((uelement->element.data.teffect1.effect & ~celement->info.data.teffect1.effect) != 0) {
error("Wrong (unsupported) input for the element %s.", element_id(&celement->element.eid, cardno, devno, id));
return 1;
}
celement->element.data.teffect1.effect = uelement->element.data.teffect1.effect;
celement->element.data.teffect1.sw = uelement->element.data.teffect1.sw;
celement->element.data.teffect1.mono_sw = uelement->element.data.teffect1.mono_sw;
celement->element.data.teffect1.wide = uelement->element.data.teffect1.wide;
celement->element.data.teffect1.volume = uelement->element.data.teffect1.volume;
celement->element.data.teffect1.center = uelement->element.data.teffect1.center;
celement->element.data.teffect1.space = uelement->element.data.teffect1.space;
celement->element.data.teffect1.depth = uelement->element.data.teffect1.depth;
celement->element.data.teffect1.delay = uelement->element.data.teffect1.delay;
celement->element.data.teffect1.feedback = uelement->element.data.teffect1.feedback;
break;
case SND_MIXER_ETYPE_PRE_EFFECT1:
if (celement->element.data.peffect1.pparameters)
free(celement->element.data.peffect1.pparameters);
celement->element.data.peffect1.parameters_size = 0;
celement->element.data.peffect1.parameters = 0;
celement->element.data.peffect1.parameters_over = 0;
celement->element.data.peffect1.item = uelement->element.data.peffect1.item;
if (celement->element.data.peffect1.item < 0) {
celement->element.data.peffect1.pparameters = (int *)malloc(uelement->element.data.peffect1.parameters_size * sizeof(int));
if (!celement->element.data.peffect1.pparameters) {
error("No enough memory..");
return 1;
}
celement->element.data.peffect1.parameters_size = uelement->element.data.peffect1.parameters_size;
celement->element.data.peffect1.parameters = uelement->element.data.peffect1.parameters;
tmp = celement->element.data.peffect1.parameters * sizeof(int);
memcpy(celement->element.data.peffect1.pparameters, uelement->element.data.peffect1.pparameters, tmp);
}
break;
default:
error("The element type %i for the element %s is not known.", celement->element.eid.type, mixer_element_id(&celement->element.eid));
}
return 0;
}
static int soundcard_setup_merge_element(struct mixer_element *celement, struct mixer_element *uelement, int cardno, int devno, const char *id)
{
struct mixer_element *element;
for ( ; uelement; uelement = uelement->next) {
for (element = celement; element; element = element->next) {
if (!strncmp(element->element.eid.name, uelement->element.eid.name, sizeof(element->element.eid.name)) &&
element->element.eid.index == uelement->element.eid.index &&
element->element.eid.type == uelement->element.eid.type) {
merge_one_element(element, uelement, cardno, devno, id);
break;
}
}
if (!element) {
error("Cannot find the element %s...", element_id(&uelement->element.eid, cardno, devno, id));
}
}
return 0;
}
int soundcard_setup_merge_data(int cardno)
{
struct soundcard *soundcard, *rsoundcard;
struct mixer *mixer, *rmixer;
for (rsoundcard = rsoundcards; rsoundcard; rsoundcard = rsoundcard->next) {
for (soundcard = soundcards; soundcard; soundcard = soundcard->next) {
if (!strncmp(soundcard->control.hwinfo.id, rsoundcard->control.hwinfo.id, sizeof(soundcard->control.hwinfo.id)))
break;
}
if (!soundcard) {
error("The soundcard '%s' was not found...\n", rsoundcard->control.hwinfo.id);
continue;
}
if (cardno >= 0 && soundcard->no != cardno)
continue;
for (rmixer = rsoundcard->mixers; rmixer; rmixer = rmixer->next) {
for (mixer = soundcard->mixers; mixer; mixer = rmixer->next) {
if (!strncmp(mixer->info.name, rmixer->info.name, sizeof(mixer->info.name)))
break;
}
if (!mixer) {
error("The mixer device '%s' from the soundcard %i was not found...\n", rmixer->info.name, soundcard->no);
continue;
}
soundcard_setup_merge_element(mixer->elements, rmixer->elements, soundcard->no, mixer->no, "mixer");
}
}
return 0;
}
static int soundcard_open_ctl(void **ctlhandle, struct soundcard *soundcard)
{
int err;
if (*ctlhandle)
return 0;
if ((err = snd_ctl_open(ctlhandle, soundcard->no)) < 0) {
error("Cannot open control interface for soundcard #%i.", soundcard->no + 1);
return 1;
}
return 0;
}
static int soundcard_open_mix(void **mixhandle, struct soundcard *soundcard, struct mixer *mixer)
{
int err;
if (*mixhandle)
return 0;
if ((err = snd_mixer_open(mixhandle, soundcard->no, mixer->no)) < 0) {
error("Cannot open mixer interface for soundcard #%i.", soundcard->no + 1);
return 1;
}
return 0;
}
int soundcard_setup_process_switches(int cardno)
{
int err;
void *ctlhandle = NULL;
struct soundcard *soundcard;
struct ctl_switch *ctlsw;
struct mixer *mixer;
struct pcm *pcm;
struct rawmidi *rawmidi;
for (soundcard = soundcards; soundcard; soundcard = soundcard->next) {
if (cardno >= 0 && soundcard->no != cardno)
continue;
for (ctlsw = soundcard->control.switches; ctlsw; ctlsw = ctlsw->next) {
if (ctlsw->change)
if (!soundcard_open_ctl(&ctlhandle, soundcard)) {
if ((err = snd_ctl_switch_write(ctlhandle, &ctlsw->s)) < 0)
error("Control switch '%s' write error: %s", ctlsw->s.name, snd_strerror(err));
}
}
for (mixer = soundcard->mixers; mixer; mixer = mixer->next) {
for (ctlsw = mixer->switches; ctlsw; ctlsw = ctlsw->next)
if (ctlsw->change)
if (!soundcard_open_ctl(&ctlhandle, soundcard)) {
if ((err = snd_ctl_mixer_switch_write(ctlhandle, mixer->no, &ctlsw->s)) < 0)
error("Mixer switch '%s' write error: %s", ctlsw->s.name, snd_strerror(err));
}
}
for (pcm = soundcard->pcms; pcm; pcm = pcm->next) {
for (ctlsw = pcm->pswitches; ctlsw; ctlsw = ctlsw->next) {
if (ctlsw->change)
if (!soundcard_open_ctl(&ctlhandle, soundcard)) {
if ((err = snd_ctl_pcm_playback_switch_write(ctlhandle, pcm->no, &ctlsw->s)) < 0)
error("PCM playback switch '%s' write error: %s", ctlsw->s.name, snd_strerror(err));
}
}
for (ctlsw = pcm->rswitches; ctlsw; ctlsw = ctlsw->next) {
if (ctlsw->change)
if (!soundcard_open_ctl(&ctlhandle, soundcard)) {
if ((err = snd_ctl_pcm_record_switch_write(ctlhandle, pcm->no, &ctlsw->s)) < 0)
error("PCM record switch '%s' write error: %s", ctlsw->s.name, snd_strerror(err));
}
}
}
for (rawmidi = soundcard->rawmidis; rawmidi; rawmidi = rawmidi->next) {
for (ctlsw = rawmidi->oswitches; ctlsw; ctlsw = ctlsw->next) {
if (ctlsw->change)
if (!soundcard_open_ctl(&ctlhandle, soundcard)) {
if ((err = snd_ctl_rawmidi_output_switch_write(ctlhandle, rawmidi->no, &ctlsw->s)) < 0)
error("RAWMIDI output switch '%s' write error: %s", ctlsw->s.name, snd_strerror(err));
}
}
for (ctlsw = rawmidi->iswitches; ctlsw; ctlsw = ctlsw->next) {
if (ctlsw->change)
if (!soundcard_open_ctl(&ctlhandle, soundcard)) {
if ((err = snd_ctl_rawmidi_output_switch_write(ctlhandle, rawmidi->no, &ctlsw->s)) < 0)
error("RAWMIDI input switch '%s' write error: %s", ctlsw->s.name, snd_strerror(err));
}
}
}
if(ctlhandle) {
snd_ctl_close(ctlhandle);
ctlhandle = NULL;
}
}
return 0;
}
int soundcard_setup_process_data(int cardno)
{
int err;
void *ctlhandle = NULL;
void *mixhandle = NULL;
struct soundcard *soundcard;
struct mixer *mixer;
struct mixer_element *element;
for (soundcard = soundcards; soundcard; soundcard = soundcard->next) {
if (cardno >= 0 && soundcard->no != cardno)
continue;
for (mixer = soundcard->mixers; mixer; mixer = mixer->next) {
for (element = mixer->elements; element; element = element->next)
if (snd_mixer_element_has_control(&element->element.eid) == 1) {
if (!soundcard_open_mix(&mixhandle, soundcard, mixer)) {
if ((err = snd_mixer_element_write(mixhandle, &element->element)) < 0)
error("Mixer element %s write error: %s", mixer_element_id(&element->element.eid), snd_strerror(err));
}
}
if (mixhandle) {
snd_mixer_close(mixhandle);
mixhandle = NULL;
}
}
if(ctlhandle) {
snd_ctl_close(ctlhandle);
ctlhandle = NULL;
}
}
return 0;
}

View file

@ -31,6 +31,32 @@ extern int linecount;
extern FILE *yyin;
extern int yydebug;
struct soundcard *soundcards = NULL;
struct soundcard *rsoundcards = NULL;
/*
* misc functions
*/
static char *mixer_element_name(snd_mixer_eid_t *eid)
{
static char str[32];
if (!eid)
return "???";
strncpy(str, eid->name, sizeof(eid->name));
str[sizeof(eid->name)] = '\0';
return str;
}
char *mixer_element_id(snd_mixer_eid_t *eid)
{
static char str[64];
if (!eid)
return "???";
sprintf(str, "%s:%i:%i", mixer_element_name(eid), eid->index, eid->type);
return str;
}
/*
* free functions
@ -47,12 +73,14 @@ static void soundcard_ctl_switch_free(struct ctl_switch *first)
}
}
static void soundcard_mixer_channel_free(struct mixer_channel *first)
static void soundcard_mixer_element_free(struct mixer_element *first)
{
struct mixer_channel *next;
struct mixer_element *next;
while (first) {
next = first->next;
snd_mixer_element_info_free(&first->info);
snd_mixer_element_free(&first->element);
free(first);
first = next;
}
@ -62,7 +90,7 @@ static void soundcard_mixer_free1(struct mixer *mixer)
{
if (!mixer)
return;
soundcard_mixer_channel_free(mixer->channels);
soundcard_mixer_element_free(mixer->elements);
soundcard_ctl_switch_free(mixer->switches);
free(mixer);
}
@ -173,6 +201,7 @@ void soundcard_setup_init(void)
void soundcard_setup_done(void)
{
soundcard_free(soundcards);
soundcard_free(rsoundcards);
soundcards = NULL;
}
@ -294,13 +323,12 @@ static int determine_switches(void *handle, struct ctl_switch **csw, int interfa
return 0;
}
int soundcard_setup_collect(int cardno)
static int soundcard_setup_collect_switches1(int cardno)
{
void *handle, *mhandle;
struct soundcard *card, *first, *prev;
int err, idx, count, device;
int err, device;
struct mixer *mixer, *mixerprev;
struct mixer_channel *mixerchannel, *mixerchannelprev;
struct pcm *pcm, *pcmprev;
struct rawmidi *rawmidi, *rawmidiprev;
@ -369,73 +397,15 @@ int soundcard_setup_collect(int cardno)
mixerprev = mixer;
if ((err = snd_mixer_open(&mhandle, cardno, device)) < 0) {
snd_ctl_close(handle);
error("MIXER open error: %s\n", snd_strerror(err));
return 1;
}
if ((err = snd_mixer_exact_mode(mhandle, 1)) < 0) {
snd_mixer_close(mhandle);
snd_ctl_close(handle);
error("MIXER exact mode error: %s\n", snd_strerror(err));
error("MIXER open error: %s", snd_strerror(err));
return 1;
}
if ((err = snd_mixer_info(mhandle, &mixer->info)) < 0) {
snd_mixer_close(mhandle);
snd_ctl_close(handle);
error("MIXER info error: %s\n", snd_strerror(err));
error("MIXER info error: %s", snd_strerror(err));
return 1;
}
count = snd_mixer_channels(mhandle);
for (idx = 0, mixerchannelprev = NULL; idx < count; idx++) {
mixerchannel = (struct mixer_channel *) malloc(sizeof(struct mixer_channel));
if (!mixerchannel) {
snd_mixer_close(mhandle);
snd_ctl_close(handle);
error("malloc problem");
return 1;
}
bzero(mixerchannel, sizeof(struct mixer_channel));
mixerchannel->no = idx;
if ((err = snd_mixer_channel_info(mhandle, idx, &mixerchannel->info)) < 0) {
free(mixerchannel);
error("MIXER channel info error (%s) - skipping", snd_strerror(err));
break;
}
if ((mixerchannel->info.caps & SND_MIXER_CINFO_CAP_OUTPUT) &&
(err = snd_mixer_channel_output_info(mhandle, idx, &mixerchannel->dinfo[OUTPUT])) < 0) {
free(mixerchannel);
error("MIXER channel output info error (%s) - skipping", snd_strerror(err));
break;
}
if ((mixerchannel->info.caps & SND_MIXER_CINFO_CAP_INPUT) &&
(err = snd_mixer_channel_input_info(mhandle, idx, &mixerchannel->dinfo[INPUT])) < 0) {
free(mixerchannel);
error("MIXER channel input info error (%s) - skipping", snd_strerror(err));
break;
}
if ((err = snd_mixer_channel_read(mhandle, idx, &mixerchannel->data)) < 0) {
free(mixerchannel);
error("MIXER channel read error (%s) - skipping", snd_strerror(err));
break;
}
if ((mixerchannel->info.caps & SND_MIXER_CINFO_CAP_OUTPUT) &&
(err = snd_mixer_channel_output_read(mhandle, idx, &mixerchannel->ddata[OUTPUT])) < 0) {
free(mixerchannel);
error("MIXER channel output read error (%s) - skipping", snd_strerror(err));
break;
}
if ((mixerchannel->info.caps & SND_MIXER_CINFO_CAP_INPUT) &&
(err = snd_mixer_channel_input_read(mhandle, idx, &mixerchannel->ddata[INPUT])) < 0) {
free(mixerchannel);
error("MIXER channel input read error (%s) - skipping", snd_strerror(err));
break;
}
if (!mixerchannelprev) {
mixer->channels = mixerchannel;
} else {
mixerchannelprev->next = mixerchannel;
}
mixerchannelprev = mixerchannel;
}
snd_mixer_close(mhandle);
}
/* --- */
@ -450,7 +420,7 @@ int soundcard_setup_collect(int cardno)
pcm->no = device;
if ((err = snd_ctl_pcm_info(handle, device, &pcm->info)) < 0) {
snd_ctl_close(handle);
error("PCM info error: %s\n", snd_strerror(err));
error("PCM info error: %s", snd_strerror(err));
return 1;
}
if (determine_switches(handle, &pcm->pswitches, 2, device)) {
@ -480,7 +450,7 @@ int soundcard_setup_collect(int cardno)
rawmidi->no = device;
if ((err = snd_ctl_rawmidi_info(handle, device, &rawmidi->info)) < 0) {
snd_ctl_close(handle);
error("RAWMIDI info error: %s\n", snd_strerror(err));
error("RAWMIDI info error: %s", snd_strerror(err));
return 1;
}
if (determine_switches(handle, &rawmidi->oswitches, 4, device)) {
@ -503,6 +473,133 @@ int soundcard_setup_collect(int cardno)
return 0;
}
int soundcard_setup_collect_switches(int cardno)
{
int err;
unsigned int mask;
if (cardno >= 0) {
return soundcard_setup_collect_switches1(cardno);
} else {
mask = snd_cards_mask();
for (cardno = 0; cardno < SND_CARDS; cardno++) {
if (!(mask & (1 << cardno)))
continue;
err = soundcard_setup_collect_switches1(cardno);
if (err)
return err;
}
return 0;
}
}
static int soundcard_setup_collect_data1(int cardno)
{
void *handle, *mhandle;
struct soundcard *card;
int err, idx;
struct mixer *mixer;
snd_mixer_elements_t elements;
struct mixer_element *mixerelement, *mixerelementprev;
if ((err = snd_ctl_open(&handle, cardno)) < 0) {
error("SND CTL open error: %s", snd_strerror(err));
return 1;
}
/* --- */
for (card = soundcards; card && card->no != cardno; card = card->next);
if (!card) {
snd_ctl_close(handle);
error("The soundcard %i does not exist.", cardno);
return 1;
}
for (mixer = card->mixers; mixer; mixer = mixer->next) {
if ((err = snd_mixer_open(&mhandle, cardno, mixer->no)) < 0) {
snd_ctl_close(handle);
error("MIXER open error: %s", snd_strerror(err));
return 1;
}
if (mixer->elements)
soundcard_mixer_element_free(mixer->elements);
mixer->elements = NULL;
bzero(&elements, sizeof(elements));
if ((err = snd_mixer_elements(mhandle, &elements)) < 0) {
snd_mixer_close(mhandle);
snd_ctl_close(handle);
error("MIXER elements error: %s", snd_strerror(err));
return 1;
}
elements.elements_size = elements.elements_over + 16;
elements.elements = elements.elements_over = 0;
elements.pelements = (snd_mixer_eid_t *)malloc(elements.elements_size * sizeof(snd_mixer_eid_t));
if ((err = snd_mixer_elements(mhandle, &elements)) < 0) {
snd_mixer_close(mhandle);
snd_ctl_close(handle);
error("MIXER elements (2) error: %s", snd_strerror(err));
return 1;
}
for (idx = 0, mixerelementprev = NULL; idx < elements.elements; idx++) {
mixerelement = (struct mixer_element *) malloc(sizeof(struct mixer_element));
if (!mixerelement) {
snd_mixer_close(mhandle);
snd_ctl_close(handle);
error("malloc problem");
return 1;
}
bzero(mixerelement, sizeof(*mixerelement));
mixerelement->info.eid = elements.pelements[idx];
mixerelement->element.eid = elements.pelements[idx];
if (snd_mixer_element_has_info(&elements.pelements[idx]) == 1) {
if ((err = snd_mixer_element_info_build(mhandle, &mixerelement->info)) < 0) {
free(mixerelement);
error("MIXER element %s info error (%s) - skipping", mixer_element_id(&mixerelement->info.eid), snd_strerror(err));
break;
}
}
if (snd_mixer_element_has_control(&elements.pelements[idx]) == 1) {
if ((err = snd_mixer_element_build(mhandle, &mixerelement->element)) < 0) {
free(mixerelement);
error("MIXER element %s build error (%s) - skipping", mixer_element_id(&mixerelement->element.eid), snd_strerror(err));
break;
}
}
if (!mixerelementprev) {
mixer->elements = mixerelement;
} else {
mixerelementprev->next = mixerelement;
}
mixerelementprev = mixerelement;
}
free(elements.pelements);
snd_mixer_close(mhandle);
}
/* --- */
snd_ctl_close(handle);
return 0;
}
int soundcard_setup_collect_data(int cardno)
{
int err;
unsigned int mask;
if (cardno >= 0) {
return soundcard_setup_collect_data1(cardno);
} else {
mask = snd_cards_mask();
for (cardno = 0; cardno < SND_CARDS; cardno++) {
if (!(mask & (1 << cardno)))
continue;
err = soundcard_setup_collect_data1(cardno);
if (err)
return err;
}
return 0;
}
}
int soundcard_setup_load(const char *cfgfile, int skip)
{
extern int yyparse(void);
@ -570,9 +667,9 @@ static void soundcard_setup_write_switch(FILE * out, const char *space, int inte
default:
s = "unknown";
}
fprintf(out, "%s; Type is '%s'.\n", space, s);
fprintf(out, "%s; The type is '%s'.\n", space, s);
if (sw->low != 0 || sw->high != 0)
fprintf(out, "%s; Accepted switch range is from %u to %u.\n", space, sw->low, sw->high);
fprintf(out, "%s; The accepted switch range is from %u to %u.\n", space, sw->low, sw->high);
if (interface == SND_INTERFACE_CONTROL && sw->type == SND_SW_TYPE_WORD &&
!strcmp(sw->name, SND_CTL_SW_JOYSTICK_ADDRESS)) {
for (idx = 1, first = 1; idx < 16; idx++) {
@ -589,36 +686,36 @@ static void soundcard_setup_write_switch(FILE * out, const char *space, int inte
fprintf(out, "\n");
}
if (interface == SND_INTERFACE_MIXER && sw->type == SND_SW_TYPE_BOOLEAN &&
!strcmp(sw->name, SND_MIXER_SW_IEC958OUT)) {
fprintf(out, "%sswitch( \"%s\", ", space, sw->name);
!strcmp(sw->name, SND_MIXER_SW_IEC958_OUTPUT)) {
fprintf(out, "%sswitch(\"%s\",", space, sw->name);
if (sw->value.data32[1] == (('C' << 8) | 'S')) { /* Cirrus Crystal */
switchok = 0;
fprintf(out, "iec958ocs( %s", sw->value.enable ? "enable" : "disable");
fprintf(out, "iec958ocs(%s", sw->value.enable ? "enable" : "disable");
if (sw->value.data16[4] & 0x2000)
fprintf(out, " 3d");
fprintf(out, ",3d");
if (sw->value.data16[4] & 0x0040)
fprintf(out, " reset");
fprintf(out, ",reset");
if (sw->value.data16[4] & 0x0020)
fprintf(out, " user");
fprintf(out, ",user");
if (sw->value.data16[4] & 0x0010)
fprintf(out, " valid");
fprintf(out, ",valid");
if (sw->value.data16[5] & 0x0002)
fprintf(out, " data");
fprintf(out, ",data");
if (!(sw->value.data16[5] & 0x0004))
fprintf(out, " protect");
fprintf(out, ",protect");
switch (sw->value.data16[5] & 0x0018) {
case 0x0008:
fprintf(out, " pre2");
fprintf(out, ",pre2");
break;
default:
break;
}
if (sw->value.data16[5] & 0x0020)
fprintf(out, " fsunlock");
fprintf(out, " type( 0x%x )", (sw->value.data16[5] >> 6) & 0x7f);
fprintf(out, ",fsunlock");
fprintf(out, ",type(0x%x)", (sw->value.data16[5] >> 6) & 0x7f);
if (sw->value.data16[5] & 0x2000)
fprintf(out, " gstatus");
fprintf(out, " )");
fprintf(out, ",gstatus");
fprintf(out, ")");
goto __end;
}
}
@ -627,15 +724,15 @@ static void soundcard_setup_write_switch(FILE * out, const char *space, int inte
fprintf(out, v);
if (sw->type < 0 || sw->type > SND_SW_TYPE_LIST_ITEM) {
/* TODO: some well known types should be verbose */
fprintf(out, " rawdata( ");
fprintf(out, "rawdata(");
for (idx = 0; idx < 31; idx++) {
fprintf(out, "@%02x:", sw->value.data8[idx]);
}
fprintf(out, "%02x@ )\n", sw->value.data8[31]);
fprintf(out, "%02x@)\n", sw->value.data8[31]);
}
}
__end:
fprintf(out, " )\n");
fprintf(out, ")\n");
}
static void soundcard_setup_write_switches(FILE *out, const char *space, int interface, struct ctl_switch **switches)
@ -648,112 +745,183 @@ static void soundcard_setup_write_switches(FILE *out, const char *space, int int
soundcard_setup_write_switch(out, space, interface, &sw->s);
}
static void soundcard_setup_write_mixer_channel(FILE * out, struct mixer_channel * channel)
static void soundcard_setup_write_mixer_element(FILE * out, struct mixer_element * xelement)
{
int k, d;
struct capdes {
unsigned int flag;
char* description;
};
static struct capdes caps[] = {
{ SND_MIXER_CINFO_CAP_OUTPUT, "output" },
{ SND_MIXER_CINFO_CAP_INPUT, "input" },
{ SND_MIXER_CINFO_CAP_EXTINPUT, "external-input" },
{ SND_MIXER_CINFO_CAP_EFFECT, "effect" }
};
static struct capdes dcaps[] = {
{ SND_MIXER_CINFO_DCAP_STEREO, "stereo" },
{ SND_MIXER_CINFO_DCAP_HWMUTE, "hardware-mute" },
{ SND_MIXER_CINFO_DCAP_JOINMUTE, "join-mute" },
{ SND_MIXER_CINFO_DCAP_ROUTE, "route" },
{ SND_MIXER_CINFO_DCAP_SWAPROUTE, "swap-route" },
{ SND_MIXER_CINFO_DCAP_DIGITAL, "digital" },
{ SND_MIXER_CINFO_DCAP_RECORDBYMUTE, "recordbymute" },
};
fprintf(out, " ; Capabilities:");
for (k = 0; k < sizeof(caps)/sizeof(*caps); ++k) {
if (channel->info.caps & caps[k].flag)
fprintf(out, " %s", caps[k].description);
}
fprintf(out, "\n");
for (d = OUTPUT; d <= INPUT; ++d) {
snd_mixer_channel_direction_info_t *di;
if (d == OUTPUT &&
!(channel->info.caps & SND_MIXER_CINFO_CAP_OUTPUT))
continue;
if (d == INPUT &&
!(channel->info.caps & SND_MIXER_CINFO_CAP_INPUT))
continue;
di = &channel->dinfo[d];
fprintf(out, " ; %s capabilities:",
d == OUTPUT ? "Output" : "Input" );
if (di->caps & SND_MIXER_CINFO_DCAP_VOLUME)
fprintf(out, " volume(%i, %i)", di->min, di->max);
for (k = 0; k < sizeof(caps)/sizeof(*caps); ++k) {
if (di->caps & dcaps[k].flag)
fprintf(out, " %s", dcaps[k].description);
snd_mixer_element_info_t *info;
snd_mixer_element_t *element;
int idx;
info = &xelement->info;
element = &xelement->element;
if (snd_mixer_element_has_control(&element->eid) != 1)
return;
switch (element->eid.type) {
case SND_MIXER_ETYPE_SWITCH1:
fprintf(out, " element(\"%s\",%i,%i,Switch1(", mixer_element_name(&element->eid), element->eid.index, element->eid.type);
for (idx = 0; idx < element->data.switch1.sw; idx++)
fprintf(out, "%s%s", idx > 0 ? "," : "", snd_mixer_get_bit(element->data.switch1.psw, idx) ? "on" : "off");
fprintf(out, "))\n");
break;
case SND_MIXER_ETYPE_SWITCH2:
fprintf(out, " element(\"%s\",%i,%i,Switch2(%s))\n", mixer_element_name(&element->eid), element->eid.index, element->eid.type, element->data.switch2.sw ? "on" : "off");
break;
case SND_MIXER_ETYPE_SWITCH3:
fprintf(out, " element(\"%s\",%i,%i,Switch3(", mixer_element_name(&element->eid), element->eid.index, element->eid.type);
for (idx = 0; idx < element->data.switch3.rsw; idx++)
fprintf(out, "%s%s", idx > 0 ? "," : "", snd_mixer_get_bit(element->data.switch3.prsw, idx) ? "on" : "off");
fprintf(out, "))\n");
break;
case SND_MIXER_ETYPE_VOLUME1:
for (idx = 0; idx < info->data.volume1.range; idx++)
fprintf(out, " ; Voice %i : Min %i Max %i\n", idx, info->data.volume1.prange[idx].min, info->data.volume1.prange[idx].max);
fprintf(out, " element(\"%s\",%i,%i,Volume1(", mixer_element_name(&element->eid), element->eid.index, element->eid.type);
for (idx = 0; idx < element->data.volume1.voices; idx++)
fprintf(out, "%s%i", idx > 0 ? "," : "", element->data.volume1.pvoices[idx]);
fprintf(out, "))\n");
break;
case SND_MIXER_ETYPE_ACCU3:
for (idx = 0; idx < info->data.accu3.range; idx++)
fprintf(out, " ; Voice %i : Min %i Max %i\n", idx, info->data.accu3.prange[idx].min, info->data.accu3.prange[idx].max);
fprintf(out, " element(\"%s\",%i,%i,Accu3(", mixer_element_name(&element->eid), element->eid.index, element->eid.type);
for (idx = 0; idx < element->data.accu3.voices; idx++)
fprintf(out, "%s%i", idx > 0 ? "," : "", element->data.accu3.pvoices[idx]);
fprintf(out, "))\n");
break;
case SND_MIXER_ETYPE_MUX1:
fprintf(out, " element(\"%s\",%i,%i,Mux1(", mixer_element_name(&element->eid), element->eid.index, element->eid.type);
for (idx = 0; idx < element->data.mux1.output; idx++) {
fprintf(out, "%selement(\"%s\",%i,%i)", idx > 0 ? "," : "", mixer_element_name(&element->data.mux1.poutput[idx]), element->data.mux1.poutput[idx].index, element->data.mux1.poutput[idx].type);
}
fprintf(out, "\n");
fprintf(out, "))\n");
break;
case SND_MIXER_ETYPE_MUX2:
fprintf(out, " element(\"%s\",%i,%i,Mux2(", mixer_element_name(&element->eid), element->eid.index, element->eid.type);
fprintf(out, "element(\"%s\",%i,%i)", mixer_element_name(&element->data.mux2.output), element->data.mux2.output.index, element->data.mux2.output.type);
fprintf(out, "))\n");
break;
case SND_MIXER_ETYPE_TONE_CONTROL1:
if (info->data.tc1.tc & SND_MIXER_TC1_SW)
fprintf(out, " ; The tone control has an on/off switch.\n");
if (info->data.tc1.tc & SND_MIXER_TC1_BASS)
fprintf(out, " ; Bass : Min %i Max %i\n", info->data.tc1.min_bass, info->data.tc1.max_bass);
if (info->data.tc1.tc & SND_MIXER_TC1_TREBLE)
fprintf(out, " ; Treble : Min %i Max %i\n", info->data.tc1.min_treble, info->data.tc1.max_treble);
fprintf(out, " element(\"%s\",%i,%i,ToneControl1(", mixer_element_name(&element->eid), element->eid.index, element->eid.type);
idx = 0;
if (element->data.tc1.tc & SND_MIXER_TC1_SW)
fprintf(out, "%ssw=%s", idx++ > 0 ? "," : "", element->data.tc1.sw ? "on" : "off");
if (element->data.tc1.tc & SND_MIXER_TC1_BASS)
fprintf(out, "%sbass=%i", idx++ > 0 ? "," : "", element->data.tc1.bass);
if (element->data.tc1.tc & SND_MIXER_TC1_TREBLE)
fprintf(out, "%streble=%i", idx++ > 0 ? "," : "", element->data.tc1.treble);
fprintf(out, "))\n");
break;
case SND_MIXER_ETYPE_3D_EFFECT1:
if (info->data.teffect1.effect & SND_MIXER_EFF1_SW)
fprintf(out, " ; The 3D effect has an on/off switch.\n");
if (info->data.teffect1.effect & SND_MIXER_EFF1_MONO_SW)
fprintf(out, " ; The 3D effect has an mono processing on/off switch.\n");
if (info->data.teffect1.effect & SND_MIXER_EFF1_WIDE)
fprintf(out, " ; Wide : Min %i Max %i\n", info->data.teffect1.min_wide, info->data.teffect1.max_wide);
if (info->data.teffect1.effect & SND_MIXER_EFF1_VOLUME)
fprintf(out, " ; Volume : Min %i Max %i\n", info->data.teffect1.min_volume, info->data.teffect1.max_volume);
if (info->data.teffect1.effect & SND_MIXER_EFF1_CENTER)
fprintf(out, " ; Center : Min %i Max %i\n", info->data.teffect1.min_center, info->data.teffect1.max_center);
if (info->data.teffect1.effect & SND_MIXER_EFF1_SPACE)
fprintf(out, " ; Space : Min %i Max %i\n", info->data.teffect1.min_space, info->data.teffect1.max_space);
if (info->data.teffect1.effect & SND_MIXER_EFF1_DEPTH)
fprintf(out, " ; Depth : Min %i Max %i\n", info->data.teffect1.min_depth, info->data.teffect1.max_depth);
if (info->data.teffect1.effect & SND_MIXER_EFF1_DELAY)
fprintf(out, " ; Delay : Min %i Max %i\n", info->data.teffect1.min_delay, info->data.teffect1.max_delay);
if (info->data.teffect1.effect & SND_MIXER_EFF1_FEEDBACK)
fprintf(out, " ; Feedback : Min %i Max %i\n", info->data.teffect1.min_feedback, info->data.teffect1.max_feedback);
fprintf(out, " element(\"%s\",%i,%i,_3D_Effect1(", mixer_element_name(&element->eid), element->eid.index, element->eid.type);
idx = 0;
if (element->data.teffect1.effect & SND_MIXER_EFF1_SW)
fprintf(out, "%ssw=%s", idx++ > 0 ? "," : "", element->data.teffect1.sw ? "on" : "off");
if (element->data.teffect1.effect & SND_MIXER_EFF1_MONO_SW)
fprintf(out, "%smono_sw=%s", idx++ > 0 ? "," : "", element->data.teffect1.mono_sw ? "on" : "off");
if (element->data.teffect1.effect & SND_MIXER_EFF1_WIDE)
fprintf(out, "%swide=%i", idx++ > 0 ? "," : "", element->data.teffect1.wide);
if (element->data.teffect1.effect & SND_MIXER_EFF1_VOLUME)
fprintf(out, "%svolume=%i", idx++ > 0 ? "," : "", element->data.teffect1.volume);
if (element->data.teffect1.effect & SND_MIXER_EFF1_CENTER)
fprintf(out, "%scenter=%i", idx++ > 0 ? "," : "", element->data.teffect1.center);
if (element->data.teffect1.effect & SND_MIXER_EFF1_SPACE)
fprintf(out, "%sspace=%i", idx++ > 0 ? "," : "", element->data.teffect1.space);
if (element->data.teffect1.effect & SND_MIXER_EFF1_DEPTH)
fprintf(out, "%sdepth=%i", idx++ > 0 ? "," : "", element->data.teffect1.depth);
if (element->data.teffect1.effect & SND_MIXER_EFF1_DELAY)
fprintf(out, "%sdelay=%i", idx++ > 0 ? "," : "", element->data.teffect1.delay);
if (element->data.teffect1.effect & SND_MIXER_EFF1_FEEDBACK)
fprintf(out, "%sfeedback=%i", idx++ > 0 ? "," : "", element->data.teffect1.feedback);
fprintf(out, "))\n");
break;
default:
fprintf(out, " ; Unknown element %s\n", mixer_element_id(&element->eid));
}
fprintf(out, " channel(\"%s\"", channel->info.name);
for (d = OUTPUT; d <= INPUT; ++d) {
snd_mixer_channel_direction_info_t *di;
snd_mixer_channel_direction_t *dd;
if (d == OUTPUT &&
!(channel->info.caps & SND_MIXER_CINFO_CAP_OUTPUT))
continue;
if (d == INPUT &&
!(channel->info.caps & SND_MIXER_CINFO_CAP_INPUT))
continue;
dd = &channel->ddata[d];
di = &channel->dinfo[d];
fprintf(out, ", %s ", d == OUTPUT ? "output" : "input" );
if (di->caps & SND_MIXER_CINFO_DCAP_STEREO) {
fprintf(out, "stereo(");
if (di->caps & SND_MIXER_CINFO_DCAP_VOLUME)
fprintf(out, " %i", dd->left);
fprintf(out, "%s%s,",
dd->flags & SND_MIXER_DFLG_MUTE_LEFT ? " mute" : "",
dd->flags & SND_MIXER_DFLG_LTOR ? " swap" : ""
);
if (di->caps & SND_MIXER_CINFO_DCAP_VOLUME)
fprintf(out, " %i", dd->right);
fprintf(out, "%s%s)",
dd->flags & SND_MIXER_DFLG_MUTE_RIGHT ? " mute" : "",
dd->flags & SND_MIXER_DFLG_RTOL ? " swap" : ""
);
}
else {
fprintf(out, "mono(");
if (di->caps & SND_MIXER_CINFO_DCAP_VOLUME)
fprintf(out, " %i", (dd->left + dd->right)/2);
fprintf(out, "%s)",
dd->flags & SND_MIXER_DFLG_MUTE ? " mute" : ""
);
}
}
fprintf(out, " )\n");
}
int soundcard_setup_write(const char *cfgfile)
#define MAX_LINE (32 * 1024)
int soundcard_setup_write(const char *cfgfile, int cardno)
{
FILE *out;
struct soundcard *first;
FILE *out, *out1, *out2, *in;
char *tmpfile1, *tmpfile2;
struct soundcard *first, *sel = NULL;
struct mixer *mixer;
struct mixer_channel *mixerchannel;
struct mixer_element *mixerelement;
struct pcm *pcm;
struct rawmidi *rawmidi;
char *line, cardname[sizeof(first->control.hwinfo.name)+16], *ptr1;
int mark, size, ok;
if ((out = fopen(cfgfile, "w+")) == NULL) {
error("Cannot open file '%s' for writing...\n", cfgfile);
tmpfile1 = (char *)malloc(strlen(cfgfile) + 16);
tmpfile2 = (char *)malloc(strlen(cfgfile) + 16);
if (!tmpfile1 || !tmpfile2) {
error("No enough memory...\n");
if (tmpfile1)
free(tmpfile1);
if (tmpfile2)
free(tmpfile2);
return 1;
}
strcpy(tmpfile1, cfgfile);
strcat(tmpfile1, ".new");
strcpy(tmpfile2, cfgfile);
strcat(tmpfile2, ".insert");
if (cardno >= 0) {
line = (char *)malloc(MAX_LINE);
if (!line) {
error("No enough memory...\n");
return 1;
}
if ((in = fopen(cfgfile, "r")) == NULL)
cardno = -1;
} else {
line = NULL;
in = NULL;
}
if ((out = out1 = fopen(tmpfile1, "w+")) == NULL) {
error("Cannot open file '%s' for writing...\n", tmpfile1);
return 1;
}
fprintf(out, "# ALSA driver configuration\n");
fprintf(out, "# Generated by alsactl\n");
fprintf(out, "# This configuration is generated with the alsactl program.\n");
fprintf(out, "\n");
if (cardno >= 0) {
if ((out = out2 = fopen(tmpfile2, "w+")) == NULL) {
error("Cannot open file '%s' for writing...\n", tmpfile2);
return 1;
}
} else {
out2 = NULL;
}
for (first = soundcards; first; first = first->next) {
if (cardno >= 0 && first->no != cardno)
continue;
sel = first;
fprintf(out, "soundcard(\"%s\") {\n", first->control.hwinfo.id);
if (first->control.switches) {
fprintf(out, " control {\n");
@ -763,8 +931,8 @@ int soundcard_setup_write(const char *cfgfile)
for (mixer = first->mixers; mixer; mixer = mixer->next) {
fprintf(out, " mixer(\"%s\") {\n", mixer->info.name);
soundcard_setup_write_switches(out, " ", SND_INTERFACE_MIXER, &mixer->switches);
for (mixerchannel = mixer->channels; mixerchannel; mixerchannel = mixerchannel->next)
soundcard_setup_write_mixer_channel(out, mixerchannel);
for (mixerelement = mixer->elements; mixerelement; mixerelement = mixerelement->next)
soundcard_setup_write_mixer_element(out, mixerelement);
fprintf(out, " }\n");
}
for (pcm = first->pcms; pcm; pcm = pcm->next) {
@ -799,121 +967,80 @@ int soundcard_setup_write(const char *cfgfile)
}
fprintf(out, " }\n");
}
fprintf(out, "}\n%s", first->next ? "\n" : "");
fprintf(out, "}\n%s", cardno < 0 && first->next ? "\n" : "");
}
fclose(out);
return 0;
}
static int soundcard_open_ctl(void **ctlhandle, struct soundcard *soundcard)
{
int err;
if (*ctlhandle)
return 0;
if ((err = snd_ctl_open(ctlhandle, soundcard->no)) < 0) {
error("Cannot open control interface for soundcard #%i.", soundcard->no + 1);
return 1;
}
return 0;
}
static int soundcard_open_mix(void **mixhandle, struct soundcard *soundcard, struct mixer *mixer)
{
int err;
if (*mixhandle)
return 0;
if ((err = snd_mixer_open(mixhandle, soundcard->no, mixer->no)) < 0) {
error("Cannot open mixer interface for soundcard #%i.", soundcard->no + 1);
return 1;
}
if ((err = snd_mixer_exact_mode(*mixhandle, 1)) < 0) {
error("Cannot setup exact mode for mixer #%i/#%i: %s", soundcard->no + 1, mixer->no, snd_strerror(err));
return 1;
}
return 0;
}
int soundcard_setup_process(int cardno)
{
int err;
void *ctlhandle = NULL;
void *mixhandle = NULL;
struct soundcard *soundcard;
struct ctl_switch *ctlsw;
struct mixer *mixer;
struct mixer_channel *channel;
struct pcm *pcm;
struct rawmidi *rawmidi;
for (soundcard = soundcards; soundcard; soundcard = soundcard->next) {
if (cardno >= 0 && soundcard->no != cardno)
continue;
for (ctlsw = soundcard->control.switches; ctlsw; ctlsw = ctlsw->next) {
if (ctlsw->change)
if (!soundcard_open_ctl(&ctlhandle, soundcard)) {
if ((err = snd_ctl_switch_write(ctlhandle, &ctlsw->s)) < 0)
error("Control switch '%s' write error: %s", ctlsw->s.name, snd_strerror(err));
/* merge the old and new text */
if (cardno >= 0) {
fseek(out2, 0, SEEK_SET);
mark = ok = 0;
__1:
while (fgets(line, MAX_LINE - 1, in)) {
line[MAX_LINE - 1] = '\0';
if (!strncmp(line, "soundcard(", 10))
break;
}
while (!feof(in)) {
ptr1 = line + 10;
while (*ptr1 && *ptr1 != '"')
ptr1++;
if (*ptr1)
ptr1++;
strncpy(cardname, sel->control.hwinfo.id, sizeof(sel->control.hwinfo.id));
cardname[sizeof(sel->control.hwinfo.id)] = '\0';
strcat(cardname, "\"");
if (!strncmp(ptr1, cardname, strlen(cardname))) {
if (mark)
fprintf(out1, "\n");
do {
size = fread(line, 1, MAX_LINE, out2);
if (size > 0)
fwrite(line, 1, size, out1);
} while (size > 0);
mark = ok = 1;
goto __1;
} else {
if (mark)
fprintf(out1, "\n");
fprintf(out1, line);
while (fgets(line, MAX_LINE - 1, in)) {
line[MAX_LINE - 1] = '\0';
fprintf(out1, line);
if (line[0] == '}') {
mark = 1;
goto __1;
}
}
}
for (mixer = soundcard->mixers; mixer; mixer = mixer->next) {
for (channel = mixer->channels; channel; channel = channel->next)
if (!soundcard_open_mix(&mixhandle, soundcard, mixer)) {
if ((channel->info.caps & SND_MIXER_CINFO_CAP_OUTPUT) &&
(err = snd_mixer_channel_output_write(mixhandle, channel->no, &channel->ddata[OUTPUT])) < 0)
error("Mixer channel '%s' write error: %s", channel->info.name, snd_strerror(err));
if ((channel->info.caps & SND_MIXER_CINFO_CAP_INPUT) &&
(err = snd_mixer_channel_input_write(mixhandle, channel->no, &channel->ddata[INPUT])) < 0)
error("Mixer channel '%s' record write error: %s", channel->info.name, snd_strerror(err));
}
if (mixhandle) {
snd_mixer_close(mixhandle);
mixhandle = NULL;
}
for (ctlsw = mixer->switches; ctlsw; ctlsw = ctlsw->next)
if (ctlsw->change)
if (!soundcard_open_ctl(&ctlhandle, soundcard)) {
if ((err = snd_ctl_mixer_switch_write(ctlhandle, mixer->no, &ctlsw->s)) < 0)
error("Mixer switch '%s' write error: %s", ctlsw->s.name, snd_strerror(err));
}
}
for (pcm = soundcard->pcms; pcm; pcm = pcm->next) {
for (ctlsw = pcm->pswitches; ctlsw; ctlsw = ctlsw->next) {
if (ctlsw->change)
if (!soundcard_open_ctl(&ctlhandle, soundcard)) {
if ((err = snd_ctl_pcm_playback_switch_write(ctlhandle, pcm->no, &ctlsw->s)) < 0)
error("PCM playback switch '%s' write error: %s", ctlsw->s.name, snd_strerror(err));
}
}
for (ctlsw = pcm->rswitches; ctlsw; ctlsw = ctlsw->next) {
if (ctlsw->change)
if (!soundcard_open_ctl(&ctlhandle, soundcard)) {
if ((err = snd_ctl_pcm_record_switch_write(ctlhandle, pcm->no, &ctlsw->s)) < 0)
error("PCM record switch '%s' write error: %s", ctlsw->s.name, snd_strerror(err));
}
}
}
for (rawmidi = soundcard->rawmidis; rawmidi; rawmidi = rawmidi->next) {
for (ctlsw = rawmidi->oswitches; ctlsw; ctlsw = ctlsw->next) {
if (ctlsw->change)
if (!soundcard_open_ctl(&ctlhandle, soundcard)) {
if ((err = snd_ctl_rawmidi_output_switch_write(ctlhandle, rawmidi->no, &ctlsw->s)) < 0)
error("RAWMIDI output switch '%s' write error: %s", ctlsw->s.name, snd_strerror(err));
}
}
for (ctlsw = rawmidi->iswitches; ctlsw; ctlsw = ctlsw->next) {
if (ctlsw->change)
if (!soundcard_open_ctl(&ctlhandle, soundcard)) {
if ((err = snd_ctl_rawmidi_output_switch_write(ctlhandle, rawmidi->no, &ctlsw->s)) < 0)
error("RAWMIDI input switch '%s' write error: %s", ctlsw->s.name, snd_strerror(err));
}
}
}
if(ctlhandle) {
snd_ctl_close(ctlhandle);
ctlhandle = NULL;
if (!ok) {
if (mark)
fprintf(out1, "\n");
do {
size = fread(line, 1, MAX_LINE, out2);
printf("size = %i\n", size);
if (size > 0)
fwrite(line, 1, size, out1);
} while (size > 0);
}
}
if (in)
fclose(in);
if (out2)
fclose(out2);
if (!access(cfgfile, F_OK) && remove(cfgfile))
error("Cannot remove file '%s'...", cfgfile);
if (rename(tmpfile1, cfgfile) < 0)
error("Cannot rename file '%s' to '%s'...", tmpfile1, cfgfile);
fclose(out1);
if (line)
free(line);
if (tmpfile2) {
remove(tmpfile2);
free(tmpfile2);
}
if (tmpfile1) {
remove(tmpfile1);
free(tmpfile1);
}
return 0;
}

View file

@ -1,964 +1,6 @@
/* AlsaMixer - Commandline mixer for the ALSA project
* Copyright (C) 1998 Jaroslav Kysela <perex@jcu.cz>,
* Tim Janik <timj@gtk.org>,
* Carl van Schaik <carl@dreamcoat.che.uct.ac.za>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/signal.h>
#ifndef CURSESINC
#include <ncurses.h>
#else
#include CURSESINC
#endif
#include <time.h>
#include <sys/asoundlib.h>
/* example compilation commandline:
* clear; gcc -Wall -pipe -O2 alsamixer.c -o alsamixer -lncurses
*/
/* --- defines --- */
#define PRGNAME "alsamixer"
#define PRGNAME_UPPER "AlsaMixer"
#define VERSION "v0.9"
#undef MAX
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#undef MIN
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#undef ABS
#define ABS(a) (((a) < 0) ? -(a) : (a))
#undef CLAMP
#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
#define MIXER_MIN_X (23) /* minimum: 23 */
#define MIXER_MIN_Y (19) /* minimum: 19 */
#define MIXER_BLACK (COLOR_BLACK)
#define MIXER_DARK_RED (COLOR_RED)
#define MIXER_RED (COLOR_RED | A_BOLD)
#define MIXER_GREEN (COLOR_GREEN | A_BOLD)
#define MIXER_ORANGE (COLOR_YELLOW)
#define MIXER_YELLOW (COLOR_YELLOW | A_BOLD)
#define MIXER_MARIN (COLOR_BLUE)
#define MIXER_BLUE (COLOR_BLUE | A_BOLD)
#define MIXER_MAGENTA (COLOR_MAGENTA)
#define MIXER_DARK_CYAN (COLOR_CYAN)
#define MIXER_CYAN (COLOR_CYAN | A_BOLD)
#define MIXER_GREY (COLOR_WHITE)
#define MIXER_GRAY (MIXER_GREY)
#define MIXER_WHITE (COLOR_WHITE | A_BOLD)
/* --- variables --- */
static WINDOW *mixer_window = NULL;
static int mixer_max_x = 0;
static int mixer_max_y = 0;
static int mixer_ofs_x = 0;
static float mixer_extra_space = 0;
static int mixer_ofs_y = 0;
static int mixer_cbar_height = 0;
static int card_id = 0;
static int mixer_id = 0;
static void *mixer_handle;
static char *mixer_card_name = NULL;
static char *mixer_device_name = NULL;
static int mixer_n_channels = 0;
static int mixer_n_vis_channels = 0;
static int mixer_first_vis_channel = 0;
static int mixer_focus_channel = 0;
static int mixer_exact = 0;
static int mixer_input_volumes = 0;
static int mixer_lvolume_delta = 0;
static int mixer_rvolume_delta = 0;
static int mixer_balance_volumes = 0;
static int mixer_toggle_mute_left = 0;
static int mixer_toggle_mute_right = 0;
/* By Carl */
static int mixer_toggle_record_left = 0;
static int mixer_toggle_record_right = 0;
static int mixer_route_ltor_in = 0;
static int mixer_route_rtol_in = 0;
#if 0
static int mixer_route_ltor_out = 0;
static int mixer_route_rtol_out = 0;
#endif
/* --- draw contexts --- */
enum {
DC_DEFAULT,
DC_BACK,
DC_TEXT,
DC_PROMPT,
DC_CBAR_MUTE,
DC_CBAR_NOMUTE,
DC_CBAR_RECORD,
DC_CBAR_NORECORD,
DC_CBAR_EMPTY,
DC_CBAR_FULL_1,
DC_CBAR_FULL_2,
DC_CBAR_FULL_3,
DC_CBAR_LABEL,
DC_CBAR_FOCUS_LABEL,
DC_FOCUS,
DC_LAST
};
static int dc_fg[DC_LAST] =
{0};
static int dc_attrib[DC_LAST] =
{0};
static int dc_char[DC_LAST] =
{0};
static int mixer_do_color = 1;
static void mixer_init_dc(int c,
int n,
int f,
int b,
int a)
void main(void)
{
dc_fg[n] = f;
dc_attrib[n] = a;
dc_char[n] = c;
if (n > 0)
init_pair(n, dc_fg[n] & 0xf, b & 0x0f);
printf("TODO\n");
}
static int mixer_dc(int n)
{
if (mixer_do_color)
attrset(COLOR_PAIR(n) | (dc_fg[n] & 0xfffffff0));
else
attrset(dc_attrib[n]);
return dc_char[n];
}
static void mixer_init_draw_contexts(void)
{
start_color();
mixer_init_dc('.', DC_BACK, MIXER_WHITE, MIXER_BLACK, A_NORMAL);
mixer_init_dc('.', DC_TEXT, MIXER_YELLOW, MIXER_BLACK, A_BOLD);
mixer_init_dc('.', DC_PROMPT, MIXER_DARK_CYAN, MIXER_BLACK, A_NORMAL);
mixer_init_dc('M', DC_CBAR_MUTE, MIXER_CYAN, MIXER_BLACK, A_BOLD);
mixer_init_dc('-', DC_CBAR_NOMUTE, MIXER_CYAN, MIXER_BLACK, A_NORMAL);
mixer_init_dc('x', DC_CBAR_RECORD, MIXER_DARK_RED, MIXER_BLACK, A_BOLD);
mixer_init_dc('-', DC_CBAR_NORECORD, MIXER_GRAY, MIXER_BLACK, A_NORMAL);
mixer_init_dc(' ', DC_CBAR_EMPTY, MIXER_GRAY, MIXER_BLACK, A_DIM);
mixer_init_dc('#', DC_CBAR_FULL_1, MIXER_WHITE, MIXER_BLACK, A_BOLD);
mixer_init_dc('#', DC_CBAR_FULL_2, MIXER_GREEN, MIXER_BLACK, A_BOLD);
mixer_init_dc('#', DC_CBAR_FULL_3, MIXER_RED, MIXER_BLACK, A_BOLD);
mixer_init_dc('.', DC_CBAR_LABEL, MIXER_WHITE, MIXER_BLUE, A_REVERSE | A_BOLD);
mixer_init_dc('.', DC_CBAR_FOCUS_LABEL, MIXER_RED, MIXER_BLUE, A_REVERSE | A_BOLD);
mixer_init_dc('.', DC_FOCUS, MIXER_RED, MIXER_BLACK, A_BOLD);
}
#define DC_CBAR_FRAME (DC_CBAR_MUTE)
#define DC_FRAME (DC_PROMPT)
/* --- error types --- */
typedef enum {
ERR_NONE,
ERR_OPEN,
ERR_FCN,
ERR_SIGNAL,
ERR_WINSIZE,
} ErrType;
/* --- prototypes --- */
static void mixer_abort(ErrType error,
const char *err_string)
__attribute__
((noreturn));
/* --- functions --- */
static void mixer_clear(void)
{
int x, y;
mixer_dc(DC_BACK);
clear();
/* buggy ncurses doesn't really write spaces with the specified
* color into the screen on clear ();
*/
for (x = 0; x < mixer_max_x; x++)
for (y = 0; y < mixer_max_y; y++)
mvaddch(y, x, ' ');
refresh();
}
static void mixer_abort(ErrType error,
const char *err_string)
{
if (mixer_window) {
mixer_clear();
endwin();
mixer_window = NULL;
}
printf("\n");
switch (error) {
case ERR_OPEN:
fprintf(stderr,
PRGNAME ": failed to open mixer #%i/#%i: %s\n",
card_id,
mixer_id,
snd_strerror(errno));
break;
case ERR_FCN:
fprintf(stderr,
PRGNAME ": function %s failed: %s\n",
err_string,
snd_strerror(errno));
break;
case ERR_SIGNAL:
fprintf(stderr,
PRGNAME ": aborting due to signal `%s'\n",
err_string);
break;
case ERR_WINSIZE:
fprintf(stderr,
PRGNAME ": screen size too small (%dx%d)\n",
mixer_max_x,
mixer_max_y);
break;
default:
break;
}
exit(error);
}
static int mixer_cbar_get_pos(int channel_index,
int *x_p,
int *y_p)
{
int x;
int y;
if (channel_index < mixer_first_vis_channel ||
channel_index - mixer_first_vis_channel >= mixer_n_vis_channels)
return FALSE;
channel_index -= mixer_first_vis_channel;
x = mixer_ofs_x + 1;
y = mixer_ofs_y;
x += channel_index * (3 + 2 + 3 + 1 + mixer_extra_space);
y += mixer_max_y / 2;
y += mixer_cbar_height / 2 + 1;
if (x_p)
*x_p = x;
if (y_p)
*y_p = y;
return TRUE;
}
static void mixer_update_cbar(int channel_index)
{
char string[64];
char c;
snd_mixer_channel_info_t cinfo =
{0};
snd_mixer_channel_direction_info_t cpinfo =
{0};
snd_mixer_channel_direction_info_t crinfo =
{0};
snd_mixer_channel_direction_t cpdata =
{0};
snd_mixer_channel_direction_t crdata =
{0};
int vleft, vright;
int x, y, i;
int output = 0, input = 0, volume;
/* set specified EXACT mode
*/
if (snd_mixer_exact_mode(mixer_handle, mixer_exact) < 0)
mixer_abort(ERR_FCN, "snd_mixer_exact");
/* set new channel indices and read info
*/
if (snd_mixer_channel_info(mixer_handle, channel_index, &cinfo) < 0)
mixer_abort(ERR_FCN, "snd_mixer_channel_info");
if (cinfo.caps & SND_MIXER_CINFO_CAP_OUTPUT) {
if (snd_mixer_channel_output_info(mixer_handle, channel_index, &cpinfo) < 0)
mixer_abort(ERR_FCN, "snd_mixer_channel_output_info");
output = 1;
}
if (cinfo.caps & SND_MIXER_CINFO_CAP_INPUT) {
if (snd_mixer_channel_input_info(mixer_handle, channel_index, &crinfo) < 0)
mixer_abort(ERR_FCN, "snd_mixer_channel_input_info");
input = 1;
}
if (mixer_input_volumes)
volume=(input && (crinfo.caps & SND_MIXER_CINFO_DCAP_VOLUME));
else
volume=(output && (cpinfo.caps & SND_MIXER_CINFO_DCAP_VOLUME));
/* set new channel values
*/
if (channel_index == mixer_focus_channel &&
(mixer_lvolume_delta || mixer_rvolume_delta ||
mixer_toggle_mute_left || mixer_toggle_mute_right ||
mixer_balance_volumes ||
mixer_toggle_record_left || mixer_toggle_record_right ||
mixer_route_rtol_in || mixer_route_ltor_in)) {
if (output && snd_mixer_channel_output_read(mixer_handle, channel_index, &cpdata) < 0)
mixer_abort(ERR_FCN, "snd_mixer_channel_output_read");
if (input && snd_mixer_channel_input_read(mixer_handle, channel_index, &crdata) < 0)
mixer_abort(ERR_FCN, "snd_mixer_channel_input_read");
cpdata.flags &= ~SND_MIXER_DFLG_DECIBEL;
crdata.flags &= ~SND_MIXER_DFLG_DECIBEL;
if (volume) {
if (mixer_input_volumes) {
crdata.left = CLAMP(crdata.left + mixer_lvolume_delta, crinfo.min, crinfo.max);
crdata.right = CLAMP(crdata.right + mixer_rvolume_delta, crinfo.min, crinfo.max);
if (mixer_balance_volumes) {
crdata.left = (crdata.left + crdata.right) / 2;
crdata.right = crdata.left;
}
}
else {
cpdata.left = CLAMP(cpdata.left + mixer_lvolume_delta, cpinfo.min, cpinfo.max);
cpdata.right = CLAMP(cpdata.right + mixer_rvolume_delta, cpinfo.min, cpinfo.max);
if (mixer_balance_volumes) {
cpdata.left = (cpdata.left + cpdata.right) / 2;
cpdata.right = cpdata.left;
}
}
}
mixer_lvolume_delta = 0;
mixer_rvolume_delta = 0;
mixer_balance_volumes = 0;
if (output) {
if (mixer_toggle_mute_left) {
cpdata.flags ^= SND_MIXER_DFLG_MUTE_LEFT;
}
if (mixer_toggle_mute_right) {
cpdata.flags ^= SND_MIXER_DFLG_MUTE_RIGHT;
}
}
mixer_toggle_mute_left = mixer_toggle_mute_right = 0;
if (input) {
if (mixer_toggle_record_left) {
crdata.flags ^= SND_MIXER_DFLG_MUTE_LEFT;
}
if (mixer_toggle_record_right) {
crdata.flags ^= SND_MIXER_DFLG_MUTE_RIGHT;
}
if (mixer_route_ltor_in) {
crdata.flags ^= SND_MIXER_DFLG_LTOR;
}
if (mixer_route_rtol_in) {
crdata.flags ^= SND_MIXER_DFLG_RTOL;
}
}
mixer_toggle_record_left = mixer_toggle_record_right = 0;
mixer_route_ltor_in = mixer_route_rtol_in = 0;
if (output &&
snd_mixer_channel_output_write(mixer_handle, channel_index, &cpdata) < 0)
mixer_abort(ERR_FCN, "snd_mixer_channel_output_write");
if (input &&
snd_mixer_channel_input_write(mixer_handle, channel_index, &crdata) < 0)
mixer_abort(ERR_FCN, "snd_mixer_channel_input_write");
}
/* first, read values for the numbers to be displayed in
* specified EXACT mode
*/
if (output &&
snd_mixer_channel_output_read(mixer_handle, channel_index, &cpdata) < 0)
mixer_abort(ERR_FCN, "snd_mixer_ioctl_channel_output_read");
if (input &&
snd_mixer_channel_input_read(mixer_handle, channel_index, &crdata) < 0)
mixer_abort(ERR_FCN, "snd_mixer_channel_input_read");
if (mixer_input_volumes) {
if (input) {
vleft = crdata.left;
vright = crdata.right;
}
else {
vleft = vright = 0;
}
}
else {
if (output) {
vleft = cpdata.left;
vright = cpdata.right;
}
else {
vleft = vright = 0;
}
}
/* then, always use percentage values for the bars. if we don't do
* this, we will see aliasing effects on specific circumstances.
* (actually they don't really dissapear, but they are transfered
* to bar<->smaller-scale ambiguities).
*/
if (mixer_exact) {
i = 0;
if (snd_mixer_exact_mode(mixer_handle, 0) < 0)
mixer_abort(ERR_FCN, "snd_mixer_exact");
if (output &&
snd_mixer_channel_output_read(mixer_handle, channel_index, &cpdata) < 0)
mixer_abort(ERR_FCN, "snd_mixer_channel_output_read");
if (input &&
snd_mixer_channel_input_read(mixer_handle, channel_index, &crdata) < 0)
mixer_abort(ERR_FCN, "snd_mixer_channel_input_read");
}
/* get channel bar position
*/
if (!mixer_cbar_get_pos(channel_index, &x, &y))
return;
/* channel bar name
*/
mixer_dc(channel_index == mixer_focus_channel ? DC_CBAR_FOCUS_LABEL : DC_CBAR_LABEL);
cinfo.name[8] = 0;
for (i = 0; i < 8; i++) {
string[i] = ' ';
}
sprintf(string + (8 - strlen(cinfo.name)) / 2, "%s ", cinfo.name);
string[8] = 0;
mvaddstr(y, x, string);
y--;
/* current channel values
*/
mixer_dc(DC_BACK);
mvaddstr(y, x, " ");
mixer_dc(DC_TEXT);
sprintf(string, "%d", vleft);
mvaddstr(y, x + 3 - strlen(string), string);
mixer_dc(DC_CBAR_FRAME);
mvaddch(y, x + 3, '<');
mvaddch(y, x + 4, '>');
mixer_dc(DC_TEXT);
sprintf(string, "%d", vright);
mvaddstr(y, x + 5, string);
y--;
/* left/right bar
*/
mixer_dc(DC_CBAR_FRAME);
mvaddstr(y, x, " ");
mvaddch(y, x + 2, ACS_LLCORNER);
mvaddch(y, x + 3, ACS_HLINE);
mvaddch(y, x + 4, ACS_HLINE);
mvaddch(y, x + 5, ACS_LRCORNER);
y--;
for (i = 0; i < mixer_cbar_height; i++) {
mvaddstr(y - i, x, " ");
mvaddch(y - i, x + 2, ACS_VLINE);
mvaddch(y - i, x + 5, ACS_VLINE);
}
string[2] = 0;
for (i = 0; i < mixer_cbar_height; i++) {
int dc;
if (i + 1 >= 0.8 * mixer_cbar_height)
dc = DC_CBAR_FULL_3;
else if (i + 1 >= 0.4 * mixer_cbar_height)
dc = DC_CBAR_FULL_2;
else
dc = DC_CBAR_FULL_1;
mvaddch(y, x + 3, mixer_dc(vleft > i * 100 / mixer_cbar_height ? dc : DC_CBAR_EMPTY));
mvaddch(y, x + 4, mixer_dc(vright > i * 100 / mixer_cbar_height ? dc : DC_CBAR_EMPTY));
y--;
}
/* muted?
*/
mixer_dc(DC_BACK);
mvaddstr(y, x, " ");
if (output) {
c = (cpinfo.caps & SND_MIXER_CINFO_DCAP_MUTE) ? '-' : ' ';
mixer_dc(DC_CBAR_FRAME);
mvaddch(y, x + 2, ACS_ULCORNER);
mvaddch(y, x + 3, mixer_dc(cpdata.flags & SND_MIXER_DFLG_MUTE_LEFT ?
DC_CBAR_MUTE : DC_CBAR_NOMUTE));
mvaddch(y, x + 4, mixer_dc(cpdata.flags & SND_MIXER_DFLG_MUTE_RIGHT ?
DC_CBAR_MUTE : DC_CBAR_NOMUTE));
mixer_dc(DC_CBAR_FRAME);
mvaddch(y, x + 5, ACS_URCORNER);
}
y--;
/* record input?
*/
mixer_dc(DC_BACK);
mvaddstr(y, x, " ");
if (input) {
if ((crdata.flags & SND_MIXER_DFLG_MUTE) != SND_MIXER_DFLG_MUTE) {
mixer_dc(DC_CBAR_RECORD);
mvaddstr(y, x + 1, "RECORD");
if (!(crdata.flags & SND_MIXER_DFLG_MUTE_LEFT)) {
if (crdata.flags & SND_MIXER_DFLG_LTOR)
mvaddstr(y + 2, x + 6, "L");
else
mvaddstr(y + 1, x + 1, "L");
}
if (!(crdata.flags & SND_MIXER_DFLG_MUTE_RIGHT)) {
if (crdata.flags & SND_MIXER_DFLG_RTOL)
mvaddstr(y + 2, x + 1, "R");
else
mvaddstr(y + 1, x + 6, "R");
}
} else {
for (i = 0; i < 6; i++)
mvaddch(y, x + 1 + i, mixer_dc(DC_CBAR_NORECORD));
}
}
y--;
}
static void mixer_update_cbars(void)
{
static int o_x = 0;
static int o_y = 0;
int i, x, y;
if (!mixer_cbar_get_pos(mixer_focus_channel, &x, &y)) {
if (mixer_focus_channel < mixer_first_vis_channel)
mixer_first_vis_channel = mixer_focus_channel;
else if (mixer_focus_channel >= mixer_first_vis_channel + mixer_n_vis_channels)
mixer_first_vis_channel = mixer_focus_channel - mixer_n_vis_channels + 1;
mixer_cbar_get_pos(mixer_focus_channel, &x, &y);
}
for (i = 0; i < mixer_n_vis_channels; i++)
mixer_update_cbar(i + mixer_first_vis_channel);
/* draw focused cbar
*/
mixer_dc(DC_BACK);
mvaddstr(o_y, o_x, " ");
mvaddstr(o_y, o_x + 9, " ");
o_x = x - 1;
o_y = y;
mixer_dc(DC_FOCUS);
mvaddstr(o_y, o_x, "<");
mvaddstr(o_y, o_x + 9, ">");
}
static void mixer_draw_frame(void)
{
char string[128];
int i;
int max_len;
mixer_dc(DC_FRAME);
/* corners
*/
mvaddch(0, 0, ACS_ULCORNER);
mvaddch(mixer_max_y - 1, 0, ACS_LLCORNER);
mvaddch(mixer_max_y - 1, mixer_max_x - 1, ACS_LRCORNER);
mvaddch(0, mixer_max_x - 1, ACS_URCORNER);
/* lines
*/
for (i = 1; i < mixer_max_y - 1; i++) {
mvaddch(i, 0, ACS_VLINE);
mvaddch(i, mixer_max_x - 1, ACS_VLINE);
}
for (i = 1; i < mixer_max_x - 1; i++) {
mvaddch(0, i, ACS_HLINE);
mvaddch(mixer_max_y - 1, i, ACS_HLINE);
}
/* program title
*/
sprintf(string, "%s %s", PRGNAME_UPPER, VERSION);
max_len = strlen(string);
mvaddch(0, mixer_max_x / 2 - max_len / 2 - 1, '[');
mvaddch(0, mixer_max_x / 2 - max_len / 2 + max_len, ']');
mixer_dc(DC_TEXT);
mvaddstr(0, mixer_max_x / 2 - max_len / 2, string);
/* card name
*/
mixer_dc(DC_PROMPT);
mvaddstr(1, 2, "Card:");
mixer_dc(DC_TEXT);
sprintf(string, "%s", mixer_card_name);
max_len = mixer_max_x - 2 - 6 - 2;
if (strlen(string) > max_len)
string[max_len] = 0;
mvaddstr(1, 2 + 6, string);
/* device name
*/
mixer_dc(DC_PROMPT);
mvaddstr(2, 2, "Chip: ");
mixer_dc(DC_TEXT);
sprintf(string, "%s", mixer_device_name);
max_len = mixer_max_x - 2 - 6 - 2;
if (strlen(string) > max_len)
string[max_len] = 0;
mvaddstr(2, 2 + 6, string);
if (mixer_input_volumes)
mvaddstr(3, 2, "Record mixer");
else
mvaddstr(3, 2, " ");
}
static void mixer_init(void)
{
static snd_mixer_info_t mixer_info =
{0};
static struct snd_ctl_hw_info hw_info;
void *ctl_handle;
if (snd_ctl_open(&ctl_handle, card_id) < 0)
mixer_abort(ERR_OPEN, "snd_ctl_open");
if (snd_ctl_hw_info(ctl_handle, &hw_info) < 0)
mixer_abort(ERR_FCN, "snd_ctl_hw_info");
snd_ctl_close(ctl_handle);
/* open mixer device
*/
if (snd_mixer_open(&mixer_handle, card_id, mixer_id) < 0)
mixer_abort(ERR_OPEN, "snd_mixer_open");
/* setup global variables
*/
if (snd_mixer_info(mixer_handle, &mixer_info) < 0)
mixer_abort(ERR_FCN, "snd_mixer_info");
mixer_n_channels = mixer_info.channels;
mixer_card_name = hw_info.name;
mixer_device_name = mixer_info.name;
}
static void mixer_iteration_update(void *dummy, int channel)
{
#if 0
fprintf(stderr, "*** channel = %i\n", channel);
#endif
mixer_update_cbar(channel);
refresh();
}
static int mixer_iteration(void)
{
snd_mixer_callbacks_t callbacks;
int key;
int finished = 0;
int mixer_fd;
fd_set in;
bzero(&callbacks, sizeof(callbacks));
callbacks.channel_was_changed = mixer_iteration_update;
callbacks.output_channel_was_changed = mixer_iteration_update;
callbacks.input_channel_was_changed = mixer_iteration_update;
mixer_fd = snd_mixer_file_descriptor(mixer_handle);
while (1) {
FD_ZERO(&in);
FD_SET(fileno(stdin), &in);
FD_SET(mixer_fd, &in);
if (select(mixer_fd + 1, &in, NULL, NULL, NULL) <= 0)
return 1;
if (FD_ISSET(mixer_fd, &in))
snd_mixer_read(mixer_handle, &callbacks);
if (FD_ISSET(fileno(stdin), &in))
break;
}
key = getch();
switch (key) {
case 27: /* Escape */
finished = 1;
break;
case 9: /* Tab */
mixer_exact = !mixer_exact;
break;
case KEY_RIGHT:
case 'n':
mixer_focus_channel += 1;
break;
case KEY_LEFT:
case 'p':
mixer_focus_channel -= 1;
break;
case KEY_PPAGE:
if (mixer_exact) {
mixer_lvolume_delta = 8;
mixer_rvolume_delta = 8;
} else {
mixer_lvolume_delta = 10;
mixer_rvolume_delta = 10;
}
break;
case KEY_NPAGE:
if (mixer_exact) {
mixer_lvolume_delta = -8;
mixer_rvolume_delta = -8;
} else {
mixer_lvolume_delta = -10;
mixer_rvolume_delta = -10;
}
break;
case KEY_BEG:
case KEY_HOME:
mixer_lvolume_delta = 512;
mixer_rvolume_delta = 512;
break;
case KEY_LL:
case KEY_END:
mixer_lvolume_delta = -512;
mixer_rvolume_delta = -512;
break;
case '+':
mixer_lvolume_delta = 1;
mixer_rvolume_delta = 1;
break;
case '-':
mixer_lvolume_delta = -1;
mixer_rvolume_delta = -1;
break;
case 'w':
case KEY_UP:
mixer_lvolume_delta = 1;
mixer_rvolume_delta = 1;
case 'W':
mixer_lvolume_delta += 1;
mixer_rvolume_delta += 1;
break;
case 'x':
case KEY_DOWN:
mixer_lvolume_delta = -1;
mixer_rvolume_delta = -1;
case 'X':
mixer_lvolume_delta += -1;
mixer_rvolume_delta += -1;
break;
case 'q':
mixer_lvolume_delta = 1;
case 'Q':
mixer_lvolume_delta += 1;
break;
case 'y':
case 'z':
mixer_lvolume_delta = -1;
case 'Y':
case 'Z':
mixer_lvolume_delta += -1;
break;
case 'e':
mixer_rvolume_delta = 1;
case 'E':
mixer_rvolume_delta += 1;
break;
case 'c':
mixer_rvolume_delta = -1;
case 'C':
mixer_rvolume_delta += -1;
break;
case 'm':
case 'M':
mixer_input_volumes = 0;
mixer_toggle_mute_left = 1;
mixer_toggle_mute_right = 1;
break;
case 'b':
case 'B':
case '=':
mixer_balance_volumes = 1;
break;
case '<':
case ',':
mixer_input_volumes = 0;
mixer_toggle_mute_left = 1;
break;
case '>':
case '.':
mixer_input_volumes = 0;
mixer_toggle_mute_right = 1;
break;
case 'R':
case 'r':
mixer_input_volumes = !mixer_input_volumes;
break;
case 'L':
case 'l':
mixer_clear();
break;
case ' ':
mixer_input_volumes = 1;
mixer_toggle_record_left = 1;
mixer_toggle_record_right = 1;
break;
case KEY_IC:
case ';':
mixer_input_volumes = 1;
mixer_toggle_record_left = 1;
break;
case '\'':
case KEY_DC:
mixer_input_volumes = 1;
mixer_toggle_record_right = 1;
break;
case '1':
mixer_input_volumes = 1;
mixer_route_rtol_in = 1;
break;
case '2':
mixer_input_volumes = 1;
mixer_route_ltor_in = 1;
break;
}
mixer_focus_channel = CLAMP(mixer_focus_channel, 0, mixer_n_channels - 1);
return finished;
}
static void mixer_init_screen(void)
{
signal(SIGWINCH, (void *) mixer_init_screen);
getmaxyx(mixer_window, mixer_max_y, mixer_max_x);
mixer_clear();
mixer_max_x = MAX(MIXER_MIN_X, mixer_max_x);
mixer_max_y = MAX(MIXER_MIN_Y, mixer_max_y);
mixer_clear();
mixer_ofs_x = 2;
mixer_ofs_y = 2;
mixer_extra_space = 0;
mixer_n_vis_channels = MIN((mixer_max_x - 2 * mixer_ofs_x + 1) / (9 + mixer_extra_space),
mixer_n_channels);
mixer_extra_space = ((mixer_max_x - 2 * mixer_ofs_x - 1 - mixer_n_vis_channels * 9.0) /
(mixer_n_vis_channels - 1));
if (mixer_n_vis_channels < mixer_n_channels) {
/* recalc
*/
mixer_extra_space = MAX(mixer_extra_space, 1);
mixer_n_vis_channels = MIN((mixer_max_x - 2 * mixer_ofs_x + 1) / (9 + mixer_extra_space),
mixer_n_channels);
mixer_extra_space = ((mixer_max_x - 2 * mixer_ofs_x - 1 - mixer_n_vis_channels * 9.0) /
(mixer_n_vis_channels - 1));
}
mixer_first_vis_channel = 0;
mixer_cbar_height = 10 + MAX(0, (mixer_max_y - MIXER_MIN_Y - 1)) / 2;
}
static void mixer_signal_handler(int signal)
{
mixer_abort(ERR_SIGNAL, sys_siglist[signal]);
}
int main(int argc,
char **argv)
{
int opt;
/* parse args
*/
do {
opt = getopt(argc, argv, "c:m:ehg");
switch (opt) {
case '?':
case 'h':
fprintf(stderr, "%s %s\n", PRGNAME_UPPER, VERSION);
fprintf(stderr, "Usage: %s [-e] [-c <card: 1..%i>] [-m <mixer: 0..1>]\n", PRGNAME, snd_cards());
mixer_abort(ERR_NONE, "");
case 'c':
card_id = snd_card_name(optarg);
break;
case 'e':
mixer_exact = !mixer_exact;
break;
case 'g':
mixer_do_color = !mixer_do_color;
break;
case 'm':
mixer_id = CLAMP(optarg[0], '0', '1') - '0';
break;
}
}
while (opt > 0);
/* initialize mixer
*/
mixer_init();
/* setup signal handlers
*/
signal(SIGINT, mixer_signal_handler);
signal(SIGTRAP, mixer_signal_handler);
signal(SIGABRT, mixer_signal_handler);
signal(SIGQUIT, mixer_signal_handler);
signal(SIGBUS, mixer_signal_handler);
signal(SIGSEGV, mixer_signal_handler);
signal(SIGPIPE, mixer_signal_handler);
signal(SIGTERM, mixer_signal_handler);
/* initialize ncurses
*/
mixer_window = initscr();
if (mixer_do_color)
mixer_do_color = has_colors();
mixer_init_draw_contexts();
mixer_init_screen();
if (mixer_max_x < MIXER_MIN_X ||
mixer_max_y < MIXER_MIN_Y)
mixer_abort(ERR_WINSIZE, "");
/* react on key presses
* and draw window
*/
keypad(mixer_window, TRUE);
leaveok(mixer_window, TRUE);
cbreak();
noecho();
do {
mixer_update_cbars();
mixer_draw_frame();
refresh();
}
while (!mixer_iteration());
mixer_abort(ERR_NONE, "");
};

View file

@ -0,0 +1,964 @@
/* AlsaMixer - Commandline mixer for the ALSA project
* Copyright (C) 1998 Jaroslav Kysela <perex@jcu.cz>,
* Tim Janik <timj@gtk.org>,
* Carl van Schaik <carl@dreamcoat.che.uct.ac.za>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/signal.h>
#ifndef CURSESINC
#include <ncurses.h>
#else
#include CURSESINC
#endif
#include <time.h>
#include <sys/asoundlib.h>
/* example compilation commandline:
* clear; gcc -Wall -pipe -O2 alsamixer.c -o alsamixer -lncurses
*/
/* --- defines --- */
#define PRGNAME "alsamixer"
#define PRGNAME_UPPER "AlsaMixer"
#define VERSION "v0.9"
#undef MAX
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#undef MIN
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#undef ABS
#define ABS(a) (((a) < 0) ? -(a) : (a))
#undef CLAMP
#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
#define MIXER_MIN_X (23) /* minimum: 23 */
#define MIXER_MIN_Y (19) /* minimum: 19 */
#define MIXER_BLACK (COLOR_BLACK)
#define MIXER_DARK_RED (COLOR_RED)
#define MIXER_RED (COLOR_RED | A_BOLD)
#define MIXER_GREEN (COLOR_GREEN | A_BOLD)
#define MIXER_ORANGE (COLOR_YELLOW)
#define MIXER_YELLOW (COLOR_YELLOW | A_BOLD)
#define MIXER_MARIN (COLOR_BLUE)
#define MIXER_BLUE (COLOR_BLUE | A_BOLD)
#define MIXER_MAGENTA (COLOR_MAGENTA)
#define MIXER_DARK_CYAN (COLOR_CYAN)
#define MIXER_CYAN (COLOR_CYAN | A_BOLD)
#define MIXER_GREY (COLOR_WHITE)
#define MIXER_GRAY (MIXER_GREY)
#define MIXER_WHITE (COLOR_WHITE | A_BOLD)
/* --- variables --- */
static WINDOW *mixer_window = NULL;
static int mixer_max_x = 0;
static int mixer_max_y = 0;
static int mixer_ofs_x = 0;
static float mixer_extra_space = 0;
static int mixer_ofs_y = 0;
static int mixer_cbar_height = 0;
static int card_id = 0;
static int mixer_id = 0;
static void *mixer_handle;
static char *mixer_card_name = NULL;
static char *mixer_device_name = NULL;
static int mixer_n_channels = 0;
static int mixer_n_vis_channels = 0;
static int mixer_first_vis_channel = 0;
static int mixer_focus_channel = 0;
static int mixer_exact = 0;
static int mixer_input_volumes = 0;
static int mixer_lvolume_delta = 0;
static int mixer_rvolume_delta = 0;
static int mixer_balance_volumes = 0;
static int mixer_toggle_mute_left = 0;
static int mixer_toggle_mute_right = 0;
/* By Carl */
static int mixer_toggle_record_left = 0;
static int mixer_toggle_record_right = 0;
static int mixer_route_ltor_in = 0;
static int mixer_route_rtol_in = 0;
#if 0
static int mixer_route_ltor_out = 0;
static int mixer_route_rtol_out = 0;
#endif
/* --- draw contexts --- */
enum {
DC_DEFAULT,
DC_BACK,
DC_TEXT,
DC_PROMPT,
DC_CBAR_MUTE,
DC_CBAR_NOMUTE,
DC_CBAR_RECORD,
DC_CBAR_NORECORD,
DC_CBAR_EMPTY,
DC_CBAR_FULL_1,
DC_CBAR_FULL_2,
DC_CBAR_FULL_3,
DC_CBAR_LABEL,
DC_CBAR_FOCUS_LABEL,
DC_FOCUS,
DC_LAST
};
static int dc_fg[DC_LAST] =
{0};
static int dc_attrib[DC_LAST] =
{0};
static int dc_char[DC_LAST] =
{0};
static int mixer_do_color = 1;
static void mixer_init_dc(int c,
int n,
int f,
int b,
int a)
{
dc_fg[n] = f;
dc_attrib[n] = a;
dc_char[n] = c;
if (n > 0)
init_pair(n, dc_fg[n] & 0xf, b & 0x0f);
}
static int mixer_dc(int n)
{
if (mixer_do_color)
attrset(COLOR_PAIR(n) | (dc_fg[n] & 0xfffffff0));
else
attrset(dc_attrib[n]);
return dc_char[n];
}
static void mixer_init_draw_contexts(void)
{
start_color();
mixer_init_dc('.', DC_BACK, MIXER_WHITE, MIXER_BLACK, A_NORMAL);
mixer_init_dc('.', DC_TEXT, MIXER_YELLOW, MIXER_BLACK, A_BOLD);
mixer_init_dc('.', DC_PROMPT, MIXER_DARK_CYAN, MIXER_BLACK, A_NORMAL);
mixer_init_dc('M', DC_CBAR_MUTE, MIXER_CYAN, MIXER_BLACK, A_BOLD);
mixer_init_dc('-', DC_CBAR_NOMUTE, MIXER_CYAN, MIXER_BLACK, A_NORMAL);
mixer_init_dc('x', DC_CBAR_RECORD, MIXER_DARK_RED, MIXER_BLACK, A_BOLD);
mixer_init_dc('-', DC_CBAR_NORECORD, MIXER_GRAY, MIXER_BLACK, A_NORMAL);
mixer_init_dc(' ', DC_CBAR_EMPTY, MIXER_GRAY, MIXER_BLACK, A_DIM);
mixer_init_dc('#', DC_CBAR_FULL_1, MIXER_WHITE, MIXER_BLACK, A_BOLD);
mixer_init_dc('#', DC_CBAR_FULL_2, MIXER_GREEN, MIXER_BLACK, A_BOLD);
mixer_init_dc('#', DC_CBAR_FULL_3, MIXER_RED, MIXER_BLACK, A_BOLD);
mixer_init_dc('.', DC_CBAR_LABEL, MIXER_WHITE, MIXER_BLUE, A_REVERSE | A_BOLD);
mixer_init_dc('.', DC_CBAR_FOCUS_LABEL, MIXER_RED, MIXER_BLUE, A_REVERSE | A_BOLD);
mixer_init_dc('.', DC_FOCUS, MIXER_RED, MIXER_BLACK, A_BOLD);
}
#define DC_CBAR_FRAME (DC_CBAR_MUTE)
#define DC_FRAME (DC_PROMPT)
/* --- error types --- */
typedef enum {
ERR_NONE,
ERR_OPEN,
ERR_FCN,
ERR_SIGNAL,
ERR_WINSIZE,
} ErrType;
/* --- prototypes --- */
static void mixer_abort(ErrType error,
const char *err_string)
__attribute__
((noreturn));
/* --- functions --- */
static void mixer_clear(void)
{
int x, y;
mixer_dc(DC_BACK);
clear();
/* buggy ncurses doesn't really write spaces with the specified
* color into the screen on clear ();
*/
for (x = 0; x < mixer_max_x; x++)
for (y = 0; y < mixer_max_y; y++)
mvaddch(y, x, ' ');
refresh();
}
static void mixer_abort(ErrType error,
const char *err_string)
{
if (mixer_window) {
mixer_clear();
endwin();
mixer_window = NULL;
}
printf("\n");
switch (error) {
case ERR_OPEN:
fprintf(stderr,
PRGNAME ": failed to open mixer #%i/#%i: %s\n",
card_id,
mixer_id,
snd_strerror(errno));
break;
case ERR_FCN:
fprintf(stderr,
PRGNAME ": function %s failed: %s\n",
err_string,
snd_strerror(errno));
break;
case ERR_SIGNAL:
fprintf(stderr,
PRGNAME ": aborting due to signal `%s'\n",
err_string);
break;
case ERR_WINSIZE:
fprintf(stderr,
PRGNAME ": screen size too small (%dx%d)\n",
mixer_max_x,
mixer_max_y);
break;
default:
break;
}
exit(error);
}
static int mixer_cbar_get_pos(int channel_index,
int *x_p,
int *y_p)
{
int x;
int y;
if (channel_index < mixer_first_vis_channel ||
channel_index - mixer_first_vis_channel >= mixer_n_vis_channels)
return FALSE;
channel_index -= mixer_first_vis_channel;
x = mixer_ofs_x + 1;
y = mixer_ofs_y;
x += channel_index * (3 + 2 + 3 + 1 + mixer_extra_space);
y += mixer_max_y / 2;
y += mixer_cbar_height / 2 + 1;
if (x_p)
*x_p = x;
if (y_p)
*y_p = y;
return TRUE;
}
static void mixer_update_cbar(int channel_index)
{
char string[64];
char c;
snd_mixer_channel_info_t cinfo =
{0};
snd_mixer_channel_direction_info_t cpinfo =
{0};
snd_mixer_channel_direction_info_t crinfo =
{0};
snd_mixer_channel_direction_t cpdata =
{0};
snd_mixer_channel_direction_t crdata =
{0};
int vleft, vright;
int x, y, i;
int output = 0, input = 0, volume;
/* set specified EXACT mode
*/
if (snd_mixer_exact_mode(mixer_handle, mixer_exact) < 0)
mixer_abort(ERR_FCN, "snd_mixer_exact");
/* set new channel indices and read info
*/
if (snd_mixer_channel_info(mixer_handle, channel_index, &cinfo) < 0)
mixer_abort(ERR_FCN, "snd_mixer_channel_info");
if (cinfo.caps & SND_MIXER_CINFO_CAP_OUTPUT) {
if (snd_mixer_channel_output_info(mixer_handle, channel_index, &cpinfo) < 0)
mixer_abort(ERR_FCN, "snd_mixer_channel_output_info");
output = 1;
}
if (cinfo.caps & SND_MIXER_CINFO_CAP_INPUT) {
if (snd_mixer_channel_input_info(mixer_handle, channel_index, &crinfo) < 0)
mixer_abort(ERR_FCN, "snd_mixer_channel_input_info");
input = 1;
}
if (mixer_input_volumes)
volume=(input && (crinfo.caps & SND_MIXER_CINFO_DCAP_VOLUME));
else
volume=(output && (cpinfo.caps & SND_MIXER_CINFO_DCAP_VOLUME));
/* set new channel values
*/
if (channel_index == mixer_focus_channel &&
(mixer_lvolume_delta || mixer_rvolume_delta ||
mixer_toggle_mute_left || mixer_toggle_mute_right ||
mixer_balance_volumes ||
mixer_toggle_record_left || mixer_toggle_record_right ||
mixer_route_rtol_in || mixer_route_ltor_in)) {
if (output && snd_mixer_channel_output_read(mixer_handle, channel_index, &cpdata) < 0)
mixer_abort(ERR_FCN, "snd_mixer_channel_output_read");
if (input && snd_mixer_channel_input_read(mixer_handle, channel_index, &crdata) < 0)
mixer_abort(ERR_FCN, "snd_mixer_channel_input_read");
cpdata.flags &= ~SND_MIXER_DFLG_DECIBEL;
crdata.flags &= ~SND_MIXER_DFLG_DECIBEL;
if (volume) {
if (mixer_input_volumes) {
crdata.left = CLAMP(crdata.left + mixer_lvolume_delta, crinfo.min, crinfo.max);
crdata.right = CLAMP(crdata.right + mixer_rvolume_delta, crinfo.min, crinfo.max);
if (mixer_balance_volumes) {
crdata.left = (crdata.left + crdata.right) / 2;
crdata.right = crdata.left;
}
}
else {
cpdata.left = CLAMP(cpdata.left + mixer_lvolume_delta, cpinfo.min, cpinfo.max);
cpdata.right = CLAMP(cpdata.right + mixer_rvolume_delta, cpinfo.min, cpinfo.max);
if (mixer_balance_volumes) {
cpdata.left = (cpdata.left + cpdata.right) / 2;
cpdata.right = cpdata.left;
}
}
}
mixer_lvolume_delta = 0;
mixer_rvolume_delta = 0;
mixer_balance_volumes = 0;
if (output) {
if (mixer_toggle_mute_left) {
cpdata.flags ^= SND_MIXER_DFLG_MUTE_LEFT;
}
if (mixer_toggle_mute_right) {
cpdata.flags ^= SND_MIXER_DFLG_MUTE_RIGHT;
}
}
mixer_toggle_mute_left = mixer_toggle_mute_right = 0;
if (input) {
if (mixer_toggle_record_left) {
crdata.flags ^= SND_MIXER_DFLG_MUTE_LEFT;
}
if (mixer_toggle_record_right) {
crdata.flags ^= SND_MIXER_DFLG_MUTE_RIGHT;
}
if (mixer_route_ltor_in) {
crdata.flags ^= SND_MIXER_DFLG_LTOR;
}
if (mixer_route_rtol_in) {
crdata.flags ^= SND_MIXER_DFLG_RTOL;
}
}
mixer_toggle_record_left = mixer_toggle_record_right = 0;
mixer_route_ltor_in = mixer_route_rtol_in = 0;
if (output &&
snd_mixer_channel_output_write(mixer_handle, channel_index, &cpdata) < 0)
mixer_abort(ERR_FCN, "snd_mixer_channel_output_write");
if (input &&
snd_mixer_channel_input_write(mixer_handle, channel_index, &crdata) < 0)
mixer_abort(ERR_FCN, "snd_mixer_channel_input_write");
}
/* first, read values for the numbers to be displayed in
* specified EXACT mode
*/
if (output &&
snd_mixer_channel_output_read(mixer_handle, channel_index, &cpdata) < 0)
mixer_abort(ERR_FCN, "snd_mixer_ioctl_channel_output_read");
if (input &&
snd_mixer_channel_input_read(mixer_handle, channel_index, &crdata) < 0)
mixer_abort(ERR_FCN, "snd_mixer_channel_input_read");
if (mixer_input_volumes) {
if (input) {
vleft = crdata.left;
vright = crdata.right;
}
else {
vleft = vright = 0;
}
}
else {
if (output) {
vleft = cpdata.left;
vright = cpdata.right;
}
else {
vleft = vright = 0;
}
}
/* then, always use percentage values for the bars. if we don't do
* this, we will see aliasing effects on specific circumstances.
* (actually they don't really dissapear, but they are transfered
* to bar<->smaller-scale ambiguities).
*/
if (mixer_exact) {
i = 0;
if (snd_mixer_exact_mode(mixer_handle, 0) < 0)
mixer_abort(ERR_FCN, "snd_mixer_exact");
if (output &&
snd_mixer_channel_output_read(mixer_handle, channel_index, &cpdata) < 0)
mixer_abort(ERR_FCN, "snd_mixer_channel_output_read");
if (input &&
snd_mixer_channel_input_read(mixer_handle, channel_index, &crdata) < 0)
mixer_abort(ERR_FCN, "snd_mixer_channel_input_read");
}
/* get channel bar position
*/
if (!mixer_cbar_get_pos(channel_index, &x, &y))
return;
/* channel bar name
*/
mixer_dc(channel_index == mixer_focus_channel ? DC_CBAR_FOCUS_LABEL : DC_CBAR_LABEL);
cinfo.name[8] = 0;
for (i = 0; i < 8; i++) {
string[i] = ' ';
}
sprintf(string + (8 - strlen(cinfo.name)) / 2, "%s ", cinfo.name);
string[8] = 0;
mvaddstr(y, x, string);
y--;
/* current channel values
*/
mixer_dc(DC_BACK);
mvaddstr(y, x, " ");
mixer_dc(DC_TEXT);
sprintf(string, "%d", vleft);
mvaddstr(y, x + 3 - strlen(string), string);
mixer_dc(DC_CBAR_FRAME);
mvaddch(y, x + 3, '<');
mvaddch(y, x + 4, '>');
mixer_dc(DC_TEXT);
sprintf(string, "%d", vright);
mvaddstr(y, x + 5, string);
y--;
/* left/right bar
*/
mixer_dc(DC_CBAR_FRAME);
mvaddstr(y, x, " ");
mvaddch(y, x + 2, ACS_LLCORNER);
mvaddch(y, x + 3, ACS_HLINE);
mvaddch(y, x + 4, ACS_HLINE);
mvaddch(y, x + 5, ACS_LRCORNER);
y--;
for (i = 0; i < mixer_cbar_height; i++) {
mvaddstr(y - i, x, " ");
mvaddch(y - i, x + 2, ACS_VLINE);
mvaddch(y - i, x + 5, ACS_VLINE);
}
string[2] = 0;
for (i = 0; i < mixer_cbar_height; i++) {
int dc;
if (i + 1 >= 0.8 * mixer_cbar_height)
dc = DC_CBAR_FULL_3;
else if (i + 1 >= 0.4 * mixer_cbar_height)
dc = DC_CBAR_FULL_2;
else
dc = DC_CBAR_FULL_1;
mvaddch(y, x + 3, mixer_dc(vleft > i * 100 / mixer_cbar_height ? dc : DC_CBAR_EMPTY));
mvaddch(y, x + 4, mixer_dc(vright > i * 100 / mixer_cbar_height ? dc : DC_CBAR_EMPTY));
y--;
}
/* muted?
*/
mixer_dc(DC_BACK);
mvaddstr(y, x, " ");
if (output) {
c = (cpinfo.caps & SND_MIXER_CINFO_DCAP_MUTE) ? '-' : ' ';
mixer_dc(DC_CBAR_FRAME);
mvaddch(y, x + 2, ACS_ULCORNER);
mvaddch(y, x + 3, mixer_dc(cpdata.flags & SND_MIXER_DFLG_MUTE_LEFT ?
DC_CBAR_MUTE : DC_CBAR_NOMUTE));
mvaddch(y, x + 4, mixer_dc(cpdata.flags & SND_MIXER_DFLG_MUTE_RIGHT ?
DC_CBAR_MUTE : DC_CBAR_NOMUTE));
mixer_dc(DC_CBAR_FRAME);
mvaddch(y, x + 5, ACS_URCORNER);
}
y--;
/* record input?
*/
mixer_dc(DC_BACK);
mvaddstr(y, x, " ");
if (input) {
if ((crdata.flags & SND_MIXER_DFLG_MUTE) != SND_MIXER_DFLG_MUTE) {
mixer_dc(DC_CBAR_RECORD);
mvaddstr(y, x + 1, "RECORD");
if (!(crdata.flags & SND_MIXER_DFLG_MUTE_LEFT)) {
if (crdata.flags & SND_MIXER_DFLG_LTOR)
mvaddstr(y + 2, x + 6, "L");
else
mvaddstr(y + 1, x + 1, "L");
}
if (!(crdata.flags & SND_MIXER_DFLG_MUTE_RIGHT)) {
if (crdata.flags & SND_MIXER_DFLG_RTOL)
mvaddstr(y + 2, x + 1, "R");
else
mvaddstr(y + 1, x + 6, "R");
}
} else {
for (i = 0; i < 6; i++)
mvaddch(y, x + 1 + i, mixer_dc(DC_CBAR_NORECORD));
}
}
y--;
}
static void mixer_update_cbars(void)
{
static int o_x = 0;
static int o_y = 0;
int i, x, y;
if (!mixer_cbar_get_pos(mixer_focus_channel, &x, &y)) {
if (mixer_focus_channel < mixer_first_vis_channel)
mixer_first_vis_channel = mixer_focus_channel;
else if (mixer_focus_channel >= mixer_first_vis_channel + mixer_n_vis_channels)
mixer_first_vis_channel = mixer_focus_channel - mixer_n_vis_channels + 1;
mixer_cbar_get_pos(mixer_focus_channel, &x, &y);
}
for (i = 0; i < mixer_n_vis_channels; i++)
mixer_update_cbar(i + mixer_first_vis_channel);
/* draw focused cbar
*/
mixer_dc(DC_BACK);
mvaddstr(o_y, o_x, " ");
mvaddstr(o_y, o_x + 9, " ");
o_x = x - 1;
o_y = y;
mixer_dc(DC_FOCUS);
mvaddstr(o_y, o_x, "<");
mvaddstr(o_y, o_x + 9, ">");
}
static void mixer_draw_frame(void)
{
char string[128];
int i;
int max_len;
mixer_dc(DC_FRAME);
/* corners
*/
mvaddch(0, 0, ACS_ULCORNER);
mvaddch(mixer_max_y - 1, 0, ACS_LLCORNER);
mvaddch(mixer_max_y - 1, mixer_max_x - 1, ACS_LRCORNER);
mvaddch(0, mixer_max_x - 1, ACS_URCORNER);
/* lines
*/
for (i = 1; i < mixer_max_y - 1; i++) {
mvaddch(i, 0, ACS_VLINE);
mvaddch(i, mixer_max_x - 1, ACS_VLINE);
}
for (i = 1; i < mixer_max_x - 1; i++) {
mvaddch(0, i, ACS_HLINE);
mvaddch(mixer_max_y - 1, i, ACS_HLINE);
}
/* program title
*/
sprintf(string, "%s %s", PRGNAME_UPPER, VERSION);
max_len = strlen(string);
mvaddch(0, mixer_max_x / 2 - max_len / 2 - 1, '[');
mvaddch(0, mixer_max_x / 2 - max_len / 2 + max_len, ']');
mixer_dc(DC_TEXT);
mvaddstr(0, mixer_max_x / 2 - max_len / 2, string);
/* card name
*/
mixer_dc(DC_PROMPT);
mvaddstr(1, 2, "Card:");
mixer_dc(DC_TEXT);
sprintf(string, "%s", mixer_card_name);
max_len = mixer_max_x - 2 - 6 - 2;
if (strlen(string) > max_len)
string[max_len] = 0;
mvaddstr(1, 2 + 6, string);
/* device name
*/
mixer_dc(DC_PROMPT);
mvaddstr(2, 2, "Chip: ");
mixer_dc(DC_TEXT);
sprintf(string, "%s", mixer_device_name);
max_len = mixer_max_x - 2 - 6 - 2;
if (strlen(string) > max_len)
string[max_len] = 0;
mvaddstr(2, 2 + 6, string);
if (mixer_input_volumes)
mvaddstr(3, 2, "Record mixer");
else
mvaddstr(3, 2, " ");
}
static void mixer_init(void)
{
static snd_mixer_info_t mixer_info =
{0};
static struct snd_ctl_hw_info hw_info;
void *ctl_handle;
if (snd_ctl_open(&ctl_handle, card_id) < 0)
mixer_abort(ERR_OPEN, "snd_ctl_open");
if (snd_ctl_hw_info(ctl_handle, &hw_info) < 0)
mixer_abort(ERR_FCN, "snd_ctl_hw_info");
snd_ctl_close(ctl_handle);
/* open mixer device
*/
if (snd_mixer_open(&mixer_handle, card_id, mixer_id) < 0)
mixer_abort(ERR_OPEN, "snd_mixer_open");
/* setup global variables
*/
if (snd_mixer_info(mixer_handle, &mixer_info) < 0)
mixer_abort(ERR_FCN, "snd_mixer_info");
mixer_n_channels = mixer_info.channels;
mixer_card_name = hw_info.name;
mixer_device_name = mixer_info.name;
}
static void mixer_iteration_update(void *dummy, int channel)
{
#if 0
fprintf(stderr, "*** channel = %i\n", channel);
#endif
mixer_update_cbar(channel);
refresh();
}
static int mixer_iteration(void)
{
snd_mixer_callbacks_t callbacks;
int key;
int finished = 0;
int mixer_fd;
fd_set in;
bzero(&callbacks, sizeof(callbacks));
callbacks.channel_was_changed = mixer_iteration_update;
callbacks.output_channel_was_changed = mixer_iteration_update;
callbacks.input_channel_was_changed = mixer_iteration_update;
mixer_fd = snd_mixer_file_descriptor(mixer_handle);
while (1) {
FD_ZERO(&in);
FD_SET(fileno(stdin), &in);
FD_SET(mixer_fd, &in);
if (select(mixer_fd + 1, &in, NULL, NULL, NULL) <= 0)
return 1;
if (FD_ISSET(mixer_fd, &in))
snd_mixer_read(mixer_handle, &callbacks);
if (FD_ISSET(fileno(stdin), &in))
break;
}
key = getch();
switch (key) {
case 27: /* Escape */
finished = 1;
break;
case 9: /* Tab */
mixer_exact = !mixer_exact;
break;
case KEY_RIGHT:
case 'n':
mixer_focus_channel += 1;
break;
case KEY_LEFT:
case 'p':
mixer_focus_channel -= 1;
break;
case KEY_PPAGE:
if (mixer_exact) {
mixer_lvolume_delta = 8;
mixer_rvolume_delta = 8;
} else {
mixer_lvolume_delta = 10;
mixer_rvolume_delta = 10;
}
break;
case KEY_NPAGE:
if (mixer_exact) {
mixer_lvolume_delta = -8;
mixer_rvolume_delta = -8;
} else {
mixer_lvolume_delta = -10;
mixer_rvolume_delta = -10;
}
break;
case KEY_BEG:
case KEY_HOME:
mixer_lvolume_delta = 512;
mixer_rvolume_delta = 512;
break;
case KEY_LL:
case KEY_END:
mixer_lvolume_delta = -512;
mixer_rvolume_delta = -512;
break;
case '+':
mixer_lvolume_delta = 1;
mixer_rvolume_delta = 1;
break;
case '-':
mixer_lvolume_delta = -1;
mixer_rvolume_delta = -1;
break;
case 'w':
case KEY_UP:
mixer_lvolume_delta = 1;
mixer_rvolume_delta = 1;
case 'W':
mixer_lvolume_delta += 1;
mixer_rvolume_delta += 1;
break;
case 'x':
case KEY_DOWN:
mixer_lvolume_delta = -1;
mixer_rvolume_delta = -1;
case 'X':
mixer_lvolume_delta += -1;
mixer_rvolume_delta += -1;
break;
case 'q':
mixer_lvolume_delta = 1;
case 'Q':
mixer_lvolume_delta += 1;
break;
case 'y':
case 'z':
mixer_lvolume_delta = -1;
case 'Y':
case 'Z':
mixer_lvolume_delta += -1;
break;
case 'e':
mixer_rvolume_delta = 1;
case 'E':
mixer_rvolume_delta += 1;
break;
case 'c':
mixer_rvolume_delta = -1;
case 'C':
mixer_rvolume_delta += -1;
break;
case 'm':
case 'M':
mixer_input_volumes = 0;
mixer_toggle_mute_left = 1;
mixer_toggle_mute_right = 1;
break;
case 'b':
case 'B':
case '=':
mixer_balance_volumes = 1;
break;
case '<':
case ',':
mixer_input_volumes = 0;
mixer_toggle_mute_left = 1;
break;
case '>':
case '.':
mixer_input_volumes = 0;
mixer_toggle_mute_right = 1;
break;
case 'R':
case 'r':
mixer_input_volumes = !mixer_input_volumes;
break;
case 'L':
case 'l':
mixer_clear();
break;
case ' ':
mixer_input_volumes = 1;
mixer_toggle_record_left = 1;
mixer_toggle_record_right = 1;
break;
case KEY_IC:
case ';':
mixer_input_volumes = 1;
mixer_toggle_record_left = 1;
break;
case '\'':
case KEY_DC:
mixer_input_volumes = 1;
mixer_toggle_record_right = 1;
break;
case '1':
mixer_input_volumes = 1;
mixer_route_rtol_in = 1;
break;
case '2':
mixer_input_volumes = 1;
mixer_route_ltor_in = 1;
break;
}
mixer_focus_channel = CLAMP(mixer_focus_channel, 0, mixer_n_channels - 1);
return finished;
}
static void mixer_init_screen(void)
{
signal(SIGWINCH, (void *) mixer_init_screen);
getmaxyx(mixer_window, mixer_max_y, mixer_max_x);
mixer_clear();
mixer_max_x = MAX(MIXER_MIN_X, mixer_max_x);
mixer_max_y = MAX(MIXER_MIN_Y, mixer_max_y);
mixer_clear();
mixer_ofs_x = 2;
mixer_ofs_y = 2;
mixer_extra_space = 0;
mixer_n_vis_channels = MIN((mixer_max_x - 2 * mixer_ofs_x + 1) / (9 + mixer_extra_space),
mixer_n_channels);
mixer_extra_space = ((mixer_max_x - 2 * mixer_ofs_x - 1 - mixer_n_vis_channels * 9.0) /
(mixer_n_vis_channels - 1));
if (mixer_n_vis_channels < mixer_n_channels) {
/* recalc
*/
mixer_extra_space = MAX(mixer_extra_space, 1);
mixer_n_vis_channels = MIN((mixer_max_x - 2 * mixer_ofs_x + 1) / (9 + mixer_extra_space),
mixer_n_channels);
mixer_extra_space = ((mixer_max_x - 2 * mixer_ofs_x - 1 - mixer_n_vis_channels * 9.0) /
(mixer_n_vis_channels - 1));
}
mixer_first_vis_channel = 0;
mixer_cbar_height = 10 + MAX(0, (mixer_max_y - MIXER_MIN_Y - 1)) / 2;
}
static void mixer_signal_handler(int signal)
{
mixer_abort(ERR_SIGNAL, sys_siglist[signal]);
}
int main(int argc,
char **argv)
{
int opt;
/* parse args
*/
do {
opt = getopt(argc, argv, "c:m:ehg");
switch (opt) {
case '?':
case 'h':
fprintf(stderr, "%s %s\n", PRGNAME_UPPER, VERSION);
fprintf(stderr, "Usage: %s [-e] [-c <card: 1..%i>] [-m <mixer: 0..1>]\n", PRGNAME, snd_cards());
mixer_abort(ERR_NONE, "");
case 'c':
card_id = snd_card_name(optarg);
break;
case 'e':
mixer_exact = !mixer_exact;
break;
case 'g':
mixer_do_color = !mixer_do_color;
break;
case 'm':
mixer_id = CLAMP(optarg[0], '0', '1') - '0';
break;
}
}
while (opt > 0);
/* initialize mixer
*/
mixer_init();
/* setup signal handlers
*/
signal(SIGINT, mixer_signal_handler);
signal(SIGTRAP, mixer_signal_handler);
signal(SIGABRT, mixer_signal_handler);
signal(SIGQUIT, mixer_signal_handler);
signal(SIGBUS, mixer_signal_handler);
signal(SIGSEGV, mixer_signal_handler);
signal(SIGPIPE, mixer_signal_handler);
signal(SIGTERM, mixer_signal_handler);
/* initialize ncurses
*/
mixer_window = initscr();
if (mixer_do_color)
mixer_do_color = has_colors();
mixer_init_draw_contexts();
mixer_init_screen();
if (mixer_max_x < MIXER_MIN_X ||
mixer_max_y < MIXER_MIN_Y)
mixer_abort(ERR_WINSIZE, "");
/* react on key presses
* and draw window
*/
keypad(mixer_window, TRUE);
leaveok(mixer_window, TRUE);
cbreak();
noecho();
do {
mixer_update_cbars();
mixer_draw_frame();
refresh();
}
while (!mixer_iteration());
mixer_abort(ERR_NONE, "");
};

View file

@ -1,8 +1,8 @@
INCLUDES = -I$(top_srcdir)/include
LDADD = -lasound
LDADD = -lasound -lm
EXTRA_DIST = README.first
bin_PROGRAMS = amixer
amixer_SOURCES = amain.cpp amixer.cpp
noinst_HEADERS = amixer.h atypes.h
amixer_SOURCES = amixer.c
noinst_HEADERS = amixer.h
man_MANS = amixer.1

View file

@ -1,39 +0,0 @@
Intro
-----
This is a quick mixer program for the ALSA project. It will grow out
to include a GTK interface eventually, if noone beats me to it :)
Building
--------
Edit the Makefile where the -I (include option) is. This should
point to the directory where the alsadriver is located. Then
type "make" and it should build without complaining. If you have
trouble with it please mail me at <arloafoe@cs.vu.nl>
Running
-------
amixer -h should display the syntax
Bugs & Todo
-----------
A lot, let me know..
Changes
-------
v0.001 March 20 1998 - Initial code
v0.1 Apr 21 1997 - Actually useful now
Contact
-------
Andy Lo A Foe
arloafoe@cs.vu.nl

View file

@ -1,270 +0,0 @@
/*
* Copyright 1998, Andy Lo A Foe <arloafoe@cs.vu.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/asoundlib.h>
#include "amixer.h"
#define MIXER_RC ".amixerrc"
char *rc_file(void)
{
static char rc_path[1024];
char *p;
p = getenv("HOME");
if (p) {
sprintf(rc_path, "%s/%s", p, MIXER_RC);
} else {
printf("Error reading HOME env. variable\n");
return NULL;
}
return rc_path;
}
void copyright()
{
printf("CLI ALSA Mixer v0.11 (c) 1998 Adnans\n\n");
}
void usage()
{
printf("\n"
"Usage: amixer [-c card] [-d dev] device [vol|L:R] [mute|unmute] [rec|norec]\n\n"
" amixer [-p path] -r\tRead %s or <path> settings\n"
" amixer [-p path] -w\tWrite %s or <path> settings\n"
" amixer -q ...\t\tQuiet mode\n"
" amixer -h\t\tHelp\n\n"
"Example: amixer line-out 80:50 unmute rec\n\n", rc_file(), rc_file());
}
void read_config(Mixer * mix, const char *path)
{
FILE *rc;
char buf[1024];
int opt1;
int opt2;
int left, right;
int dev;
int count = 0;
int flags;
if (path && strcmp(path, "-") == 0)
rc = stdin;
else if ((rc = fopen(path ? path : rc_file(), "r")) == NULL) {
printf("Mixer values not read\n");
return;
}
while (!feof(rc) && fgets(buf, 1024, rc)) {
count++;
if (buf[0] == '\n')
continue;
if (buf[0] == '#' || strlen(buf) == 0)
continue;
if (sscanf(buf, "%d %d:%d %d %d\n", &dev, &left, &right, &opt1, &opt2) != 5) {
printf("WARNING: unable to make out line %d of .rc file -> \"%s\"\n", count, buf);
continue;
}
flags = 0;
if (opt1)
flags |= E_MIXER_MUTE;
if (opt2)
flags |= E_MIXER_RECORD;
// Set mixer settings
mix->DeviceSet(dev);
mix->Write(left, right, flags);
}
if (rc != stdin)
fclose(rc);
return;
}
void write_config(Mixer * mix, const char *path)
{
FILE *rc;
int32 left, right, flags;
if (path && strcmp(path, "-") == 0)
rc = stdout;
else if ((rc = fopen(path ? path : rc_file(), "w+")) == NULL) {
printf("Mixer values not written\n");
return;
}
fprintf(rc, "# CLI ALSA mixer settings file. Autogenerated\n"
"# Modify at your own risk :)\n\n");
for (int i = 0; i < mix->NumDevices(); i++) {
mix->DeviceSet(i);
mix->Read(&left, &right, &flags);
fprintf(rc, "%d %d:%d %d %d\n", i, mix->Left(), mix->Right(), flags & E_MIXER_MUTE ? 1 : 0, flags & E_MIXER_RECORD ? 1 : 0);
}
if (rc != stdout)
fclose(rc);
return;
}
int main(int argc, char **argv)
{
int card = 0, device = 0;
char device_name[64] = "";
int32 exact, mute, unmute, norec, rec, left, right, flags, device_index;
int32 left_dB, right_dB;
int32 cur_left, cur_right, cur_flags;
int count, quiet = 0;
int i, add;
int pathind = 0;
Mixer *the_mixer;
exact = mute = rec = norec = unmute = device_index = left = right = -1;
left_dB = right_dB = -1;
for (add = 0; add + 1 < argc; i++) {
if (!strcmp(argv[add + 1], "--help")) {
usage();
return 0;
}
if (argv[add + 1][0] == '-') {
add++;
if (argv[add][1] == 'c') {
card = snd_card_name(argv[++add]);
if (card < 0) {
fprintf(stderr, "Invalid card: %s\n", argv[2]);
exit(1);
}
} else if (argv[add][1] == 'd') {
device = atoi(argv[++add]);
if (device < 0 || device > 128) {
fprintf(stderr, "Invalid device: %s\n", argv[2]);
exit(1);
}
} else if (argv[add][1] == 'h') {
usage();
return 0;
} else if (argv[add][1] == 'p') {
pathind = ++add;
} else if (argv[add][1] == 'r') {
the_mixer = new Mixer(card, device);
if (the_mixer && the_mixer->Init())
read_config(the_mixer, pathind ? argv[pathind] : (const char *) NULL);
delete the_mixer;
return 0;
} else if (argv[add][1] == 'w') {
the_mixer = new Mixer(card, device);
if (the_mixer && the_mixer->Init())
write_config(the_mixer, pathind ? argv[pathind] : (const char *) NULL);
delete the_mixer;
return 0;
} else if (argv[add][1] == 'q') {
quiet = 1;
} else {
fprintf(stderr, "Invalid option: %s\n", argv[add] + 1);
return 1;
}
} else {
break;
}
}
for (i = 1 + add; i < argc; i++) {
if (strcmp(argv[i], "exact") == 0) {
exact = 1;
} else if (strcmp(argv[i], "mute") == 0) {
mute = 1;
} else if (strcmp(argv[i], "unmute") == 0) {
unmute = 1;
} else if (strcmp(argv[i], "rec") == 0) {
rec = 1;
} else if (strcmp(argv[i], "norec") == 0) {
norec = 1;
} else if (sscanf(argv[i], "%d:%d", &left, &right) == 2) {
} else if (sscanf(argv[i], "%d", &left) == 1) {
right = left;
} else {
strncpy(device_name, argv[i], sizeof(device_name));
device_name[sizeof(device_name) - 1] = 0;
}
}
Mixer mixer(card, device);
if (mixer.Init() == false) {
fprintf(stderr, "Failed to open mixer device\n");
return 1;
}
count = mixer.NumDevices();
for (i = 0; i < count; i++) {
mixer.DeviceSet(i);
if (strcasecmp(device_name, mixer.Name()) == 0)
device_index = i;
}
if (!quiet)
copyright();
if (device_index >= 0) {
mixer.DeviceSet(device_index);
mixer.Read(&cur_left, &cur_right, &cur_flags);
if (left >= 0)
cur_left = left;
if (right >= 0)
cur_right = right;
if (rec == 1)
cur_flags |= E_MIXER_RECORD;
else if (norec == 1)
cur_flags &= ~E_MIXER_RECORD;
if (mute == 1)
cur_flags |= E_MIXER_MUTE;
else if (unmute == 1)
cur_flags &= ~E_MIXER_MUTE;
if (left != -1 || rec != -1 || norec != -1 || mute != -1 || unmute != -1) {
mixer.Write(cur_left, cur_right, cur_flags);
}
if (!quiet) {
printf("%-16s", mixer.Name());
mixer.Read(&left, &right, &flags);
mixer.Read_dB(&left_dB, &right_dB);
printf("%-3d%% (%6.2fdB) : %-3d%% (%6.2fdB) %s %s\n\n",
left, ((float) left_dB) / 100.0,
right, ((float) right_dB) / 100.0,
(flags & E_MIXER_MUTE) ? "Mute" : " ",
(flags & E_MIXER_RECORD) ? "Rec" : " ");
}
} else {
if (quiet) {
usage();
return 1;
}
if (strlen(device_name))
printf("Device not found: %s\n\n", device_name);
for (i = 0; i < count; i++) {
mixer.DeviceSet(i);
printf("%-16s", mixer.Name());
mixer.Read(&left, &right, &flags);
mixer.Read_dB(&left_dB, &right_dB);
printf("%-3d%% (%6.2fdB) : %-3d%% (%6.2fdB) %s %s\n",
left, ((float) left_dB) / 100.0, right, ((float) right_dB) / 100.0,
(flags & E_MIXER_MUTE) ? "Mute" : " ",
(flags & E_MIXER_RECORD) ? "Rec" : " ");
}
return 0;
}
return 0;
}

1259
amixer/amixer.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,128 +0,0 @@
/*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/asoundlib.h>
#include "amixer.h"
Mixer::Mixer(int card, int device)
{
mixer_handle = NULL;
if (snd_mixer_open(&mixer_handle, card, device) < 0) {
fprintf(stderr, "Can't access mixer %i/%i\n", card+1, device);
mixer_status = ~E_MIXER_SUCCESS;
return;
}
mixer_status = E_MIXER_SUCCESS;
mixer_status |= E_MIXER_NEED_CLOSE;
}
bool Mixer::Open(int card, int device)
{
Close();
if (snd_mixer_open(&mixer_handle, card, device) < 0) {
fprintf(stderr, "Can't access mixer %i/%i\n", card + 1, device);
mixer_status = ~E_MIXER_SUCCESS;
} else{
mixer_status = E_MIXER_SUCCESS;
mixer_status |= E_MIXER_NEED_CLOSE;
}
return Init();
}
void Mixer::Close()
{
if (mixer_handle != NULL && mixer_status & E_MIXER_NEED_CLOSE) {
snd_mixer_close(mixer_handle);
}
mixer_handle = NULL;
mixer_status = ~E_MIXER_SUCCESS;
}
Mixer::~Mixer()
{
Close();
}
bool Mixer::Init()
{
if (!(mixer_status & E_MIXER_SUCCESS))
return false;
if ((nr_devices = snd_mixer_channels(mixer_handle)) < 0)
return false;
return true;
}
char* Mixer::Name()
{
return Name(current_device);
}
char* Mixer::Name(int32 device)
{
if (snd_mixer_channel_info(mixer_handle,device,&ch_info) < 0)
return "Unknown";
return (char *)ch_info.name;
}
void Mixer::Update()
{
if(snd_mixer_channel_output_read(mixer_handle, current_device, &ch_data) < 0) {
fprintf(stderr, "Can't read data from channel %i\n", current_device);
return; /* No fail code? */
}
}
void Mixer::DeviceRead(int32 device, int32 *left, int32 *right, int32 *flags)
{
current_device = device;
Update();
*left = ch_data.left;
*right = ch_data.right;
*flags = ch_data.flags;
}
void Mixer::DeviceWrite(int32 device, int32 left, int32 right, int32 flags)
{
current_device = device;
ch_data.channel = device;
ch_data.left = left;
ch_data.right = right;
ch_data.flags = flags;
if(snd_mixer_channel_output_write(mixer_handle, device, &ch_data) < 0) {
fprintf(stderr, "Can't write data to channel %i\n", device);
return; /* No fail code? */
}
}

View file

@ -1,4 +1,6 @@
/*
* ALSA command line mixer utility
* Copyright (c) 1999 by Jaroslav Kysela <perex@jcu.cz>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -16,60 +18,4 @@
*
*/
#include "atypes.h"
#define E_MIXER_SUCCESS 1
#define E_MIXER_NEED_CLOSE 2
/* FIXME */
#define E_MIXER_RECORD 0
#define E_MIXER_MUTE_LEFT SND_MIXER_DFLG_MUTE_LEFT
#define E_MIXER_MUTE_RIGHT SND_MIXER_DFLG_MUTE_RIGHT
#define E_MIXER_MUTE SND_MIXER_DFLG_MUTE
class Mixer
{
public:
Mixer(int card = 0, int device = 0);
~Mixer();
bool Init();
bool Open(int card, int device);
void Close();
void DeviceSet(int32 device) {
current_device = device;
Update();
}
char* Name(int32 device);
char* Name();
int32 NumDevices() { return nr_devices; }
void Update();
void DeviceRead(int32 device, int32 *left, int32 *right, int32 *flag);
void DeviceWrite(int32 device, int32 left, int32 right, int32 flag);
void Read(int32 *left, int32 *right, int32 *flags) {
DeviceRead(current_device, left, right, flags);
}
void Read_dB(int32 *left_dB, int32 *right_dB) {
*left_dB = ch_data.left_dB;
*right_dB = ch_data.right_dB;
}
void Write(int32 left, int32 right, int32 flags) {
DeviceWrite(current_device, left, right, flags);
}
int Left() { return ch_data.left; }
int Right() { return ch_data.right; }
Mixer& operator[](int32 device) {
DeviceSet(device);
return (*this);
}
private:
snd_mixer_info_t info;
snd_mixer_channel_direction_t ch_data;
snd_mixer_channel_info_t ch_info;
void * mixer_handle;
int32 mixer_status;
int32 current_device;
int32 nr_devices;
};
#include "../include/version.h"

View file

@ -1 +0,0 @@
typedef int int32;