diff --git a/alsactl/Makefile.am b/alsactl/Makefile.am index 0ebd56e..a4ce4d2 100644 --- a/alsactl/Makefile.am +++ b/alsactl/Makefile.am @@ -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 + diff --git a/alsactl/alsactl.c b/alsactl/alsactl.c index 18ebf6d..31b231c 100644 --- a/alsactl/alsactl.c +++ b/alsactl/alsactl.c @@ -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; } - - diff --git a/alsactl/alsactl.h b/alsactl/alsactl.h index a7c6884..9b80f86 100644 --- a/alsactl/alsactl.h +++ b/alsactl/alsactl.h @@ -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); diff --git a/alsactl/alsactl_lexer.l b/alsactl/alsactl_lexer.l index 8d7f962..4b8b2d1 100644 --- a/alsactl/alsactl_lexer.l +++ b/alsactl/alsactl_lexer.l @@ -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 */ diff --git a/alsactl/alsactl_parser.y b/alsactl/alsactl_parser.y index 8c54750..3287032 100644 --- a/alsactl/alsactl_parser.y +++ b/alsactl/alsactl_parser.y @@ -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 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 boolean %type integer %type string -%type bytearray +%type 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 */ diff --git a/alsactl/merge.c b/alsactl/merge.c new file mode 100644 index 0000000..6232e87 --- /dev/null +++ b/alsactl/merge.c @@ -0,0 +1,492 @@ +/* + * Advanced Linux Sound Architecture Control Program + * Copyright (c) 1999 by Jaroslav Kysela + * + * + * 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; +} diff --git a/alsactl/setup.c b/alsactl/setup.c index 637b525..d59e500 100644 --- a/alsactl/setup.c +++ b/alsactl/setup.c @@ -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; } diff --git a/alsamixer/alsamixer.c b/alsamixer/alsamixer.c index 31cae79..1fdd57b 100644 --- a/alsamixer/alsamixer.c +++ b/alsamixer/alsamixer.c @@ -1,964 +1,6 @@ -/* AlsaMixer - Commandline mixer for the ALSA project - * Copyright (C) 1998 Jaroslav Kysela , - * Tim Janik , - * Carl van Schaik - * - * 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 -#include -#include -#include -#include - -#include -#include -#include -#include - -#ifndef CURSESINC -#include -#else -#include CURSESINC -#endif -#include - -#include - -/* 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 ] [-m ]\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, ""); -}; diff --git a/alsamixer/alsamixer_abramo.c b/alsamixer/alsamixer_abramo.c new file mode 100644 index 0000000..31cae79 --- /dev/null +++ b/alsamixer/alsamixer_abramo.c @@ -0,0 +1,964 @@ +/* AlsaMixer - Commandline mixer for the ALSA project + * Copyright (C) 1998 Jaroslav Kysela , + * Tim Janik , + * Carl van Schaik + * + * 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 +#include +#include +#include + +#include + +#include +#include +#include +#include + +#ifndef CURSESINC +#include +#else +#include CURSESINC +#endif +#include + +#include + +/* 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 ] [-m ]\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, ""); +}; diff --git a/amixer/Makefile.am b/amixer/Makefile.am index 6d4d0d1..3e410e8 100644 --- a/amixer/Makefile.am +++ b/amixer/Makefile.am @@ -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 diff --git a/amixer/README.first b/amixer/README.first deleted file mode 100644 index daf520a..0000000 --- a/amixer/README.first +++ /dev/null @@ -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 - - -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 diff --git a/amixer/amain.cpp b/amixer/amain.cpp deleted file mode 100644 index 1324bf0..0000000 --- a/amixer/amain.cpp +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright 1998, Andy Lo A Foe - * - * 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 -#include -#include -#include -#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 settings\n" - " amixer [-p path] -w\tWrite %s or 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; -} diff --git a/amixer/amixer.c b/amixer/amixer.c new file mode 100644 index 0000000..e71f0fe --- /dev/null +++ b/amixer/amixer.c @@ -0,0 +1,1259 @@ +/* + * ALSA command line mixer utility + * Copyright (c) 1999 by Jaroslav Kysela + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include "amixer.h" + +#define HELPID_HELP 1000 +#define HELPID_CARD 1001 +#define HELPID_DEVICE 1002 +#define HELPID_QUIET 1003 +#define HELPID_DEBUG 1004 +#define HELPID_VERSION 1005 + +int quiet = 0; +int debugflag = 0; +int card; +int device; + +struct mixer_types { + int type; + char *name; +}; + +struct mixer_types mixer_types[] = { + { SND_MIXER_ETYPE_INPUT, "Input" }, + { SND_MIXER_ETYPE_OUTPUT, "Output" }, + { SND_MIXER_ETYPE_CAPTURE, "Capture" }, + { SND_MIXER_ETYPE_PLAYBACK, "Playback" }, + { SND_MIXER_ETYPE_ADC, "ADC" }, + { SND_MIXER_ETYPE_DAC, "DAC" }, + { SND_MIXER_ETYPE_SWITCH1, "Switch1" }, + { SND_MIXER_ETYPE_SWITCH2, "Switch2" }, + { SND_MIXER_ETYPE_SWITCH3, "Switch3" }, + { SND_MIXER_ETYPE_VOLUME1, "Volume1" }, + { SND_MIXER_ETYPE_VOLUME2, "Volume2" }, + { SND_MIXER_ETYPE_ACCU1, "Accumulator1" }, + { SND_MIXER_ETYPE_ACCU2, "Accumulator2" }, + { SND_MIXER_ETYPE_ACCU3, "Accumulator3" }, + { SND_MIXER_ETYPE_MUX1, "Mux1" }, + { SND_MIXER_ETYPE_MUX2, "Mux2" }, + { SND_MIXER_ETYPE_TONE_CONTROL1, "ToneControl1" }, + { SND_MIXER_ETYPE_EQUALIZER1, "Equalizer1" }, + { SND_MIXER_ETYPE_3D_EFFECT1, "3D-Effect1" }, + { SND_MIXER_ETYPE_PRE_EFFECT1, "PredefinedEffect1" }, + { -1, NULL } +}; + +void error(const char *fmt,...) +{ + va_list va; + + va_start(va, fmt); + fprintf(stderr, "amixer: "); + vfprintf(stderr, fmt, va); + fprintf(stderr, "\n"); + va_end(va); +} + +static void help(void) +{ + printf("Usage: amixer command\n"); + printf("\nAvailable options:\n"); + printf(" -h,--help this help\n"); + printf(" -c,--card # use a card number (0-%i) or the card name, default %i\n", snd_cards() - 1, card); + printf(" -d,--device # use a device number, default %i\n", device); + printf(" -D,--debug debug mode\n"); + printf(" -v,--version print version of this program\n"); + printf("\nAvailable commands:\n"); + printf(" info show useful information for the selected mixer\n"); + printf(" elements show information about all mixer elements\n"); + printf(" contents show contents of all mixer elements\n"); + printf(" groups show all mixer groups\n"); + printf(" eset E P set extended setup for one mixer element\n"); + printf(" eget E P get extended information for one mixer element\n"); +} + +int info(void) +{ + int err; + void *handle; + snd_mixer_info_t info; + + if ((err = snd_mixer_open(&handle, card, device)) < 0) { + error("Mixer %i/%i open error: %s", card, device, snd_strerror(err)); + return -1; + } + if ((err = snd_mixer_info(handle, &info)) < 0) { + error("Mixer %i/%i info error: %s", card, device, snd_strerror(err)); + return -1; + } + printf("Mixer '%s/%s':\n", info.id, info.name); + printf(" Elements : %i\n", info.elements); + printf(" Groups : %i\n", info.groups); + printf(" Switches : %i\n", info.switches); + printf(" Attribute : 0x%x\n", info.attribute); + snd_mixer_close(handle); + return 0; +} + +static const char *element_name(const char *name) +{ + static char res[25]; + + strncpy(res, name, 24); + res[24] = '\0'; + return res; +} + +static const char *element_type(int type) +{ + int idx; + static char str[32]; + + for (idx = 0; mixer_types[idx].type >= 0; idx++) + if (type == mixer_types[idx].type) + return mixer_types[idx].name; + sprintf(str, "Type%i", type); + return str; +} + +static int check_range(int val, int min, int max) +{ + if (val < min) + return min; + if (val > max) + return max; + return val; +} + +static int convert_range(int val, int omin, int omax, int nmin, int nmax) +{ + int orange = omax - omin, nrange = nmax - nmin; + + if (orange == 0) + return 0; + return rint((((double)nrange * ((double)val - (double)omin)) + ((double)orange / 2.0)) / (double)orange + (double)nmin); +} + +static int convert_db_range(int val, int omin, int omax, int nmin, int nmax) +{ + int orange = omax - omin, nrange = nmax - nmin; + int tmp; + + if (orange == 0) + return 0; + tmp = rint((((double)nrange * ((double)val - (double)omin)) + ((double)orange / 2.0)) / (double)orange + (double)nmin); + return tmp; +} + +static int convert_prange(int val, int min, int max) +{ + return convert_range(val, 0, 100, min, max); +} + +static const char *get_percent(int val, int min, int max) +{ + static char str[32]; + int p; + + p = convert_range(val, min, max, 0, 100); + sprintf(str, "%i [%i%%]", val, p); + return str; +} + +static const char *get_percent1(int val, int min, int max, int min_dB, int max_dB) +{ + static char str[32]; + int p, db; + + p = convert_range(val, min, max, 0, 100); + db = convert_db_range(val, min, max, min_dB, max_dB); + sprintf(str, "%i [%i%%] [%i.%02idB]", val, p, db / 100, abs(db % 100)); + return str; +} + +static int get_volume(char **ptr, int min, int max, int min_dB, int max_dB) +{ + int tmp, tmp1, tmp2; + + if (**ptr == ':') + (*ptr)++; + if (**ptr == '\0' || (!isdigit(**ptr) && **ptr != '-')) + return min; + tmp = atoi(*ptr); + if (**ptr == '-') + (*ptr)++; + while (isdigit(**ptr)) + (*ptr)++; + tmp1 = tmp; + tmp2 = 0; + if (**ptr == '.') { + (*ptr)++; + tmp2 = atoi(*ptr); + while (isdigit(**ptr)) + (*ptr)++; + } + if (**ptr == '%') { + tmp1 = convert_prange(tmp, min, max); + (*ptr)++; + } else if (**ptr == 'd') { + tmp1 *= 100; tmp1 += tmp2 % 100; + tmp1 = convert_range(tmp1, min_dB, max_dB, min, max); + (*ptr)++; + } + tmp1 = check_range(tmp1, min, max); + if (**ptr == ',') + (*ptr)++; + return tmp1; +} + +int show_element(void *handle, snd_mixer_eid_t *eid, const char *space) +{ + int err, idx; + snd_mixer_routes_t routes; + snd_mixer_eid_t *element; + + bzero(&routes, sizeof(routes)); + routes.eid = *eid; + if ((err = snd_mixer_routes(handle, &routes)) < 0) { + error("Mixer %i/%i route error: %s", card, device, snd_strerror(err)); + return -1; + } + if (!routes.routes_over) + return 0; + routes.proutes = (snd_mixer_eid_t *)malloc(routes.routes_over * sizeof(snd_mixer_eid_t)); + if (!routes.proutes) { + error("No enough memory..."); + return -1; + } + routes.routes_size = routes.routes_over; + routes.routes = routes.routes_over = 0; + if ((err = snd_mixer_routes(handle, &routes)) < 0) { + error("Mixer %i/%i group (2) error: %s", card, device, snd_strerror(err)); + return -1; + } + for (idx = 0; idx < routes.routes; idx++) { + element = &routes.proutes[idx]; + printf("%sRoute to element '%s',%i,%s\n", space, element_name(element->name), element->index, element_type(element->type)); + } + free(routes.proutes); + return 0; +} + +static const char *speaker_position(int position) +{ + static char str[32]; + + switch (position) { + case SND_MIXER_VOICE_UNUSED: + return "Unused"; + case SND_MIXER_VOICE_MONO: + return "Mono"; + case SND_MIXER_VOICE_LEFT: + return "Left"; + case SND_MIXER_VOICE_RIGHT: + return "Right"; + case SND_MIXER_VOICE_CENTER: + return "Center"; + case SND_MIXER_VOICE_REAR_LEFT: + return "Read-Left"; + case SND_MIXER_VOICE_REAR_RIGHT: + return "Read-Right"; + default: + sprintf(str, "Speaker%i", position); + return str; + } +} + +int show_mux1_info(void *handle, snd_mixer_element_info_t *info, const char *space) +{ + int idx, idx1, err; + snd_mixer_elements_t elements; + snd_mixer_routes_t routes; + snd_mixer_eid_t *element; + + printf("%sMux supports none input: %s\n", space, (info->data.mux1.attribute & SND_MIXER_MUX1_NONE) ? "YES" : "NO"); + bzero(&elements, sizeof(elements)); + if ((err = snd_mixer_elements(handle, &elements)) < 0) { + error("Mixer %i/%i elements error: %s", card, device, snd_strerror(err)); + return -1; + } + elements.pelements = (snd_mixer_eid_t *)malloc(elements.elements_over * sizeof(snd_mixer_eid_t)); + if (!elements.pelements) { + error("No enough memory"); + return -1; + } + elements.elements_size = elements.elements_over; + elements.elements_over = elements.elements = 0; + if ((err = snd_mixer_elements(handle, &elements)) < 0) { + error("Mixer %i/%i elements (2) error: %s", card, device, snd_strerror(err)); + return -1; + } + for (idx = 0; idx < elements.elements; idx++) { + bzero(&routes, sizeof(routes)); + routes.eid = elements.pelements[idx]; + if ((err = snd_mixer_routes(handle, &routes)) < 0) { + error("Mixer %i/%i route error: %s", card, device, snd_strerror(err)); + free(elements.pelements); + return -1; + } + if (!routes.routes_over) + continue; + routes.proutes = (snd_mixer_eid_t *)malloc(routes.routes_over * sizeof(snd_mixer_eid_t)); + if (!routes.proutes) { + error("No enough memory..."); + free(elements.pelements); + return -1; + } + routes.routes_size = routes.routes_over; + routes.routes = routes.routes_over = 0; + if ((err = snd_mixer_routes(handle, &routes)) < 0) { + error("Mixer %i/%i group (2) error: %s", card, device, snd_strerror(err)); + free(elements.pelements); + return -1; + } + for (idx1 = 0; idx1 < routes.routes; idx1++) { + element = &routes.proutes[idx1]; + if (!strncmp(element->name, info->eid.name, sizeof(element->name)) && + element->index == info->eid.index && + element->type == info->eid.type) + printf("%sInput element '%s',%i,%s\n", space, element_name(routes.eid.name), routes.eid.index, element_type(routes.eid.type)); + } + free(routes.proutes); + } + free(elements.pelements); + return 0; +} + +int show_element_info(void *handle, snd_mixer_eid_t *eid, const char *space) +{ + int err, idx; + snd_mixer_element_info_t info; + + if (snd_mixer_element_has_info(eid) != 1) + return 0; + bzero(&info, sizeof(info)); + info.eid = *eid; + if ((err = snd_mixer_element_info_build(handle, &info)) < 0) { + error("Mixer %i/%i info error: %s", card, device, snd_strerror(err)); + return -1; + } + switch (info.eid.type) { + case SND_MIXER_ETYPE_INPUT: + case SND_MIXER_ETYPE_OUTPUT: + if (info.data.io.attribute) { + printf("%sAttributes%s\n", space, + info.data.io.attribute & SND_MIXER_EIO_DIGITAL ? " digital" : ""); + } + for (idx = 0; idx < info.data.io.voices; idx++) { + if (!info.data.io.pvoices[idx].vindex) { + printf("%sVoice %i: %s\n", + space, + idx, + speaker_position(info.data.io.pvoices[idx].voice)); + } else { + printf("%sVoice %i: %i\n", + space, + idx, + info.data.io.pvoices[idx].voice); + } + } + break; + case SND_MIXER_ETYPE_CAPTURE: + case SND_MIXER_ETYPE_PLAYBACK: + for (idx = 0; idx < info.data.pcm.devices; idx++) { + printf("%sPCM device %i %i\n", + space, + idx, + info.data.pcm.pdevices[idx]); + } + break; + case SND_MIXER_ETYPE_ADC: + case SND_MIXER_ETYPE_DAC: + printf("%sResolution %i-bits\n", space, info.data.converter.resolution); + break; + case SND_MIXER_ETYPE_SWITCH3: + printf("%sSwitch type is ", space); + switch (info.data.switch3.type) { + case SND_MIXER_SWITCH3_FULL_FEATURED: + printf("full featured\n"); + break; + case SND_MIXER_SWITCH3_ALWAYS_DESTINATION: + printf("always destination\n"); + break; + case SND_MIXER_SWITCH3_ONE_DESTINATION: + printf("one destination\n"); + break; + case SND_MIXER_SWITCH3_ALWAYS_ONE_DESTINATION: + printf("always one destination\n"); + break; + default: + printf("unknown %i\n", info.data.switch3.type); + } + for (idx = 0; idx < info.data.switch3.voices; idx++) { + snd_mixer_voice_t voice = info.data.switch3.pvoices[idx]; + if (voice.vindex) { + printf("%sVoice %i: %i\n", space, idx, voice.voice); + } else { + printf("%sSpeaker %i: %s\n", space, idx, speaker_position(voice.voice)); + } + } + break; + case SND_MIXER_ETYPE_VOLUME1: + for (idx = 0; idx < info.data.volume1.range; idx++) { + struct snd_mixer_element_volume1_range *range = &info.data.volume1.prange[idx]; + printf("%sVoice %i: Min %i (%i.%02idB), Max %i (%i.%02idB)\n", + space, + idx, + range->min, range->min_dB / 100, abs(range->min_dB % 100), + range->max, range->max_dB / 100, abs(range->max_dB % 100)); + } + break; + case SND_MIXER_ETYPE_ACCU1: + printf("%sAttenuation %i.%02idB\n", space, + info.data.accu1.attenuation / 100, + abs(info.data.accu1.attenuation % 100)); + break; + case SND_MIXER_ETYPE_ACCU2: + printf("%sAttenuation %i.%02idB\n", space, + info.data.accu2.attenuation / 100, + abs(info.data.accu1.attenuation % 100)); + break; + case SND_MIXER_ETYPE_ACCU3: + for (idx = 0; idx < info.data.accu3.range; idx++) { + struct snd_mixer_element_accu3_range *range = &info.data.accu3.prange[idx]; + printf("%sVoice %i: Min %i (%i.%02idB), Max %i (%i.%02idB)\n", + space, + idx, + range->min, range->min_dB / 100, abs(range->min_dB % 100), + range->max, range->max_dB / 100, abs(range->max_dB % 100)); + } + break; + case SND_MIXER_ETYPE_MUX1: + show_mux1_info(handle, &info, space); + break; + case SND_MIXER_ETYPE_TONE_CONTROL1: + if (info.data.tc1.tc & SND_MIXER_TC1_SW) + printf("%sOn/Off switch\n", space); + if (info.data.tc1.tc & SND_MIXER_TC1_BASS) + printf("%sBass control: Min %i (%i.%02idB), Max %i (%i.%02idB)\n", + space, + info.data.tc1.min_bass, + info.data.tc1.min_bass_dB / 100, + abs(info.data.tc1.min_bass_dB % 100), + info.data.tc1.max_bass, + info.data.tc1.max_bass_dB / 100, + abs(info.data.tc1.max_bass_dB % 100)); + if (info.data.tc1.tc & SND_MIXER_TC1_TREBLE) + printf("%sTreble control: Min %i (%i.%02idB), Max %i (%i.%02idB)\n", + space, + info.data.tc1.min_treble, + info.data.tc1.min_treble_dB / 100, + abs(info.data.tc1.min_treble_dB % 100), + info.data.tc1.max_treble, + info.data.tc1.max_treble_dB / 100, + abs(info.data.tc1.max_treble_dB % 100)); + break; + case SND_MIXER_ETYPE_3D_EFFECT1: + if (info.data.teffect1.effect & SND_MIXER_EFF1_SW) + printf("%sOn/Off switch\n", space); + if (info.data.teffect1.effect & SND_MIXER_EFF1_MONO_SW) + printf("%sMono processing switch\n", space); + if (info.data.teffect1.effect & SND_MIXER_EFF1_WIDE) + printf("%sWide: Min %i, Max %i\n", space, + info.data.teffect1.min_wide, + info.data.teffect1.max_wide); + if (info.data.teffect1.effect & SND_MIXER_EFF1_VOLUME) + printf("%sVolume: Min %i, Max %i\n", space, + info.data.teffect1.min_volume, + info.data.teffect1.max_volume); + if (info.data.teffect1.effect & SND_MIXER_EFF1_CENTER) + printf("%sCenter: Min %i, Max %i\n", space, + info.data.teffect1.min_center, + info.data.teffect1.max_center); + if (info.data.teffect1.effect & SND_MIXER_EFF1_SPACE) + printf("%sSpace: Min %i, Max %i\n", space, + info.data.teffect1.min_space, + info.data.teffect1.max_space); + if (info.data.teffect1.effect & SND_MIXER_EFF1_DEPTH) + printf("%sDepth: Min %i, Max %i\n", space, + info.data.teffect1.min_depth, + info.data.teffect1.max_depth); + if (info.data.teffect1.effect & SND_MIXER_EFF1_DELAY) + printf("%sDelay: Min %i, Max %i\n", space, + info.data.teffect1.min_delay, + info.data.teffect1.max_delay); + if (info.data.teffect1.effect & SND_MIXER_EFF1_FEEDBACK) + printf("%sFeedback: Min %i, Max %i\n", space, + info.data.teffect1.min_feedback, + info.data.teffect1.max_feedback); + break; + default: + printf("%sInfo handler for type %i is not available\n", space, info.eid.type); + } + snd_mixer_element_info_free(&info); + return 0; +} + +int show_element_contents(void *handle, snd_mixer_eid_t *eid, const char *space) +{ + int err, idx; + snd_mixer_element_t element; + snd_mixer_element_info_t info; + + if (snd_mixer_element_has_control(eid) != 1) + return 0; + bzero(&element, sizeof(element)); + bzero(&info, sizeof(info)); + element.eid = info.eid = *eid; + if ((err = snd_mixer_element_build(handle, &element)) < 0) { + error("Mixer %i/%i element error: %s", card, device, snd_strerror(err)); + return -1; + } + if (snd_mixer_element_has_info(eid) == 1) { + if ((err = snd_mixer_element_info_build(handle, &info)) < 0) { + error("Mixer %i/%i element error: %s", card, device, snd_strerror(err)); + return -1; + } + } + switch (element.eid.type) { + case SND_MIXER_ETYPE_SWITCH1: + for (idx = 0; idx < element.data.switch1.sw; idx++) { + int val = snd_mixer_get_bit(element.data.switch1.psw, idx); + printf("%sVoice %i: Switch is %s\n", space, idx, val ? "ON" : "OFF"); + } + break; + case SND_MIXER_ETYPE_SWITCH2: + printf("%sSwitch is %s\n", space, element.data.switch2.sw ? "ON" : "OFF"); + break; + case SND_MIXER_ETYPE_SWITCH3: + if (element.data.switch3.rsw != info.data.switch3.voices * info.data.switch3.voices) { + error("Switch3 !!!\n"); + goto __end; + } + for (idx = 0; idx < element.data.switch3.rsw; idx++) { + snd_mixer_voice_t input, output; + int val = snd_mixer_get_bit(element.data.switch3.prsw, idx); + printf("%sInput <", space); + input = info.data.switch3.pvoices[idx / info.data.switch3.voices]; + output = info.data.switch3.pvoices[idx % info.data.switch3.voices]; + if (input.vindex) { + printf("voice %i", input.voice); + } else { + printf(speaker_position(input.voice)); + } + printf("> Output <"); + if (output.vindex) { + printf("voice %i", output.voice); + } else { + printf(speaker_position(output.voice)); + } + printf(">: Switch is %s\n", val ? "ON" : "OFF"); + } + break; + case SND_MIXER_ETYPE_VOLUME1: + for (idx = 0; idx < element.data.volume1.voices; idx++) { + int val = element.data.volume1.pvoices[idx]; + printf("%sVoice %i: Value %s\n", space, idx, + get_percent1(val, info.data.volume1.prange[idx].min, + info.data.volume1.prange[idx].max, + info.data.volume1.prange[idx].min_dB, + info.data.volume1.prange[idx].max_dB)); + } + break; + case SND_MIXER_ETYPE_ACCU3: + for (idx = 0; idx < element.data.accu3.voices; idx++) { + int val = element.data.accu3.pvoices[idx]; + printf("%sVoice %i: Value %s\n", space, idx, + get_percent1(val, info.data.accu3.prange[idx].min, + info.data.accu3.prange[idx].max, + info.data.accu3.prange[idx].min_dB, + info.data.accu3.prange[idx].max_dB)); + } + break; + case SND_MIXER_ETYPE_MUX1: + for (idx = 0; idx < element.data.mux1.output; idx++) { + snd_mixer_eid_t *eid = &element.data.mux1.poutput[idx]; + printf("%sVoice %i: Element '%s',%i,%i\n", space, idx, + element_name(eid->name), + eid->index, eid->type); + } + break; + case SND_MIXER_ETYPE_TONE_CONTROL1: + if (element.data.tc1.tc & SND_MIXER_TC1_SW) + printf("%sOn/Off switch is %s\n", space, element.data.tc1.sw ? "ON" : "OFF"); + if (element.data.tc1.tc & SND_MIXER_TC1_BASS) + printf("%sBass: %s\n", space, get_percent1(element.data.tc1.bass, info.data.tc1.min_bass, info.data.tc1.max_bass, info.data.tc1.min_bass_dB, info.data.tc1.max_bass_dB)); + if (element.data.tc1.tc & SND_MIXER_TC1_TREBLE) + printf("%sTreble: %s\n", space, get_percent1(element.data.tc1.treble, info.data.tc1.min_treble, info.data.tc1.max_treble, info.data.tc1.min_treble_dB, info.data.tc1.max_treble_dB)); + break; + case SND_MIXER_ETYPE_3D_EFFECT1: + if (element.data.teffect1.effect & SND_MIXER_EFF1_SW) + printf("%sOn/Off switch is %s\n", space, element.data.teffect1.sw ? "ON" : "OFF"); + if (element.data.teffect1.effect & SND_MIXER_EFF1_MONO_SW) + printf("%sMono processing switch is %s\n", space, element.data.teffect1.mono_sw ? "ON" : "OFF"); + if (element.data.teffect1.effect & SND_MIXER_EFF1_WIDE) + printf("%sWide: %s\n", space, get_percent(element.data.teffect1.wide, info.data.teffect1.min_wide, info.data.teffect1.max_wide)); + if (element.data.teffect1.effect & SND_MIXER_EFF1_VOLUME) + printf("%sVolume: %s\n", space, get_percent(element.data.teffect1.volume, info.data.teffect1.min_volume, info.data.teffect1.max_volume)); + if (element.data.teffect1.effect & SND_MIXER_EFF1_CENTER) + printf("%sCenter: %s\n", space, get_percent(element.data.teffect1.center, info.data.teffect1.min_center, info.data.teffect1.max_center)); + if (element.data.teffect1.effect & SND_MIXER_EFF1_SPACE) + printf("%sSpace: %s\n", space, get_percent(element.data.teffect1.space, info.data.teffect1.min_space, info.data.teffect1.max_space)); + if (element.data.teffect1.effect & SND_MIXER_EFF1_DEPTH) + printf("%sDepth: %s\n", space, get_percent(element.data.teffect1.depth, info.data.teffect1.min_depth, info.data.teffect1.max_depth)); + if (element.data.teffect1.effect & SND_MIXER_EFF1_DELAY) + printf("%sDelay: %s\n", space, get_percent(element.data.teffect1.delay, info.data.teffect1.min_delay, info.data.teffect1.max_delay)); + if (element.data.teffect1.effect & SND_MIXER_EFF1_FEEDBACK) + printf("%sFeedback: %s\n", space, get_percent(element.data.teffect1.feedback, info.data.teffect1.min_feedback, info.data.teffect1.max_feedback)); + break; + default: + printf("%sRead handler for type %i is not available\n", space, element.eid.type); + } + __end: + snd_mixer_element_free(&element); + if (snd_mixer_element_has_info(eid)) + snd_mixer_element_info_free(&info); + return 0; +} + +int elements(void) +{ + int err, idx; + void *handle; + snd_mixer_elements_t elements; + snd_mixer_eid_t *element; + + if ((err = snd_mixer_open(&handle, card, device)) < 0) { + error("Mixer %i/%i open error: %s", card, device, snd_strerror(err)); + return -1; + } + bzero(&elements, sizeof(elements)); + if ((err = snd_mixer_elements(handle, &elements)) < 0) { + error("Mixer %i/%i elements error: %s", card, device, snd_strerror(err)); + return -1; + } + elements.pelements = (snd_mixer_eid_t *)malloc(elements.elements_over * sizeof(snd_mixer_eid_t)); + if (!elements.pelements) { + error("No enough memory"); + return -1; + } + elements.elements_size = elements.elements_over; + elements.elements_over = elements.elements = 0; + if ((err = snd_mixer_elements(handle, &elements)) < 0) { + error("Mixer %i/%i elements (2) error: %s", card, device, snd_strerror(err)); + return -1; + } + for (idx = 0; idx < elements.elements; idx++) { + element = &elements.pelements[idx]; + printf("Element '%s',%i,%s\n", element_name(element->name), element->index, element_type(element->type)); + show_element(handle, element, " "); + show_element_info(handle, element, " "); + } + free(elements.pelements); + snd_mixer_close(handle); + return 0; +} + +int elements_contents(void) +{ + int err, idx; + void *handle; + snd_mixer_elements_t elements; + snd_mixer_eid_t *element; + + if ((err = snd_mixer_open(&handle, card, device)) < 0) { + error("Mixer %i/%i open error: %s", card, device, snd_strerror(err)); + return -1; + } + bzero(&elements, sizeof(elements)); + if ((err = snd_mixer_elements(handle, &elements)) < 0) { + error("Mixer %i/%i elements error: %s", card, device, snd_strerror(err)); + return -1; + } + elements.pelements = (snd_mixer_eid_t *)malloc(elements.elements_over * sizeof(snd_mixer_eid_t)); + if (!elements.pelements) { + error("No enough memory"); + return -1; + } + elements.elements_size = elements.elements_over; + elements.elements_over = elements.elements = 0; + if ((err = snd_mixer_elements(handle, &elements)) < 0) { + error("Mixer %i/%i elements (2) error: %s", card, device, snd_strerror(err)); + return -1; + } + for (idx = 0; idx < elements.elements; idx++) { + element = &elements.pelements[idx]; + printf("Element '%s',%i,%s\n", element_name(element->name), element->index, element_type(element->type)); + show_element_info(handle, element, " "); + show_element_contents(handle, element, " "); + } + free(elements.pelements); + snd_mixer_close(handle); + return 0; +} + +static const char *group_name(const char *name) +{ + static char res[25]; + + strncpy(res, name, 24); + res[24] = '\0'; + return res; +} + +int show_group(void *handle, snd_mixer_gid_t *gid, const char *space) +{ + int err, idx; + snd_mixer_group_t group; + snd_mixer_eid_t *element; + + bzero(&group, sizeof(group)); + group.gid = *gid; + if ((err = snd_mixer_group(handle, &group)) < 0) { + error("Mixer %i/%i group error: %s", card, device, snd_strerror(err)); + return -1; + } + group.pelements = (snd_mixer_eid_t *)malloc(group.elements_over * sizeof(snd_mixer_eid_t)); + if (!group.pelements) { + error("No enough memory..."); + return -1; + } + group.elements_size = group.elements_over; + group.elements = group.elements_over = 0; + if ((err = snd_mixer_group(handle, &group)) < 0) { + error("Mixer %i/%i group (2) error: %s", card, device, snd_strerror(err)); + return -1; + } + for (idx = 0; idx < group.elements; idx++) { + element = &group.pelements[idx]; + printf("%sElement '%s',%i,%s\n", space, element_name(element->name), element->index, element_type(element->type)); + } + free(group.pelements); + return 0; +} + +int groups(void) +{ + int err, idx; + void *handle; + snd_mixer_groups_t groups; + snd_mixer_gid_t *group; + + if ((err = snd_mixer_open(&handle, card, device)) < 0) { + error("Mixer %i/%i open error: %s", card, device, snd_strerror(err)); + return -1; + } + bzero(&groups, sizeof(groups)); + if ((err = snd_mixer_groups(handle, &groups)) < 0) { + error("Mixer %i/%i groups error: %s", card, device, snd_strerror(err)); + return -1; + } + groups.pgroups = (snd_mixer_gid_t *)malloc(groups.groups_over * sizeof(snd_mixer_eid_t)); + if (!groups.pgroups) { + error("No enough memory"); + return -1; + } + groups.groups_size = groups.groups_over; + groups.groups_over = groups.groups = 0; + if ((err = snd_mixer_groups(handle, &groups)) < 0) { + error("Mixer %i/%i groups (2) error: %s", card, device, snd_strerror(err)); + return -1; + } + for (idx = 0; idx < groups.groups; idx++) { + group = &groups.pgroups[idx]; + printf("Group '%s', %i\n", group_name(group->name), group->index); + show_group(handle, group, " "); + } + free(groups.pgroups); + snd_mixer_close(handle); + return 0; +} + +static int parse_eid(const char *str, snd_mixer_eid_t *eid) +{ + int c, size, idx; + char *ptr; + + while (*str == ' ' || *str == '\t') + str++; + if (!(*str)) + return 1; + bzero(eid, sizeof(*eid)); + ptr = eid->name; + size = 0; + if (*str != '"' && *str != '\'') { + while (*str && *str != ',') { + if (size < sizeof(eid->name)) { + *ptr++ = *str; + size++; + } + str++; + } + } else { + c = *str++; + while (*str && *str != c) { + if (size < sizeof(eid->name)) { + *ptr++ = *str; + size++; + } + str++; + } + if (*str == c) + str++; + } + if (*str != ',') + return 1; + str++; + if (!isdigit(*str)) + return 1; + eid->type = atoi(str); + while (isdigit(*str)) + str++; + if (*str != ',') + return 1; + str++; + if (isdigit(*str)) { + eid->type = atoi(str); + return 0; + } else { + for (idx = 0; mixer_types[idx].type >= 0; idx++) + if (!strncmp(mixer_types[idx].name, str, strlen(mixer_types[idx].name))) { + eid->type = mixer_types[idx].type; + return 0; + } + } + return 1; +} + +int eset_switch1(int argc, char *argv[], void *handle, snd_mixer_eid_t *eid) +{ + int err, tmp, idx = 0; + snd_mixer_element_t element; + char *ptr; + + if (argc != 1) { + fprintf(stderr, "The set Switch1 command requires an argument:\n"); + fprintf(stderr, " on/off[,on/off] ...\n"); + return 1; + } + bzero(&element, sizeof(element)); + element.eid = *eid; + if ((err = snd_mixer_element_build(handle, &element)) < 0) { + error("Mixer element build error: %s", snd_strerror(err)); + return 1; + } + if (!strcmp(argv[0], "on") || !strcmp(argv[0], "off")) { + tmp = !strcmp(argv[0], "on"); + for (idx = 0; idx < element.data.switch1.sw; idx++) + snd_mixer_set_bit(element.data.switch1.psw, idx, tmp); + } else { + ptr = argv[idx]; + for (idx = 0; idx < element.data.switch1.sw; idx++) { + tmp = !strncmp(ptr, "on", 2); + snd_mixer_set_bit(element.data.switch1.psw, idx, tmp); + while (*ptr && *ptr != ',') + ptr++; + if (*ptr == ',') + ptr++; + } + } + if ((err = snd_mixer_element_write(handle, &element)) < 0) { + error("Mixer element write error: %s\n", snd_strerror(err)); + snd_mixer_element_free(&element); + return 1; + } + snd_mixer_element_free(&element); + return 0; +} + +int eset_switch2(int argc, char *argv[], void *handle, snd_mixer_eid_t *eid) +{ + int err; + snd_mixer_element_t element; + + if (argc != 1) { + fprintf(stderr, "The set Switch2 command requires an argument:\n"); + fprintf(stderr, " on/off\n"); + return 1; + } + bzero(&element, sizeof(element)); + element.eid = *eid; + if ((err = snd_mixer_element_build(handle, &element)) < 0) { + error("Mixer element build error: %s", snd_strerror(err)); + return 1; + } + element.data.switch2.sw = !strcmp(argv[0], "on") ? 1 : 0; + if ((err = snd_mixer_element_write(handle, &element)) < 0) { + error("Mixer element write error: %s\n", snd_strerror(err)); + snd_mixer_element_free(&element); + return 1; + } + snd_mixer_element_free(&element); + return 0; +} + +int eset_volume1(int argc, char *argv[], void *handle, snd_mixer_eid_t *eid) +{ + int err, tmp, idx = 0; + snd_mixer_element_t element; + snd_mixer_element_info_t info; + char *ptr; + + if (argc != 1 || (!isdigit(*argv[0]) && *argv[0] != ':')) { + fprintf(stderr, "The set Volume1 command requires an argument:\n"); + fprintf(stderr, " vol[,vol] ...\n"); + return 1; + } + bzero(&info, sizeof(info)); + info.eid = *eid; + if ((err = snd_mixer_element_info_build(handle, &info)) < 0) { + error("Mixer element read error: %s", snd_strerror(err)); + return 1; + } + bzero(&element, sizeof(element)); + element.eid = *eid; + if ((err = snd_mixer_element_build(handle, &element)) < 0) { + error("Mixer element read error: %s", snd_strerror(err)); + snd_mixer_element_info_free(&info); + return 1; + } + if (!strchr(argv[0], ',')) { + for (idx = 0; idx < element.data.volume1.voices; idx++) { + ptr = argv[0]; + tmp = get_volume(&ptr, + info.data.volume1.prange[idx].min, + info.data.volume1.prange[idx].max, + info.data.volume1.prange[idx].min_dB, + info.data.volume1.prange[idx].max_dB); + element.data.volume1.pvoices[idx] = tmp; + } + } else { + ptr = argv[idx]; + for (idx = 0; idx < element.data.volume1.voices; idx++) { + tmp = get_volume(&ptr, + info.data.volume1.prange[idx].min, + info.data.volume1.prange[idx].max, + info.data.volume1.prange[idx].min_dB, + info.data.volume1.prange[idx].max_dB); + element.data.volume1.pvoices[idx] = tmp; + } + } + if ((err = snd_mixer_element_write(handle, &element)) < 0) { + error("Mixer element write error: %s\n", snd_strerror(err)); + snd_mixer_element_free(&element); + snd_mixer_element_info_free(&info); + return 1; + } + snd_mixer_element_free(&element); + snd_mixer_element_info_free(&info); + return 0; +} + +int eset_accu3(int argc, char *argv[], void *handle, snd_mixer_eid_t *eid) +{ + int err, tmp, idx = 0; + snd_mixer_element_t element; + snd_mixer_element_info_t info; + char *ptr; + + if (argc != 1 || (!isdigit(*argv[0]) && *argv[0] != ':')) { + fprintf(stderr, "The set Accu3 command requires an argument:\n"); + fprintf(stderr, " vol[,vol] ...\n"); + return 1; + } + bzero(&info, sizeof(info)); + info.eid = *eid; + if ((err = snd_mixer_element_info_build(handle, &info)) < 0) { + error("Mixer element read error: %s", snd_strerror(err)); + return 1; + } + bzero(&element, sizeof(element)); + element.eid = *eid; + if ((err = snd_mixer_element_build(handle, &element)) < 0) { + error("Mixer element read error: %s", snd_strerror(err)); + snd_mixer_element_info_free(&info); + return 1; + } + if (!strchr(argv[0], ',')) { + for (idx = 0; idx < element.data.accu3.voices; idx++) { + ptr = argv[0]; + tmp = get_volume(&ptr, + info.data.accu3.prange[idx].min, + info.data.accu3.prange[idx].max, + info.data.accu3.prange[idx].min_dB, + info.data.accu3.prange[idx].max_dB); + element.data.accu3.pvoices[idx] = tmp; + } + } else { + ptr = argv[idx]; + for (idx = 0; idx < element.data.volume1.voices; idx++) { + tmp = get_volume(&ptr, + info.data.accu3.prange[idx].min, + info.data.accu3.prange[idx].max, + info.data.accu3.prange[idx].min_dB, + info.data.accu3.prange[idx].max_dB); + element.data.accu3.pvoices[idx] = tmp; + } + } + if ((err = snd_mixer_element_write(handle, &element)) < 0) { + error("Mixer element write error: %s\n", snd_strerror(err)); + snd_mixer_element_free(&element); + snd_mixer_element_info_free(&info); + return 1; + } + snd_mixer_element_free(&element); + snd_mixer_element_info_free(&info); + return 0; +} + +int eset_mux1(int argc, char *argv[], void *handle, snd_mixer_eid_t *eid) +{ + int err, idx = 0; + snd_mixer_element_t element; + snd_mixer_element_info_t info; + snd_mixer_eid_t xeid; + + if (argc < 1) { + fprintf(stderr, "The set Mux1 command requires an argument:\n"); + fprintf(stderr, " element[ element] ...\n"); + return 1; + } + bzero(&info, sizeof(info)); + info.eid = *eid; + if ((err = snd_mixer_element_info_build(handle, &info)) < 0) { + error("Mixer element read error: %s", snd_strerror(err)); + return 1; + } + bzero(&element, sizeof(element)); + element.eid = *eid; + if ((err = snd_mixer_element_build(handle, &element)) < 0) { + error("Mixer element read error: %s", snd_strerror(err)); + snd_mixer_element_info_free(&info); + return 1; + } + if (argc == 1) { + if (parse_eid(argv[0], &xeid)) { + fprintf(stderr, "Wrong element identifier: %s\n", argv[0]); + snd_mixer_element_free(&element); + snd_mixer_element_info_free(&info); + return 1; + } + for (idx = 0; idx < element.data.mux1.output; idx++) + element.data.mux1.poutput[idx] = xeid; + } else { + for (idx = 0; idx < element.data.volume1.voices; idx++) { + if (parse_eid(argv[idx >= argc ? argc - 1 : idx], &xeid)) { + fprintf(stderr, "Wrong element identifier: %s\n", argv[0]); + snd_mixer_element_free(&element); + snd_mixer_element_info_free(&info); + return 1; + } + element.data.mux1.poutput[idx] = xeid; + } + } + if ((err = snd_mixer_element_write(handle, &element)) < 0) { + error("Mixer element write error: %s\n", snd_strerror(err)); + snd_mixer_element_free(&element); + snd_mixer_element_info_free(&info); + return 1; + } + snd_mixer_element_free(&element); + snd_mixer_element_info_free(&info); + return 0; +} + +int eset(int argc, char *argv[]) +{ + int err; + void *handle; + snd_mixer_eid_t eid; + + if (argc < 1) { + fprintf(stderr, "Specify a full element identifier: 'name',index,type\n"); + return 1; + } + if (parse_eid(argv[0], &eid)) { + fprintf(stderr, "Wrong element identifier: %s\n", argv[0]); + return 1; + } + if ((err = snd_mixer_open(&handle, card, device)) < 0) { + error("Mixer %i/%i open error: %s\n", card, device, snd_strerror(err)); + return -1; + } + if (!quiet) { + printf("Element '%s',%i,%s\n", element_name(eid.name), eid.index, element_type(eid.type)); + } + switch (eid.type) { + case SND_MIXER_ETYPE_SWITCH1: + if (eset_switch1(argc - 1, argv + 1, handle, &eid)) + goto __end; + break; + case SND_MIXER_ETYPE_SWITCH2: + if (eset_switch2(argc - 1, argv + 1, handle, &eid)) + goto __end; + break; + case SND_MIXER_ETYPE_VOLUME1: + if (eset_volume1(argc - 1, argv + 1, handle, &eid)) + goto __end; + break; + case SND_MIXER_ETYPE_ACCU3: + if (eset_accu3(argc - 1, argv + 1, handle, &eid)) + goto __end; + break; + case SND_MIXER_ETYPE_MUX1: + if (eset_mux1(argc - 1, argv + 1, handle, &eid)) + goto __end; + break; + } + if (!quiet) { + if (snd_mixer_element_has_info(&eid)) { + show_element_info(handle, &eid, " "); + } + if (snd_mixer_element_has_control(&eid)) { + show_element_contents(handle, &eid, " "); + } + } + __end: + snd_mixer_close(handle); + return 0; +} + +int eget(int argc, char *argv[]) +{ + int err; + void *handle; + snd_mixer_eid_t eid; + + if (argc < 1) { + fprintf(stderr, "Specify a full element identifier: 'name',index,type\n"); + return 1; + } + if (parse_eid(argv[0], &eid)) { + fprintf(stderr, "Wrong element identifier: %s\n", argv[0]); + return 1; + } + if ((err = snd_mixer_open(&handle, card, device)) < 0) { + error("Mixer %i/%i open error: %s\n", card, device, snd_strerror(err)); + return -1; + } + printf("Element '%s',%i,%s\n", element_name(eid.name), eid.index, element_type(eid.type)); + if (show_element(handle, &eid, " ") >= 0) { + if (snd_mixer_element_has_info(&eid)) { + show_element_info(handle, &eid, " "); + } + if (snd_mixer_element_has_control(&eid)) { + show_element_contents(handle, &eid, " "); + } + } + snd_mixer_close(handle); + return 0; +} + +int main(int argc, char *argv[]) +{ + int morehelp; + struct option long_option[] = + { + {"help", 0, NULL, HELPID_HELP}, + {"card", 1, NULL, HELPID_CARD}, + {"device", 1, NULL, HELPID_DEVICE}, + {"quiet", 0, NULL, HELPID_QUIET}, + {"debug", 0, NULL, HELPID_DEBUG}, + {"version", 0, NULL, HELPID_VERSION}, + {NULL, 0, NULL, 0}, + }; + + morehelp = 0; + card = snd_defaults_mixer_card(); + device = snd_defaults_mixer_device(); + if (card < 0 || device < 0) { + fprintf(stderr, "The ALSA sound driver was not detected in this system.\n"); + return 1; + } + while (1) { + int c; + + if ((c = getopt_long(argc, argv, "hc:d:qDv", long_option, NULL)) < 0) + break; + switch (c) { + case 'h': + case HELPID_HELP: + morehelp++; + break; + case 'c': + case HELPID_CARD: + card = snd_card_name(optarg); + break; + case 'd': + case HELPID_DEVICE: + device = device; + break; + case 'q': + case HELPID_QUIET: + quiet = 1; + break; + case 'D': + case HELPID_DEBUG: + debugflag = 1; + break; + case 'v': + case HELPID_VERSION: + printf("amixer version " SND_UTIL_VERSION_STR "\n"); + return 1; + default: + fprintf(stderr, "\07Invalid switch or option needs an argument.\n"); + morehelp++; + } + } + if (morehelp) { + help(); + return 1; + } + if (argc - optind <= 0) { + fprintf(stderr, "amixer: Specify command...\n"); + return 0; + } + if (!strcmp(argv[optind], "info")) { + return info() ? 1 : 0; + } else if (!strcmp(argv[optind], "elements")) { + return elements() ? 1 : 0; + } else if (!strcmp(argv[optind], "contents")) { + return elements_contents() ? 1 : 0; + } else if (!strcmp(argv[optind], "groups")) { + return groups() ? 1 : 0; + } else if (!strcmp(argv[optind], "eset")) { + return eset(argc - optind - 1, argc - optind > 1 ? argv + optind + 1 : NULL) ? 1 : 0; + } else if (!strcmp(argv[optind], "eget")) { + return eget(argc - optind - 1, argc - optind > 1 ? argv + optind + 1 : NULL) ? 1 : 0; + } else { + fprintf(stderr, "amixer: Unknown command '%s'...\n", argv[optind]); + } + + return 0; +} diff --git a/amixer/amixer.cpp b/amixer/amixer.cpp deleted file mode 100644 index 46fcba2..0000000 --- a/amixer/amixer.cpp +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include -#include -#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? */ - } -} - diff --git a/amixer/amixer.h b/amixer/amixer.h index 64c9fa3..7e6f5aa 100644 --- a/amixer/amixer.h +++ b/amixer/amixer.h @@ -1,4 +1,6 @@ /* + * ALSA command line mixer utility + * Copyright (c) 1999 by Jaroslav Kysela * * 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" diff --git a/amixer/atypes.h b/amixer/atypes.h deleted file mode 100644 index 8ccd461..0000000 --- a/amixer/atypes.h +++ /dev/null @@ -1 +0,0 @@ -typedef int int32;