]> git.alsa-project.org Git - alsa-utils.git/commitdiff
Mixer v2.0 merged to the main CVS tree...
authorJaroslav Kysela <perex@perex.cz>
Sun, 2 May 1999 16:21:40 +0000 (16:21 +0000)
committerJaroslav Kysela <perex@perex.cz>
Sun, 2 May 1999 16:21:40 +0000 (16:21 +0000)
16 files changed:
alsactl/Makefile.am
alsactl/alsactl.c
alsactl/alsactl.h
alsactl/alsactl_lexer.l
alsactl/alsactl_parser.y
alsactl/merge.c [new file with mode: 0644]
alsactl/setup.c
alsamixer/alsamixer.c
alsamixer/alsamixer_abramo.c [new file with mode: 0644]
amixer/Makefile.am
amixer/README.first [deleted file]
amixer/amain.cpp [deleted file]
amixer/amixer.c [new file with mode: 0644]
amixer/amixer.cpp [deleted file]
amixer/amixer.h
amixer/atypes.h [deleted file]

index 0ebd56ec9cd231d27d6d3ef2127ba609c25942da..a4ce4d2af601311bdac3e99cf15ed14edd5157dd 100644 (file)
@@ -2,5 +2,10 @@ sbin_PROGRAMS=alsactl
 noinst_HEADERS=alsactl.h
 man_MANS=alsactl.1
 
-alsactl_SOURCES=alsactl.c alsactl_parser.y setup.c alsactl_lexer.l
+alsactl_SOURCES=alsactl.c alsactl_parser.y setup.c merge.c alsactl_lexer.l
 YFLAGS=-d
+
+# lexer / parser debug
+#CFLAGS=-pipe -g -DYYDEBUG
+#LFLAGS=-d
+
index 18ebf6d37d1c1322bc1fb54ba66171bfc6657087..31b231c259fb49f836170151b270dea7acceea99 100644 (file)
@@ -64,28 +64,6 @@ static void help(void)
        printf("                    from configuration file\n");
 }
 
-static int collect_all(void)
-{
-       int idx, err;
-       unsigned int card_mask;
-
-       card_mask = snd_cards_mask();
-       if (!card_mask) {
-               error("No soundcards found...");
-               return 1;
-       }
-       soundcard_setup_init();
-       for (idx = 0; idx < 32; idx++) {
-               if (card_mask & (1 << idx)) {   /* find each installed soundcards */
-                       if ((err = soundcard_setup_collect(idx))) {
-                               soundcard_setup_done();
-                               return err;
-                       }
-               }
-       }
-       return 0;
-}
-
 static int store_setup(const char *cardname)
 {
        int err;
@@ -101,12 +79,18 @@ static int store_setup(const char *cardname)
                soundcard_setup_init();
                for (idx = 0; idx < 32; idx++) {
                        if (card_mask & (1 << idx)) {   /* find each installed soundcards */
-                               if ((err = soundcard_setup_collect(idx))) {
+                               if ((err = soundcard_setup_collect_switches(idx))) {
+                                       soundcard_setup_done();
+                                       return err;
+                               }
+                               if ((err = soundcard_setup_collect_data(idx))) {
                                        soundcard_setup_done();
                                        return err;
                                }
                        }
                }
+               err = soundcard_setup_write(cfgfile, -1);
+               soundcard_setup_done();
        } else {
                int cardno;
 
@@ -115,17 +99,17 @@ static int store_setup(const char *cardname)
                        error("Cannot find soundcard '%s'...", cardname);
                        return 1;
                }
-               if ((err = collect_all()))
-                       return err;
-               if ((err = soundcard_setup_load(cfgfile, 1)))
+               if ((err = soundcard_setup_collect_switches(cardno))) {
+                       soundcard_setup_done();
                        return err;
-               if ((err = soundcard_setup_collect(cardno))) {
+               }
+               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;
 }
-
-
index a7c68848292bac921c2872c1a7ce8d396bc7ea64..9b80f8677f2980dfbaebe976259b33de97f8db9b 100644 (file)
@@ -48,21 +48,16 @@ struct ctl {
        struct ctl_switch *switches;
 };
 
-struct mixer_channel {
-       int no;
-       int direction;
-       int voice;
-       snd_mixer_channel_info_t info;
-       snd_mixer_channel_t data;
-       snd_mixer_channel_direction_info_t dinfo[2];
-       snd_mixer_channel_direction_t ddata[2];
-       struct mixer_channel *next;
+struct mixer_element {
+       snd_mixer_element_info_t info;
+       snd_mixer_element_t element;
+       struct mixer_element *next;
 };
 
 struct mixer {
        int no;
        snd_mixer_info_t info;
-       struct mixer_channel *channels;
+       struct mixer_element *elements;
        struct ctl_switch *switches;
        struct mixer *next;
 };
@@ -93,10 +88,17 @@ struct soundcard {
 };
 
 extern struct soundcard *soundcards;
+extern struct soundcard *rsoundcards;  /* read soundcards */
 
 void soundcard_setup_init(void);
 void soundcard_setup_done(void);
 int soundcard_setup_load(const char *filename, int skip);
-int soundcard_setup_write(const char *filename);
-int soundcard_setup_collect(int cardno);
-int soundcard_setup_process(int cardno);
+int soundcard_setup_write(const char *filename, int cardno);
+int soundcard_setup_collect_switches(int cardno);
+int soundcard_setup_collect_data(int cardno);
+int soundcard_setup_merge_switches(int cardno);
+int soundcard_setup_merge_data(int cardno);
+int soundcard_setup_process_switches(int cardno);
+int soundcard_setup_process_data(int cardno);
+
+char *mixer_element_id(snd_mixer_eid_t *eid);
index 8d7f962823ac0b5824e52079f56a1bec106e9801..4b8b2d1d353d6beb864def4e4dd05f853f31947f 100644 (file)
 %{
 
 #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 */
index 8c547504168401cbe7eb5fe72b809a4350330c8f..3287032b9019160c4ad164140d0badc6e4d70c8e 100644 (file)
@@ -32,36 +32,53 @@ extern char cfgfile[];
 extern int linecount;
 extern FILE *yyin;
 
+       /* structure for byte arrays */ 
+
+struct bytearray {
+  unsigned char *data;
+  size_t datalen;
+};
+
        /* local functions */
 
 static void yyerror(char *, ...);
 
-static void select_soundcard(char *name);
-static void select_mixer(char *name);
-static void select_pcm(char *name);
-static void select_rawmidi(char *name);
-
-static void 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);
-
-#define SWITCH_CONTROL         0
-#define SWITCH_MIXER           1
-#define SWITCH_PCM             2
-#define SWITCH_RAWMIDI         3
-
-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 build_soundcard(char *name);
+static void build_mixer(char *name);
+static void build_pcm(char *name);
+static void build_rawmidi(char *name);
+
+static void build_mixer_element(char *name, int index, int etype);
+
+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 mixer_switch1(int end);
+static void mixer_switch1_value(int val);
+static void mixer_switch2(int end);
+static void mixer_switch2_value(int val);
+static void mixer_switch3(int end);
+static void mixer_switch3_value(int val);
+static void mixer_volume1(int end);
+static void mixer_volume1_value(int val);
+static void mixer_3d_effect1(int end);
+static void mixer_3d_effect1_value(unsigned int effect, int val);
+static void mixer_accu3(int end);
+static void mixer_accu3_value(int val);
+static void mixer_mux1(int end);
+static void mixer_mux1_value(char *str, int index, int type);
+static void mixer_mux2(int end);
+static void mixer_mux2_value(char *str, int index, int type);
+static void mixer_tone_control1(int end);
+static void mixer_tone_control1_value(unsigned int effect, int val);
 
 static void set_switch_boolean(int val);
 static void set_switch_integer(int val);
+static void set_switch_bytearray(struct bytearray val);
 static void set_switch_iec958ocs_begin(int end);
 static void set_switch_iec958ocs(int idx, unsigned short val, unsigned short mask);
 
@@ -71,10 +88,8 @@ static struct soundcard *Xsoundcard = NULL;
 static struct mixer *Xmixer = NULL;
 static struct pcm *Xpcm = NULL;
 static struct rawmidi *Xrawmidi = NULL;
-static struct mixer_channel *Xchannel = NULL;
-static int Xswitchtype = SWITCH_CONTROL;
-static int *Xswitchchange = NULL;
-static snd_switch_t *Xswitch = NULL;
+static struct mixer_element *Xelement  = NULL;
+static struct ctl_switch *Xswitch = NULL;
 static unsigned int Xswitchiec958ocs = 0;
 static unsigned short Xswitchiec958ocs1[16];
 
@@ -86,7 +101,7 @@ static unsigned short Xswitchiec958ocs1[16];
     int b_value;
     int i_value;
     char *s_value;
-    unsigned char *a_value;
+    struct bytearray a_value;
   };
 
 %token <b_value> L_TRUE L_FALSE
@@ -101,15 +116,20 @@ static unsigned short Xswitchiec958ocs1[16];
        /* misc */
 %token L_DOUBLE1
        /* other keywords */
-%token L_SOUNDCARD L_MIXER L_CHANNEL L_STEREO L_MONO L_SWITCH L_RAWDATA
-%token L_CONTROL L_PCM L_RAWMIDI L_PLAYBACK L_RECORD L_OUTPUT L_INPUT
+%token L_SOUNDCARD L_MIXER L_ELEMENT L_SWITCH L_RAWDATA
+%token L_CONTROL L_PCM L_RAWMIDI L_PLAYBACK L_RECORD L_INPUT L_OUTPUT
+%token L_SWITCH1 L_SWITCH2 L_SWITCH3 L_VOLUME1 L_3D_EFFECT1 L_ACCU3
+%token L_MUX1 L_MUX2 L_TONE_CONTROL1
 %token L_IEC958OCS L_3D L_RESET L_USER L_VALID L_DATA L_PROTECT L_PRE2
-%token L_FSUNLOCK L_TYPE L_GSTATUS L_ENABLE L_DISABLE L_MUTE L_SWAP
+%token L_FSUNLOCK L_TYPE L_GSTATUS L_ENABLE L_DISABLE
+%token L_SW L_MONO_SW L_WIDE L_VOLUME L_CENTER L_SPACE L_DEPTH L_DELAY
+%token L_FEEDBACK L_BASS L_TREBLE
+
 
 %type <b_value> boolean
 %type <i_value> integer
 %type <s_value> string
-%type <a_value> bytearray
+%type <a_value> rawdata
 
 %%
 
@@ -117,8 +137,9 @@ lines       : line
        | lines line
        ;
 
-line   : L_SOUNDCARD '(' string { select_soundcard( $3 ); } L_DOUBLE1 soundcards { select_soundcard( NULL ); } '}'
-       | error                 { yyerror( "unknown keyword in top level" ); }
+line   : L_SOUNDCARD '(' string { build_soundcard($3); }
+         L_DOUBLE1 soundcards '}' { build_soundcard(NULL); }
+       | error                 { yyerror("unknown keyword in top level"); }
        ;
 
 soundcards : soundcard
@@ -126,85 +147,165 @@ soundcards : soundcard
        ;
 
 soundcard : L_CONTROL '{' controls '}'
-       | L_MIXER '(' string { select_mixer( $3 ); } L_DOUBLE1 mixers { select_mixer( NULL ); } '}'
-       | L_PCM '(' string { select_pcm( $3 ); } L_DOUBLE1 pcms { select_pcm( NULL ); } '}'
-       | L_RAWMIDI '(' string { select_rawmidi( $3 ); } L_DOUBLE1 rawmidis { select_rawmidi( NULL ); } '}'
-       | error                 { yyerror( "unknown keyword in soundcard{} level" ); }
+       | L_MIXER '(' string    { build_mixer($3); }
+         L_DOUBLE1 mixers '}'  { build_mixer(NULL); }
+       | L_PCM '(' string      { build_pcm($3); }
+         L_DOUBLE1 pcms '}'    { build_pcm(NULL); }
+       | L_RAWMIDI '(' string  { build_rawmidi($3); }
+         L_DOUBLE1 rawmidis '}' { build_rawmidi(NULL); }
+       | error                 { yyerror( "an unknown keyword in the soundcard{} level"); }
        ;
 
 controls : control
        | controls control
        ;
 
-control : L_SWITCH '(' string { select_control_switch( $3 ); } ',' switches ')' { select_control_switch( NULL ); }
-       | error                 { yyerror( "unknown keyword in control{} level" ); }
+control : L_SWITCH '(' string  { build_control_switch($3); }
+         ',' switches ')'      { build_control_switch(NULL); }
+       | error                 { yyerror("an unknown keyword in the control{} level"); }
        ;
 
+
 mixers : mixer
        | mixers mixer
        ;
 
-mixer  : L_CHANNEL '(' string  { select_mixer_channel( $3 ); } 
-         ',' settings ')'      { select_mixer_channel_end(); 
-                                 select_mixer_channel( NULL ); }
-       | L_SWITCH '(' string   { select_mixer_switch( $3 ); }
-         ',' switches ')'      { select_mixer_switch( NULL ); }
-       | error                 { yyerror( "unknown keyword in mixer level" ); }
+mixer  : L_ELEMENT '(' string
+         ',' integer ',' integer { build_mixer_element($3, $5, $7); } 
+         ',' etype ')'         { build_mixer_element(NULL, -1, -1); }
+       | L_SWITCH '(' string   { build_mixer_switch($3); }
+         ',' switches ')'      { build_mixer_switch(NULL); }
+       | error                 { yyerror("an unknown keyword in the mixer level"); }
+       ;
+
+
+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"); }
+       ;
+
+m_switch1 : m_switch1_0
+       | m_switch1 ',' m_switch1_0
+       ;
+
+m_switch1_0 : boolean          { mixer_switch1_value($1); }
+       | error                 { yyerror("an unknown keyword in the Switch1 element level"); }
+       ;
+
+m_switch2 : m_switch2_0
+       | m_switch2 ',' m_switch2_0
+       ;
+
+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"); }
+       ;
 
-settings: setting
-       | settings ',' setting
+m_accu3 : m_accu3_0
+       | m_accu3 ',' m_accu3_0
        ;
 
-setting        : L_OUTPUT              { select_mixer_direction(OUTPUT); }
-         dsetting
-       | L_INPUT               { select_mixer_direction(INPUT); }
-         dsetting
-       | error                 { yyerror( "unknown keyword in mixer channel level" ); }
+m_accu3_0 : integer            { mixer_accu3_value($1); }
+       | error                 { yyerror("an unknown keyword in the Accu3 element level"); }
        ;
 
-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_mux1 : m_mux1_0
+       | m_mux1 ',' m_mux1_0
        ;
 
-vsettings: vsetting
-       | vsettings vsetting
+m_mux1_0 : L_ELEMENT '(' string
+          ',' integer ','
+          integer ')'          { mixer_mux1_value($3, $5, $7); }
+       | error                 { yyerror("an unknown keyword in the Mux1 element level"); }
        ;
 
-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_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);
+       Xrawmidi = (struct rawmidi *)malloc(sizeof(struct rawmidi));
+       if (!Xrawmidi) {
+               free(name);
+               error_nomem();
+               return;
+       }
+       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_channel(char *name)
+static void build_mixer_element(char *name, int index, int etype)
 {
-       struct mixer_channel *channel;
+       struct mixer_element *element;
 
        if (!name) {
-               Xchannel = NULL;
+               Xelement = NULL;
                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);
+       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 mixer_type_check(int type)
+{
+       if (Xelement->element.eid.type != type)
+               yyerror("The element has got the unexpected data type.");
+}
+
+static void mixer_switch1(int end)
+{
+       mixer_type_check(SND_MIXER_ETYPE_SWITCH1);
+}
+
+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;
                }
-       yyerror("Cannot find mixer channel '%s'...", name);
-       free(name);
+               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 select_mixer_direction(int direction)
+static void mixer_switch2(int end)
 {
-       Xchannel->direction = direction;
+       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 select_mixer_voice(int voice)
+static void mixer_volume1(int end)
 {
-       Xchannel->voice = voice;
+       mixer_type_check(SND_MIXER_ETYPE_VOLUME1);
 }
 
-static void set_mixer_volume(int volume)
+static void mixer_volume1_value(int val)
 {
-       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;
+       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;
        }
-       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->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 set_mixer_flags(int flags)
+static void mixer_accu3_value(int val)
 {
-       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;
+       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;
        }
-       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;
+       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 select_mixer_channel_end(void)
+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 find_switch(int xtype, struct ctl_switch *first, char *name, char *err)
+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;
+       }
+       bzero(Xswitch, sizeof(*Xswitch));
+       for (sw = *first; sw && sw->next; sw = sw->next);
+       if (sw) {
+               sw->next = Xswitch;
+       } else {
+               *first = Xswitch;
        }
-       yyerror("Cannot find %s switch '%s'...", err, name);
+       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_iec958ocs_begin(int end)
+static void set_switch_bytearray(struct bytearray val)
 {
-       snd_switch_t *sw = Xswitch;
+       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)
+{
        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 (file)
index 0000000..6232e87
--- /dev/null
@@ -0,0 +1,492 @@
+/*
+ *  Advanced Linux Sound Architecture Control Program
+ *  Copyright (c) 1999 by Jaroslav Kysela <perex@jcu.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "alsactl.h"
+
+static char *sw_id(const char *name, int cardno, int devno, const char *id)
+{
+       static char str[256];
+       
+       sprintf(str, "%s %s card %i", name, id, cardno);
+       if (devno >= 0)
+               sprintf(str + strlen(str)," device %i", devno);
+       return str;
+}
+
+static int merge_one_sw(struct ctl_switch *csw, struct ctl_switch *usw, int cardno, int devno, const char *id)
+{
+       switch (csw->s.type) {
+       case SND_SW_TYPE_BOOLEAN:
+               if (usw->s.type != SND_SW_TYPE_BOOLEAN) {
+                       error("A wrong type for the switch %s. The type boolean is expected. Skipping...", sw_id(usw->s.name, cardno, devno, id));
+                       return 1;
+               }
+               if (csw->s.value.enable != usw->s.value.enable) {
+                       csw->change = 1;
+                       csw->s.value.enable = usw->s.value.enable;
+               }
+               if (!strncmp(csw->s.name, SND_MIXER_SW_IEC958_OUTPUT, sizeof(csw->s.name))) {
+                       if (usw->s.value.data32[1] == (('C' << 8) | 'S')) {
+                               if (csw->s.value.data16[4] != usw->s.value.data16[4] ||
+                                   csw->s.value.data16[5] != usw->s.value.data16[5]) {
+                                       csw->change = 1;
+                                       csw->s.value.data16[4] = usw->s.value.data16[4];
+                                       csw->s.value.data16[5] = usw->s.value.data16[5];
+                               }
+                       }
+               }
+               break;                  
+       case SND_SW_TYPE_BYTE:
+               if (usw->s.type != SND_SW_TYPE_DWORD) {
+                       error("A wrong type for the switch %s. The type byte is expected. Skipping...", sw_id(usw->s.name, cardno, devno, id));
+                       return 1;
+               }
+               if (csw->s.low > usw->s.value.data32[0] ||
+                   csw->s.high < usw->s.value.data32[0]) {
+                       error("The value %u for the switch %s is out of range %i-%i.", usw->s.value.data32[0], sw_id(usw->s.name, cardno, devno, id), csw->s.low, csw->s.high);
+                       return 1;
+               }
+               if (csw->s.value.data8[0] != (unsigned char)usw->s.value.data32[0]) {
+                       csw->change = 1;
+                       csw->s.value.data8[0] = (unsigned char)usw->s.value.data32[0];
+               }
+               break;
+       case SND_SW_TYPE_WORD:
+               if (usw->s.type != SND_SW_TYPE_DWORD) {
+                       error("A wrong type for the switch %s. The type word is expected. Skipping...", sw_id(usw->s.name, cardno, devno, id));
+                       return 1;
+               }
+               if (csw->s.low > usw->s.value.data32[0] ||
+                   csw->s.high < usw->s.value.data32[0]) {
+                       error("The value %u for the switch %s is out of range %i-%i.", usw->s.value.data32[0], sw_id(usw->s.name, cardno, devno, id), csw->s.low, csw->s.high);
+                       return 1;
+               }
+               if (csw->s.value.data16[0] != (unsigned short)usw->s.value.data32[0]) {
+                       csw->change = 1;
+                       csw->s.value.data16[0] = (unsigned short)usw->s.value.data32[0];
+               }
+               break;
+       case SND_SW_TYPE_DWORD:
+               if (usw->s.type != SND_SW_TYPE_DWORD) {
+                       error("A wrong type for the switch %s. The type dword is expected. Skipping...", sw_id(usw->s.name, cardno, devno, id));
+                       return 1;
+               }
+               if (csw->s.low > usw->s.value.data32[0] ||
+                   csw->s.high < usw->s.value.data32[0]) {
+                       error("The value %u for the switch %s is out of range %i-%i.", usw->s.value.data32[0], sw_id(usw->s.name, cardno, devno, id), csw->s.low, csw->s.high);
+                       return 1;
+               }
+               if (csw->s.value.data32[0] != usw->s.value.data32[0]) {
+                       csw->change = 1;
+                       csw->s.value.data32[0] = usw->s.value.data32[0];
+               }
+               break;
+       default:
+               error("The switch type %i is not known.", csw->s.type);
+       }
+       return 0;
+}
+
+static int soundcard_setup_merge_sw(struct ctl_switch *csw, struct ctl_switch *usw, int cardno, int devno, const char *id)
+{
+       struct ctl_switch *csw1;
+       
+       for ( ; usw; usw = usw->next) {
+               for (csw1 = csw; csw1; csw1 = csw1->next) {
+                       if (!strncmp(csw1->s.name, usw->s.name, sizeof(csw1->s.name))) {
+                               merge_one_sw(csw1, usw, cardno, devno, id);
+                               break;
+                       }
+               }
+               if (!csw1) {
+                       error("Cannot find the switch %s...", sw_id(usw->s.name, cardno, devno, id));
+               }
+       }
+       return 0;
+}
+
+int soundcard_setup_merge_switches(int cardno)
+{
+       struct soundcard *soundcard, *rsoundcard;
+       struct mixer *mixer, *rmixer;
+       struct pcm *pcm, *rpcm;
+       struct rawmidi *rawmidi, *rrawmidi;
+
+       for (rsoundcard = rsoundcards; rsoundcard; rsoundcard = rsoundcard->next) {
+               for (soundcard = soundcards; soundcard; soundcard = soundcard->next) {
+                       if (!strncmp(soundcard->control.hwinfo.id, rsoundcard->control.hwinfo.id, sizeof(soundcard->control.hwinfo.id)))
+                               break;
+               }
+               if (!soundcard) {
+                       error("The soundcard '%s' was not found...\n", rsoundcard->control.hwinfo.id);
+                       continue;
+               }
+               if (cardno >= 0 && soundcard->no != cardno)
+                       continue;
+               soundcard_setup_merge_sw(soundcard->control.switches, rsoundcard->control.switches, soundcard->no, -1, "control");
+               for (rmixer = rsoundcard->mixers; rmixer; rmixer = rmixer->next) {
+                       for (mixer = soundcard->mixers; mixer; mixer = rmixer->next) {
+                               if (!strncmp(mixer->info.name, rmixer->info.name, sizeof(mixer->info.name)))
+                                       break;
+                       }
+                       if (!mixer) {
+                               error("The mixer device '%s' from the soundcard %i was not found...\n", rmixer->info.name, soundcard->no);
+                               continue;
+                       }
+                       soundcard_setup_merge_sw(mixer->switches, rmixer->switches, soundcard->no, mixer->no, "mixer");
+               }
+               for (rpcm = rsoundcard->pcms; rpcm; rpcm = rpcm->next) {
+                       for (pcm = rsoundcard->pcms; pcm; pcm = pcm->next) {
+                               if (!strncmp(pcm->info.name, rpcm->info.name, sizeof(pcm->info.name)))
+                                       break;
+                       }
+                       if (!rpcm) {
+                               error("The PCM device '%s' from the soundcard %i was not found...\n", rpcm->info.name, soundcard->no);
+                               continue;
+                       }
+                       soundcard_setup_merge_sw(pcm->pswitches, rpcm->pswitches, soundcard->no, pcm->no, "PCM playback");
+                       soundcard_setup_merge_sw(pcm->rswitches, rpcm->rswitches, soundcard->no, pcm->no, "PCM record");
+               }
+               for (rrawmidi = rsoundcard->rawmidis; rrawmidi; rrawmidi = rrawmidi->next) {
+                       for (rawmidi = soundcard->rawmidis; rawmidi; rawmidi = rawmidi->next) {
+                               if (!strncmp(rawmidi->info.name, rrawmidi->info.name, sizeof(rawmidi->info.name)))
+                                       break;
+                       }
+                       if (!rrawmidi) {
+                               error("The rawmidi device '%s' from the soundcard %i was not found...\n", rrawmidi->info.name, soundcard->no);
+                               continue;
+                       }
+                       soundcard_setup_merge_sw(rawmidi->iswitches, rrawmidi->iswitches, soundcard->no, rawmidi->no, "rawmidi input");
+                       soundcard_setup_merge_sw(rawmidi->oswitches, rrawmidi->oswitches, soundcard->no, rawmidi->no, "rawmidi output");
+               }
+       }
+       return 0;
+}
+
+static char *element_id(snd_mixer_eid_t *eid, int cardno, int devno, const char *id)
+{
+       static char str[256];
+
+       sprintf(str, "%s %s card %i", mixer_element_id(eid), id, cardno);
+       if (devno >= 0)
+               sprintf(str + strlen(str)," device %i", devno);
+       return str;
+}
+
+static int merge_one_element(struct mixer_element *celement, struct mixer_element *uelement, int cardno, int devno, const char *id)
+{
+       int tmp;
+
+       if (snd_mixer_element_has_control(&celement->element.eid) != 1)
+               return 0;
+       switch (celement->element.eid.type) {
+       case SND_MIXER_ETYPE_SWITCH1:
+               if (celement->element.data.switch1.sw != uelement->element.data.switch1.sw) {
+                       error("Element %s has got a wrong count of voices.", element_id(&celement->element.eid, cardno, devno, id));
+                       return 1;
+               }
+               tmp = ((celement->element.data.switch1.sw + 31) / 32) * sizeof(unsigned int);
+               memcpy(celement->element.data.switch1.psw, uelement->element.data.switch1.psw, tmp);
+               break;
+       case SND_MIXER_ETYPE_SWITCH2:
+               celement->element.data.switch2.sw = uelement->element.data.switch2.sw;
+               break;
+       case SND_MIXER_ETYPE_SWITCH3:
+               if (celement->element.data.switch3.rsw != uelement->element.data.switch3.rsw) {
+                       error("Element %s has got a wrong count of switches.", element_id(&celement->element.eid, cardno, devno, id));
+                       return 1;
+               }
+               tmp = ((celement->element.data.switch3.rsw + 31) / 32) * sizeof(unsigned int);
+               memcpy(celement->element.data.switch3.prsw, uelement->element.data.switch3.prsw, tmp);
+               break;
+       case SND_MIXER_ETYPE_VOLUME1:
+               if (celement->element.data.volume1.voices != uelement->element.data.volume1.voices) {
+                       error("Element %s has got a wrong count of voices.", element_id(&celement->element.eid, cardno, devno, id));
+                       return 1;
+               }
+               tmp = celement->element.data.volume1.voices * sizeof(int);
+               memcpy(celement->element.data.volume1.pvoices, uelement->element.data.volume1.pvoices, tmp);
+               break;
+       case SND_MIXER_ETYPE_VOLUME2:
+               if (celement->element.data.volume2.avoices != uelement->element.data.volume2.avoices) {
+                       error("Element %s has got a wrong count of voices.", element_id(&celement->element.eid, cardno, devno, id));
+                       return 1;
+               }
+               tmp = celement->element.data.volume2.avoices * sizeof(int);
+               memcpy(celement->element.data.volume2.pavoices, uelement->element.data.volume2.pavoices, tmp);
+               break;
+       case SND_MIXER_ETYPE_ACCU3:
+               if (celement->element.data.accu3.voices != uelement->element.data.accu3.voices) {
+                       error("Element %s has got a wrong count of voices.", element_id(&celement->element.eid, cardno, devno, id));
+                       return 1;
+               }
+               tmp = celement->element.data.accu3.voices * sizeof(int);
+               memcpy(celement->element.data.accu3.pvoices, uelement->element.data.accu3.pvoices, tmp);
+               break;
+       case SND_MIXER_ETYPE_MUX1:
+               if (celement->element.data.mux1.poutput)
+                       free(celement->element.data.mux1.poutput);
+               celement->element.data.mux1.output_size = 0;
+               celement->element.data.mux1.output = 0;
+               celement->element.data.mux1.output_over = 0;
+               tmp = uelement->element.data.mux1.output * sizeof(snd_mixer_eid_t);
+               if (tmp > 0) {
+                       celement->element.data.mux1.poutput = (snd_mixer_eid_t *)malloc(uelement->element.data.mux1.output_size * sizeof(snd_mixer_eid_t));
+                       if (!celement->element.data.mux1.poutput) {
+                               error("No enough memory...");
+                               return 1;
+                       }
+                       celement->element.data.mux1.output_size = uelement->element.data.mux1.output_size;
+                       celement->element.data.mux1.output = uelement->element.data.mux1.output;
+                       memcpy(celement->element.data.mux1.poutput, uelement->element.data.mux1.poutput, tmp);
+               }
+               break;
+       case SND_MIXER_ETYPE_MUX2:
+               celement->element.data.mux2.output = uelement->element.data.mux2.output;
+               break;
+       case SND_MIXER_ETYPE_TONE_CONTROL1:
+               if ((uelement->element.data.tc1.tc & ~celement->info.data.tc1.tc) != 0) {
+                       error("Wrong (unsupported) input for the element %s.", element_id(&celement->element.eid, cardno, devno, id));
+                       return 1;
+               }
+               celement->element.data.tc1.tc = uelement->element.data.tc1.tc;
+               celement->element.data.tc1.sw = uelement->element.data.tc1.sw;
+               celement->element.data.tc1.bass = uelement->element.data.tc1.bass;
+               celement->element.data.tc1.treble = uelement->element.data.tc1.treble;
+               break;
+       case SND_MIXER_ETYPE_3D_EFFECT1:
+               if ((uelement->element.data.teffect1.effect & ~celement->info.data.teffect1.effect) != 0) {
+                       error("Wrong (unsupported) input for the element %s.", element_id(&celement->element.eid, cardno, devno, id));
+                       return 1;
+               }
+               celement->element.data.teffect1.effect = uelement->element.data.teffect1.effect;
+               celement->element.data.teffect1.sw = uelement->element.data.teffect1.sw;
+               celement->element.data.teffect1.mono_sw = uelement->element.data.teffect1.mono_sw;
+               celement->element.data.teffect1.wide = uelement->element.data.teffect1.wide;
+               celement->element.data.teffect1.volume = uelement->element.data.teffect1.volume;
+               celement->element.data.teffect1.center = uelement->element.data.teffect1.center;
+               celement->element.data.teffect1.space = uelement->element.data.teffect1.space;
+               celement->element.data.teffect1.depth = uelement->element.data.teffect1.depth;
+               celement->element.data.teffect1.delay = uelement->element.data.teffect1.delay;
+               celement->element.data.teffect1.feedback = uelement->element.data.teffect1.feedback;
+               break;
+       case SND_MIXER_ETYPE_PRE_EFFECT1:
+               if (celement->element.data.peffect1.pparameters)
+                       free(celement->element.data.peffect1.pparameters);
+               celement->element.data.peffect1.parameters_size = 0;
+               celement->element.data.peffect1.parameters = 0;
+               celement->element.data.peffect1.parameters_over = 0;
+               celement->element.data.peffect1.item = uelement->element.data.peffect1.item;
+               if (celement->element.data.peffect1.item < 0) {
+                       celement->element.data.peffect1.pparameters = (int *)malloc(uelement->element.data.peffect1.parameters_size * sizeof(int));
+                       if (!celement->element.data.peffect1.pparameters) {
+                               error("No enough memory..");
+                               return 1;
+                       }
+                       celement->element.data.peffect1.parameters_size = uelement->element.data.peffect1.parameters_size;
+                       celement->element.data.peffect1.parameters = uelement->element.data.peffect1.parameters;
+                       tmp = celement->element.data.peffect1.parameters * sizeof(int);
+                       memcpy(celement->element.data.peffect1.pparameters, uelement->element.data.peffect1.pparameters, tmp);
+               }
+               break;
+       default:
+               error("The element type %i for the element %s is not known.", celement->element.eid.type, mixer_element_id(&celement->element.eid));
+       }
+       return 0;
+}
+
+static int soundcard_setup_merge_element(struct mixer_element *celement, struct mixer_element *uelement, int cardno, int devno, const char *id)
+{
+       struct mixer_element *element;
+       
+       for ( ; uelement; uelement = uelement->next) {
+               for (element = celement; element; element = element->next) {
+                       if (!strncmp(element->element.eid.name, uelement->element.eid.name, sizeof(element->element.eid.name)) &&
+                           element->element.eid.index == uelement->element.eid.index &&
+                           element->element.eid.type == uelement->element.eid.type) {
+                               merge_one_element(element, uelement, cardno, devno, id);
+                               break;
+                       }
+               }
+               if (!element) {
+                       error("Cannot find the element %s...", element_id(&uelement->element.eid, cardno, devno, id));
+               }
+       }
+       return 0;
+}
+
+int soundcard_setup_merge_data(int cardno)
+{
+       struct soundcard *soundcard, *rsoundcard;
+       struct mixer *mixer, *rmixer;
+
+       for (rsoundcard = rsoundcards; rsoundcard; rsoundcard = rsoundcard->next) {
+               for (soundcard = soundcards; soundcard; soundcard = soundcard->next) {
+                       if (!strncmp(soundcard->control.hwinfo.id, rsoundcard->control.hwinfo.id, sizeof(soundcard->control.hwinfo.id)))
+                               break;
+               }
+               if (!soundcard) {
+                       error("The soundcard '%s' was not found...\n", rsoundcard->control.hwinfo.id);
+                       continue;
+               }
+               if (cardno >= 0 && soundcard->no != cardno)
+                       continue;
+               for (rmixer = rsoundcard->mixers; rmixer; rmixer = rmixer->next) {
+                       for (mixer = soundcard->mixers; mixer; mixer = rmixer->next) {
+                               if (!strncmp(mixer->info.name, rmixer->info.name, sizeof(mixer->info.name)))
+                                       break;
+                       }
+                       if (!mixer) {
+                               error("The mixer device '%s' from the soundcard %i was not found...\n", rmixer->info.name, soundcard->no);
+                               continue;
+                       }
+                       soundcard_setup_merge_element(mixer->elements, rmixer->elements, soundcard->no, mixer->no, "mixer");
+               }
+       }
+       return 0;
+}
+
+static int soundcard_open_ctl(void **ctlhandle, struct soundcard *soundcard)
+{
+       int err;
+
+       if (*ctlhandle)
+               return 0;
+       if ((err = snd_ctl_open(ctlhandle, soundcard->no)) < 0) {
+               error("Cannot open control interface for soundcard #%i.", soundcard->no + 1);
+               return 1;
+       }
+       return 0;
+}
+
+static int soundcard_open_mix(void **mixhandle, struct soundcard *soundcard, struct mixer *mixer)
+{
+       int err;
+
+       if (*mixhandle)
+               return 0;
+       if ((err = snd_mixer_open(mixhandle, soundcard->no, mixer->no)) < 0) {
+               error("Cannot open mixer interface for soundcard #%i.", soundcard->no + 1);
+               return 1;
+       }
+       return 0;
+}
+
+int soundcard_setup_process_switches(int cardno)
+{
+       int err;
+       void *ctlhandle = NULL;
+       struct soundcard *soundcard;
+       struct ctl_switch *ctlsw;
+       struct mixer *mixer;
+       struct pcm *pcm;
+       struct rawmidi *rawmidi;
+
+       for (soundcard = soundcards; soundcard; soundcard = soundcard->next) {
+               if (cardno >= 0 && soundcard->no != cardno)
+                       continue;
+               for (ctlsw = soundcard->control.switches; ctlsw; ctlsw = ctlsw->next) {
+                       if (ctlsw->change)
+                               if (!soundcard_open_ctl(&ctlhandle, soundcard)) {
+                                       if ((err = snd_ctl_switch_write(ctlhandle, &ctlsw->s)) < 0)
+                                               error("Control switch '%s' write error: %s", ctlsw->s.name, snd_strerror(err));
+                               }
+               }
+               for (mixer = soundcard->mixers; mixer; mixer = mixer->next) {
+                       for (ctlsw = mixer->switches; ctlsw; ctlsw = ctlsw->next)
+                               if (ctlsw->change)
+                                       if (!soundcard_open_ctl(&ctlhandle, soundcard)) {
+                                               if ((err = snd_ctl_mixer_switch_write(ctlhandle, mixer->no, &ctlsw->s)) < 0)
+                                                       error("Mixer switch '%s' write error: %s", ctlsw->s.name, snd_strerror(err));
+                                       }
+               }
+               for (pcm = soundcard->pcms; pcm; pcm = pcm->next) {
+                       for (ctlsw = pcm->pswitches; ctlsw; ctlsw = ctlsw->next) {
+                               if (ctlsw->change)
+                                       if (!soundcard_open_ctl(&ctlhandle, soundcard)) {
+                                               if ((err = snd_ctl_pcm_playback_switch_write(ctlhandle, pcm->no, &ctlsw->s)) < 0)
+                                                       error("PCM playback switch '%s' write error: %s", ctlsw->s.name, snd_strerror(err));
+                                       }
+                       }
+                       for (ctlsw = pcm->rswitches; ctlsw; ctlsw = ctlsw->next) {
+                               if (ctlsw->change)
+                                       if (!soundcard_open_ctl(&ctlhandle, soundcard)) {
+                                               if ((err = snd_ctl_pcm_record_switch_write(ctlhandle, pcm->no, &ctlsw->s)) < 0)
+                                                       error("PCM record switch '%s' write error: %s", ctlsw->s.name, snd_strerror(err));
+                                       }
+                       }
+               }
+               for (rawmidi = soundcard->rawmidis; rawmidi; rawmidi = rawmidi->next) {
+                       for (ctlsw = rawmidi->oswitches; ctlsw; ctlsw = ctlsw->next) {
+                               if (ctlsw->change)
+                                       if (!soundcard_open_ctl(&ctlhandle, soundcard)) {
+                                               if ((err = snd_ctl_rawmidi_output_switch_write(ctlhandle, rawmidi->no, &ctlsw->s)) < 0)
+                                                       error("RAWMIDI output switch '%s' write error: %s", ctlsw->s.name, snd_strerror(err));
+                                       }
+                       }
+                       for (ctlsw = rawmidi->iswitches; ctlsw; ctlsw = ctlsw->next) {
+                               if (ctlsw->change)
+                                       if (!soundcard_open_ctl(&ctlhandle, soundcard)) {
+                                               if ((err = snd_ctl_rawmidi_output_switch_write(ctlhandle, rawmidi->no, &ctlsw->s)) < 0)
+                                                       error("RAWMIDI input switch '%s' write error: %s", ctlsw->s.name, snd_strerror(err));
+                                       }
+                       }
+               }
+               if(ctlhandle) {
+                       snd_ctl_close(ctlhandle);
+                       ctlhandle = NULL;
+               }
+       }
+       return 0;
+}
+
+int soundcard_setup_process_data(int cardno)
+{
+       int err;
+       void *ctlhandle = NULL;
+       void *mixhandle = NULL;
+       struct soundcard *soundcard;
+       struct mixer *mixer;
+       struct mixer_element *element;
+
+       for (soundcard = soundcards; soundcard; soundcard = soundcard->next) {
+               if (cardno >= 0 && soundcard->no != cardno)
+                       continue;
+               for (mixer = soundcard->mixers; mixer; mixer = mixer->next) {
+                       for (element = mixer->elements; element; element = element->next)
+                               if (snd_mixer_element_has_control(&element->element.eid) == 1) {
+                                       if (!soundcard_open_mix(&mixhandle, soundcard, mixer)) {
+                                               if ((err = snd_mixer_element_write(mixhandle, &element->element)) < 0)
+                                                       error("Mixer element %s write error: %s", mixer_element_id(&element->element.eid), snd_strerror(err));
+                                       }
+                               }
+                       if (mixhandle) {
+                               snd_mixer_close(mixhandle);
+                               mixhandle = NULL;
+                       }
+               }
+               if(ctlhandle) {
+                       snd_ctl_close(ctlhandle);
+                       ctlhandle = NULL;
+               }
+       }
+       return 0;
+}
index 637b525a62b31c1ee37b5d54456417ce4bffccb1..d59e500665d971e51b3a89d476d5d51b9fcd0d52 100644 (file)
@@ -31,6 +31,32 @@ extern int linecount;
 extern FILE *yyin;
 extern int yydebug;
 struct soundcard *soundcards = NULL;
+struct soundcard *rsoundcards = NULL;
+
+/*
+ *  misc functions
+ */
+
+static char *mixer_element_name(snd_mixer_eid_t *eid)
+{
+       static char str[32];
+       
+       if (!eid)
+               return "???"; 
+       strncpy(str, eid->name, sizeof(eid->name));
+       str[sizeof(eid->name)] = '\0';
+       return str;
+}
+
+char *mixer_element_id(snd_mixer_eid_t *eid)
+{
+       static char str[64];
+       
+       if (!eid)
+               return "???"; 
+       sprintf(str, "%s:%i:%i", mixer_element_name(eid), eid->index, eid->type);
+       return str;
+}
 
 /*
  *  free functions
@@ -47,12 +73,14 @@ static void soundcard_ctl_switch_free(struct ctl_switch *first)
        }
 }
 
-static void soundcard_mixer_channel_free(struct mixer_channel *first)
+static void soundcard_mixer_element_free(struct mixer_element *first)
 {
-       struct mixer_channel *next;
+       struct mixer_element *next;
 
        while (first) {
                next = first->next;
+               snd_mixer_element_info_free(&first->info);
+               snd_mixer_element_free(&first->element);
                free(first);
                first = next;
        }
@@ -62,7 +90,7 @@ static void soundcard_mixer_free1(struct mixer *mixer)
 {
        if (!mixer)
                return;
-       soundcard_mixer_channel_free(mixer->channels);
+       soundcard_mixer_element_free(mixer->elements);
        soundcard_ctl_switch_free(mixer->switches);
        free(mixer);
 }
@@ -173,6 +201,7 @@ void soundcard_setup_init(void)
 void soundcard_setup_done(void)
 {
        soundcard_free(soundcards);
+       soundcard_free(rsoundcards);
        soundcards = NULL;
 }
 
@@ -294,13 +323,12 @@ static int determine_switches(void *handle, struct ctl_switch **csw, int interfa
        return 0;
 }
 
-int soundcard_setup_collect(int cardno)
+static int soundcard_setup_collect_switches1(int cardno)
 {
        void *handle, *mhandle;
        struct soundcard *card, *first, *prev;
-       int err, idx, count, device;
+       int err, device;
        struct mixer *mixer, *mixerprev;
-       struct mixer_channel *mixerchannel, *mixerchannelprev;
        struct pcm *pcm, *pcmprev;
        struct rawmidi *rawmidi, *rawmidiprev;
 
@@ -369,73 +397,15 @@ int soundcard_setup_collect(int cardno)
                mixerprev = mixer;
                if ((err = snd_mixer_open(&mhandle, cardno, device)) < 0) {
                        snd_ctl_close(handle);
-                       error("MIXER open error: %s\n", snd_strerror(err));
-                       return 1;
-               }
-               if ((err = snd_mixer_exact_mode(mhandle, 1)) < 0) {
-                       snd_mixer_close(mhandle);
-                       snd_ctl_close(handle);
-                       error("MIXER exact mode error: %s\n", snd_strerror(err));
+                       error("MIXER open error: %s", snd_strerror(err));
                        return 1;
                }
                if ((err = snd_mixer_info(mhandle, &mixer->info)) < 0) {
                        snd_mixer_close(mhandle);
                        snd_ctl_close(handle);
-                       error("MIXER info error: %s\n", snd_strerror(err));
+                       error("MIXER info error: %s", snd_strerror(err));
                        return 1;
                }
-               count = snd_mixer_channels(mhandle);
-               for (idx = 0, mixerchannelprev = NULL; idx < count; idx++) {
-                       mixerchannel = (struct mixer_channel *) malloc(sizeof(struct mixer_channel));
-                       if (!mixerchannel) {
-                               snd_mixer_close(mhandle);
-                               snd_ctl_close(handle);
-                               error("malloc problem");
-                               return 1;
-                       }
-                       bzero(mixerchannel, sizeof(struct mixer_channel));
-                       mixerchannel->no = idx;
-                       if ((err = snd_mixer_channel_info(mhandle, idx, &mixerchannel->info)) < 0) {
-                               free(mixerchannel);
-                               error("MIXER channel info error (%s) - skipping", snd_strerror(err));
-                               break;
-                       }
-                       if ((mixerchannel->info.caps & SND_MIXER_CINFO_CAP_OUTPUT) &&
-                           (err = snd_mixer_channel_output_info(mhandle, idx, &mixerchannel->dinfo[OUTPUT])) < 0) {
-                               free(mixerchannel);
-                               error("MIXER channel output info error (%s) - skipping", snd_strerror(err));
-                               break;
-                       }
-                       if ((mixerchannel->info.caps & SND_MIXER_CINFO_CAP_INPUT) &&
-                           (err = snd_mixer_channel_input_info(mhandle, idx, &mixerchannel->dinfo[INPUT])) < 0) {
-                               free(mixerchannel);
-                               error("MIXER channel input info error (%s) - skipping", snd_strerror(err));
-                               break;
-                       }
-                       if ((err = snd_mixer_channel_read(mhandle, idx, &mixerchannel->data)) < 0) {
-                               free(mixerchannel);
-                               error("MIXER channel read error (%s) - skipping", snd_strerror(err));
-                               break;
-                       }
-                       if ((mixerchannel->info.caps & SND_MIXER_CINFO_CAP_OUTPUT) &&
-                           (err = snd_mixer_channel_output_read(mhandle, idx, &mixerchannel->ddata[OUTPUT])) < 0) {
-                               free(mixerchannel);
-                               error("MIXER channel output read error (%s) - skipping", snd_strerror(err));
-                               break;
-                       }
-                       if ((mixerchannel->info.caps & SND_MIXER_CINFO_CAP_INPUT) &&
-                           (err = snd_mixer_channel_input_read(mhandle, idx, &mixerchannel->ddata[INPUT])) < 0) {
-                               free(mixerchannel);
-                               error("MIXER channel input read error (%s) - skipping", snd_strerror(err));
-                               break;
-                       }
-                       if (!mixerchannelprev) {
-                               mixer->channels = mixerchannel;
-                       } else {
-                               mixerchannelprev->next = mixerchannel;
-                       }
-                       mixerchannelprev = mixerchannel;
-               }
                snd_mixer_close(mhandle);
        }
        /* --- */
@@ -450,7 +420,7 @@ int soundcard_setup_collect(int cardno)
                pcm->no = device;
                if ((err = snd_ctl_pcm_info(handle, device, &pcm->info)) < 0) {
                        snd_ctl_close(handle);
-                       error("PCM info error: %s\n", snd_strerror(err));
+                       error("PCM info error: %s", snd_strerror(err));
                        return 1;
                }
                if (determine_switches(handle, &pcm->pswitches, 2, device)) {
@@ -480,7 +450,7 @@ int soundcard_setup_collect(int cardno)
                rawmidi->no = device;
                if ((err = snd_ctl_rawmidi_info(handle, device, &rawmidi->info)) < 0) {
                        snd_ctl_close(handle);
-                       error("RAWMIDI info error: %s\n", snd_strerror(err));
+                       error("RAWMIDI info error: %s", snd_strerror(err));
                        return 1;
                }
                if (determine_switches(handle, &rawmidi->oswitches, 4, device)) {
@@ -503,6 +473,133 @@ int soundcard_setup_collect(int cardno)
        return 0;
 }
 
+int soundcard_setup_collect_switches(int cardno)
+{
+       int err;
+       unsigned int mask;
+
+       if (cardno >= 0) {
+               return soundcard_setup_collect_switches1(cardno);
+       } else {
+               mask = snd_cards_mask();
+               for (cardno = 0; cardno < SND_CARDS; cardno++) {
+                       if (!(mask & (1 << cardno)))
+                               continue;
+                       err = soundcard_setup_collect_switches1(cardno);
+                       if (err)
+                               return err;
+               }
+               return 0;
+       }
+}
+
+static int soundcard_setup_collect_data1(int cardno)
+{
+       void *handle, *mhandle;
+       struct soundcard *card;
+       int err, idx;
+       struct mixer *mixer;
+       snd_mixer_elements_t elements;
+       struct mixer_element *mixerelement, *mixerelementprev;
+
+       if ((err = snd_ctl_open(&handle, cardno)) < 0) {
+               error("SND CTL open error: %s", snd_strerror(err));
+               return 1;
+       }
+       /* --- */
+       for (card = soundcards; card && card->no != cardno; card = card->next);
+       if (!card) {
+               snd_ctl_close(handle);
+               error("The soundcard %i does not exist.", cardno);
+               return 1;
+       }
+       for (mixer = card->mixers; mixer; mixer = mixer->next) {
+               if ((err = snd_mixer_open(&mhandle, cardno, mixer->no)) < 0) {
+                       snd_ctl_close(handle);
+                       error("MIXER open error: %s", snd_strerror(err));
+                       return 1;
+               }
+               if (mixer->elements)
+                       soundcard_mixer_element_free(mixer->elements);
+               mixer->elements = NULL;
+               bzero(&elements, sizeof(elements));
+               if ((err = snd_mixer_elements(mhandle, &elements)) < 0) {
+                       snd_mixer_close(mhandle);
+                       snd_ctl_close(handle);
+                       error("MIXER elements error: %s", snd_strerror(err));
+                       return 1;
+               }
+               elements.elements_size = elements.elements_over + 16;
+               elements.elements = elements.elements_over = 0;
+               elements.pelements = (snd_mixer_eid_t *)malloc(elements.elements_size * sizeof(snd_mixer_eid_t));
+               if ((err = snd_mixer_elements(mhandle, &elements)) < 0) {
+                       snd_mixer_close(mhandle);
+                       snd_ctl_close(handle);
+                       error("MIXER elements (2) error: %s", snd_strerror(err));
+                       return 1;
+               }
+               for (idx = 0, mixerelementprev = NULL; idx < elements.elements; idx++) {
+                       mixerelement = (struct mixer_element *) malloc(sizeof(struct mixer_element));
+                       if (!mixerelement) {
+                               snd_mixer_close(mhandle);
+                               snd_ctl_close(handle);
+                               error("malloc problem");
+                               return 1;
+                       }
+                       bzero(mixerelement, sizeof(*mixerelement));
+                       mixerelement->info.eid = elements.pelements[idx];
+                       mixerelement->element.eid = elements.pelements[idx];
+                       if (snd_mixer_element_has_info(&elements.pelements[idx]) == 1) {
+                               if ((err = snd_mixer_element_info_build(mhandle, &mixerelement->info)) < 0) {
+                                       free(mixerelement);
+                                       error("MIXER element %s info error (%s) - skipping", mixer_element_id(&mixerelement->info.eid), snd_strerror(err));
+                                       break;
+                               }
+                       }
+                       if (snd_mixer_element_has_control(&elements.pelements[idx]) == 1) {
+                               if ((err = snd_mixer_element_build(mhandle, &mixerelement->element)) < 0) {
+                                       free(mixerelement);
+                                       error("MIXER element %s build error (%s) - skipping", mixer_element_id(&mixerelement->element.eid), snd_strerror(err));
+                                       break;
+                               }
+                       }
+                       if (!mixerelementprev) {
+                               mixer->elements = mixerelement;
+                       } else {
+                               mixerelementprev->next = mixerelement;
+                       }
+                       mixerelementprev = mixerelement;
+               }
+               free(elements.pelements);
+               snd_mixer_close(mhandle);
+       }
+       /* --- */
+       snd_ctl_close(handle);
+       return 0;
+}
+
+int soundcard_setup_collect_data(int cardno)
+{
+       int err;
+       unsigned int mask;
+
+       if (cardno >= 0) {
+               return soundcard_setup_collect_data1(cardno);
+       } else {
+               mask = snd_cards_mask();
+               for (cardno = 0; cardno < SND_CARDS; cardno++) {
+                       if (!(mask & (1 << cardno)))
+                               continue;
+                       err = soundcard_setup_collect_data1(cardno);
+                       if (err)
+                               return err;
+               }
+               return 0;
+       }
+}
+
+
+
 int soundcard_setup_load(const char *cfgfile, int skip)
 {
        extern int yyparse(void);
@@ -570,9 +667,9 @@ static void soundcard_setup_write_switch(FILE * out, const char *space, int inte
        default:
                s = "unknown";
        }
-       fprintf(out, "%s; Type is '%s'.\n", space, s);
+       fprintf(out, "%s; The type is '%s'.\n", space, s);
        if (sw->low != 0 || sw->high != 0)
-               fprintf(out, "%s; Accepted switch range is from %u to %u.\n", space, sw->low, sw->high);
+               fprintf(out, "%s; The accepted switch range is from %u to %u.\n", space, sw->low, sw->high);
        if (interface == SND_INTERFACE_CONTROL && sw->type == SND_SW_TYPE_WORD &&
            !strcmp(sw->name, SND_CTL_SW_JOYSTICK_ADDRESS)) {
                for (idx = 1, first = 1; idx < 16; idx++) {
@@ -589,36 +686,36 @@ static void soundcard_setup_write_switch(FILE * out, const char *space, int inte
                        fprintf(out, "\n");
        }
        if (interface == SND_INTERFACE_MIXER && sw->type == SND_SW_TYPE_BOOLEAN &&
-           !strcmp(sw->name, SND_MIXER_SW_IEC958OUT)) {
-               fprintf(out, "%sswitch( \"%s\", ", space, sw->name);
+           !strcmp(sw->name, SND_MIXER_SW_IEC958_OUTPUT)) {
+               fprintf(out, "%sswitch(\"%s\",", space, sw->name);
                if (sw->value.data32[1] == (('C' << 8) | 'S')) {        /* Cirrus Crystal */
                        switchok = 0;
-                       fprintf(out, "iec958ocs( %s", sw->value.enable ? "enable" : "disable");
+                       fprintf(out, "iec958ocs(%s", sw->value.enable ? "enable" : "disable");
                        if (sw->value.data16[4] & 0x2000)
-                               fprintf(out, " 3d");
+                               fprintf(out, ",3d");
                        if (sw->value.data16[4] & 0x0040)
-                               fprintf(out, " reset");
+                               fprintf(out, ",reset");
                        if (sw->value.data16[4] & 0x0020)
-                               fprintf(out, " user");
+                               fprintf(out, ",user");
                        if (sw->value.data16[4] & 0x0010)
-                               fprintf(out, " valid");
+                               fprintf(out, ",valid");
                        if (sw->value.data16[5] & 0x0002)
-                               fprintf(out, " data");
+                               fprintf(out, ",data");
                        if (!(sw->value.data16[5] & 0x0004))
-                               fprintf(out, " protect");
+                               fprintf(out, ",protect");
                        switch (sw->value.data16[5] & 0x0018) {
                        case 0x0008:
-                               fprintf(out, " pre2");
+                               fprintf(out, ",pre2");
                                break;
                        default:
                                break;
                        }
                        if (sw->value.data16[5] & 0x0020)
-                               fprintf(out, " fsunlock");
-                       fprintf(out, " type( 0x%x )", (sw->value.data16[5] >> 6) & 0x7f);
+                               fprintf(out, ",fsunlock");
+                       fprintf(out, ",type(0x%x)", (sw->value.data16[5] >> 6) & 0x7f);
                        if (sw->value.data16[5] & 0x2000)
-                               fprintf(out, " gstatus");
-                       fprintf(out, " )");
+                               fprintf(out, ",gstatus");
+                       fprintf(out, ")");
                        goto __end;
                }
        }
@@ -627,15 +724,15 @@ static void soundcard_setup_write_switch(FILE * out, const char *space, int inte
                fprintf(out, v);
                if (sw->type < 0 || sw->type > SND_SW_TYPE_LIST_ITEM) {
                        /* TODO: some well known types should be verbose */
-                       fprintf(out, " rawdata( ");
+                       fprintf(out, "rawdata(");
                        for (idx = 0; idx < 31; idx++) {
                                fprintf(out, "@%02x:", sw->value.data8[idx]);
                        }
-                       fprintf(out, "%02x@ )\n", sw->value.data8[31]);
+                       fprintf(out, "%02x@)\n", sw->value.data8[31]);
                }
        }
       __end:
-       fprintf(out, " )\n");
+       fprintf(out, ")\n");
 }
 
 static void soundcard_setup_write_switches(FILE *out, const char *space, int interface, struct ctl_switch **switches)
@@ -648,112 +745,183 @@ static void soundcard_setup_write_switches(FILE *out, const char *space, int int
                soundcard_setup_write_switch(out, space, interface, &sw->s);
 }
 
-static void soundcard_setup_write_mixer_channel(FILE * out, struct mixer_channel * channel)
+static void soundcard_setup_write_mixer_element(FILE * out, struct mixer_element * xelement)
 {
-       int k, d;
-       struct capdes {
-               unsigned int flag;
-               char* description;
-       };
-       static struct capdes caps[] = {
-               { SND_MIXER_CINFO_CAP_OUTPUT, "output" },
-               { SND_MIXER_CINFO_CAP_INPUT, "input" },
-               { SND_MIXER_CINFO_CAP_EXTINPUT, "external-input" },
-               { SND_MIXER_CINFO_CAP_EFFECT, "effect" }
-       };
-       static struct capdes dcaps[] = {
-               { SND_MIXER_CINFO_DCAP_STEREO, "stereo" },
-               { SND_MIXER_CINFO_DCAP_HWMUTE, "hardware-mute" },
-               { SND_MIXER_CINFO_DCAP_JOINMUTE, "join-mute" },
-               { SND_MIXER_CINFO_DCAP_ROUTE, "route" },
-               { SND_MIXER_CINFO_DCAP_SWAPROUTE, "swap-route" },
-               { SND_MIXER_CINFO_DCAP_DIGITAL, "digital" },
-               { SND_MIXER_CINFO_DCAP_RECORDBYMUTE, "recordbymute" },
-       };
-
-       fprintf(out, "    ; Capabilities:");
-       for (k = 0; k < sizeof(caps)/sizeof(*caps); ++k) {
-               if (channel->info.caps & caps[k].flag)
-                       fprintf(out, " %s", caps[k].description);
+       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");
+               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, "\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);
-               }
-               fprintf(out, "\n");
-       }
-       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" : "");
-       }
-       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;
+               fprintf(out, "}\n%s", cardno < 0 && first->next ? "\n" : "");
        }
-       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));
-                               }
-               }
-               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));
-                                       }
-                       }
+       /* 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;
                }
-               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));
+               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;
                                        }
+                               }
                        }
                }
-               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;
 }
index 31cae798d7bd5416384fd0cd882a60064f5c7f01..1fdd57b913eeac7dcee060a82f5f5d10a0842653 100644 (file)
@@ -1,964 +1,6 @@
-/* AlsaMixer - Commandline mixer for the ALSA project
- * Copyright (C) 1998 Jaroslav Kysela <perex@jcu.cz>,
- *                    Tim Janik <timj@gtk.org>,
- *                    Carl van Schaik <carl@dreamcoat.che.uct.ac.za>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
- */
-
 #include <stdio.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-
-#include <errno.h>
-
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/signal.h>
-
-#ifndef CURSESINC
-#include <ncurses.h>
-#else
-#include CURSESINC
-#endif
-#include <time.h>
-
-#include <sys/asoundlib.h>
-
-/* example compilation commandline:
- * clear; gcc -Wall -pipe -O2 alsamixer.c -o alsamixer -lncurses
- */
-
-/* --- defines --- */
-#define        PRGNAME "alsamixer"
-#define        PRGNAME_UPPER "AlsaMixer"
-#define        VERSION "v0.9"
-
-#undef MAX
-#define MAX(a, b)  (((a) > (b)) ? (a) : (b))
-#undef MIN
-#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
-#undef ABS
-#define ABS(a)     (((a) < 0) ? -(a) : (a))
-#undef CLAMP
-#define CLAMP(x, low, high)  (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
-
-#define MIXER_MIN_X    (23)    /* minimum: 23 */
-#define        MIXER_MIN_Y     (19)    /* minimum: 19 */
-
-#define MIXER_BLACK    (COLOR_BLACK)
-#define MIXER_DARK_RED  (COLOR_RED)
-#define MIXER_RED       (COLOR_RED | A_BOLD)
-#define MIXER_GREEN     (COLOR_GREEN | A_BOLD)
-#define MIXER_ORANGE    (COLOR_YELLOW)
-#define MIXER_YELLOW    (COLOR_YELLOW | A_BOLD)
-#define MIXER_MARIN     (COLOR_BLUE)
-#define MIXER_BLUE      (COLOR_BLUE | A_BOLD)
-#define MIXER_MAGENTA   (COLOR_MAGENTA)
-#define MIXER_DARK_CYAN (COLOR_CYAN)
-#define MIXER_CYAN      (COLOR_CYAN | A_BOLD)
-#define MIXER_GREY      (COLOR_WHITE)
-#define MIXER_GRAY      (MIXER_GREY)
-#define MIXER_WHITE     (COLOR_WHITE | A_BOLD)
-
-
-/* --- variables --- */
-static WINDOW *mixer_window = NULL;
-static int mixer_max_x = 0;
-static int mixer_max_y = 0;
-static int mixer_ofs_x = 0;
-static float mixer_extra_space = 0;
-static int mixer_ofs_y = 0;
-static int mixer_cbar_height = 0;
-
-static int card_id = 0;
-static int mixer_id = 0;
-static void *mixer_handle;
-static char *mixer_card_name = NULL;
-static char *mixer_device_name = NULL;
-
-static int mixer_n_channels = 0;
-static int mixer_n_vis_channels = 0;
-static int mixer_first_vis_channel = 0;
-static int mixer_focus_channel = 0;
-static int mixer_exact = 0;
-
-static int mixer_input_volumes = 0;
-
-static int mixer_lvolume_delta = 0;
-static int mixer_rvolume_delta = 0;
-static int mixer_balance_volumes = 0;
-static int mixer_toggle_mute_left = 0;
-static int mixer_toggle_mute_right = 0;
-
-/* By Carl */
-static int mixer_toggle_record_left = 0;
-static int mixer_toggle_record_right = 0;
-static int mixer_route_ltor_in = 0;
-static int mixer_route_rtol_in = 0;
-#if 0
-static int mixer_route_ltor_out = 0;
-static int mixer_route_rtol_out = 0;
-#endif
-
-
-/* --- draw contexts --- */
-enum {
-       DC_DEFAULT,
-       DC_BACK,
-       DC_TEXT,
-       DC_PROMPT,
-       DC_CBAR_MUTE,
-       DC_CBAR_NOMUTE,
-       DC_CBAR_RECORD,
-       DC_CBAR_NORECORD,
-       DC_CBAR_EMPTY,
-       DC_CBAR_FULL_1,
-       DC_CBAR_FULL_2,
-       DC_CBAR_FULL_3,
-       DC_CBAR_LABEL,
-       DC_CBAR_FOCUS_LABEL,
-       DC_FOCUS,
-       DC_LAST
-};
-
-static int dc_fg[DC_LAST] =
-{0};
-static int dc_attrib[DC_LAST] =
-{0};
-static int dc_char[DC_LAST] =
-{0};
-static int mixer_do_color = 1;
-
-static void mixer_init_dc(int c,
-                         int n,
-                         int f,
-                         int b,
-                         int a)
-{
-       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)
+void main(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;
+       printf("TODO\n");
 }
-
-static void mixer_signal_handler(int signal)
-{
-       mixer_abort(ERR_SIGNAL, sys_siglist[signal]);
-}
-
-int main(int argc,
-        char **argv)
-{
-       int opt;
-
-       /* parse args
-        */
-       do {
-               opt = getopt(argc, argv, "c:m:ehg");
-               switch (opt) {
-               case '?':
-               case 'h':
-                       fprintf(stderr, "%s %s\n", PRGNAME_UPPER, VERSION);
-                       fprintf(stderr, "Usage: %s [-e] [-c <card: 1..%i>] [-m <mixer: 0..1>]\n", PRGNAME, snd_cards());
-                       mixer_abort(ERR_NONE, "");
-               case 'c':
-                       card_id = snd_card_name(optarg);
-                       break;
-               case 'e':
-                       mixer_exact = !mixer_exact;
-                       break;
-               case 'g':
-                       mixer_do_color = !mixer_do_color;
-                       break;
-               case 'm':
-                       mixer_id = CLAMP(optarg[0], '0', '1') - '0';
-                       break;
-               }
-       }
-       while (opt > 0);
-
-       /* initialize mixer
-        */
-       mixer_init();
-
-       /* setup signal handlers
-        */
-       signal(SIGINT, mixer_signal_handler);
-       signal(SIGTRAP, mixer_signal_handler);
-       signal(SIGABRT, mixer_signal_handler);
-       signal(SIGQUIT, mixer_signal_handler);
-       signal(SIGBUS, mixer_signal_handler);
-       signal(SIGSEGV, mixer_signal_handler);
-       signal(SIGPIPE, mixer_signal_handler);
-       signal(SIGTERM, mixer_signal_handler);
-
-       /* initialize ncurses
-        */
-       mixer_window = initscr();
-       if (mixer_do_color)
-               mixer_do_color = has_colors();
-       mixer_init_draw_contexts();
-       mixer_init_screen();
-       if (mixer_max_x < MIXER_MIN_X ||
-           mixer_max_y < MIXER_MIN_Y)
-               mixer_abort(ERR_WINSIZE, "");
-
-       /* react on key presses
-        * and draw window
-        */
-       keypad(mixer_window, TRUE);
-       leaveok(mixer_window, TRUE);
-       cbreak();
-       noecho();
-       do {
-               mixer_update_cbars();
-               mixer_draw_frame();
-               refresh();
-       }
-       while (!mixer_iteration());
-
-       mixer_abort(ERR_NONE, "");
-};
diff --git a/alsamixer/alsamixer_abramo.c b/alsamixer/alsamixer_abramo.c
new file mode 100644 (file)
index 0000000..31cae79
--- /dev/null
@@ -0,0 +1,964 @@
+/* AlsaMixer - Commandline mixer for the ALSA project
+ * Copyright (C) 1998 Jaroslav Kysela <perex@jcu.cz>,
+ *                    Tim Janik <timj@gtk.org>,
+ *                    Carl van Schaik <carl@dreamcoat.che.uct.ac.za>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#include <errno.h>
+
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/signal.h>
+
+#ifndef CURSESINC
+#include <ncurses.h>
+#else
+#include CURSESINC
+#endif
+#include <time.h>
+
+#include <sys/asoundlib.h>
+
+/* example compilation commandline:
+ * clear; gcc -Wall -pipe -O2 alsamixer.c -o alsamixer -lncurses
+ */
+
+/* --- defines --- */
+#define        PRGNAME "alsamixer"
+#define        PRGNAME_UPPER "AlsaMixer"
+#define        VERSION "v0.9"
+
+#undef MAX
+#define MAX(a, b)  (((a) > (b)) ? (a) : (b))
+#undef MIN
+#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
+#undef ABS
+#define ABS(a)     (((a) < 0) ? -(a) : (a))
+#undef CLAMP
+#define CLAMP(x, low, high)  (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
+
+#define MIXER_MIN_X    (23)    /* minimum: 23 */
+#define        MIXER_MIN_Y     (19)    /* minimum: 19 */
+
+#define MIXER_BLACK    (COLOR_BLACK)
+#define MIXER_DARK_RED  (COLOR_RED)
+#define MIXER_RED       (COLOR_RED | A_BOLD)
+#define MIXER_GREEN     (COLOR_GREEN | A_BOLD)
+#define MIXER_ORANGE    (COLOR_YELLOW)
+#define MIXER_YELLOW    (COLOR_YELLOW | A_BOLD)
+#define MIXER_MARIN     (COLOR_BLUE)
+#define MIXER_BLUE      (COLOR_BLUE | A_BOLD)
+#define MIXER_MAGENTA   (COLOR_MAGENTA)
+#define MIXER_DARK_CYAN (COLOR_CYAN)
+#define MIXER_CYAN      (COLOR_CYAN | A_BOLD)
+#define MIXER_GREY      (COLOR_WHITE)
+#define MIXER_GRAY      (MIXER_GREY)
+#define MIXER_WHITE     (COLOR_WHITE | A_BOLD)
+
+
+/* --- variables --- */
+static WINDOW *mixer_window = NULL;
+static int mixer_max_x = 0;
+static int mixer_max_y = 0;
+static int mixer_ofs_x = 0;
+static float mixer_extra_space = 0;
+static int mixer_ofs_y = 0;
+static int mixer_cbar_height = 0;
+
+static int card_id = 0;
+static int mixer_id = 0;
+static void *mixer_handle;
+static char *mixer_card_name = NULL;
+static char *mixer_device_name = NULL;
+
+static int mixer_n_channels = 0;
+static int mixer_n_vis_channels = 0;
+static int mixer_first_vis_channel = 0;
+static int mixer_focus_channel = 0;
+static int mixer_exact = 0;
+
+static int mixer_input_volumes = 0;
+
+static int mixer_lvolume_delta = 0;
+static int mixer_rvolume_delta = 0;
+static int mixer_balance_volumes = 0;
+static int mixer_toggle_mute_left = 0;
+static int mixer_toggle_mute_right = 0;
+
+/* By Carl */
+static int mixer_toggle_record_left = 0;
+static int mixer_toggle_record_right = 0;
+static int mixer_route_ltor_in = 0;
+static int mixer_route_rtol_in = 0;
+#if 0
+static int mixer_route_ltor_out = 0;
+static int mixer_route_rtol_out = 0;
+#endif
+
+
+/* --- draw contexts --- */
+enum {
+       DC_DEFAULT,
+       DC_BACK,
+       DC_TEXT,
+       DC_PROMPT,
+       DC_CBAR_MUTE,
+       DC_CBAR_NOMUTE,
+       DC_CBAR_RECORD,
+       DC_CBAR_NORECORD,
+       DC_CBAR_EMPTY,
+       DC_CBAR_FULL_1,
+       DC_CBAR_FULL_2,
+       DC_CBAR_FULL_3,
+       DC_CBAR_LABEL,
+       DC_CBAR_FOCUS_LABEL,
+       DC_FOCUS,
+       DC_LAST
+};
+
+static int dc_fg[DC_LAST] =
+{0};
+static int dc_attrib[DC_LAST] =
+{0};
+static int dc_char[DC_LAST] =
+{0};
+static int mixer_do_color = 1;
+
+static void mixer_init_dc(int c,
+                         int n,
+                         int f,
+                         int b,
+                         int a)
+{
+       dc_fg[n] = f;
+       dc_attrib[n] = a;
+       dc_char[n] = c;
+       if (n > 0)
+               init_pair(n, dc_fg[n] & 0xf, b & 0x0f);
+}
+
+static int mixer_dc(int n)
+{
+       if (mixer_do_color)
+               attrset(COLOR_PAIR(n) | (dc_fg[n] & 0xfffffff0));
+       else
+               attrset(dc_attrib[n]);
+
+       return dc_char[n];
+}
+
+static void mixer_init_draw_contexts(void)
+{
+       start_color();
+
+       mixer_init_dc('.', DC_BACK, MIXER_WHITE, MIXER_BLACK, A_NORMAL);
+       mixer_init_dc('.', DC_TEXT, MIXER_YELLOW, MIXER_BLACK, A_BOLD);
+       mixer_init_dc('.', DC_PROMPT, MIXER_DARK_CYAN, MIXER_BLACK, A_NORMAL);
+       mixer_init_dc('M', DC_CBAR_MUTE, MIXER_CYAN, MIXER_BLACK, A_BOLD);
+       mixer_init_dc('-', DC_CBAR_NOMUTE, MIXER_CYAN, MIXER_BLACK, A_NORMAL);
+       mixer_init_dc('x', DC_CBAR_RECORD, MIXER_DARK_RED, MIXER_BLACK, A_BOLD);
+       mixer_init_dc('-', DC_CBAR_NORECORD, MIXER_GRAY, MIXER_BLACK, A_NORMAL);
+       mixer_init_dc(' ', DC_CBAR_EMPTY, MIXER_GRAY, MIXER_BLACK, A_DIM);
+       mixer_init_dc('#', DC_CBAR_FULL_1, MIXER_WHITE, MIXER_BLACK, A_BOLD);
+       mixer_init_dc('#', DC_CBAR_FULL_2, MIXER_GREEN, MIXER_BLACK, A_BOLD);
+       mixer_init_dc('#', DC_CBAR_FULL_3, MIXER_RED, MIXER_BLACK, A_BOLD);
+       mixer_init_dc('.', DC_CBAR_LABEL, MIXER_WHITE, MIXER_BLUE, A_REVERSE | A_BOLD);
+       mixer_init_dc('.', DC_CBAR_FOCUS_LABEL, MIXER_RED, MIXER_BLUE, A_REVERSE | A_BOLD);
+       mixer_init_dc('.', DC_FOCUS, MIXER_RED, MIXER_BLACK, A_BOLD);
+}
+
+#define        DC_CBAR_FRAME   (DC_CBAR_MUTE)
+#define        DC_FRAME        (DC_PROMPT)
+
+
+/* --- error types --- */
+typedef enum {
+       ERR_NONE,
+       ERR_OPEN,
+       ERR_FCN,
+       ERR_SIGNAL,
+       ERR_WINSIZE,
+} ErrType;
+
+
+/* --- prototypes --- */
+static void mixer_abort(ErrType error,
+                       const char *err_string)
+ __attribute__
+ ((noreturn));
+
+
+/* --- functions --- */
+static void mixer_clear(void)
+{
+       int x, y;
+
+       mixer_dc(DC_BACK);
+       clear();
+
+       /* buggy ncurses doesn't really write spaces with the specified
+        * color into the screen on clear ();
+        */
+       for (x = 0; x < mixer_max_x; x++)
+               for (y = 0; y < mixer_max_y; y++)
+                       mvaddch(y, x, ' ');
+       refresh();
+}
+
+static void mixer_abort(ErrType error,
+                       const char *err_string)
+{
+       if (mixer_window) {
+               mixer_clear();
+               endwin();
+               mixer_window = NULL;
+       }
+       printf("\n");
+
+       switch (error) {
+       case ERR_OPEN:
+               fprintf(stderr,
+                       PRGNAME ": failed to open mixer #%i/#%i: %s\n",
+                       card_id,
+                       mixer_id,
+                       snd_strerror(errno));
+               break;
+       case ERR_FCN:
+               fprintf(stderr,
+                       PRGNAME ": function %s failed: %s\n",
+                       err_string,
+                       snd_strerror(errno));
+               break;
+       case ERR_SIGNAL:
+               fprintf(stderr,
+                       PRGNAME ": aborting due to signal `%s'\n",
+                       err_string);
+               break;
+       case ERR_WINSIZE:
+               fprintf(stderr,
+                       PRGNAME ": screen size too small (%dx%d)\n",
+                       mixer_max_x,
+                       mixer_max_y);
+               break;
+       default:
+               break;
+       }
+
+       exit(error);
+}
+
+static int mixer_cbar_get_pos(int channel_index,
+                             int *x_p,
+                             int *y_p)
+{
+       int x;
+       int y;
+
+       if (channel_index < mixer_first_vis_channel ||
+        channel_index - mixer_first_vis_channel >= mixer_n_vis_channels)
+               return FALSE;
+
+       channel_index -= mixer_first_vis_channel;
+
+       x = mixer_ofs_x + 1;
+       y = mixer_ofs_y;
+       x += channel_index * (3 + 2 + 3 + 1 + mixer_extra_space);
+       y += mixer_max_y / 2;
+       y += mixer_cbar_height / 2 + 1;
+
+       if (x_p)
+               *x_p = x;
+       if (y_p)
+               *y_p = y;
+
+       return TRUE;
+}
+
+static void mixer_update_cbar(int channel_index)
+{
+       char string[64];
+       char c;
+       snd_mixer_channel_info_t cinfo =
+       {0};
+       snd_mixer_channel_direction_info_t cpinfo =
+       {0};
+       snd_mixer_channel_direction_info_t crinfo =
+       {0};
+       snd_mixer_channel_direction_t cpdata =
+       {0};
+       snd_mixer_channel_direction_t crdata =
+       {0};
+       int vleft, vright;
+       int x, y, i;
+       int output = 0, input = 0, volume;
+
+       /* set specified EXACT mode
+        */
+       if (snd_mixer_exact_mode(mixer_handle, mixer_exact) < 0)
+               mixer_abort(ERR_FCN, "snd_mixer_exact");
+
+       /* set new channel indices and read info
+        */
+       if (snd_mixer_channel_info(mixer_handle, channel_index, &cinfo) < 0)
+               mixer_abort(ERR_FCN, "snd_mixer_channel_info");
+       if (cinfo.caps & SND_MIXER_CINFO_CAP_OUTPUT) {
+               if (snd_mixer_channel_output_info(mixer_handle, channel_index, &cpinfo) < 0)
+                       mixer_abort(ERR_FCN, "snd_mixer_channel_output_info");
+               output = 1;
+       }
+       if (cinfo.caps & SND_MIXER_CINFO_CAP_INPUT) {
+               if (snd_mixer_channel_input_info(mixer_handle, channel_index, &crinfo) < 0)
+                       mixer_abort(ERR_FCN, "snd_mixer_channel_input_info");
+               input = 1;
+       }
+       if (mixer_input_volumes)
+               volume=(input && (crinfo.caps & SND_MIXER_CINFO_DCAP_VOLUME));
+       else
+               volume=(output && (cpinfo.caps & SND_MIXER_CINFO_DCAP_VOLUME));
+
+       /* set new channel values
+        */
+       if (channel_index == mixer_focus_channel &&
+           (mixer_lvolume_delta || mixer_rvolume_delta ||
+            mixer_toggle_mute_left || mixer_toggle_mute_right ||
+            mixer_balance_volumes ||
+            mixer_toggle_record_left || mixer_toggle_record_right ||
+            mixer_route_rtol_in || mixer_route_ltor_in)) {
+               if (output && snd_mixer_channel_output_read(mixer_handle, channel_index, &cpdata) < 0)
+                       mixer_abort(ERR_FCN, "snd_mixer_channel_output_read");
+               if (input && snd_mixer_channel_input_read(mixer_handle, channel_index, &crdata) < 0)
+                       mixer_abort(ERR_FCN, "snd_mixer_channel_input_read");
+
+               cpdata.flags &= ~SND_MIXER_DFLG_DECIBEL;
+               crdata.flags &= ~SND_MIXER_DFLG_DECIBEL;
+               if (volume) {
+                       if (mixer_input_volumes) {
+                               crdata.left = CLAMP(crdata.left + mixer_lvolume_delta, crinfo.min, crinfo.max);
+                               crdata.right = CLAMP(crdata.right + mixer_rvolume_delta, crinfo.min, crinfo.max);
+                               if (mixer_balance_volumes) {
+                                       crdata.left = (crdata.left + crdata.right) / 2;
+                                       crdata.right = crdata.left;
+                               }
+                       }
+                       else {
+                               cpdata.left = CLAMP(cpdata.left + mixer_lvolume_delta, cpinfo.min, cpinfo.max);
+                               cpdata.right = CLAMP(cpdata.right + mixer_rvolume_delta, cpinfo.min, cpinfo.max);
+                               if (mixer_balance_volumes) {
+                                       cpdata.left = (cpdata.left + cpdata.right) / 2;
+                                       cpdata.right = cpdata.left;
+                               }
+                       }
+               }
+               mixer_lvolume_delta = 0;
+               mixer_rvolume_delta = 0;
+               mixer_balance_volumes = 0;
+
+               if (output) {
+                       if (mixer_toggle_mute_left) {
+                               cpdata.flags ^= SND_MIXER_DFLG_MUTE_LEFT;
+                       }
+                       if (mixer_toggle_mute_right) {
+                               cpdata.flags ^= SND_MIXER_DFLG_MUTE_RIGHT;
+                       }
+               }
+               mixer_toggle_mute_left = mixer_toggle_mute_right = 0;
+
+               if (input) {
+                       if (mixer_toggle_record_left) {
+                               crdata.flags ^= SND_MIXER_DFLG_MUTE_LEFT;
+                       }
+                       if (mixer_toggle_record_right) {
+                               crdata.flags ^= SND_MIXER_DFLG_MUTE_RIGHT;
+                       }
+
+                       if (mixer_route_ltor_in) {
+                               crdata.flags ^= SND_MIXER_DFLG_LTOR;
+                       }
+                       if (mixer_route_rtol_in) {
+                               crdata.flags ^= SND_MIXER_DFLG_RTOL;
+                       }
+               }
+               mixer_toggle_record_left = mixer_toggle_record_right = 0;
+               mixer_route_ltor_in = mixer_route_rtol_in = 0;
+
+               if (output &&
+                   snd_mixer_channel_output_write(mixer_handle, channel_index, &cpdata) < 0)
+                       mixer_abort(ERR_FCN, "snd_mixer_channel_output_write");
+               if (input &&
+                   snd_mixer_channel_input_write(mixer_handle, channel_index, &crdata) < 0)
+                       mixer_abort(ERR_FCN, "snd_mixer_channel_input_write");
+       }
+       /* first, read values for the numbers to be displayed in
+        * specified EXACT mode
+        */
+       if (output &&
+           snd_mixer_channel_output_read(mixer_handle, channel_index, &cpdata) < 0)
+               mixer_abort(ERR_FCN, "snd_mixer_ioctl_channel_output_read");
+       if (input &&
+           snd_mixer_channel_input_read(mixer_handle, channel_index, &crdata) < 0)
+               mixer_abort(ERR_FCN, "snd_mixer_channel_input_read");
+       if (mixer_input_volumes) {
+               if (input) {
+                       vleft = crdata.left;
+                       vright = crdata.right;
+               }
+               else {
+                       vleft = vright = 0;
+               }
+       }
+       else {
+               if (output) {
+                       vleft = cpdata.left;
+                       vright = cpdata.right;
+               }
+               else {
+                       vleft = vright = 0;
+               }
+       }
+
+       /* then, always use percentage values for the bars. if we don't do
+        * this, we will see aliasing effects on specific circumstances.
+        * (actually they don't really dissapear, but they are transfered
+        *  to bar<->smaller-scale ambiguities).
+        */
+       if (mixer_exact) {
+               i = 0;
+               if (snd_mixer_exact_mode(mixer_handle, 0) < 0)
+                       mixer_abort(ERR_FCN, "snd_mixer_exact");
+               if (output &&
+                   snd_mixer_channel_output_read(mixer_handle, channel_index, &cpdata) < 0)
+                       mixer_abort(ERR_FCN, "snd_mixer_channel_output_read");
+               if (input &&
+                   snd_mixer_channel_input_read(mixer_handle, channel_index, &crdata) < 0)
+                       mixer_abort(ERR_FCN, "snd_mixer_channel_input_read");
+       }
+       /* get channel bar position
+        */
+       if (!mixer_cbar_get_pos(channel_index, &x, &y))
+               return;
+
+       /* channel bar name
+        */
+       mixer_dc(channel_index == mixer_focus_channel ? DC_CBAR_FOCUS_LABEL : DC_CBAR_LABEL);
+       cinfo.name[8] = 0;
+       for (i = 0; i < 8; i++) {
+               string[i] = ' ';
+       }
+       sprintf(string + (8 - strlen(cinfo.name)) / 2, "%s          ", cinfo.name);
+       string[8] = 0;
+       mvaddstr(y, x, string);
+       y--;
+
+       /* current channel values
+        */
+       mixer_dc(DC_BACK);
+       mvaddstr(y, x, "         ");
+       mixer_dc(DC_TEXT);
+       sprintf(string, "%d", vleft);
+       mvaddstr(y, x + 3 - strlen(string), string);
+       mixer_dc(DC_CBAR_FRAME);
+       mvaddch(y, x + 3, '<');
+       mvaddch(y, x + 4, '>');
+       mixer_dc(DC_TEXT);
+       sprintf(string, "%d", vright);
+       mvaddstr(y, x + 5, string);
+       y--;
+
+       /* left/right bar
+        */
+       mixer_dc(DC_CBAR_FRAME);
+       mvaddstr(y, x, "         ");
+       mvaddch(y, x + 2, ACS_LLCORNER);
+       mvaddch(y, x + 3, ACS_HLINE);
+       mvaddch(y, x + 4, ACS_HLINE);
+       mvaddch(y, x + 5, ACS_LRCORNER);
+       y--;
+       for (i = 0; i < mixer_cbar_height; i++) {
+               mvaddstr(y - i, x, "         ");
+               mvaddch(y - i, x + 2, ACS_VLINE);
+               mvaddch(y - i, x + 5, ACS_VLINE);
+       }
+       string[2] = 0;
+       for (i = 0; i < mixer_cbar_height; i++) {
+               int dc;
+
+               if (i + 1 >= 0.8 * mixer_cbar_height)
+                       dc = DC_CBAR_FULL_3;
+               else if (i + 1 >= 0.4 * mixer_cbar_height)
+                       dc = DC_CBAR_FULL_2;
+               else
+                       dc = DC_CBAR_FULL_1;
+               mvaddch(y, x + 3, mixer_dc(vleft > i * 100 / mixer_cbar_height ? dc : DC_CBAR_EMPTY));
+               mvaddch(y, x + 4, mixer_dc(vright > i * 100 / mixer_cbar_height ? dc : DC_CBAR_EMPTY));
+               y--;
+       }
+
+       /* muted?
+        */
+       mixer_dc(DC_BACK);
+       mvaddstr(y, x, "         ");
+       if (output) {
+               c = (cpinfo.caps & SND_MIXER_CINFO_DCAP_MUTE) ? '-' : ' ';
+               mixer_dc(DC_CBAR_FRAME);
+               mvaddch(y, x + 2, ACS_ULCORNER);
+               mvaddch(y, x + 3, mixer_dc(cpdata.flags & SND_MIXER_DFLG_MUTE_LEFT ?
+                                          DC_CBAR_MUTE : DC_CBAR_NOMUTE));
+               mvaddch(y, x + 4, mixer_dc(cpdata.flags & SND_MIXER_DFLG_MUTE_RIGHT ?
+                                          DC_CBAR_MUTE : DC_CBAR_NOMUTE));
+               mixer_dc(DC_CBAR_FRAME);
+               mvaddch(y, x + 5, ACS_URCORNER);
+       }
+       y--;
+
+       /* record input?
+        */
+       mixer_dc(DC_BACK);
+       mvaddstr(y, x, "         ");
+       if (input) {
+               if ((crdata.flags & SND_MIXER_DFLG_MUTE) != SND_MIXER_DFLG_MUTE) {
+                       mixer_dc(DC_CBAR_RECORD);
+                       mvaddstr(y, x + 1, "RECORD");
+                       if (!(crdata.flags & SND_MIXER_DFLG_MUTE_LEFT)) {
+                               if (crdata.flags & SND_MIXER_DFLG_LTOR)
+                                       mvaddstr(y + 2, x + 6, "L");
+                               else
+                                       mvaddstr(y + 1, x + 1, "L");
+                       }
+                       if (!(crdata.flags & SND_MIXER_DFLG_MUTE_RIGHT)) {
+                               if (crdata.flags & SND_MIXER_DFLG_RTOL)
+                                       mvaddstr(y + 2, x + 1, "R");
+                               else
+                                       mvaddstr(y + 1, x + 6, "R");
+                       }
+               } else {
+                       for (i = 0; i < 6; i++)
+                               mvaddch(y, x + 1 + i, mixer_dc(DC_CBAR_NORECORD));
+               }
+       }
+       y--;
+}
+
+static void mixer_update_cbars(void)
+{
+       static int o_x = 0;
+       static int o_y = 0;
+       int i, x, y;
+
+       if (!mixer_cbar_get_pos(mixer_focus_channel, &x, &y)) {
+               if (mixer_focus_channel < mixer_first_vis_channel)
+                       mixer_first_vis_channel = mixer_focus_channel;
+               else if (mixer_focus_channel >= mixer_first_vis_channel + mixer_n_vis_channels)
+                       mixer_first_vis_channel = mixer_focus_channel - mixer_n_vis_channels + 1;
+               mixer_cbar_get_pos(mixer_focus_channel, &x, &y);
+       }
+       for (i = 0; i < mixer_n_vis_channels; i++)
+               mixer_update_cbar(i + mixer_first_vis_channel);
+
+       /* draw focused cbar
+        */
+       mixer_dc(DC_BACK);
+       mvaddstr(o_y, o_x, " ");
+       mvaddstr(o_y, o_x + 9, " ");
+       o_x = x - 1;
+       o_y = y;
+       mixer_dc(DC_FOCUS);
+       mvaddstr(o_y, o_x, "<");
+       mvaddstr(o_y, o_x + 9, ">");
+}
+
+static void mixer_draw_frame(void)
+{
+       char string[128];
+       int i;
+       int max_len;
+
+       mixer_dc(DC_FRAME);
+
+       /* corners
+        */
+       mvaddch(0, 0, ACS_ULCORNER);
+       mvaddch(mixer_max_y - 1, 0, ACS_LLCORNER);
+       mvaddch(mixer_max_y - 1, mixer_max_x - 1, ACS_LRCORNER);
+       mvaddch(0, mixer_max_x - 1, ACS_URCORNER);
+
+       /* lines
+        */
+       for (i = 1; i < mixer_max_y - 1; i++) {
+               mvaddch(i, 0, ACS_VLINE);
+               mvaddch(i, mixer_max_x - 1, ACS_VLINE);
+       }
+       for (i = 1; i < mixer_max_x - 1; i++) {
+               mvaddch(0, i, ACS_HLINE);
+               mvaddch(mixer_max_y - 1, i, ACS_HLINE);
+       }
+
+       /* program title
+        */
+       sprintf(string, "%s %s", PRGNAME_UPPER, VERSION);
+       max_len = strlen(string);
+       mvaddch(0, mixer_max_x / 2 - max_len / 2 - 1, '[');
+       mvaddch(0, mixer_max_x / 2 - max_len / 2 + max_len, ']');
+       mixer_dc(DC_TEXT);
+       mvaddstr(0, mixer_max_x / 2 - max_len / 2, string);
+
+       /* card name
+        */
+       mixer_dc(DC_PROMPT);
+       mvaddstr(1, 2, "Card:");
+       mixer_dc(DC_TEXT);
+       sprintf(string, "%s", mixer_card_name);
+       max_len = mixer_max_x - 2 - 6 - 2;
+       if (strlen(string) > max_len)
+               string[max_len] = 0;
+       mvaddstr(1, 2 + 6, string);
+
+       /* device name
+        */
+       mixer_dc(DC_PROMPT);
+       mvaddstr(2, 2, "Chip: ");
+       mixer_dc(DC_TEXT);
+       sprintf(string, "%s", mixer_device_name);
+       max_len = mixer_max_x - 2 - 6 - 2;
+       if (strlen(string) > max_len)
+               string[max_len] = 0;
+       mvaddstr(2, 2 + 6, string);
+       if (mixer_input_volumes)
+               mvaddstr(3, 2, "Record mixer");
+       else
+               mvaddstr(3, 2, "            ");
+}
+
+static void mixer_init(void)
+{
+       static snd_mixer_info_t mixer_info =
+       {0};
+       static struct snd_ctl_hw_info hw_info;
+       void *ctl_handle;
+
+       if (snd_ctl_open(&ctl_handle, card_id) < 0)
+               mixer_abort(ERR_OPEN, "snd_ctl_open");
+       if (snd_ctl_hw_info(ctl_handle, &hw_info) < 0)
+               mixer_abort(ERR_FCN, "snd_ctl_hw_info");
+       snd_ctl_close(ctl_handle);
+       /* open mixer device
+        */
+       if (snd_mixer_open(&mixer_handle, card_id, mixer_id) < 0)
+               mixer_abort(ERR_OPEN, "snd_mixer_open");
+
+       /* setup global variables
+        */
+       if (snd_mixer_info(mixer_handle, &mixer_info) < 0)
+               mixer_abort(ERR_FCN, "snd_mixer_info");
+       mixer_n_channels = mixer_info.channels;
+       mixer_card_name = hw_info.name;
+       mixer_device_name = mixer_info.name;
+}
+
+static void mixer_iteration_update(void *dummy, int channel)
+{
+#if 0
+       fprintf(stderr, "*** channel = %i\n", channel);
+#endif
+       mixer_update_cbar(channel);
+       refresh();
+}
+
+static int mixer_iteration(void)
+{
+       snd_mixer_callbacks_t callbacks;
+       int key;
+       int finished = 0;
+       int mixer_fd;
+       fd_set in;
+
+       bzero(&callbacks, sizeof(callbacks));
+       callbacks.channel_was_changed = mixer_iteration_update;
+       callbacks.output_channel_was_changed = mixer_iteration_update;
+       callbacks.input_channel_was_changed = mixer_iteration_update;
+       mixer_fd = snd_mixer_file_descriptor(mixer_handle);
+       while (1) {
+               FD_ZERO(&in);
+               FD_SET(fileno(stdin), &in);
+               FD_SET(mixer_fd, &in);
+               if (select(mixer_fd + 1, &in, NULL, NULL, NULL) <= 0)
+                       return 1;
+               if (FD_ISSET(mixer_fd, &in))
+                       snd_mixer_read(mixer_handle, &callbacks);
+               if (FD_ISSET(fileno(stdin), &in))
+                       break;
+       }
+       key = getch();
+       switch (key) {
+       case 27:                /* Escape */
+               finished = 1;
+               break;
+       case 9:         /* Tab */
+               mixer_exact = !mixer_exact;
+               break;
+       case KEY_RIGHT:
+       case 'n':
+               mixer_focus_channel += 1;
+               break;
+       case KEY_LEFT:
+       case 'p':
+               mixer_focus_channel -= 1;
+               break;
+       case KEY_PPAGE:
+               if (mixer_exact) {
+                       mixer_lvolume_delta = 8;
+                       mixer_rvolume_delta = 8;
+               } else {
+                       mixer_lvolume_delta = 10;
+                       mixer_rvolume_delta = 10;
+               }
+               break;
+       case KEY_NPAGE:
+               if (mixer_exact) {
+                       mixer_lvolume_delta = -8;
+                       mixer_rvolume_delta = -8;
+               } else {
+                       mixer_lvolume_delta = -10;
+                       mixer_rvolume_delta = -10;
+               }
+               break;
+       case KEY_BEG:
+       case KEY_HOME:
+               mixer_lvolume_delta = 512;
+               mixer_rvolume_delta = 512;
+               break;
+       case KEY_LL:
+       case KEY_END:
+               mixer_lvolume_delta = -512;
+               mixer_rvolume_delta = -512;
+               break;
+       case '+':
+               mixer_lvolume_delta = 1;
+               mixer_rvolume_delta = 1;
+               break;
+       case '-':
+               mixer_lvolume_delta = -1;
+               mixer_rvolume_delta = -1;
+               break;
+       case 'w':
+       case KEY_UP:
+               mixer_lvolume_delta = 1;
+               mixer_rvolume_delta = 1;
+       case 'W':
+               mixer_lvolume_delta += 1;
+               mixer_rvolume_delta += 1;
+               break;
+       case 'x':
+       case KEY_DOWN:
+               mixer_lvolume_delta = -1;
+               mixer_rvolume_delta = -1;
+       case 'X':
+               mixer_lvolume_delta += -1;
+               mixer_rvolume_delta += -1;
+               break;
+       case 'q':
+               mixer_lvolume_delta = 1;
+       case 'Q':
+               mixer_lvolume_delta += 1;
+               break;
+       case 'y':
+       case 'z':
+               mixer_lvolume_delta = -1;
+       case 'Y':
+       case 'Z':
+               mixer_lvolume_delta += -1;
+               break;
+       case 'e':
+               mixer_rvolume_delta = 1;
+       case 'E':
+               mixer_rvolume_delta += 1;
+               break;
+       case 'c':
+               mixer_rvolume_delta = -1;
+       case 'C':
+               mixer_rvolume_delta += -1;
+               break;
+       case 'm':
+       case 'M':
+               mixer_input_volumes = 0;
+               mixer_toggle_mute_left = 1;
+               mixer_toggle_mute_right = 1;
+               break;
+       case 'b':
+       case 'B':
+       case '=':
+               mixer_balance_volumes = 1;
+               break;
+       case '<':
+       case ',':
+               mixer_input_volumes = 0;
+               mixer_toggle_mute_left = 1;
+               break;
+       case '>':
+       case '.':
+               mixer_input_volumes = 0;
+               mixer_toggle_mute_right = 1;
+               break;
+       case 'R':
+       case 'r':
+               mixer_input_volumes = !mixer_input_volumes;
+               break;
+       case 'L':
+       case 'l':
+               mixer_clear();
+               break;
+       case ' ':
+               mixer_input_volumes = 1;
+               mixer_toggle_record_left = 1;
+               mixer_toggle_record_right = 1;
+               break;
+       case KEY_IC:
+       case ';':
+               mixer_input_volumes = 1;
+               mixer_toggle_record_left = 1;
+               break;
+       case '\'':
+       case KEY_DC:
+               mixer_input_volumes = 1;
+               mixer_toggle_record_right = 1;
+               break;
+       case '1':
+               mixer_input_volumes = 1;
+               mixer_route_rtol_in = 1;
+               break;
+       case '2':
+               mixer_input_volumes = 1;
+               mixer_route_ltor_in = 1;
+               break;
+       }
+       mixer_focus_channel = CLAMP(mixer_focus_channel, 0, mixer_n_channels - 1);
+
+       return finished;
+}
+
+static void mixer_init_screen(void)
+{
+       signal(SIGWINCH, (void *) mixer_init_screen);
+
+       getmaxyx(mixer_window, mixer_max_y, mixer_max_x);
+       mixer_clear();
+       mixer_max_x = MAX(MIXER_MIN_X, mixer_max_x);
+       mixer_max_y = MAX(MIXER_MIN_Y, mixer_max_y);
+       mixer_clear();
+       mixer_ofs_x = 2;
+       mixer_ofs_y = 2;
+       mixer_extra_space = 0;
+       mixer_n_vis_channels = MIN((mixer_max_x - 2 * mixer_ofs_x + 1) / (9 + mixer_extra_space),
+                                  mixer_n_channels);
+       mixer_extra_space = ((mixer_max_x - 2 * mixer_ofs_x - 1 - mixer_n_vis_channels * 9.0) /
+                            (mixer_n_vis_channels - 1));
+       if (mixer_n_vis_channels < mixer_n_channels) {
+               /* recalc
+                */
+               mixer_extra_space = MAX(mixer_extra_space, 1);
+               mixer_n_vis_channels = MIN((mixer_max_x - 2 * mixer_ofs_x + 1) / (9 + mixer_extra_space),
+                                          mixer_n_channels);
+               mixer_extra_space = ((mixer_max_x - 2 * mixer_ofs_x - 1 - mixer_n_vis_channels * 9.0) /
+                                    (mixer_n_vis_channels - 1));
+       }
+       mixer_first_vis_channel = 0;
+       mixer_cbar_height = 10 + MAX(0, (mixer_max_y - MIXER_MIN_Y - 1)) / 2;
+}
+
+static void mixer_signal_handler(int signal)
+{
+       mixer_abort(ERR_SIGNAL, sys_siglist[signal]);
+}
+
+int main(int argc,
+        char **argv)
+{
+       int opt;
+
+       /* parse args
+        */
+       do {
+               opt = getopt(argc, argv, "c:m:ehg");
+               switch (opt) {
+               case '?':
+               case 'h':
+                       fprintf(stderr, "%s %s\n", PRGNAME_UPPER, VERSION);
+                       fprintf(stderr, "Usage: %s [-e] [-c <card: 1..%i>] [-m <mixer: 0..1>]\n", PRGNAME, snd_cards());
+                       mixer_abort(ERR_NONE, "");
+               case 'c':
+                       card_id = snd_card_name(optarg);
+                       break;
+               case 'e':
+                       mixer_exact = !mixer_exact;
+                       break;
+               case 'g':
+                       mixer_do_color = !mixer_do_color;
+                       break;
+               case 'm':
+                       mixer_id = CLAMP(optarg[0], '0', '1') - '0';
+                       break;
+               }
+       }
+       while (opt > 0);
+
+       /* initialize mixer
+        */
+       mixer_init();
+
+       /* setup signal handlers
+        */
+       signal(SIGINT, mixer_signal_handler);
+       signal(SIGTRAP, mixer_signal_handler);
+       signal(SIGABRT, mixer_signal_handler);
+       signal(SIGQUIT, mixer_signal_handler);
+       signal(SIGBUS, mixer_signal_handler);
+       signal(SIGSEGV, mixer_signal_handler);
+       signal(SIGPIPE, mixer_signal_handler);
+       signal(SIGTERM, mixer_signal_handler);
+
+       /* initialize ncurses
+        */
+       mixer_window = initscr();
+       if (mixer_do_color)
+               mixer_do_color = has_colors();
+       mixer_init_draw_contexts();
+       mixer_init_screen();
+       if (mixer_max_x < MIXER_MIN_X ||
+           mixer_max_y < MIXER_MIN_Y)
+               mixer_abort(ERR_WINSIZE, "");
+
+       /* react on key presses
+        * and draw window
+        */
+       keypad(mixer_window, TRUE);
+       leaveok(mixer_window, TRUE);
+       cbreak();
+       noecho();
+       do {
+               mixer_update_cbars();
+               mixer_draw_frame();
+               refresh();
+       }
+       while (!mixer_iteration());
+
+       mixer_abort(ERR_NONE, "");
+};
index 6d4d0d1d177cd2eb1b92dbbc092878a056e63a05..3e410e8143bbd8ad3c8c42150811d26e6f960ec9 100644 (file)
@@ -1,8 +1,8 @@
 INCLUDES = -I$(top_srcdir)/include
-LDADD = -lasound
+LDADD = -lasound -lm
 EXTRA_DIST = README.first
 
 bin_PROGRAMS = amixer
-amixer_SOURCES = amain.cpp amixer.cpp
-noinst_HEADERS = amixer.h atypes.h
+amixer_SOURCES = amixer.c
+noinst_HEADERS = amixer.h
 man_MANS = amixer.1
diff --git a/amixer/README.first b/amixer/README.first
deleted file mode 100644 (file)
index daf520a..0000000
+++ /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 <arloafoe@cs.vu.nl>
-
-
-Running
--------
-
-amixer -h should display the syntax
-
-
-Bugs & Todo
------------
-
-A lot, let me know..
-
-
-Changes
--------
-
-v0.001 March 20 1998 - Initial code
-v0.1   Apr   21 1997 - Actually useful now
-
-
-Contact
--------
-Andy Lo A Foe
-arloafoe@cs.vu.nl
diff --git a/amixer/amain.cpp b/amixer/amain.cpp
deleted file mode 100644 (file)
index 1324bf0..0000000
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * Copyright 1998, Andy Lo A Foe <arloafoe@cs.vu.nl>
- * 
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/asoundlib.h>
-#include "amixer.h"
-
-#define MIXER_RC ".amixerrc"
-
-char *rc_file(void)
-{
-       static char rc_path[1024];
-       char *p;
-
-       p = getenv("HOME");
-       if (p) {
-               sprintf(rc_path, "%s/%s", p, MIXER_RC);
-       } else {
-               printf("Error reading HOME env. variable\n");
-               return NULL;
-       }
-       return rc_path;
-}
-
-void copyright()
-{
-       printf("CLI ALSA Mixer v0.11 (c) 1998 Adnans\n\n");
-}
-
-void usage()
-{
-       printf("\n"
-              "Usage: amixer [-c card] [-d dev] device [vol|L:R] [mute|unmute] [rec|norec]\n\n"
-              "       amixer [-p path] -r\tRead %s or <path> settings\n"
-              "       amixer [-p path] -w\tWrite %s or <path> settings\n"
-              "       amixer -q ...\t\tQuiet mode\n"
-              "       amixer -h\t\tHelp\n\n"
-              "Example: amixer line-out 80:50 unmute rec\n\n", rc_file(), rc_file());
-}
-
-
-void read_config(Mixer * mix, const char *path)
-{
-       FILE *rc;
-       char buf[1024];
-       int opt1;
-       int opt2;
-       int left, right;
-       int dev;
-       int count = 0;
-       int flags;
-
-       if (path && strcmp(path, "-") == 0)
-               rc = stdin;
-       else if ((rc = fopen(path ? path : rc_file(), "r")) == NULL) {
-               printf("Mixer values not read\n");
-               return;
-       }
-       while (!feof(rc) && fgets(buf, 1024, rc)) {
-               count++;
-               if (buf[0] == '\n')
-                       continue;
-               if (buf[0] == '#' || strlen(buf) == 0)
-                       continue;
-
-               if (sscanf(buf, "%d %d:%d %d %d\n", &dev, &left, &right, &opt1, &opt2) != 5) {
-                       printf("WARNING: unable to make out line %d of .rc file -> \"%s\"\n", count, buf);
-                       continue;
-               }
-               flags = 0;
-               if (opt1)
-                       flags |= E_MIXER_MUTE;
-               if (opt2)
-                       flags |= E_MIXER_RECORD;
-               // Set mixer settings
-               mix->DeviceSet(dev);
-               mix->Write(left, right, flags);
-       }
-
-       if (rc != stdin)
-               fclose(rc);
-       return;
-}
-
-void write_config(Mixer * mix, const char *path)
-{
-       FILE *rc;
-       int32 left, right, flags;
-
-       if (path && strcmp(path, "-") == 0)
-               rc = stdout;
-       else if ((rc = fopen(path ? path : rc_file(), "w+")) == NULL) {
-               printf("Mixer values not written\n");
-               return;
-       }
-       fprintf(rc, "# CLI ALSA mixer settings file. Autogenerated\n"
-               "# Modify at your own risk :)\n\n");
-       for (int i = 0; i < mix->NumDevices(); i++) {
-               mix->DeviceSet(i);
-               mix->Read(&left, &right, &flags);
-               fprintf(rc, "%d %d:%d %d %d\n", i, mix->Left(), mix->Right(), flags & E_MIXER_MUTE ? 1 : 0, flags & E_MIXER_RECORD ? 1 : 0);
-       }
-       if (rc != stdout)
-               fclose(rc);
-       return;
-}
-
-int main(int argc, char **argv)
-{
-       int card = 0, device = 0;
-       char device_name[64] = "";
-       int32 exact, mute, unmute, norec, rec, left, right, flags, device_index;
-       int32 left_dB, right_dB;
-       int32 cur_left, cur_right, cur_flags;
-       int count, quiet = 0;
-       int i, add;
-       int pathind = 0;
-       Mixer *the_mixer;
-
-       exact = mute = rec = norec = unmute = device_index = left = right = -1;
-       left_dB = right_dB = -1;
-
-       for (add = 0; add + 1 < argc; i++) {
-               if (!strcmp(argv[add + 1], "--help")) {
-                       usage();
-                       return 0;
-               }
-               if (argv[add + 1][0] == '-') {
-                       add++;
-                       if (argv[add][1] == 'c') {
-                               card = snd_card_name(argv[++add]);
-                               if (card < 0) {
-                                       fprintf(stderr, "Invalid card: %s\n", argv[2]);
-                                       exit(1);
-                               }
-                       } else if (argv[add][1] == 'd') {
-                               device = atoi(argv[++add]);
-                               if (device < 0 || device > 128) {
-                                       fprintf(stderr, "Invalid device: %s\n", argv[2]);
-                                       exit(1);
-                               }
-                       } else if (argv[add][1] == 'h') {
-                               usage();
-                               return 0;
-                       } else if (argv[add][1] == 'p') {
-                               pathind = ++add;
-                       } else if (argv[add][1] == 'r') {
-                               the_mixer = new Mixer(card, device);
-                               if (the_mixer && the_mixer->Init())
-                                       read_config(the_mixer, pathind ? argv[pathind] : (const char *) NULL);
-                               delete the_mixer;
-                               return 0;
-                       } else if (argv[add][1] == 'w') {
-                               the_mixer = new Mixer(card, device);
-                               if (the_mixer && the_mixer->Init())
-                                       write_config(the_mixer, pathind ? argv[pathind] : (const char *) NULL);
-                               delete the_mixer;
-                               return 0;
-                       } else if (argv[add][1] == 'q') {
-                               quiet = 1;
-                       } else {
-                               fprintf(stderr, "Invalid option: %s\n", argv[add] + 1);
-                               return 1;
-                       }
-               } else {
-                       break;
-               }
-       }
-       for (i = 1 + add; i < argc; i++) {
-               if (strcmp(argv[i], "exact") == 0) {
-                       exact = 1;
-               } else if (strcmp(argv[i], "mute") == 0) {
-                       mute = 1;
-               } else if (strcmp(argv[i], "unmute") == 0) {
-                       unmute = 1;
-               } else if (strcmp(argv[i], "rec") == 0) {
-                       rec = 1;
-               } else if (strcmp(argv[i], "norec") == 0) {
-                       norec = 1;
-               } else if (sscanf(argv[i], "%d:%d", &left, &right) == 2) {
-               } else if (sscanf(argv[i], "%d", &left) == 1) {
-                       right = left;
-               } else {
-                       strncpy(device_name, argv[i], sizeof(device_name));
-                       device_name[sizeof(device_name) - 1] = 0;
-               }
-       }
-       Mixer mixer(card, device);
-
-       if (mixer.Init() == false) {
-               fprintf(stderr, "Failed to open mixer device\n");
-               return 1;
-       }
-       count = mixer.NumDevices();
-
-       for (i = 0; i < count; i++) {
-               mixer.DeviceSet(i);
-               if (strcasecmp(device_name, mixer.Name()) == 0)
-                       device_index = i;
-       }
-       if (!quiet)
-               copyright();
-       if (device_index >= 0) {
-               mixer.DeviceSet(device_index);
-               mixer.Read(&cur_left, &cur_right, &cur_flags);
-               if (left >= 0)
-                       cur_left = left;
-               if (right >= 0)
-                       cur_right = right;
-               if (rec == 1)
-                       cur_flags |= E_MIXER_RECORD;
-               else if (norec == 1)
-                       cur_flags &= ~E_MIXER_RECORD;
-               if (mute == 1)
-                       cur_flags |= E_MIXER_MUTE;
-               else if (unmute == 1)
-                       cur_flags &= ~E_MIXER_MUTE;
-               if (left != -1 || rec != -1 || norec != -1 || mute != -1 || unmute != -1) {
-                       mixer.Write(cur_left, cur_right, cur_flags);
-               }
-               if (!quiet) {
-                       printf("%-16s", mixer.Name());
-                       mixer.Read(&left, &right, &flags);
-                       mixer.Read_dB(&left_dB, &right_dB);
-                       printf("%-3d%% (%6.2fdB) : %-3d%% (%6.2fdB) %s %s\n\n",
-                              left, ((float) left_dB) / 100.0,
-                              right, ((float) right_dB) / 100.0,
-                              (flags & E_MIXER_MUTE) ? "Mute" : "    ",
-                              (flags & E_MIXER_RECORD) ? "Rec" : "   ");
-               }
-       } else {
-               if (quiet) {
-                       usage();
-                       return 1;
-               }
-               if (strlen(device_name))
-                       printf("Device not found: %s\n\n", device_name);
-               for (i = 0; i < count; i++) {
-                       mixer.DeviceSet(i);
-                       printf("%-16s", mixer.Name());
-                       mixer.Read(&left, &right, &flags);
-                       mixer.Read_dB(&left_dB, &right_dB);
-                       printf("%-3d%% (%6.2fdB) : %-3d%% (%6.2fdB) %s %s\n",
-                              left, ((float) left_dB) / 100.0, right, ((float) right_dB) / 100.0,
-                              (flags & E_MIXER_MUTE) ? "Mute" : "    ",
-                              (flags & E_MIXER_RECORD) ? "Rec" : "   ");
-               }
-               return 0;
-       }
-
-       return 0;
-}
diff --git a/amixer/amixer.c b/amixer/amixer.c
new file mode 100644 (file)
index 0000000..e71f0fe
--- /dev/null
@@ -0,0 +1,1259 @@
+/*
+ *   ALSA command line mixer utility
+ *   Copyright (c) 1999 by Jaroslav Kysela <perex@jcu.cz>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <math.h>
+#include <sys/asoundlib.h>
+#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 <options> 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 (file)
index 46fcba2..0000000
+++ /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 <sys/types.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/asoundlib.h>
-#include "amixer.h"
-
-
-Mixer::Mixer(int card, int device)
-{
-       mixer_handle = NULL;    
-
-       if (snd_mixer_open(&mixer_handle, card, device) < 0) {
-               fprintf(stderr, "Can't access mixer %i/%i\n", card+1, device);
-               mixer_status = ~E_MIXER_SUCCESS;
-               return;
-       }
-       mixer_status = E_MIXER_SUCCESS;
-       mixer_status |= E_MIXER_NEED_CLOSE;                     
-}
-
-
-bool Mixer::Open(int card, int device)
-{
-       Close();
-       if (snd_mixer_open(&mixer_handle, card, device) < 0) {
-               fprintf(stderr, "Can't access mixer %i/%i\n", card + 1, device);
-               mixer_status = ~E_MIXER_SUCCESS;
-       } else{
-               mixer_status = E_MIXER_SUCCESS;
-               mixer_status |= E_MIXER_NEED_CLOSE;                     
-       }
-       return Init();
-}
-
-
-void Mixer::Close()
-{
-       if (mixer_handle != NULL && mixer_status & E_MIXER_NEED_CLOSE) {
-               snd_mixer_close(mixer_handle);
-       }
-       mixer_handle = NULL;
-       mixer_status = ~E_MIXER_SUCCESS;
-}
-
-
-Mixer::~Mixer()
-{
-       Close();
-}
-
-
-bool Mixer::Init()
-{
-       if (!(mixer_status & E_MIXER_SUCCESS)) 
-               return false;
-       if ((nr_devices = snd_mixer_channels(mixer_handle)) < 0)
-               return false;
-
-       return true;
-}
-
-
-char* Mixer::Name()
-{
-       return Name(current_device);
-}
-
-char* Mixer::Name(int32 device)
-{
-       if (snd_mixer_channel_info(mixer_handle,device,&ch_info) < 0)
-               return "Unknown";
-       return (char *)ch_info.name;
-}
-
-void Mixer::Update()
-{
-       if(snd_mixer_channel_output_read(mixer_handle, current_device, &ch_data) < 0) {
-               fprintf(stderr, "Can't read data from channel %i\n", current_device);
-               return;         /* No fail code? */
-       }
-}
-
-void Mixer::DeviceRead(int32 device, int32 *left, int32 *right, int32 *flags)
-{
-       current_device = device;
-       Update();
-       *left = ch_data.left;
-       *right = ch_data.right;
-       *flags = ch_data.flags;
-}
-
-
-void Mixer::DeviceWrite(int32 device, int32 left, int32 right, int32 flags)
-{
-       current_device = device;
-       ch_data.channel = device;
-       ch_data.left = left;
-       ch_data.right = right;
-       ch_data.flags = flags;
-       if(snd_mixer_channel_output_write(mixer_handle, device, &ch_data) < 0) {
-               fprintf(stderr, "Can't write data to channel %i\n", device);
-               return;         /* No fail code? */
-       }
-}
-
index 64c9fa3b033fadf809400f817eba0c1bd8faadb5..7e6f5aaa323195a75d66141b0cdac452a0e9ce50 100644 (file)
@@ -1,4 +1,6 @@
 /*
+ *   ALSA command line mixer utility
+ *   Copyright (c) 1999 by Jaroslav Kysela <perex@jcu.cz>
  *
  *   This program is free software; you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
  *
  */
 
-#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 (file)
index 8ccd461..0000000
+++ /dev/null
@@ -1 +0,0 @@
-typedef int int32;