diff --git a/README b/README index ecf5a00..64c9294 100644 --- a/README +++ b/README @@ -6,6 +6,7 @@ This packages contains command line utilities for the ALSA project. Package should be compiled only with installed ALSA driver and ALSA C library. +alsactl - utility for store / restore of soundcard settings aplay/arecord - utility for playback / record of .wav,.voc,.au files amixer - a command line mixer alsamixer - ncurses mixer diff --git a/acinclude.m4 b/acinclude.m4 new file mode 100644 index 0000000..a7bfd8d --- /dev/null +++ b/acinclude.m4 @@ -0,0 +1,12 @@ +AC_DEFUN(SAVE_UTIL_VERSION, [ +SND_UTIL_VERSION=$VERSION +echo $VERSION > $srcdir/version +AC_DEFINE_UNQUOTED(VERSION, "$SND_UTIL_VERSION") +AC_SUBST(SND_UTIL_VERSION) +SND_UTIL_MAJOR=`echo $VERSION | cut -d . -f 1` +AC_SUBST(SND_UTIL_MAJOR) +SND_UTIL_MINOR=`echo $VERSION | cut -d . -f 2` +AC_SUBST(SND_UTIL_MINOR) +SND_UTIL_SUBMINOR=`echo $VERSION | cut -d . -f 3 | sed -e 's/pre[[0-9]]*//g'` +AC_SUBST(SND_UTIL_SUBMINOR) +]) diff --git a/alsactl/alsactl.h b/alsactl/alsactl.h index e17f9ef..271e98a 100644 --- a/alsactl/alsactl.h +++ b/alsactl/alsactl.h @@ -26,6 +26,12 @@ #define ALSACTL_FILE "/etc/asound.conf" +#define LEFT 1 +#define RIGHT 2 + +#define OUTPUT 0 +#define INPUT 1 + extern int debugflag; extern void error(const char *fmt,...); @@ -44,10 +50,12 @@ struct ctl { struct mixer_channel { int no; - int change; - snd_mixer_channel_info_t i; - snd_mixer_channel_t c; - snd_mixer_channel_t cr; + 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; }; diff --git a/alsactl/alsactl_lexer.l b/alsactl/alsactl_lexer.l index a0e3712..8d7f962 100644 --- a/alsactl/alsactl_lexer.l +++ b/alsactl/alsactl_lexer.l @@ -72,8 +72,7 @@ gstatus return L_GSTATUS; enable return L_ENABLE; disable return L_DISABLE; mute return L_MUTE; -swout return L_SWOUT; -swin return L_SWIN; +swap return L_SWAP; /* boolean */ diff --git a/alsactl/alsactl_parser.y b/alsactl/alsactl_parser.y index 5948eed..eee80c7 100644 --- a/alsactl/alsactl_parser.y +++ b/alsactl/alsactl_parser.y @@ -42,10 +42,12 @@ static void select_pcm(char *name); static void select_rawmidi(char *name); static void select_mixer_channel(char *name); -static void set_mixer_channel(int left, int right); -static void set_mixer_channel_record(int left, int right); -static void set_mixer_channel_flags(unsigned int mask, unsigned int flags); -static void set_mixer_channel_end(void); +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 @@ -70,8 +72,7 @@ static struct soundcard *Xsoundcard = NULL; static struct mixer *Xmixer = NULL; static struct pcm *Xpcm = NULL; static struct rawmidi *Xrawmidi = NULL; -static struct mixer_channel *Xmixerchannel = NULL; -static unsigned int Xmixerchannelflags = 0; +static struct mixer_channel *Xchannel = NULL; static int Xswitchtype = SWITCH_CONTROL; static int *Xswitchchange = NULL; static void *Xswitch = NULL; @@ -104,7 +105,7 @@ static unsigned short Xswitchiec958ocs1[16]; %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_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_SWOUT L_SWIN +%token L_FSUNLOCK L_TYPE L_GSTATUS L_ENABLE L_DISABLE L_MUTE L_SWAP %type boolean %type integer @@ -144,55 +145,42 @@ mixers : mixer | mixers mixer ; -mixer : L_CHANNEL '(' string { select_mixer_channel( $3 ); } ',' mvalues ')' { set_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_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" ); } ; -mvalues : mvalue - | mvalues mvalue + +settings: setting + | settings ',' setting ; -mvalue : L_STEREO '(' xmchls ',' xmchrs ')' - | L_MONO '(' xmchs ')' - | error { yyerror( "unknown keyword in mixer channel{} level" ); } +setting : L_OUTPUT { select_mixer_direction(OUTPUT); } + dsetting + | L_INPUT { select_mixer_direction(INPUT); } + dsetting + | error { yyerror( "unknown keyword in mixer channel level" ); } ; -xmchls : xmchl - | xmchls xmchl +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" ); } ; -xmchl : L_INTEGER { set_mixer_channel( $1, -1 ); } - | '[' L_INTEGER ']' { set_mixer_channel_record( $2, -1 ); } - | L_MUTE { set_mixer_channel_flags( SND_MIXER_FLG_MUTE_LEFT, SND_MIXER_FLG_MUTE_LEFT ); } - | L_RECORD { set_mixer_channel_flags( SND_MIXER_FLG_RECORD_LEFT, SND_MIXER_FLG_RECORD_LEFT ); } - | L_SWOUT { set_mixer_channel_flags( SND_MIXER_FLG_LTOR_OUT, SND_MIXER_FLG_LTOR_OUT ); } - | L_SWIN { set_mixer_channel_flags( SND_MIXER_FLG_LTOR_IN, SND_MIXER_FLG_LTOR_IN ); } - | error { yyerror( "unknown keyword in left channel section..." ); } +vsettings: vsetting + | vsettings vsetting ; -xmchrs : xmchr - | xmchrs xmchr - ; - -xmchr : L_INTEGER { set_mixer_channel( -1, $1 ); } - | '[' L_INTEGER ']' { set_mixer_channel_record( -1, $2 ); } - | L_MUTE { set_mixer_channel_flags( SND_MIXER_FLG_MUTE_RIGHT, SND_MIXER_FLG_MUTE_RIGHT ); } - | L_RECORD { set_mixer_channel_flags( SND_MIXER_FLG_RECORD_RIGHT, SND_MIXER_FLG_RECORD_RIGHT ); } - | L_SWOUT { set_mixer_channel_flags( SND_MIXER_FLG_RTOL_OUT, SND_MIXER_FLG_RTOL_OUT ); } - | L_SWIN { set_mixer_channel_flags( SND_MIXER_FLG_RTOL_IN, SND_MIXER_FLG_RTOL_IN ); } - | error { yyerror( "unknown keyword in right channel section..." ); } - ; - -xmchs : xmch - | xmchs xmch - ; - -xmch : L_INTEGER { set_mixer_channel( $1, $1 ); } - | '[' L_INTEGER ']' { set_mixer_channel_record( $2, $2 ); } - | L_MUTE { set_mixer_channel_flags( SND_MIXER_FLG_MUTE, SND_MIXER_FLG_MUTE ); } - | L_RECORD { set_mixer_channel_flags( SND_MIXER_FLG_RECORD, SND_MIXER_FLG_RECORD ); } - | error { yyerror( "unknown keyword in mono channel section..." ); } +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" ); } ; pcms : pcm @@ -383,19 +371,14 @@ static void select_mixer_channel(char *name) struct mixer_channel *channel; if (!name) { - Xmixerchannel = NULL; + Xchannel = NULL; return; } for (channel = Xmixer->channels; channel; channel = channel->next) - if (!strcmp(channel->i.name, name)) { - Xmixerchannel = channel; - Xmixerchannelflags = Xmixerchannel->c.flags & - ~(SND_MIXER_FLG_RECORD | - SND_MIXER_FLG_MUTE | - SND_MIXER_FLG_SWITCH_OUT | - SND_MIXER_FLG_SWITCH_IN | - SND_MIXER_FLG_DECIBEL | - SND_MIXER_FLG_FORCE); + if (!strcmp(channel->info.name, name)) { + Xchannel = channel; + Xchannel->ddata[OUTPUT].flags = 0; + Xchannel->ddata[INPUT].flags = 0; free(name); return; } @@ -403,54 +386,51 @@ static void select_mixer_channel(char *name) free(name); } -static void set_mixer_channel(int left, int right) +static void select_mixer_direction(int direction) { - if (left >= 0) { - if (Xmixerchannel->i.min > left || Xmixerchannel->i.max < left) - yyerror("Value out of range (%i-%i)...", Xmixerchannel->i.min, Xmixerchannel->i.max); - if (Xmixerchannel->c.left != left) - Xmixerchannel->change = 1; - Xmixerchannel->c.left = left; + Xchannel->direction = direction; +} + +static void select_mixer_voice(int voice) +{ + Xchannel->voice = voice; +} + +static void set_mixer_volume(int volume) +{ + snd_mixer_channel_direction_info_t *i = &Xchannel->dinfo[Xchannel->direction]; + snd_mixer_channel_direction_t *d = &Xchannel->ddata[Xchannel->direction]; + if (Xchannel->voice & LEFT) { + if (i->min > volume || i->max < volume) + yyerror("Value out of range (%i-%i)...", i->min, i->max); + d->left = volume; } - if (right >= 0) { - if (Xmixerchannel->i.min > right || Xmixerchannel->i.max < right) - yyerror("Value out of range (%i-%i)...", Xmixerchannel->i.min, Xmixerchannel->i.max); - if (Xmixerchannel->c.right != right) - Xmixerchannel->change = 1; - Xmixerchannel->c.right = right; + 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; } } -static void set_mixer_channel_record(int left, int right) +static void set_mixer_flags(int flags) { - if (left >= 0) { - if (Xmixerchannel->i.min > left || Xmixerchannel->i.max < left) - yyerror("Value out of range (%i-%i)...", Xmixerchannel->i.min, Xmixerchannel->i.max); - if (Xmixerchannel->cr.left != left) - Xmixerchannel->change = 1; - Xmixerchannel->cr.left = left; + snd_mixer_channel_direction_t *d = &Xchannel->ddata[Xchannel->direction]; + if (Xchannel->voice & LEFT) { + if (flags & SND_MIXER_DFLG_MUTE) + d->flags |= SND_MIXER_DFLG_MUTE_LEFT; + if (flags & SND_MIXER_DFLG_SWAP) + d->flags |= SND_MIXER_DFLG_LTOR; } - if (right >= 0) { - if (Xmixerchannel->i.min > right || Xmixerchannel->i.max < right) - yyerror("Value out of range (%i-%i)...", Xmixerchannel->i.min, Xmixerchannel->i.max); - if (Xmixerchannel->cr.right != right) - Xmixerchannel->change = 1; - Xmixerchannel->cr.right = right; + 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; } } -static void set_mixer_channel_flags(unsigned int mask, unsigned int flags) +static void select_mixer_channel_end(void) { - Xmixerchannelflags &= ~mask; - Xmixerchannelflags |= flags; -} - -static void set_mixer_channel_end(void) -{ - if (Xmixerchannel->c.flags != Xmixerchannelflags) { - Xmixerchannel->change = 1; - } - Xmixerchannel->c.flags = Xmixerchannelflags; } #define FIND_SWITCH( xtype, first, name, err ) \ diff --git a/alsactl/setup.c b/alsactl/setup.c index 3742017..44f66b2 100644 --- a/alsactl/setup.c +++ b/alsactl/setup.c @@ -350,20 +350,38 @@ int soundcard_setup_collect(int cardno) } bzero(mixerchannel, sizeof(struct mixer_channel)); mixerchannel->no = idx; - if ((err = snd_mixer_channel_info(mhandle, idx, &mixerchannel->i)) < 0) { + 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 ((err = snd_mixer_channel_read(mhandle, idx, &mixerchannel->c)) < 0) { + 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->i.caps & SND_MIXER_CINFO_CAP_RECORDVOLUME) && - (err = snd_mixer_channel_record_read(mhandle, idx, &mixerchannel->cr)) < 0) { + 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 record read error (%s) - skipping", snd_strerror(err)); + 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) { @@ -651,55 +669,91 @@ static void soundcard_setup_write_switch(FILE * out, int interface, const unsign } -static void soundcard_setup_write_mixer_channel(FILE * out, snd_mixer_channel_info_t * info, snd_mixer_channel_t * channel, snd_mixer_channel_t * record_channel) +static void soundcard_setup_write_mixer_channel(FILE * out, struct mixer_channel * channel) { - fprintf(out, " ; Capabilities:%s%s%s%s%s%s%s%s%s%s%s%s%s.\n", - info->caps & SND_MIXER_CINFO_CAP_RECORD ? " record" : "", - info->caps & SND_MIXER_CINFO_CAP_JOINRECORD ? " join-record" : "", - info->caps & SND_MIXER_CINFO_CAP_STEREO ? " stereo" : "", - info->caps & SND_MIXER_CINFO_CAP_HWMUTE ? " hardware-mute" : "", - info->caps & SND_MIXER_CINFO_CAP_JOINMUTE ? " join-mute" : "", - info->caps & SND_MIXER_CINFO_CAP_DIGITAL ? " digital" : "", - info->caps & SND_MIXER_CINFO_CAP_INPUT ? " external-input" : "", - info->caps & SND_MIXER_CINFO_CAP_LTOR_OUT ? " ltor-out" : "", - info->caps & SND_MIXER_CINFO_CAP_RTOL_OUT ? " rtol-out" : "", - info->caps & SND_MIXER_CINFO_CAP_SWITCH_OUT ? " switch-out" : "", - info->caps & SND_MIXER_CINFO_CAP_LTOR_IN ? " ltor-in" : "", - info->caps & SND_MIXER_CINFO_CAP_RTOL_IN ? " rtol-in" : "", - info->caps & SND_MIXER_CINFO_CAP_RECORDVOLUME ? " record-volume" : ""); - fprintf(out, " ; Accepted channel range is from %i to %i.\n", info->min, info->max); - fprintf(out, " channel( \"%s\", ", info->name); - if (info->caps & SND_MIXER_CINFO_CAP_STEREO) { - char bufl[16] = ""; - char bufr[16] = ""; - if (info->caps & SND_MIXER_CINFO_CAP_RECORDVOLUME) { - sprintf(bufl, " [%i]", record_channel->left); - sprintf(bufr, " [%i]", record_channel->right); + int k, d; + struct capdes { + unsigned int flag; + char* description; + }; + static struct capdes caps[] = { + { SND_MIXER_CINFO_CAP_OUTPUT, "output" }, + { SND_MIXER_CINFO_CAP_INPUT, "input" }, + { SND_MIXER_CINFO_CAP_EXTINPUT, "external-input" }, + { SND_MIXER_CINFO_CAP_EFFECT, "effect" } + }; + static struct capdes dcaps[] = { + { SND_MIXER_CINFO_DCAP_STEREO, "stereo" }, + { SND_MIXER_CINFO_DCAP_HWMUTE, "hardware-mute" }, + { SND_MIXER_CINFO_DCAP_JOINMUTE, "join-mute" }, + { SND_MIXER_CINFO_DCAP_ROUTE, "route" }, + { SND_MIXER_CINFO_DCAP_SWAPROUTE, "swap-route" }, + { SND_MIXER_CINFO_DCAP_DIGITAL, "digital" }, + { SND_MIXER_CINFO_DCAP_RECORDBYMUTE, "recordbymute" }, + }; + + fprintf(out, " ; Capabilities:"); + for (k = 0; k < sizeof(caps)/sizeof(*caps); ++k) { + if (channel->info.caps & caps[k].flag) + fprintf(out, " %s", caps[k].description); + } + fprintf(out, "\n"); + for (d = OUTPUT; d <= INPUT; ++d) { + snd_mixer_channel_direction_info_t *di; + if (d == OUTPUT && + !(channel->info.caps & SND_MIXER_CINFO_CAP_OUTPUT)) + continue; + if (d == INPUT && + !(channel->info.caps & SND_MIXER_CINFO_CAP_INPUT)) + continue; + di = &channel->dinfo[d]; + fprintf(out, " ; %s capabilities:", + d == OUTPUT ? "Output" : "Input" ); + if (di->caps & SND_MIXER_CINFO_DCAP_VOLUME) + fprintf(out, " volume(%i, %i)", di->min, di->max); + for (k = 0; k < sizeof(caps)/sizeof(*caps); ++k) { + if (di->caps & dcaps[k].flag) + fprintf(out, " %s", dcaps[k].description); + } + 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, "stereo( %i%s%s%s%s%s, %i%s%s%s%s%s )", - channel->left, - channel->flags & SND_MIXER_FLG_MUTE_LEFT ? " mute" : "", - channel->flags & SND_MIXER_FLG_RECORD_LEFT ? " record" : "", - bufl, - channel->flags & SND_MIXER_FLG_LTOR_OUT ? " swout" : "", - channel->flags & SND_MIXER_FLG_LTOR_IN ? " swin" : "", - channel->right, - channel->flags & SND_MIXER_FLG_MUTE_RIGHT ? " mute" : "", - channel->flags & SND_MIXER_FLG_RECORD_RIGHT ? " record" : "", - bufr, - channel->flags & SND_MIXER_FLG_RTOL_OUT ? " swout" : "", - channel->flags & SND_MIXER_FLG_RTOL_IN ? " swin" : "" - ); - } else { - char buf[16] = ""; - if (info->caps & SND_MIXER_CINFO_CAP_RECORDVOLUME) - sprintf(buf, " [%i]", (record_channel->left+record_channel->right) /2); - fprintf(out, "mono( %i%s%s%s )", - (channel->left + channel->right) / 2, - channel->flags & SND_MIXER_FLG_MUTE ? " mute" : "", - channel->flags & SND_MIXER_FLG_RECORD ? " record" : "", - buf - ); } fprintf(out, " )\n"); } @@ -735,7 +789,7 @@ int soundcard_setup_write(const char *cfgfile) for (mixer = first->mixers; mixer; mixer = mixer->next) { fprintf(out, " mixer( \"%s\" ) {\n", mixer->info.name); for (mixerchannel = mixer->channels; mixerchannel; mixerchannel = mixerchannel->next) - soundcard_setup_write_mixer_channel(out, &mixerchannel->i, &mixerchannel->c, &mixerchannel->cr); + soundcard_setup_write_mixer_channel(out, mixerchannel); for (mixersw = mixer->switches; mixersw; mixersw = mixersw->next) soundcard_setup_write_switch(out, SND_INTERFACE_MIXER, mixersw->s.name, mixersw->s.type, mixersw->s.low, mixersw->s.high, (void *) (&mixersw->s.value)); fprintf(out, " }\n"); @@ -839,14 +893,14 @@ int soundcard_setup_process(int cardno) } for (mixer = soundcard->mixers; mixer; mixer = mixer->next) { for (channel = mixer->channels; channel; channel = channel->next) - if (channel->change) - if (!soundcard_open_mix(&mixhandle, soundcard, mixer)) { - if ((err = snd_mixer_channel_write(mixhandle, channel->no, &channel->c)) < 0) - error("Mixer channel '%s' write error: %s", channel->i.name, snd_strerror(err)); - if ((channel->i.caps & SND_MIXER_CINFO_CAP_RECORDVOLUME) && - (err = snd_mixer_channel_record_write(mixhandle, channel->no, &channel->cr)) < 0) - error("Mixer channel '%s' record write error: %s", channel->i.name, snd_strerror(err)); - } + 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; @@ -869,7 +923,7 @@ int soundcard_setup_process(int cardno) for (pcmsw = pcm->rswitches; pcmsw; pcmsw = pcmsw->next) { if (pcmsw->change) if (!soundcard_open_ctl(&ctlhandle, soundcard)) { - if ((err = snd_ctl_pcm_playback_switch_write(ctlhandle, pcm->no, pcmsw->no, &pcmsw->s)) < 0) + if ((err = snd_ctl_pcm_record_switch_write(ctlhandle, pcm->no, pcmsw->no, &pcmsw->s)) < 0) error("PCM record switch '%s' write error: %s", pcmsw->s.name, snd_strerror(err)); } } diff --git a/alsamixer/alsamixer.c b/alsamixer/alsamixer.c index b7d516d..31cae79 100644 --- a/alsamixer/alsamixer.c +++ b/alsamixer/alsamixer.c @@ -1,5 +1,7 @@ /* AlsaMixer - Commandline mixer for the ALSA project - * Copyright (C) 1998, 1999 Tim Janik and Jaroslav Kysela + * Copyright (C) 1998 Jaroslav Kysela , + * Tim Janik , + * Carl van Schaik * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -14,71 +16,6 @@ * 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. - * - * - * ChangeLog: - * - * Sun Feb 21 19:55:01 1999 Tim Janik - * - * * bumped version to 0.10. - * - * * added scrollable text views. - * we now feature an F1 Help screen and an F2 /proc info screen. - * the help screen does still require lots of work though. - * - * * keys are evaluated view specific now. - * - * * we feature meta-keys now, e.g. M-Tab as back-tab. - * - * * if we are already in channel view and the user still hits Return, - * we do a refresh nonetheless, since 'r'/'R' got removed as a redraw - * key (reserved for record volumes). 'l'/'L' is still preserved though, - * and actually needs to be to e.g. get around the xterm bold-artefacts. - * - * * support terminals that can't write into lower right corner. - * - * * undocumented '-s' option that will keep the screen to its - * minimum size, usefull for debugging only. - * - * Sun Feb 21 02:23:52 1999 Tim Janik - * - * * don't abort if snd_mixer_* functions failed due to EINTR, - * we simply retry on the next cycle. hopefully asoundlib preserves - * errno states correctly (Jaroslav can you asure that?). - * - * * feature WINCH correctly, so we make a complete relayout on - * screen resizes. don't abort on too-small screen sizes anymore, - * but simply beep. - * - * * redid the layout algorithm to fix some bugs and to preserve - * space for a flag indication line. the channels are - * nicer spread horizontally now (i.e. we also pad on the left and - * right screen bounds now). - * - * * various other minor fixes. - * - * * indicate whether ExactMode is active or not. - * - * * fixed coding style to follow the GNU coding conventions. - * - * * reverted record volume changes since they broke ExactMode display. - * - * * composed ChangeLog entries. - * - * 1998/11/04 19:43:45 perex - * - * * Stereo record source and route selection... - * provided by Carl van Schaik . - * - * 1998/09/20 08:05:24 perex - * - * * Fixed -m option... - * - * 1998/10/29 22:50:10 - * - * * initial checkin of alsamixer.c, written by Tim Janik, modified by - * Jaroslav Kysela to feature asoundlib.h instead of plain ioctl()s and - * automated updates after select() (i always missed that with OSS!). */ #include @@ -103,15 +40,13 @@ #include /* example compilation commandline: - * clear; gcc -Wall -pipe -O2 alsamixer.c -o alsamixer -lasound -lncurses + * clear; gcc -Wall -pipe -O2 alsamixer.c -o alsamixer -lncurses */ /* --- defines --- */ -#define PRGNAME "alsamixer" -#define PRGNAME_UPPER "AlsaMixer" -#define VERSION "v0.10" -#define CHECK_ABORT(e,s) ({ if (errno != EINTR) mixer_abort ((e), (s)); }) -#define GETCH_BLOCK(w) ({ timeout ((w) ? -1 : 0); }) +#define PRGNAME "alsamixer" +#define PRGNAME_UPPER "AlsaMixer" +#define VERSION "v0.9" #undef MAX #define MAX(a, b) (((a) > (b)) ? (a) : (b)) @@ -122,9 +57,8 @@ #undef CLAMP #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) -#define MIXER_MIN_X (18) /* abs minimum: 18 */ -#define MIXER_TEXT_Y (10) -#define MIXER_MIN_Y (MIXER_TEXT_Y + 3) /* abs minimum: 11 */ +#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) @@ -142,154 +76,115 @@ #define MIXER_WHITE (COLOR_WHITE | A_BOLD) -/* --- views --- */ -enum { - VIEW_CHANNELS, - VIEW_HELP, - VIEW_PROCINFO -}; - - /* --- variables --- */ -static WINDOW *mixer_window = NULL; -static int mixer_needs_resize = 0; -static int mixer_minimize = 0; -static int mixer_no_lrcorner = 0; -static int mixer_view = VIEW_CHANNELS; -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_cbar_height = 0; +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 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_have_old_focus = 0; -static int mixer_exact = 0; +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_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; -static int mixer_toggle_record = 0; +static int mixer_input_volumes = 0; -static int mixer_hscroll_delta = 0; -static int mixer_vscroll_delta = 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_rec_left = 0; -static int mixer_toggle_rec_right = 0; -static int mixer_route_ltor_in = 0; -static int mixer_route_rtol_in = 0; - - -/* --- text --- */ -static int mixer_procinfo_xoffs = 0; -static int mixer_procinfo_yoffs = 0; -static int mixer_help_xoffs = 0; -static int mixer_help_yoffs = 0; -static char *mixer_help_text = -( - "\n" - " Esc exit alsamixer\n" - " F1 show Help screen\n" - " F2 show /proc info screen\n" - " Return return to main screen\n" - " Space toggle Record facility\n" - " Tab toggle ExactMode\n" - " m M mute both channels\n" - " < > mute left/right channel\n" - " Up increase left and right volume\n" - " Down decrease left and right volume\n" - " Right move (scroll) to the right next channel\n" - " Left move (scroll) to the left next channel\n" - "\n" - "Alsamixer has been written and is Copyrighted in 1998, 1999 by\n" - "Tim Janik and Jaroslav Kysela .\n" - ); +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_LABEL, - DC_CBAR_FOCUS_LABEL, - DC_FOCUS, - DC_ANY_1, - DC_ANY_2, - DC_ANY_3, - DC_ANY_4, - DC_LAST + 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 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) +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); + 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) +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]; + 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) +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_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); - mixer_init_dc ('#', DC_ANY_1, MIXER_WHITE, MIXER_BLACK, A_BOLD); - mixer_init_dc ('#', DC_ANY_2, MIXER_GREEN, MIXER_BLACK, A_BOLD); - mixer_init_dc ('#', DC_ANY_3, MIXER_RED, MIXER_BLACK, A_BOLD); - mixer_init_dc ('.', DC_ANY_4, MIXER_WHITE, MIXER_GREEN, A_BOLD); - mixer_init_dc ('.', DC_ANY_4, MIXER_WHITE, MIXER_BLUE, A_BOLD); + 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) @@ -297,1344 +192,773 @@ mixer_init_draw_contexts (void) /* --- error types --- */ -typedef enum -{ - ERR_NONE, - ERR_OPEN, - ERR_FCN, - ERR_SIGNAL, - ERR_WINSIZE, +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)); +static void mixer_abort(ErrType error, + const char *err_string) + __attribute__ + ((noreturn)); /* --- functions --- */ -static void -mixer_clear (int full_redraw) +static void mixer_clear(void) { - int x, y; - int f = full_redraw ? 0 : 1; + int x, y; - mixer_dc (DC_BACK); + mixer_dc(DC_BACK); + clear(); - if (full_redraw) - clearok (mixer_window, TRUE); - - /* buggy ncurses doesn't really write spaces with the specified - * color into the screen on clear () or erase () - */ - for (x = f; x < mixer_max_x - f; x++) - for (y = f; y < mixer_max_y - f; y++) - mvaddch (y, x, ' '); + /* 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) +static void mixer_abort(ErrType error, + const char *err_string) { - if (mixer_window) - { - mixer_clear (TRUE); - refresh (); - keypad (mixer_window, FALSE); - leaveok (mixer_window, FALSE); - 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; - x += (3 + 2 + 3 + 1) * channel_index + mixer_extra_space * (channel_index + 1); - - if (MIXER_TEXT_Y + 10 < mixer_max_y) - y = mixer_max_y / 2 + 3; - else - y = (mixer_max_y + 1) / 2 + 3; - y += mixer_cbar_height / 2; - - 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_t cdata = { 0, }; - int vleft, vright; - int x, y, i; - - - /* set specified EXACT mode - */ - if (snd_mixer_exact_mode (mixer_handle, mixer_exact) < 0) - CHECK_ABORT (ERR_FCN, "snd_mixer_exact_mode()"); - - /* set new channel indices and read info - */ - if (snd_mixer_channel_info (mixer_handle, channel_index, &cinfo) < 0) - CHECK_ABORT (ERR_FCN, "snd_mixer_channel_info()"); - - /* 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 || mixer_toggle_rec_left || - mixer_toggle_rec_right || - mixer_route_rtol_in || mixer_route_ltor_in)) - { - if (snd_mixer_channel_read (mixer_handle, channel_index, &cdata) < 0) - CHECK_ABORT (ERR_FCN, "snd_mixer_channel_read()"); - - cdata.flags &= ~SND_MIXER_FLG_DECIBEL; - cdata.left = CLAMP (cdata.left + mixer_lvolume_delta, cinfo.min, cinfo.max); - cdata.right = CLAMP (cdata.right + mixer_rvolume_delta, cinfo.min, cinfo.max); - mixer_lvolume_delta = mixer_rvolume_delta = 0; - if (mixer_balance_volumes) - { - cdata.left = (cdata.left + cdata.right) / 2; - cdata.right = cdata.left; - mixer_balance_volumes = 0; + if (mixer_window) { + mixer_clear(); + endwin(); + mixer_window = NULL; } - if (mixer_toggle_mute_left) - { - if (cdata.flags & SND_MIXER_FLG_MUTE_LEFT) - cdata.flags &= ~SND_MIXER_FLG_MUTE_LEFT; - else - cdata.flags |= SND_MIXER_FLG_MUTE_LEFT; - } - if (mixer_toggle_mute_right) - { - if (cdata.flags & SND_MIXER_FLG_MUTE_RIGHT) - cdata.flags &= ~SND_MIXER_FLG_MUTE_RIGHT; - else - cdata.flags |= SND_MIXER_FLG_MUTE_RIGHT; - } - mixer_toggle_mute_left = mixer_toggle_mute_right = 0; - if (mixer_toggle_record) - { - if (cdata.flags & SND_MIXER_FLG_RECORD) - cdata.flags &= ~SND_MIXER_FLG_RECORD; - else - cdata.flags |= SND_MIXER_FLG_RECORD; - } - mixer_toggle_record = 0; - - if (mixer_toggle_rec_left) - { - if (cdata.flags & SND_MIXER_FLG_RECORD_LEFT) - cdata.flags &= ~SND_MIXER_FLG_RECORD_LEFT; - else - cdata.flags |= SND_MIXER_FLG_RECORD_LEFT; - } - mixer_toggle_rec_left = 0; - - if (mixer_toggle_rec_right) - { - if (cdata.flags & SND_MIXER_FLG_RECORD_RIGHT) - cdata.flags &= ~SND_MIXER_FLG_RECORD_RIGHT; - else - cdata.flags |= SND_MIXER_FLG_RECORD_RIGHT; - } - mixer_toggle_rec_right = 0; - - if (mixer_route_ltor_in) - { - if (cdata.flags & SND_MIXER_FLG_LTOR_IN) - cdata.flags &= ~SND_MIXER_FLG_LTOR_IN; - else - cdata.flags |= SND_MIXER_FLG_LTOR_IN; - /* printf ("state : \n %d \n",cdata.flags & SND_MIXER_FLG_LTOR_IN); - */ - } - mixer_route_ltor_in = 0; - - if (mixer_route_rtol_in) - { - if (cdata.flags & SND_MIXER_FLG_RTOL_IN) - cdata.flags &= ~SND_MIXER_FLG_RTOL_IN; - else - cdata.flags |= SND_MIXER_FLG_RTOL_IN; - } - mixer_route_rtol_in = 0; - - if (snd_mixer_channel_write (mixer_handle, channel_index, &cdata) < 0) - CHECK_ABORT (ERR_FCN, "snd_mixer_channel_write()"); - } - /* first, read values for the numbers to be displayed in - * specified EXACT mode - */ - if (snd_mixer_channel_read (mixer_handle, channel_index, &cdata) < 0) - CHECK_ABORT (ERR_FCN, "snd_mixer_channel_read()"); - vleft = cdata.left; - vright = cdata.right; - - /* 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) - CHECK_ABORT (ERR_FCN, "snd_mixer_exact_mode()"); - if (snd_mixer_channel_read (mixer_handle, channel_index, &cdata) < 0) - CHECK_ABORT (ERR_FCN, "snd_mixer_channel_read()"); - } - /* get channel bar position - */ - if (!mixer_cbar_get_pos (channel_index, &x, &y)) - return; + printf("\n"); - /* setup colors - */ - mixer_init_dc ('#', DC_ANY_1, MIXER_WHITE, MIXER_BLACK, A_BOLD); - mixer_init_dc ('#', DC_ANY_2, MIXER_GREEN, MIXER_BLACK, A_BOLD); - mixer_init_dc ('#', DC_ANY_3, MIXER_RED, MIXER_BLACK, A_BOLD); - - /* 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_ANY_3; - else if (i + 1 >= 0.4 * mixer_cbar_height) - dc = DC_ANY_2; - else - dc = DC_ANY_1; - mvaddch (y, x + 3, mixer_dc (cdata.left > i * 100 / mixer_cbar_height ? dc : DC_CBAR_EMPTY)); - mvaddch (y, x + 4, mixer_dc (cdata.right > i * 100 / mixer_cbar_height ? dc : DC_CBAR_EMPTY)); - y--; - } - - /* muted? - */ - mixer_dc (DC_BACK); - mvaddstr (y, x, " "); - c = cinfo.caps & SND_MIXER_CINFO_CAP_MUTE ? '-' : ' '; - mixer_dc (DC_CBAR_FRAME); - mvaddch (y, x + 2, ACS_ULCORNER); - mvaddch (y, x + 3, mixer_dc (cdata.flags & SND_MIXER_FLG_MUTE_LEFT ? - DC_CBAR_MUTE : DC_CBAR_NOMUTE)); - mvaddch (y, x + 4, mixer_dc (cdata.flags & SND_MIXER_FLG_MUTE_RIGHT ? - DC_CBAR_MUTE : DC_CBAR_NOMUTE)); - mixer_dc (DC_CBAR_FRAME); - mvaddch (y, x + 5, ACS_URCORNER); - y--; - - /* record input? - */ - if (cdata.flags & SND_MIXER_FLG_RECORD) - { - mixer_dc (DC_CBAR_RECORD); - mvaddstr (y, x + 1, "RECORD"); - if (cdata.flags & SND_MIXER_FLG_RECORD_LEFT) - { - if (cdata.flags & SND_MIXER_FLG_LTOR_IN) - mvaddstr (y + 2, x + 6, "L"); - else - mvaddstr (y + 1, x + 1, "L"); - } - if (cdata.flags & SND_MIXER_FLG_RECORD_RIGHT) - { - if (cdata.flags & SND_MIXER_FLG_RTOL_IN) - mvaddstr (y + 2, x + 1, "R"); - else - mvaddstr (y + 1, x + 6, "R"); - } - } - else if (cinfo.caps & SND_MIXER_CINFO_CAP_RECORD) - for (i = 0; i < 6; i++) - mvaddch (y, x + 1 + i, mixer_dc (DC_CBAR_NORECORD)); - else - { - mixer_dc (DC_BACK); - mvaddstr (y, x, " "); - } - 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 - */ - if (mixer_have_old_focus) - { - 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, ">"); - mixer_have_old_focus = 1; -} - -static void -mixer_draw_frame (void) -{ - char string[128]; - int i; - int max_len; - - mixer_dc (DC_FRAME); - - /* 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; - addstr (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; - addstr (string); - - /* indicate exact mode - */ - if (mixer_exact) - { - mixer_dc (DC_PROMPT); - mvaddstr (3, 2, "["); - mixer_dc (DC_TEXT); - addstr ("ExactMode"); - mixer_dc (DC_PROMPT); - addstr ("]"); - } - else - mvaddstr (3, 2, " "); - - /* lines - */ - mixer_dc (DC_PROMPT); - 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); - } - - /* corners - */ - mixer_dc (DC_PROMPT); - mvaddch (0, 0, ACS_ULCORNER); - mvaddch (0, mixer_max_x - 1, ACS_URCORNER); - mvaddch (mixer_max_y - 1, 0, ACS_LLCORNER); - if (!mixer_no_lrcorner) - mvaddch (mixer_max_y - 1, mixer_max_x - 1, ACS_LRCORNER); - else - { - mvaddch (mixer_max_y - 2, mixer_max_x - 1, ACS_LRCORNER); - mvaddch (mixer_max_y - 2, mixer_max_x - 2, ACS_ULCORNER); - mvaddch (mixer_max_y - 1, mixer_max_x - 2, ACS_LRCORNER); - } - - /* program title - */ - sprintf (string, "%s %s", PRGNAME_UPPER, VERSION); - max_len = strlen (string); - if (mixer_max_x >= max_len + 4) - { - mixer_dc (DC_PROMPT); - mvaddch (0, mixer_max_x / 2 - max_len / 2 - 1, '['); - mvaddch (0, mixer_max_x / 2 - max_len / 2 + max_len, ']'); - } - if (mixer_max_x >= max_len + 2) - { - mixer_dc (DC_TEXT); - mvaddstr (0, mixer_max_x / 2 - max_len / 2, string); - } -} - -static char* -mixer_offset_text (char **t, - int col, - int *length) -{ - char *p = *t; - char *r; - - while (*p && *p != '\n' && col--) - p++; - if (*p == '\n' || !*p) - { - if (*p == '\n') - p++; - *length = 0; - *t = p; - return p; - } - - r = p; - while (*r && *r != '\n' && (*length)--) - r++; - - *length = r - p; - while (*r && *r != '\n') - r++; - if (*r == '\n') - r++; - *t = r; - - return p; -} - -static void -mixer_show_text (char *title, - char *text, - int *xoffs, - int *yoffs) -{ - int tlines = 0, tcols = 0; - float hscroll, vscroll; - float hoffs, voffs; - char *p, *text_offs = text; - int x1, x2, y1, y2; - int i, n, l, r, block, stipple; - - /* coords - */ - x1 = 2; - x2 = mixer_max_x - 3; - y1 = 4; - y2 = mixer_max_y - 2; - - if ((y2 - y1) < 3 || (x2 - x1) < 3) - return; - - /* text dimensions - */ - l = 0; - for (p = text; *p; p++) - if (*p == '\n') - { - tlines++; - tcols = MAX (l, tcols); - l = 0; - } - else - l++; - tcols = MAX (l, tcols); - if (p > text && *(p - 1) != '\n') - tlines++; - - /* scroll areas / offsets - */ - l = x2 - x1 - 2; - if (l > tcols) - { - x1 += (l - tcols) / 2; - x2 = x1 + tcols + 1; - } - if (mixer_hscroll_delta) - { - *xoffs += mixer_hscroll_delta; - mixer_hscroll_delta = 0; - if (*xoffs < 0) - { - *xoffs = 0; - beep (); - } - else if (*xoffs > tcols - l - 1) - { - *xoffs = MAX (0, tcols - l - 1); - beep (); - } - } - if (tcols - l - 1 <= 0) - { - hscroll = 1; - hoffs = 0; - } - else - { - hscroll = ((float) l) / tcols; - hoffs = ((float) *xoffs) / (tcols - l - 1); - } - - l = y2 - y1 - 2; - if (l > tlines) - { - y1 += (l - tlines) / 2; - y2 = y1 + tlines + 1; - } - if (mixer_vscroll_delta) - { - *yoffs += mixer_vscroll_delta; - mixer_vscroll_delta = 0; - if (*yoffs < 0) - { - *yoffs = 0; - beep (); - } - else if (*yoffs > tlines - l - 1) - { - *yoffs = MAX (0, tlines - l - 1); - beep (); - } - } - if (tlines - l - 1 <= 0) - { - voffs = 0; - vscroll = 1; - } - else - { - vscroll = ((float) l) / tlines; - voffs = ((float) *yoffs) / (tlines - l - 1); - } - - /* colors - */ - mixer_init_dc ('.', DC_ANY_3, MIXER_YELLOW, MIXER_BLUE, A_BOLD); - mixer_init_dc ('.', DC_ANY_4, MIXER_WHITE, MIXER_BLUE, A_BOLD); - mixer_dc (DC_ANY_4); - - /* corners - */ - mvaddch (y2, x2, ACS_LRCORNER); - mvaddch (y2, x1, ACS_LLCORNER); - mvaddch (y1, x1, ACS_ULCORNER); - mvaddch (y1, x2, ACS_URCORNER); - - /* left + upper border - */ - for (i = y1 + 1; i < y2; i++) - mvaddch (i, x1, ACS_VLINE); - for (i = x1 + 1; i < x2; i++) - mvaddch (y1, i, ACS_HLINE); - if (title) - { - l = strlen (title); - if (l <= x2 - x1 - 3) - { - mvaddch (y1, x1 + 1 + (x2 - x1 - l) / 2 - 1, '['); - mvaddch (y1, x1 + 1 + (x2 - x1 - l) / 2 + l, ']'); - } - if (l <= x2 - x1 - 1) - { - mixer_dc (DC_ANY_3); - mvaddstr (y1, x1 + 1 + (x2 - x1 - l) / 2, title); - } - mixer_dc (DC_ANY_4); - } - - stipple = ACS_CKBOARD; - block = ACS_BLOCK; - if (block == '#' && ACS_BOARD == '#') - { - block = stipple; - stipple = ACS_BLOCK; - } - - /* lower scroll border - */ - l = x2 - x1 - 1; - n = hscroll * l; - r = (hoffs + 1.0 / (2 * (l - n - 1))) * (l - n - 1); - for (i = 0; i < l; i++) - mvaddch (y2, i + x1 + 1, hscroll >= 1 ? ACS_HLINE : - i >= r && i <= r + n ? block : stipple); - - /* right scroll border - */ - l = y2 - y1 - 1; - n = vscroll * l; - r = (voffs + 1.0 / (2 * (l - n - 1))) * (l - n - 1); - for (i = 0; i < l; i++) - mvaddch (i + y1 + 1, x2, vscroll >= 1 ? ACS_VLINE : - i >= r && i <= r + n ? block : stipple); - - /* show text - */ - x1++; y1++; - for (i = 0; i < *yoffs; i++) - { - l = 0; - mixer_offset_text (&text_offs, 0, &l); - } - for (i = y1; i < y2; i++) - { - l = x2 - x1; - p = mixer_offset_text (&text_offs, *xoffs, &l); - n = x1; - while (l--) - mvaddch (i, n++, *p++); - while (n < x2) - mvaddch (i, n++, ' '); - } -} - -struct vbuffer -{ - char *buffer; - int size; - int len; -}; - -static void -vbuffer_kill (struct vbuffer *vbuf) -{ - if (vbuf->size) - free (vbuf->buffer); - vbuf->buffer = NULL; - vbuf->size = 0; - vbuf->len = 0; -} - -#define vbuffer_append_string(vb,str) vbuffer_append (vb, str, strlen (str)) -static void -vbuffer_append (struct vbuffer *vbuf, - char *text, - int len) -{ - if (vbuf->size - vbuf->len <= len) - { - vbuf->size += len + 1; - vbuf->buffer = realloc (vbuf->buffer, vbuf->size); - } - memcpy (vbuf->buffer + vbuf->len, text, len); - vbuf->len += len; - vbuf->buffer[vbuf->len] = 0; -} - -static int -vbuffer_append_file (struct vbuffer *vbuf, - char *name) -{ - int fd; - - fd = open (name, O_RDONLY); - if (fd >= 0) - { - char buffer[1025]; - int l; - - do - { - l = read (fd, buffer, 1024); - - vbuffer_append (vbuf, buffer, MAX (0, l)); - } - while (l > 0 || (l < 0 && (errno == EAGAIN || errno == EINTR))); - - close (fd); - - return 0; - } - else - return 1; -} - -static void -mixer_show_procinfo (void) -{ - struct vbuffer vbuf = { NULL, 0, 0 }; - - vbuffer_append_string (&vbuf, "\n"); - vbuffer_append_string (&vbuf, "/proc/asound/version:\n"); - vbuffer_append_string (&vbuf, "====================\n"); - if (vbuffer_append_file (&vbuf, "/proc/asound/version")) - { - vbuffer_kill (&vbuf); - mixer_procinfo_xoffs = mixer_procinfo_yoffs = 0; - mixer_show_text ("/proc", - " No /proc information available. ", - &mixer_procinfo_xoffs, &mixer_procinfo_yoffs); - return; - } - else - vbuffer_append_file (&vbuf, "/proc/asound/meminfo"); - - vbuffer_append_string (&vbuf, "\n"); - vbuffer_append_string (&vbuf, "/proc/asound/cards:\n"); - vbuffer_append_string (&vbuf, "===================\n"); - if (vbuffer_append_file (&vbuf, "/proc/asound/cards")) - vbuffer_append_string (&vbuf, "No information available.\n"); - - vbuffer_append_string (&vbuf, "\n"); - vbuffer_append_string (&vbuf, "/proc/asound/devices:\n"); - vbuffer_append_string (&vbuf, "=====================\n"); - if (vbuffer_append_file (&vbuf, "/proc/asound/devices")) - vbuffer_append_string (&vbuf, "No information available.\n"); - - vbuffer_append_string (&vbuf, "\n"); - vbuffer_append_string (&vbuf, "/proc/asound/oss-devices:\n"); - vbuffer_append_string (&vbuf, "=========================\n"); - if (vbuffer_append_file (&vbuf, "/proc/asound/oss-devices")) - vbuffer_append_string (&vbuf, "No information available.\n"); - - vbuffer_append_string (&vbuf, "\n"); - vbuffer_append_string (&vbuf, "/proc/asound/timers:\n"); - vbuffer_append_string (&vbuf, "====================\n"); - if (vbuffer_append_file (&vbuf, "/proc/asound/timers")) - vbuffer_append_string (&vbuf, "No information available.\n"); - - vbuffer_append_string (&vbuf, "\n"); - vbuffer_append_string (&vbuf, "/proc/asound/pcm:\n"); - vbuffer_append_string (&vbuf, "=================\n"); - if (vbuffer_append_file (&vbuf, "/proc/asound/pcm")) - vbuffer_append_string (&vbuf, "No information available.\n"); - - mixer_show_text ("/proc", vbuf.buffer, - &mixer_procinfo_xoffs, &mixer_procinfo_yoffs); - vbuffer_kill (&vbuf); -} - -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_init_window (void) -{ - /* initialize ncurses - */ - mixer_window = initscr (); - - mixer_no_lrcorner = tigetflag ("xenl") != 1 && tigetflag ("am") != 1; - - if (mixer_do_color) - mixer_do_color = has_colors (); - mixer_init_draw_contexts (); - - /* react on key presses - */ - cbreak (); - noecho (); - leaveok (mixer_window, TRUE); - keypad (mixer_window, TRUE); - GETCH_BLOCK (1); - - /* init mixer screen - */ - getmaxyx (mixer_window, mixer_max_y, mixer_max_x); - if (mixer_minimize) - { - mixer_max_x = MIXER_MIN_X; - mixer_max_y = MIXER_MIN_Y; - } - mixer_ofs_x = 2 /* extra begin padding: */ + 1; - - /* required allocations */ - mixer_n_vis_channels = (mixer_max_x - mixer_ofs_x * 2 + 1) / 9; - mixer_n_vis_channels = CLAMP (mixer_n_vis_channels, 1, mixer_n_channels); - mixer_extra_space = mixer_max_x - mixer_ofs_x * 2 + 1 - mixer_n_vis_channels * 9; - mixer_extra_space = MAX (0, mixer_extra_space / (mixer_n_vis_channels + 1)); - if (MIXER_TEXT_Y + 10 < mixer_max_y) - mixer_cbar_height = 10 + MAX (0, mixer_max_y - MIXER_TEXT_Y - 10 ) / 2; - else - mixer_cbar_height = MAX (1, mixer_max_y - MIXER_TEXT_Y); - - mixer_clear (TRUE); -} - -static void -mixer_resize (void) -{ - struct winsize winsz = { 0, }; - - mixer_needs_resize = 0; - - if (ioctl (fileno (stdout), TIOCGWINSZ, &winsz) >= 0 && - winsz.ws_row && winsz.ws_col) - { - keypad (mixer_window, FALSE); - leaveok (mixer_window, FALSE); - - endwin (); - - mixer_max_x = MAX (2, winsz.ws_col); - mixer_max_y = MAX (2, winsz.ws_row); - - /* humpf, i don't get it, if only the number of rows change, - * ncurses will segfault shortly after (could trigger that with mc as well). - */ - resizeterm (mixer_max_y + 1, mixer_max_x + 1); - resizeterm (mixer_max_y, mixer_max_x); - - mixer_init_window (); - - if (mixer_max_x < MIXER_MIN_X || - mixer_max_y < MIXER_MIN_Y) - beep (); // mixer_abort (ERR_WINSIZE, ""); - - mixer_have_old_focus = 0; - } -} - -static void -mixer_channel_changed (void *private_data, - int channel) -{ - /* we don't actually need to update the individual channels because - * we redraw the whole screen upon every main iteration anyways. - */ -#if 0 - fprintf (stderr, "*** channel = %i\n", channel); - mixer_update_cbar (channel); -#endif -} - -static int -mixer_iteration (void) -{ - struct timeval delay = { 0, }; - snd_mixer_callbacks_t callbacks = { 0, }; - int mixer_fd; - fd_set rfds; - int finished = 0; - int key = 0; - int old_view; - - callbacks.channel_was_changed = mixer_channel_changed; - - /* setup for select on stdin and the mixer fd */ - mixer_fd = snd_mixer_file_descriptor (mixer_handle); - FD_ZERO (&rfds); - FD_SET (fileno (stdin), &rfds); - FD_SET (mixer_fd, &rfds); - - delay.tv_sec = 0; - delay.tv_usec = 0 * 100 * 1000; - - finished = select (mixer_fd + 1, &rfds, NULL, NULL, mixer_needs_resize ? &delay : NULL) < 0; - - /* don't abort on handled signals */ - if (finished && errno == EINTR) - { - FD_ZERO (&rfds); - finished = 0; - } - else if (mixer_needs_resize) - mixer_resize (); - - if (FD_ISSET (mixer_fd, &rfds)) - snd_mixer_read (mixer_handle, &callbacks); - - if (FD_ISSET (fileno (stdin), &rfds)) - key = getch (); - - old_view = mixer_view; - - /* feature Escape prefixing for some keys */ - if (key == 27) - { - GETCH_BLOCK (0); - key = getch (); - GETCH_BLOCK (1); - switch (key) - { - case 9: /* Tab */ - key = KEY_BTAB; - break; + 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: - key = 27; - break; + break; } - } - - /* general keys */ - switch (key) - { - case 0: - /* ignore */ - break; - case 27: /* Escape */ - finished = 1; - key = 0; - break; - case 13: /* Return */ - case 10: /* NewLine */ - if (mixer_view == VIEW_CHANNELS) - mixer_clear (FALSE); - mixer_view = VIEW_CHANNELS; - key = 0; - break; - case 'h': - case 'H': - case KEY_F (1): - mixer_view = VIEW_HELP; - key = 0; - break; - case '/': - case KEY_F (2): - mixer_view = VIEW_PROCINFO; - key = 0; - break; - case 'L': - case 'l': - mixer_clear (TRUE); - break; - } - - if (key && (mixer_view == VIEW_HELP || - mixer_view == VIEW_PROCINFO)) - switch (key) - { - case 9: /* Tab */ - mixer_hscroll_delta += 8; - break; - case KEY_BTAB: - mixer_hscroll_delta -= 8; - break; - case KEY_A1: - mixer_hscroll_delta -= 1; - mixer_vscroll_delta -= 1; - break; - case KEY_A3: - mixer_hscroll_delta += 1; - mixer_vscroll_delta -= 1; - break; - case KEY_C1: - mixer_hscroll_delta -= 1; - mixer_vscroll_delta += 1; - break; - case KEY_C3: - mixer_hscroll_delta += 1; - mixer_vscroll_delta += 1; - break; - case KEY_RIGHT: - case 'n': - mixer_hscroll_delta += 1; - break; - case KEY_LEFT: - case 'p': - mixer_hscroll_delta -= 1; - break; - case KEY_UP: - case 'w': - case 'W': - mixer_vscroll_delta -= 1; - break; - case KEY_DOWN: - case 'x': - case 'X': - mixer_vscroll_delta += 1; - break; - case KEY_PPAGE: - case 'B': - case 'b': - mixer_vscroll_delta -= (mixer_max_y - 5) / 2; - break; - case KEY_NPAGE: - case ' ': - mixer_vscroll_delta += (mixer_max_y - 5) / 2; - break; - case KEY_BEG: - case KEY_HOME: - mixer_hscroll_delta -= 0xffffff; - break; - case KEY_LL: - case KEY_END: - mixer_hscroll_delta += 0xffffff; - break; - } - - if (key && mixer_view == VIEW_CHANNELS) - switch (key) - { - 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; - } + + 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 - { - mixer_lvolume_delta = 10; - mixer_rvolume_delta = 10; - } - break; - case KEY_NPAGE: - if (mixer_exact) - { - mixer_lvolume_delta = -8; - mixer_rvolume_delta = -8; - } + 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 - { - 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_toggle_mute_left = 1; - mixer_toggle_mute_right = 1; - break; - case 'b': - case 'B': - case '=': - mixer_balance_volumes = 1; - break; - case '<': - case ',': - mixer_toggle_mute_left = 1; - break; - case '>': - case '.': - mixer_toggle_mute_right = 1; - break; - case ' ': - mixer_toggle_record = 1; - break; - case KEY_IC: - case ';': - mixer_toggle_rec_left = 1; - break; - case '\'': - case KEY_DC: - mixer_toggle_rec_right = 1; - break; - case '1': - mixer_route_rtol_in = 1; - break; - case '2': - mixer_route_ltor_in = 1; - break; - } - - if (old_view != mixer_view) - mixer_clear (FALSE); - - mixer_focus_channel = CLAMP (mixer_focus_channel, 0, mixer_n_channels - 1); - - return finished; + mvaddstr(3, 2, " "); } -static void -mixer_winch (void) +static void mixer_init(void) { - signal (SIGWINCH, (void*) mixer_winch); + static snd_mixer_info_t mixer_info = + {0}; + static struct snd_ctl_hw_info hw_info; + void *ctl_handle; - mixer_needs_resize++; + 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_signal_handler (int signal) +static void mixer_iteration_update(void *dummy, int channel) { - if (signal != SIGSEGV) - mixer_abort (ERR_SIGNAL, sys_siglist[signal]); - else - { - fprintf (stderr, "\nSegmentation fault.\n"); - _exit (11); - } +#if 0 + fprintf(stderr, "*** channel = %i\n", channel); +#endif + mixer_update_cbar(channel); + refresh(); } -int -main (int argc, - char **argv) +static int mixer_iteration(void) { - int opt; - - /* parse args - */ - do - { - opt = getopt (argc, argv, "c:m:eshg"); - switch (opt) - { - case '?': - case 'h': - fprintf (stderr, "%s %s\n", PRGNAME_UPPER, VERSION); - fprintf (stderr, "Usage: %s [-e] [-c ] [-m ] [-z]\n", PRGNAME, snd_cards ()); - mixer_abort (ERR_NONE, ""); - case 'c': - card_id = snd_card_name (optarg); - break; + 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_exact = !mixer_exact; - break; - case 'g': - mixer_do_color = !mixer_do_color; - break; + 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': - mixer_id = CLAMP (optarg[0], '0', '1') - '0'; - break; - case 's': - mixer_minimize = 1; - break; + 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; } - } - 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_init_window (); - if (mixer_max_x < MIXER_MIN_X || - mixer_max_y < MIXER_MIN_Y) - beep (); // mixer_abort (ERR_WINSIZE, ""); - - signal (SIGWINCH, (void*) mixer_winch); + mixer_focus_channel = CLAMP(mixer_focus_channel, 0, mixer_n_channels - 1); - do - { - /* draw window upon every iteration */ - if (!mixer_needs_resize) - { - switch (mixer_view) - { - case VIEW_CHANNELS: - mixer_update_cbars (); - break; - case VIEW_HELP: - mixer_show_text ("Help", mixer_help_text, &mixer_help_xoffs, &mixer_help_yoffs); - break; - case VIEW_PROCINFO: - mixer_show_procinfo (); - break; - } - mixer_draw_frame (); - refresh (); + 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)); } - } - while (!mixer_iteration ()); - - mixer_abort (ERR_NONE, ""); + mixer_first_vis_channel = 0; + mixer_cbar_height = 10 + MAX(0, (mixer_max_y - MIXER_MIN_Y - 1)) / 2; +} + +static void mixer_signal_handler(int signal) +{ + mixer_abort(ERR_SIGNAL, sys_siglist[signal]); +} + +int main(int argc, + char **argv) +{ + int opt; + + /* parse args + */ + do { + opt = getopt(argc, argv, "c:m:ehg"); + switch (opt) { + case '?': + case 'h': + fprintf(stderr, "%s %s\n", PRGNAME_UPPER, VERSION); + fprintf(stderr, "Usage: %s [-e] [-c ] [-m ]\n", PRGNAME, snd_cards()); + mixer_abort(ERR_NONE, ""); + case 'c': + card_id = snd_card_name(optarg); + break; + case 'e': + mixer_exact = !mixer_exact; + break; + case 'g': + mixer_do_color = !mixer_do_color; + break; + case 'm': + mixer_id = CLAMP(optarg[0], '0', '1') - '0'; + break; + } + } + while (opt > 0); + + /* initialize mixer + */ + mixer_init(); + + /* setup signal handlers + */ + signal(SIGINT, mixer_signal_handler); + signal(SIGTRAP, mixer_signal_handler); + signal(SIGABRT, mixer_signal_handler); + signal(SIGQUIT, mixer_signal_handler); + signal(SIGBUS, mixer_signal_handler); + signal(SIGSEGV, mixer_signal_handler); + signal(SIGPIPE, mixer_signal_handler); + signal(SIGTERM, mixer_signal_handler); + + /* initialize ncurses + */ + mixer_window = initscr(); + if (mixer_do_color) + mixer_do_color = has_colors(); + mixer_init_draw_contexts(); + mixer_init_screen(); + if (mixer_max_x < MIXER_MIN_X || + mixer_max_y < MIXER_MIN_Y) + mixer_abort(ERR_WINSIZE, ""); + + /* react on key presses + * and draw window + */ + keypad(mixer_window, TRUE); + leaveok(mixer_window, TRUE); + cbreak(); + noecho(); + do { + mixer_update_cbars(); + mixer_draw_frame(); + refresh(); + } + while (!mixer_iteration()); + + mixer_abort(ERR_NONE, ""); }; diff --git a/alsamixer/alsamixer_tim.c b/alsamixer/alsamixer_tim.c new file mode 100644 index 0000000..b7d516d --- /dev/null +++ b/alsamixer/alsamixer_tim.c @@ -0,0 +1,1640 @@ +/* AlsaMixer - Commandline mixer for the ALSA project + * Copyright (C) 1998, 1999 Tim Janik and Jaroslav Kysela + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU 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. + * + * + * ChangeLog: + * + * Sun Feb 21 19:55:01 1999 Tim Janik + * + * * bumped version to 0.10. + * + * * added scrollable text views. + * we now feature an F1 Help screen and an F2 /proc info screen. + * the help screen does still require lots of work though. + * + * * keys are evaluated view specific now. + * + * * we feature meta-keys now, e.g. M-Tab as back-tab. + * + * * if we are already in channel view and the user still hits Return, + * we do a refresh nonetheless, since 'r'/'R' got removed as a redraw + * key (reserved for record volumes). 'l'/'L' is still preserved though, + * and actually needs to be to e.g. get around the xterm bold-artefacts. + * + * * support terminals that can't write into lower right corner. + * + * * undocumented '-s' option that will keep the screen to its + * minimum size, usefull for debugging only. + * + * Sun Feb 21 02:23:52 1999 Tim Janik + * + * * don't abort if snd_mixer_* functions failed due to EINTR, + * we simply retry on the next cycle. hopefully asoundlib preserves + * errno states correctly (Jaroslav can you asure that?). + * + * * feature WINCH correctly, so we make a complete relayout on + * screen resizes. don't abort on too-small screen sizes anymore, + * but simply beep. + * + * * redid the layout algorithm to fix some bugs and to preserve + * space for a flag indication line. the channels are + * nicer spread horizontally now (i.e. we also pad on the left and + * right screen bounds now). + * + * * various other minor fixes. + * + * * indicate whether ExactMode is active or not. + * + * * fixed coding style to follow the GNU coding conventions. + * + * * reverted record volume changes since they broke ExactMode display. + * + * * composed ChangeLog entries. + * + * 1998/11/04 19:43:45 perex + * + * * Stereo record source and route selection... + * provided by Carl van Schaik . + * + * 1998/09/20 08:05:24 perex + * + * * Fixed -m option... + * + * 1998/10/29 22:50:10 + * + * * initial checkin of alsamixer.c, written by Tim Janik, modified by + * Jaroslav Kysela to feature asoundlib.h instead of plain ioctl()s and + * automated updates after select() (i always missed that with OSS!). + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#ifndef CURSESINC +#include +#else +#include CURSESINC +#endif +#include + +#include + +/* example compilation commandline: + * clear; gcc -Wall -pipe -O2 alsamixer.c -o alsamixer -lasound -lncurses + */ + +/* --- defines --- */ +#define PRGNAME "alsamixer" +#define PRGNAME_UPPER "AlsaMixer" +#define VERSION "v0.10" +#define CHECK_ABORT(e,s) ({ if (errno != EINTR) mixer_abort ((e), (s)); }) +#define GETCH_BLOCK(w) ({ timeout ((w) ? -1 : 0); }) + +#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 (18) /* abs minimum: 18 */ +#define MIXER_TEXT_Y (10) +#define MIXER_MIN_Y (MIXER_TEXT_Y + 3) /* abs minimum: 11 */ + +#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) + + +/* --- views --- */ +enum { + VIEW_CHANNELS, + VIEW_HELP, + VIEW_PROCINFO +}; + + +/* --- variables --- */ +static WINDOW *mixer_window = NULL; +static int mixer_needs_resize = 0; +static int mixer_minimize = 0; +static int mixer_no_lrcorner = 0; +static int mixer_view = VIEW_CHANNELS; +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_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_have_old_focus = 0; +static int mixer_exact = 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; +static int mixer_toggle_record = 0; + +static int mixer_hscroll_delta = 0; +static int mixer_vscroll_delta = 0; + +/* By Carl */ +static int mixer_toggle_rec_left = 0; +static int mixer_toggle_rec_right = 0; +static int mixer_route_ltor_in = 0; +static int mixer_route_rtol_in = 0; + + +/* --- text --- */ +static int mixer_procinfo_xoffs = 0; +static int mixer_procinfo_yoffs = 0; +static int mixer_help_xoffs = 0; +static int mixer_help_yoffs = 0; +static char *mixer_help_text = +( + "\n" + " Esc exit alsamixer\n" + " F1 show Help screen\n" + " F2 show /proc info screen\n" + " Return return to main screen\n" + " Space toggle Record facility\n" + " Tab toggle ExactMode\n" + " m M mute both channels\n" + " < > mute left/right channel\n" + " Up increase left and right volume\n" + " Down decrease left and right volume\n" + " Right move (scroll) to the right next channel\n" + " Left move (scroll) to the left next channel\n" + "\n" + "Alsamixer has been written and is Copyrighted in 1998, 1999 by\n" + "Tim Janik and Jaroslav Kysela .\n" + ); + + +/* --- 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_LABEL, + DC_CBAR_FOCUS_LABEL, + DC_FOCUS, + DC_ANY_1, + DC_ANY_2, + DC_ANY_3, + DC_ANY_4, + 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_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); + mixer_init_dc ('#', DC_ANY_1, MIXER_WHITE, MIXER_BLACK, A_BOLD); + mixer_init_dc ('#', DC_ANY_2, MIXER_GREEN, MIXER_BLACK, A_BOLD); + mixer_init_dc ('#', DC_ANY_3, MIXER_RED, MIXER_BLACK, A_BOLD); + mixer_init_dc ('.', DC_ANY_4, MIXER_WHITE, MIXER_GREEN, A_BOLD); + mixer_init_dc ('.', DC_ANY_4, MIXER_WHITE, MIXER_BLUE, 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 (int full_redraw) +{ + int x, y; + int f = full_redraw ? 0 : 1; + + mixer_dc (DC_BACK); + + if (full_redraw) + clearok (mixer_window, TRUE); + + /* buggy ncurses doesn't really write spaces with the specified + * color into the screen on clear () or erase () + */ + for (x = f; x < mixer_max_x - f; x++) + for (y = f; y < mixer_max_y - f; y++) + mvaddch (y, x, ' '); +} + +static void +mixer_abort (ErrType error, + const char *err_string) +{ + if (mixer_window) + { + mixer_clear (TRUE); + refresh (); + keypad (mixer_window, FALSE); + leaveok (mixer_window, FALSE); + 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; + x += (3 + 2 + 3 + 1) * channel_index + mixer_extra_space * (channel_index + 1); + + if (MIXER_TEXT_Y + 10 < mixer_max_y) + y = mixer_max_y / 2 + 3; + else + y = (mixer_max_y + 1) / 2 + 3; + y += mixer_cbar_height / 2; + + 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_t cdata = { 0, }; + int vleft, vright; + int x, y, i; + + + /* set specified EXACT mode + */ + if (snd_mixer_exact_mode (mixer_handle, mixer_exact) < 0) + CHECK_ABORT (ERR_FCN, "snd_mixer_exact_mode()"); + + /* set new channel indices and read info + */ + if (snd_mixer_channel_info (mixer_handle, channel_index, &cinfo) < 0) + CHECK_ABORT (ERR_FCN, "snd_mixer_channel_info()"); + + /* 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 || mixer_toggle_rec_left || + mixer_toggle_rec_right || + mixer_route_rtol_in || mixer_route_ltor_in)) + { + if (snd_mixer_channel_read (mixer_handle, channel_index, &cdata) < 0) + CHECK_ABORT (ERR_FCN, "snd_mixer_channel_read()"); + + cdata.flags &= ~SND_MIXER_FLG_DECIBEL; + cdata.left = CLAMP (cdata.left + mixer_lvolume_delta, cinfo.min, cinfo.max); + cdata.right = CLAMP (cdata.right + mixer_rvolume_delta, cinfo.min, cinfo.max); + mixer_lvolume_delta = mixer_rvolume_delta = 0; + if (mixer_balance_volumes) + { + cdata.left = (cdata.left + cdata.right) / 2; + cdata.right = cdata.left; + mixer_balance_volumes = 0; + } + if (mixer_toggle_mute_left) + { + if (cdata.flags & SND_MIXER_FLG_MUTE_LEFT) + cdata.flags &= ~SND_MIXER_FLG_MUTE_LEFT; + else + cdata.flags |= SND_MIXER_FLG_MUTE_LEFT; + } + if (mixer_toggle_mute_right) + { + if (cdata.flags & SND_MIXER_FLG_MUTE_RIGHT) + cdata.flags &= ~SND_MIXER_FLG_MUTE_RIGHT; + else + cdata.flags |= SND_MIXER_FLG_MUTE_RIGHT; + } + mixer_toggle_mute_left = mixer_toggle_mute_right = 0; + if (mixer_toggle_record) + { + if (cdata.flags & SND_MIXER_FLG_RECORD) + cdata.flags &= ~SND_MIXER_FLG_RECORD; + else + cdata.flags |= SND_MIXER_FLG_RECORD; + } + mixer_toggle_record = 0; + + if (mixer_toggle_rec_left) + { + if (cdata.flags & SND_MIXER_FLG_RECORD_LEFT) + cdata.flags &= ~SND_MIXER_FLG_RECORD_LEFT; + else + cdata.flags |= SND_MIXER_FLG_RECORD_LEFT; + } + mixer_toggle_rec_left = 0; + + if (mixer_toggle_rec_right) + { + if (cdata.flags & SND_MIXER_FLG_RECORD_RIGHT) + cdata.flags &= ~SND_MIXER_FLG_RECORD_RIGHT; + else + cdata.flags |= SND_MIXER_FLG_RECORD_RIGHT; + } + mixer_toggle_rec_right = 0; + + if (mixer_route_ltor_in) + { + if (cdata.flags & SND_MIXER_FLG_LTOR_IN) + cdata.flags &= ~SND_MIXER_FLG_LTOR_IN; + else + cdata.flags |= SND_MIXER_FLG_LTOR_IN; + /* printf ("state : \n %d \n",cdata.flags & SND_MIXER_FLG_LTOR_IN); + */ + } + mixer_route_ltor_in = 0; + + if (mixer_route_rtol_in) + { + if (cdata.flags & SND_MIXER_FLG_RTOL_IN) + cdata.flags &= ~SND_MIXER_FLG_RTOL_IN; + else + cdata.flags |= SND_MIXER_FLG_RTOL_IN; + } + mixer_route_rtol_in = 0; + + if (snd_mixer_channel_write (mixer_handle, channel_index, &cdata) < 0) + CHECK_ABORT (ERR_FCN, "snd_mixer_channel_write()"); + } + /* first, read values for the numbers to be displayed in + * specified EXACT mode + */ + if (snd_mixer_channel_read (mixer_handle, channel_index, &cdata) < 0) + CHECK_ABORT (ERR_FCN, "snd_mixer_channel_read()"); + vleft = cdata.left; + vright = cdata.right; + + /* 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) + CHECK_ABORT (ERR_FCN, "snd_mixer_exact_mode()"); + if (snd_mixer_channel_read (mixer_handle, channel_index, &cdata) < 0) + CHECK_ABORT (ERR_FCN, "snd_mixer_channel_read()"); + } + /* get channel bar position + */ + if (!mixer_cbar_get_pos (channel_index, &x, &y)) + return; + + /* setup colors + */ + mixer_init_dc ('#', DC_ANY_1, MIXER_WHITE, MIXER_BLACK, A_BOLD); + mixer_init_dc ('#', DC_ANY_2, MIXER_GREEN, MIXER_BLACK, A_BOLD); + mixer_init_dc ('#', DC_ANY_3, MIXER_RED, MIXER_BLACK, A_BOLD); + + /* 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_ANY_3; + else if (i + 1 >= 0.4 * mixer_cbar_height) + dc = DC_ANY_2; + else + dc = DC_ANY_1; + mvaddch (y, x + 3, mixer_dc (cdata.left > i * 100 / mixer_cbar_height ? dc : DC_CBAR_EMPTY)); + mvaddch (y, x + 4, mixer_dc (cdata.right > i * 100 / mixer_cbar_height ? dc : DC_CBAR_EMPTY)); + y--; + } + + /* muted? + */ + mixer_dc (DC_BACK); + mvaddstr (y, x, " "); + c = cinfo.caps & SND_MIXER_CINFO_CAP_MUTE ? '-' : ' '; + mixer_dc (DC_CBAR_FRAME); + mvaddch (y, x + 2, ACS_ULCORNER); + mvaddch (y, x + 3, mixer_dc (cdata.flags & SND_MIXER_FLG_MUTE_LEFT ? + DC_CBAR_MUTE : DC_CBAR_NOMUTE)); + mvaddch (y, x + 4, mixer_dc (cdata.flags & SND_MIXER_FLG_MUTE_RIGHT ? + DC_CBAR_MUTE : DC_CBAR_NOMUTE)); + mixer_dc (DC_CBAR_FRAME); + mvaddch (y, x + 5, ACS_URCORNER); + y--; + + /* record input? + */ + if (cdata.flags & SND_MIXER_FLG_RECORD) + { + mixer_dc (DC_CBAR_RECORD); + mvaddstr (y, x + 1, "RECORD"); + if (cdata.flags & SND_MIXER_FLG_RECORD_LEFT) + { + if (cdata.flags & SND_MIXER_FLG_LTOR_IN) + mvaddstr (y + 2, x + 6, "L"); + else + mvaddstr (y + 1, x + 1, "L"); + } + if (cdata.flags & SND_MIXER_FLG_RECORD_RIGHT) + { + if (cdata.flags & SND_MIXER_FLG_RTOL_IN) + mvaddstr (y + 2, x + 1, "R"); + else + mvaddstr (y + 1, x + 6, "R"); + } + } + else if (cinfo.caps & SND_MIXER_CINFO_CAP_RECORD) + for (i = 0; i < 6; i++) + mvaddch (y, x + 1 + i, mixer_dc (DC_CBAR_NORECORD)); + else + { + mixer_dc (DC_BACK); + mvaddstr (y, x, " "); + } + 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 + */ + if (mixer_have_old_focus) + { + 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, ">"); + mixer_have_old_focus = 1; +} + +static void +mixer_draw_frame (void) +{ + char string[128]; + int i; + int max_len; + + mixer_dc (DC_FRAME); + + /* 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; + addstr (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; + addstr (string); + + /* indicate exact mode + */ + if (mixer_exact) + { + mixer_dc (DC_PROMPT); + mvaddstr (3, 2, "["); + mixer_dc (DC_TEXT); + addstr ("ExactMode"); + mixer_dc (DC_PROMPT); + addstr ("]"); + } + else + mvaddstr (3, 2, " "); + + /* lines + */ + mixer_dc (DC_PROMPT); + 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); + } + + /* corners + */ + mixer_dc (DC_PROMPT); + mvaddch (0, 0, ACS_ULCORNER); + mvaddch (0, mixer_max_x - 1, ACS_URCORNER); + mvaddch (mixer_max_y - 1, 0, ACS_LLCORNER); + if (!mixer_no_lrcorner) + mvaddch (mixer_max_y - 1, mixer_max_x - 1, ACS_LRCORNER); + else + { + mvaddch (mixer_max_y - 2, mixer_max_x - 1, ACS_LRCORNER); + mvaddch (mixer_max_y - 2, mixer_max_x - 2, ACS_ULCORNER); + mvaddch (mixer_max_y - 1, mixer_max_x - 2, ACS_LRCORNER); + } + + /* program title + */ + sprintf (string, "%s %s", PRGNAME_UPPER, VERSION); + max_len = strlen (string); + if (mixer_max_x >= max_len + 4) + { + mixer_dc (DC_PROMPT); + mvaddch (0, mixer_max_x / 2 - max_len / 2 - 1, '['); + mvaddch (0, mixer_max_x / 2 - max_len / 2 + max_len, ']'); + } + if (mixer_max_x >= max_len + 2) + { + mixer_dc (DC_TEXT); + mvaddstr (0, mixer_max_x / 2 - max_len / 2, string); + } +} + +static char* +mixer_offset_text (char **t, + int col, + int *length) +{ + char *p = *t; + char *r; + + while (*p && *p != '\n' && col--) + p++; + if (*p == '\n' || !*p) + { + if (*p == '\n') + p++; + *length = 0; + *t = p; + return p; + } + + r = p; + while (*r && *r != '\n' && (*length)--) + r++; + + *length = r - p; + while (*r && *r != '\n') + r++; + if (*r == '\n') + r++; + *t = r; + + return p; +} + +static void +mixer_show_text (char *title, + char *text, + int *xoffs, + int *yoffs) +{ + int tlines = 0, tcols = 0; + float hscroll, vscroll; + float hoffs, voffs; + char *p, *text_offs = text; + int x1, x2, y1, y2; + int i, n, l, r, block, stipple; + + /* coords + */ + x1 = 2; + x2 = mixer_max_x - 3; + y1 = 4; + y2 = mixer_max_y - 2; + + if ((y2 - y1) < 3 || (x2 - x1) < 3) + return; + + /* text dimensions + */ + l = 0; + for (p = text; *p; p++) + if (*p == '\n') + { + tlines++; + tcols = MAX (l, tcols); + l = 0; + } + else + l++; + tcols = MAX (l, tcols); + if (p > text && *(p - 1) != '\n') + tlines++; + + /* scroll areas / offsets + */ + l = x2 - x1 - 2; + if (l > tcols) + { + x1 += (l - tcols) / 2; + x2 = x1 + tcols + 1; + } + if (mixer_hscroll_delta) + { + *xoffs += mixer_hscroll_delta; + mixer_hscroll_delta = 0; + if (*xoffs < 0) + { + *xoffs = 0; + beep (); + } + else if (*xoffs > tcols - l - 1) + { + *xoffs = MAX (0, tcols - l - 1); + beep (); + } + } + if (tcols - l - 1 <= 0) + { + hscroll = 1; + hoffs = 0; + } + else + { + hscroll = ((float) l) / tcols; + hoffs = ((float) *xoffs) / (tcols - l - 1); + } + + l = y2 - y1 - 2; + if (l > tlines) + { + y1 += (l - tlines) / 2; + y2 = y1 + tlines + 1; + } + if (mixer_vscroll_delta) + { + *yoffs += mixer_vscroll_delta; + mixer_vscroll_delta = 0; + if (*yoffs < 0) + { + *yoffs = 0; + beep (); + } + else if (*yoffs > tlines - l - 1) + { + *yoffs = MAX (0, tlines - l - 1); + beep (); + } + } + if (tlines - l - 1 <= 0) + { + voffs = 0; + vscroll = 1; + } + else + { + vscroll = ((float) l) / tlines; + voffs = ((float) *yoffs) / (tlines - l - 1); + } + + /* colors + */ + mixer_init_dc ('.', DC_ANY_3, MIXER_YELLOW, MIXER_BLUE, A_BOLD); + mixer_init_dc ('.', DC_ANY_4, MIXER_WHITE, MIXER_BLUE, A_BOLD); + mixer_dc (DC_ANY_4); + + /* corners + */ + mvaddch (y2, x2, ACS_LRCORNER); + mvaddch (y2, x1, ACS_LLCORNER); + mvaddch (y1, x1, ACS_ULCORNER); + mvaddch (y1, x2, ACS_URCORNER); + + /* left + upper border + */ + for (i = y1 + 1; i < y2; i++) + mvaddch (i, x1, ACS_VLINE); + for (i = x1 + 1; i < x2; i++) + mvaddch (y1, i, ACS_HLINE); + if (title) + { + l = strlen (title); + if (l <= x2 - x1 - 3) + { + mvaddch (y1, x1 + 1 + (x2 - x1 - l) / 2 - 1, '['); + mvaddch (y1, x1 + 1 + (x2 - x1 - l) / 2 + l, ']'); + } + if (l <= x2 - x1 - 1) + { + mixer_dc (DC_ANY_3); + mvaddstr (y1, x1 + 1 + (x2 - x1 - l) / 2, title); + } + mixer_dc (DC_ANY_4); + } + + stipple = ACS_CKBOARD; + block = ACS_BLOCK; + if (block == '#' && ACS_BOARD == '#') + { + block = stipple; + stipple = ACS_BLOCK; + } + + /* lower scroll border + */ + l = x2 - x1 - 1; + n = hscroll * l; + r = (hoffs + 1.0 / (2 * (l - n - 1))) * (l - n - 1); + for (i = 0; i < l; i++) + mvaddch (y2, i + x1 + 1, hscroll >= 1 ? ACS_HLINE : + i >= r && i <= r + n ? block : stipple); + + /* right scroll border + */ + l = y2 - y1 - 1; + n = vscroll * l; + r = (voffs + 1.0 / (2 * (l - n - 1))) * (l - n - 1); + for (i = 0; i < l; i++) + mvaddch (i + y1 + 1, x2, vscroll >= 1 ? ACS_VLINE : + i >= r && i <= r + n ? block : stipple); + + /* show text + */ + x1++; y1++; + for (i = 0; i < *yoffs; i++) + { + l = 0; + mixer_offset_text (&text_offs, 0, &l); + } + for (i = y1; i < y2; i++) + { + l = x2 - x1; + p = mixer_offset_text (&text_offs, *xoffs, &l); + n = x1; + while (l--) + mvaddch (i, n++, *p++); + while (n < x2) + mvaddch (i, n++, ' '); + } +} + +struct vbuffer +{ + char *buffer; + int size; + int len; +}; + +static void +vbuffer_kill (struct vbuffer *vbuf) +{ + if (vbuf->size) + free (vbuf->buffer); + vbuf->buffer = NULL; + vbuf->size = 0; + vbuf->len = 0; +} + +#define vbuffer_append_string(vb,str) vbuffer_append (vb, str, strlen (str)) +static void +vbuffer_append (struct vbuffer *vbuf, + char *text, + int len) +{ + if (vbuf->size - vbuf->len <= len) + { + vbuf->size += len + 1; + vbuf->buffer = realloc (vbuf->buffer, vbuf->size); + } + memcpy (vbuf->buffer + vbuf->len, text, len); + vbuf->len += len; + vbuf->buffer[vbuf->len] = 0; +} + +static int +vbuffer_append_file (struct vbuffer *vbuf, + char *name) +{ + int fd; + + fd = open (name, O_RDONLY); + if (fd >= 0) + { + char buffer[1025]; + int l; + + do + { + l = read (fd, buffer, 1024); + + vbuffer_append (vbuf, buffer, MAX (0, l)); + } + while (l > 0 || (l < 0 && (errno == EAGAIN || errno == EINTR))); + + close (fd); + + return 0; + } + else + return 1; +} + +static void +mixer_show_procinfo (void) +{ + struct vbuffer vbuf = { NULL, 0, 0 }; + + vbuffer_append_string (&vbuf, "\n"); + vbuffer_append_string (&vbuf, "/proc/asound/version:\n"); + vbuffer_append_string (&vbuf, "====================\n"); + if (vbuffer_append_file (&vbuf, "/proc/asound/version")) + { + vbuffer_kill (&vbuf); + mixer_procinfo_xoffs = mixer_procinfo_yoffs = 0; + mixer_show_text ("/proc", + " No /proc information available. ", + &mixer_procinfo_xoffs, &mixer_procinfo_yoffs); + return; + } + else + vbuffer_append_file (&vbuf, "/proc/asound/meminfo"); + + vbuffer_append_string (&vbuf, "\n"); + vbuffer_append_string (&vbuf, "/proc/asound/cards:\n"); + vbuffer_append_string (&vbuf, "===================\n"); + if (vbuffer_append_file (&vbuf, "/proc/asound/cards")) + vbuffer_append_string (&vbuf, "No information available.\n"); + + vbuffer_append_string (&vbuf, "\n"); + vbuffer_append_string (&vbuf, "/proc/asound/devices:\n"); + vbuffer_append_string (&vbuf, "=====================\n"); + if (vbuffer_append_file (&vbuf, "/proc/asound/devices")) + vbuffer_append_string (&vbuf, "No information available.\n"); + + vbuffer_append_string (&vbuf, "\n"); + vbuffer_append_string (&vbuf, "/proc/asound/oss-devices:\n"); + vbuffer_append_string (&vbuf, "=========================\n"); + if (vbuffer_append_file (&vbuf, "/proc/asound/oss-devices")) + vbuffer_append_string (&vbuf, "No information available.\n"); + + vbuffer_append_string (&vbuf, "\n"); + vbuffer_append_string (&vbuf, "/proc/asound/timers:\n"); + vbuffer_append_string (&vbuf, "====================\n"); + if (vbuffer_append_file (&vbuf, "/proc/asound/timers")) + vbuffer_append_string (&vbuf, "No information available.\n"); + + vbuffer_append_string (&vbuf, "\n"); + vbuffer_append_string (&vbuf, "/proc/asound/pcm:\n"); + vbuffer_append_string (&vbuf, "=================\n"); + if (vbuffer_append_file (&vbuf, "/proc/asound/pcm")) + vbuffer_append_string (&vbuf, "No information available.\n"); + + mixer_show_text ("/proc", vbuf.buffer, + &mixer_procinfo_xoffs, &mixer_procinfo_yoffs); + vbuffer_kill (&vbuf); +} + +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_init_window (void) +{ + /* initialize ncurses + */ + mixer_window = initscr (); + + mixer_no_lrcorner = tigetflag ("xenl") != 1 && tigetflag ("am") != 1; + + if (mixer_do_color) + mixer_do_color = has_colors (); + mixer_init_draw_contexts (); + + /* react on key presses + */ + cbreak (); + noecho (); + leaveok (mixer_window, TRUE); + keypad (mixer_window, TRUE); + GETCH_BLOCK (1); + + /* init mixer screen + */ + getmaxyx (mixer_window, mixer_max_y, mixer_max_x); + if (mixer_minimize) + { + mixer_max_x = MIXER_MIN_X; + mixer_max_y = MIXER_MIN_Y; + } + mixer_ofs_x = 2 /* extra begin padding: */ + 1; + + /* required allocations */ + mixer_n_vis_channels = (mixer_max_x - mixer_ofs_x * 2 + 1) / 9; + mixer_n_vis_channels = CLAMP (mixer_n_vis_channels, 1, mixer_n_channels); + mixer_extra_space = mixer_max_x - mixer_ofs_x * 2 + 1 - mixer_n_vis_channels * 9; + mixer_extra_space = MAX (0, mixer_extra_space / (mixer_n_vis_channels + 1)); + if (MIXER_TEXT_Y + 10 < mixer_max_y) + mixer_cbar_height = 10 + MAX (0, mixer_max_y - MIXER_TEXT_Y - 10 ) / 2; + else + mixer_cbar_height = MAX (1, mixer_max_y - MIXER_TEXT_Y); + + mixer_clear (TRUE); +} + +static void +mixer_resize (void) +{ + struct winsize winsz = { 0, }; + + mixer_needs_resize = 0; + + if (ioctl (fileno (stdout), TIOCGWINSZ, &winsz) >= 0 && + winsz.ws_row && winsz.ws_col) + { + keypad (mixer_window, FALSE); + leaveok (mixer_window, FALSE); + + endwin (); + + mixer_max_x = MAX (2, winsz.ws_col); + mixer_max_y = MAX (2, winsz.ws_row); + + /* humpf, i don't get it, if only the number of rows change, + * ncurses will segfault shortly after (could trigger that with mc as well). + */ + resizeterm (mixer_max_y + 1, mixer_max_x + 1); + resizeterm (mixer_max_y, mixer_max_x); + + mixer_init_window (); + + if (mixer_max_x < MIXER_MIN_X || + mixer_max_y < MIXER_MIN_Y) + beep (); // mixer_abort (ERR_WINSIZE, ""); + + mixer_have_old_focus = 0; + } +} + +static void +mixer_channel_changed (void *private_data, + int channel) +{ + /* we don't actually need to update the individual channels because + * we redraw the whole screen upon every main iteration anyways. + */ +#if 0 + fprintf (stderr, "*** channel = %i\n", channel); + mixer_update_cbar (channel); +#endif +} + +static int +mixer_iteration (void) +{ + struct timeval delay = { 0, }; + snd_mixer_callbacks_t callbacks = { 0, }; + int mixer_fd; + fd_set rfds; + int finished = 0; + int key = 0; + int old_view; + + callbacks.channel_was_changed = mixer_channel_changed; + + /* setup for select on stdin and the mixer fd */ + mixer_fd = snd_mixer_file_descriptor (mixer_handle); + FD_ZERO (&rfds); + FD_SET (fileno (stdin), &rfds); + FD_SET (mixer_fd, &rfds); + + delay.tv_sec = 0; + delay.tv_usec = 0 * 100 * 1000; + + finished = select (mixer_fd + 1, &rfds, NULL, NULL, mixer_needs_resize ? &delay : NULL) < 0; + + /* don't abort on handled signals */ + if (finished && errno == EINTR) + { + FD_ZERO (&rfds); + finished = 0; + } + else if (mixer_needs_resize) + mixer_resize (); + + if (FD_ISSET (mixer_fd, &rfds)) + snd_mixer_read (mixer_handle, &callbacks); + + if (FD_ISSET (fileno (stdin), &rfds)) + key = getch (); + + old_view = mixer_view; + + /* feature Escape prefixing for some keys */ + if (key == 27) + { + GETCH_BLOCK (0); + key = getch (); + GETCH_BLOCK (1); + switch (key) + { + case 9: /* Tab */ + key = KEY_BTAB; + break; + default: + key = 27; + break; + } + } + + /* general keys */ + switch (key) + { + case 0: + /* ignore */ + break; + case 27: /* Escape */ + finished = 1; + key = 0; + break; + case 13: /* Return */ + case 10: /* NewLine */ + if (mixer_view == VIEW_CHANNELS) + mixer_clear (FALSE); + mixer_view = VIEW_CHANNELS; + key = 0; + break; + case 'h': + case 'H': + case KEY_F (1): + mixer_view = VIEW_HELP; + key = 0; + break; + case '/': + case KEY_F (2): + mixer_view = VIEW_PROCINFO; + key = 0; + break; + case 'L': + case 'l': + mixer_clear (TRUE); + break; + } + + if (key && (mixer_view == VIEW_HELP || + mixer_view == VIEW_PROCINFO)) + switch (key) + { + case 9: /* Tab */ + mixer_hscroll_delta += 8; + break; + case KEY_BTAB: + mixer_hscroll_delta -= 8; + break; + case KEY_A1: + mixer_hscroll_delta -= 1; + mixer_vscroll_delta -= 1; + break; + case KEY_A3: + mixer_hscroll_delta += 1; + mixer_vscroll_delta -= 1; + break; + case KEY_C1: + mixer_hscroll_delta -= 1; + mixer_vscroll_delta += 1; + break; + case KEY_C3: + mixer_hscroll_delta += 1; + mixer_vscroll_delta += 1; + break; + case KEY_RIGHT: + case 'n': + mixer_hscroll_delta += 1; + break; + case KEY_LEFT: + case 'p': + mixer_hscroll_delta -= 1; + break; + case KEY_UP: + case 'w': + case 'W': + mixer_vscroll_delta -= 1; + break; + case KEY_DOWN: + case 'x': + case 'X': + mixer_vscroll_delta += 1; + break; + case KEY_PPAGE: + case 'B': + case 'b': + mixer_vscroll_delta -= (mixer_max_y - 5) / 2; + break; + case KEY_NPAGE: + case ' ': + mixer_vscroll_delta += (mixer_max_y - 5) / 2; + break; + case KEY_BEG: + case KEY_HOME: + mixer_hscroll_delta -= 0xffffff; + break; + case KEY_LL: + case KEY_END: + mixer_hscroll_delta += 0xffffff; + break; + } + + if (key && mixer_view == VIEW_CHANNELS) + switch (key) + { + 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_toggle_mute_left = 1; + mixer_toggle_mute_right = 1; + break; + case 'b': + case 'B': + case '=': + mixer_balance_volumes = 1; + break; + case '<': + case ',': + mixer_toggle_mute_left = 1; + break; + case '>': + case '.': + mixer_toggle_mute_right = 1; + break; + case ' ': + mixer_toggle_record = 1; + break; + case KEY_IC: + case ';': + mixer_toggle_rec_left = 1; + break; + case '\'': + case KEY_DC: + mixer_toggle_rec_right = 1; + break; + case '1': + mixer_route_rtol_in = 1; + break; + case '2': + mixer_route_ltor_in = 1; + break; + } + + if (old_view != mixer_view) + mixer_clear (FALSE); + + mixer_focus_channel = CLAMP (mixer_focus_channel, 0, mixer_n_channels - 1); + + return finished; +} + +static void +mixer_winch (void) +{ + signal (SIGWINCH, (void*) mixer_winch); + + mixer_needs_resize++; +} + +static void +mixer_signal_handler (int signal) +{ + if (signal != SIGSEGV) + mixer_abort (ERR_SIGNAL, sys_siglist[signal]); + else + { + fprintf (stderr, "\nSegmentation fault.\n"); + _exit (11); + } +} + +int +main (int argc, + char **argv) +{ + int opt; + + /* parse args + */ + do + { + opt = getopt (argc, argv, "c:m:eshg"); + switch (opt) + { + case '?': + case 'h': + fprintf (stderr, "%s %s\n", PRGNAME_UPPER, VERSION); + fprintf (stderr, "Usage: %s [-e] [-c ] [-m ] [-z]\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; + case 's': + mixer_minimize = 1; + 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_init_window (); + if (mixer_max_x < MIXER_MIN_X || + mixer_max_y < MIXER_MIN_Y) + beep (); // mixer_abort (ERR_WINSIZE, ""); + + signal (SIGWINCH, (void*) mixer_winch); + + do + { + /* draw window upon every iteration */ + if (!mixer_needs_resize) + { + switch (mixer_view) + { + case VIEW_CHANNELS: + mixer_update_cbars (); + break; + case VIEW_HELP: + mixer_show_text ("Help", mixer_help_text, &mixer_help_xoffs, &mixer_help_yoffs); + break; + case VIEW_PROCINFO: + mixer_show_procinfo (); + break; + } + mixer_draw_frame (); + refresh (); + } + } + while (!mixer_iteration ()); + + mixer_abort (ERR_NONE, ""); +}; diff --git a/amixer/amixer.cpp b/amixer/amixer.cpp index 189bd14..46fcba2 100644 --- a/amixer/amixer.cpp +++ b/amixer/amixer.cpp @@ -97,7 +97,7 @@ char* Mixer::Name(int32 device) void Mixer::Update() { - if(snd_mixer_channel_read(mixer_handle, current_device, &ch_data) < 0) { + 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? */ } @@ -120,7 +120,7 @@ void Mixer::DeviceWrite(int32 device, int32 left, int32 right, int32 flags) ch_data.left = left; ch_data.right = right; ch_data.flags = flags; - if(snd_mixer_channel_write(mixer_handle, device, &ch_data) < 0) { + if(snd_mixer_channel_output_write(mixer_handle, device, &ch_data) < 0) { fprintf(stderr, "Can't write data to channel %i\n", device); return; /* No fail code? */ } diff --git a/amixer/amixer.h b/amixer/amixer.h index 84600ff..64c9fa3 100644 --- a/amixer/amixer.h +++ b/amixer/amixer.h @@ -21,10 +21,12 @@ #define E_MIXER_SUCCESS 1 #define E_MIXER_NEED_CLOSE 2 -#define E_MIXER_RECORD SND_MIXER_FLG_RECORD -#define E_MIXER_MUTE_LEFT SND_MIXER_FLG_MUTE_LEFT -#define E_MIXER_MUTE_RIGHT SND_MIXER_FLG_MUTE_RIGHT -#define E_MIXER_MUTE SND_MIXER_FLG_MUTE +/* 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 @@ -63,7 +65,7 @@ public: } private: snd_mixer_info_t info; - snd_mixer_channel_t ch_data; + snd_mixer_channel_direction_t ch_data; snd_mixer_channel_info_t ch_info; void * mixer_handle; diff --git a/aplay/aplay.1 b/aplay/aplay.1 index 043a830..6e28e74 100644 --- a/aplay/aplay.1 +++ b/aplay/aplay.1 @@ -31,7 +31,7 @@ List all available soundcards and devices. .TP \fI-c\fP Select the soundcard to use, if you have more than one. Cards are -numbered from 1 (the default). +numbered from 0 (the default). .TP \fI-d\fP Select the soundcard device to use, if your card has more than diff --git a/aplay/aplay.c b/aplay/aplay.c index 21e6f35..9dcc3b4 100644 --- a/aplay/aplay.c +++ b/aplay/aplay.c @@ -141,7 +141,7 @@ static void usage(char *command) " -h,--help help\n" " -V,--version print current version\n" " -l list all soundcards and digital audio devices\n" - " -c select card # or card id (1-%i), defaults to 1\n" + " -c select card # or card id (0-%i), defaults to 0\n" " -d select device #, defaults to 0\n" " -q quiet mode\n" " -v file format Voc\n" @@ -155,7 +155,7 @@ static void usage(char *command) " -m set CD-ROM quality (44100Hz,stereo,16-bit linear)\n" " -M set DAT quality (48000Hz,stereo,16-bit linear)\n" " -p compression type (alaw, ulaw, adpcm)\n" - ,command, snd_cards()); + ,command, snd_cards()-1); } static void device_list(void) diff --git a/configure.in b/configure.in index c80a927..3d92545 100644 --- a/configure.in +++ b/configure.in @@ -1,7 +1,7 @@ dnl Process this file with autoconf to produce a configure script. AC_INIT(alsamixer/alsamixer.c) AC_PREFIX_DEFAULT(/usr) -AM_INIT_AUTOMAKE(alsa-utils, 0.3.0-pre3) +AM_INIT_AUTOMAKE(alsa-utils, 0.3.0pre5) dnl Checks for programs. AC_PROG_CC @@ -33,5 +33,7 @@ AC_HEADER_TIME dnl Checks for library functions. AC_PROG_GCC_TRADITIONAL +SAVE_UTIL_VERSION + AC_OUTPUT(Makefile alsactl/Makefile alsamixer/Makefile amixer/Makefile aplay/Makefile \ include/Makefile utils/Makefile utils/alsa-utils.spec) diff --git a/version b/version deleted file mode 100644 index b180c90..0000000 --- a/version +++ /dev/null @@ -1 +0,0 @@ -0.3.0-pre3