* Sun Feb 21 02:23:52 1999 Tim Janik <timj@gtk.org>

*
 *      * 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.
This commit is contained in:
Tim Janik 1999-02-21 04:41:27 +00:00
parent 4775b46362
commit d47938eab3
3 changed files with 953 additions and 823 deletions

View file

@ -79,5 +79,6 @@ You can exit with ALT + Q, or by hitting ESC.
----------------------------------------------------------------- -----------------------------------------------------------------
alsamixer is by Jaroslav Kysela <perex@jcu.cz> Alsamixer has been written by Tim Janik <timj@gtk.org> and
This document is by Paul Winkler <zarmzarm@erols.com> been furtherly improved by Jaroslav Kysela <perex@jcu.cz>.
This document was provided by Paul Winkler <zarmzarm@erols.com>.

View file

@ -107,15 +107,14 @@ arecord(1)
\fP \fP
.SH BUGS .SH BUGS
None known. Some terminal emulators (e.g. \fBnxterm\fP) may not There is no help screen implemented yet. Some terminal emulators
(e.g. \fBnxterm\fP) may not
work quite right with ncurses, but that's their own damn work quite right with ncurses, but that's their own damn
fault. Plain old \fBxterm\fP seems to be fine. fault. Plain old \fBxterm\fP seems to be fine.
.SH AUTHOR .SH AUTHOR
\fBalsamixer\fP is by Jaroslav Kysela <perex@jcu.cz> .B alsamixer
This document is by Paul Winkler <zarmzarm@erols.com>. has been written by Tim Janik <timj@gtk.org> and
been furtherly improved by Jaroslav Kysela <perex@jcu.cz>.
This manual page was provided by Paul Winkler <zarmzarm@erols.com>.

View file

@ -1,7 +1,5 @@
/* AlsaMixer - Commandline mixer for the ALSA project /* AlsaMixer - Commandline mixer for the ALSA project
* Copyright (C) 1998 Jaroslav Kysela <perex@jcu.cz>, * Copyright (C) 1998, 1999 Tim Janik <timj@gtk.org> and Jaroslav Kysela <perex@jcu.cz>
* Tim Janik <timj@gtk.org>,
* Carl van Schaik <carl@dreamcoat.che.uct.ac.za>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -16,6 +14,49 @@
* You should have received a copy of the GNU Library General Public * You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free Software * License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*
*
* ChangeLog:
*
* Sun Feb 21 02:23:52 1999 Tim Janik <timj@gtk.org>
*
* * 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 <carl@dreamcoat.che.uct.ac.za>.
*
* 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 <stdio.h> #include <stdio.h>
@ -40,13 +81,15 @@
#include <sys/asoundlib.h> #include <sys/asoundlib.h>
/* example compilation commandline: /* example compilation commandline:
* clear; gcc -Wall -pipe -O2 alsamixer.c -o alsamixer -lncurses * clear; gcc -Wall -pipe -O2 alsamixer.c -o alsamixer -lasound -lncurses
*/ */
/* --- defines --- */ /* --- defines --- */
#define PRGNAME "alsamixer" #define PRGNAME "alsamixer"
#define PRGNAME_UPPER "AlsaMixer" #define PRGNAME_UPPER "AlsaMixer"
#define VERSION "v0.9" #define VERSION "v0.9"
#define REFRESH() ({ if (!mixer_needs_resize) refresh (); })
#define CHECK_ABORT(e,s) ({ if (errno != EINTR) mixer_abort ((e), (s)); })
#undef MAX #undef MAX
#define MAX(a, b) (((a) > (b)) ? (a) : (b)) #define MAX(a, b) (((a) > (b)) ? (a) : (b))
@ -57,8 +100,9 @@
#undef CLAMP #undef CLAMP
#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
#define MIXER_MIN_X (23) /* minimum: 23 */ #define MIXER_MIN_X (18) /* abs minimum: 18 */
#define MIXER_MIN_Y (19) /* minimum: 19 */ #define MIXER_TEXT_Y (10)
#define MIXER_MIN_Y (MIXER_TEXT_Y + 3) /* abs minimum: 11 */
#define MIXER_BLACK (COLOR_BLACK) #define MIXER_BLACK (COLOR_BLACK)
#define MIXER_DARK_RED (COLOR_RED) #define MIXER_DARK_RED (COLOR_RED)
@ -78,11 +122,11 @@
/* --- variables --- */ /* --- variables --- */
static WINDOW *mixer_window = NULL; static WINDOW *mixer_window = NULL;
static int mixer_needs_resize = 0;
static int mixer_max_x = 0; static int mixer_max_x = 0;
static int mixer_max_y = 0; static int mixer_max_y = 0;
static int mixer_ofs_x = 0; static int mixer_ofs_x = 0;
static float mixer_extra_space = 0; static float mixer_extra_space = 0;
static int mixer_ofs_y = 0;
static int mixer_cbar_height = 0; static int mixer_cbar_height = 0;
static int card_id = 0; static int card_id = 0;
@ -95,10 +139,9 @@ static int mixer_n_channels = 0;
static int mixer_n_vis_channels = 0; static int mixer_n_vis_channels = 0;
static int mixer_first_vis_channel = 0; static int mixer_first_vis_channel = 0;
static int mixer_focus_channel = 0; static int mixer_focus_channel = 0;
static int mixer_have_old_focus = 0;
static int mixer_exact = 0; static int mixer_exact = 0;
static int mixer_record_volumes = 0;
static int mixer_lvolume_delta = 0; static int mixer_lvolume_delta = 0;
static int mixer_rvolume_delta = 0; static int mixer_rvolume_delta = 0;
static int mixer_balance_volumes = 0; static int mixer_balance_volumes = 0;
@ -137,15 +180,13 @@ enum {
DC_LAST DC_LAST
}; };
static int dc_fg[DC_LAST] = static int dc_fg[DC_LAST] = { 0 };
{0}; static int dc_attrib[DC_LAST] = { 0 };
static int dc_attrib[DC_LAST] = static int dc_char[DC_LAST] = { 0 };
{0};
static int dc_char[DC_LAST] =
{0};
static int mixer_do_color = 1; static int mixer_do_color = 1;
static void mixer_init_dc(int c, static void
mixer_init_dc (int c,
int n, int n,
int f, int f,
int b, int b,
@ -158,7 +199,8 @@ static void mixer_init_dc(int c,
init_pair (n, dc_fg[n] & 0xf, b & 0x0f); 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) if (mixer_do_color)
attrset (COLOR_PAIR (n) | (dc_fg[n] & 0xfffffff0)); attrset (COLOR_PAIR (n) | (dc_fg[n] & 0xfffffff0));
@ -168,7 +210,8 @@ static int mixer_dc(int n)
return dc_char[n]; return dc_char[n];
} }
static void mixer_init_draw_contexts(void) static void
mixer_init_draw_contexts (void)
{ {
start_color (); start_color ();
@ -193,7 +236,8 @@ static void mixer_init_draw_contexts(void)
/* --- error types --- */ /* --- error types --- */
typedef enum { typedef enum
{
ERR_NONE, ERR_NONE,
ERR_OPEN, ERR_OPEN,
ERR_FCN, ERR_FCN,
@ -203,18 +247,21 @@ typedef enum {
/* --- prototypes --- */ /* --- prototypes --- */
static void mixer_abort(ErrType error, static void
mixer_abort (ErrType error,
const char *err_string) const char *err_string)
__attribute__ __attribute__
((noreturn)); ((noreturn));
/* --- functions --- */ /* --- functions --- */
static void mixer_clear(void) static void
mixer_clear (void)
{ {
int x, y; int x, y;
mixer_dc (DC_BACK); mixer_dc (DC_BACK);
clearok (mixer_window, TRUE);
clear (); clear ();
/* buggy ncurses doesn't really write spaces with the specified /* buggy ncurses doesn't really write spaces with the specified
@ -223,20 +270,25 @@ static void mixer_clear(void)
for (x = 0; x < mixer_max_x; x++) for (x = 0; x < mixer_max_x; x++)
for (y = 0; y < mixer_max_y; y++) for (y = 0; y < mixer_max_y; y++)
mvaddch (y, x, ' '); mvaddch (y, x, ' ');
refresh();
} }
static void mixer_abort(ErrType error, static void
mixer_abort (ErrType error,
const char *err_string) const char *err_string)
{ {
if (mixer_window) { if (mixer_window)
{
mixer_clear (); mixer_clear ();
refresh ();
keypad (mixer_window, FALSE);
leaveok (mixer_window, FALSE);
endwin (); endwin ();
mixer_window = NULL; mixer_window = NULL;
} }
printf ("\n"); printf ("\n");
switch (error) { switch (error)
{
case ERR_OPEN: case ERR_OPEN:
fprintf (stderr, fprintf (stderr,
PRGNAME ": failed to open mixer #%i/#%i: %s\n", PRGNAME ": failed to open mixer #%i/#%i: %s\n",
@ -268,7 +320,8 @@ static void mixer_abort(ErrType error,
exit (error); exit (error);
} }
static int mixer_cbar_get_pos(int channel_index, static int
mixer_cbar_get_pos (int channel_index,
int *x_p, int *x_p,
int *y_p) int *y_p)
{ {
@ -281,11 +334,14 @@ static int mixer_cbar_get_pos(int channel_index,
channel_index -= mixer_first_vis_channel; channel_index -= mixer_first_vis_channel;
x = mixer_ofs_x + 1; x = mixer_ofs_x;
y = mixer_ofs_y; x += (3 + 2 + 3 + 1) * channel_index + mixer_extra_space * (channel_index + 1);
x += channel_index * (3 + 2 + 3 + 1 + mixer_extra_space);
y += mixer_max_y / 2; if (MIXER_TEXT_Y + 10 < mixer_max_y)
y += mixer_cbar_height / 2 + 1; y = mixer_max_y / 2 + 3;
else
y = (mixer_max_y + 1) / 2 + 3;
y += mixer_cbar_height / 2;
if (x_p) if (x_p)
*x_p = x; *x_p = x;
@ -295,31 +351,26 @@ static int mixer_cbar_get_pos(int channel_index,
return TRUE; return TRUE;
} }
static void mixer_update_cbar(int channel_index) static void
mixer_update_cbar (int channel_index)
{ {
char string[64]; char string[64];
char c; char c;
snd_mixer_channel_info_t cinfo = snd_mixer_channel_info_t cinfo = { 0, };
{0}; snd_mixer_channel_t cdata = { 0, };
snd_mixer_channel_t cdata =
{0};
snd_mixer_channel_t crdata =
{0};
int vleft, vright; int vleft, vright;
int x, y, i; int x, y, i;
int channel_record_volume;
/* set specified EXACT mode /* set specified EXACT mode
*/ */
if (snd_mixer_exact_mode (mixer_handle, mixer_exact) < 0) if (snd_mixer_exact_mode (mixer_handle, mixer_exact) < 0)
mixer_abort(ERR_FCN, "snd_mixer_exact"); CHECK_ABORT (ERR_FCN, "snd_mixer_exact_mode()");
/* set new channel indices and read info /* set new channel indices and read info
*/ */
if (snd_mixer_channel_info (mixer_handle, channel_index, &cinfo) < 0) if (snd_mixer_channel_info (mixer_handle, channel_index, &cinfo) < 0)
mixer_abort(ERR_FCN, "snd_mixer_channel_info"); CHECK_ABORT (ERR_FCN, "snd_mixer_channel_info()");
channel_record_volume = (cinfo.caps & SND_MIXER_CINFO_CAP_RECORDVOLUME);
/* set new channel values /* set new channel values
*/ */
@ -329,59 +380,38 @@ static void mixer_update_cbar(int channel_index)
mixer_balance_volumes || mixer_balance_volumes ||
mixer_toggle_record || mixer_toggle_rec_left || mixer_toggle_record || mixer_toggle_rec_left ||
mixer_toggle_rec_right || mixer_toggle_rec_right ||
mixer_route_rtol_in || mixer_route_ltor_in)) { mixer_route_rtol_in || mixer_route_ltor_in))
{
if (snd_mixer_channel_read (mixer_handle, channel_index, &cdata) < 0) if (snd_mixer_channel_read (mixer_handle, channel_index, &cdata) < 0)
mixer_abort(ERR_FCN, "snd_mixer_channel_read"); CHECK_ABORT (ERR_FCN, "snd_mixer_channel_read()");
if (mixer_record_volumes && channel_record_volume &&
snd_mixer_channel_record_read(mixer_handle, channel_index, &crdata) < 0)
mixer_abort(ERR_FCN, "snd_mixer_channel_record_read");
cdata.flags &= ~SND_MIXER_FLG_DECIBEL; cdata.flags &= ~SND_MIXER_FLG_DECIBEL;
if (mixer_lvolume_delta) {
if (mixer_record_volumes) {
if (channel_record_volume)
crdata.left = CLAMP(crdata.left + mixer_lvolume_delta, cinfo.min, cinfo.max);
}
else
cdata.left = CLAMP (cdata.left + mixer_lvolume_delta, cinfo.min, cinfo.max); cdata.left = CLAMP (cdata.left + mixer_lvolume_delta, cinfo.min, cinfo.max);
mixer_lvolume_delta = 0;
}
if (mixer_rvolume_delta) {
if (mixer_record_volumes) {
if (channel_record_volume)
crdata.right = CLAMP(crdata.right + mixer_rvolume_delta, cinfo.min, cinfo.max);
}
else
cdata.right = CLAMP (cdata.right + mixer_rvolume_delta, cinfo.min, cinfo.max); cdata.right = CLAMP (cdata.right + mixer_rvolume_delta, cinfo.min, cinfo.max);
mixer_rvolume_delta = 0; mixer_lvolume_delta = mixer_rvolume_delta = 0;
} if (mixer_balance_volumes)
if (mixer_balance_volumes) { {
if (mixer_record_volumes) {
if (channel_record_volume) {
crdata.left = (crdata.left + crdata.right) / 2;
crdata.right = crdata.left;
}
}
else {
cdata.left = (cdata.left + cdata.right) / 2; cdata.left = (cdata.left + cdata.right) / 2;
cdata.right = cdata.left; cdata.right = cdata.left;
}
mixer_balance_volumes = 0; mixer_balance_volumes = 0;
} }
if (mixer_toggle_mute_left) { if (mixer_toggle_mute_left)
{
if (cdata.flags & SND_MIXER_FLG_MUTE_LEFT) if (cdata.flags & SND_MIXER_FLG_MUTE_LEFT)
cdata.flags &= ~SND_MIXER_FLG_MUTE_LEFT; cdata.flags &= ~SND_MIXER_FLG_MUTE_LEFT;
else else
cdata.flags |= SND_MIXER_FLG_MUTE_LEFT; cdata.flags |= SND_MIXER_FLG_MUTE_LEFT;
} }
if (mixer_toggle_mute_right) { if (mixer_toggle_mute_right)
{
if (cdata.flags & SND_MIXER_FLG_MUTE_RIGHT) if (cdata.flags & SND_MIXER_FLG_MUTE_RIGHT)
cdata.flags &= ~SND_MIXER_FLG_MUTE_RIGHT; cdata.flags &= ~SND_MIXER_FLG_MUTE_RIGHT;
else else
cdata.flags |= SND_MIXER_FLG_MUTE_RIGHT; cdata.flags |= SND_MIXER_FLG_MUTE_RIGHT;
} }
mixer_toggle_mute_left = mixer_toggle_mute_right = 0; mixer_toggle_mute_left = mixer_toggle_mute_right = 0;
if (mixer_toggle_record) { if (mixer_toggle_record)
{
if (cdata.flags & SND_MIXER_FLG_RECORD) if (cdata.flags & SND_MIXER_FLG_RECORD)
cdata.flags &= ~SND_MIXER_FLG_RECORD; cdata.flags &= ~SND_MIXER_FLG_RECORD;
else else
@ -389,7 +419,8 @@ static void mixer_update_cbar(int channel_index)
} }
mixer_toggle_record = 0; mixer_toggle_record = 0;
if (mixer_toggle_rec_left) { if (mixer_toggle_rec_left)
{
if (cdata.flags & SND_MIXER_FLG_RECORD_LEFT) if (cdata.flags & SND_MIXER_FLG_RECORD_LEFT)
cdata.flags &= ~SND_MIXER_FLG_RECORD_LEFT; cdata.flags &= ~SND_MIXER_FLG_RECORD_LEFT;
else else
@ -397,7 +428,8 @@ static void mixer_update_cbar(int channel_index)
} }
mixer_toggle_rec_left = 0; mixer_toggle_rec_left = 0;
if (mixer_toggle_rec_right) { if (mixer_toggle_rec_right)
{
if (cdata.flags & SND_MIXER_FLG_RECORD_RIGHT) if (cdata.flags & SND_MIXER_FLG_RECORD_RIGHT)
cdata.flags &= ~SND_MIXER_FLG_RECORD_RIGHT; cdata.flags &= ~SND_MIXER_FLG_RECORD_RIGHT;
else else
@ -405,7 +437,8 @@ static void mixer_update_cbar(int channel_index)
} }
mixer_toggle_rec_right = 0; mixer_toggle_rec_right = 0;
if (mixer_route_ltor_in) { if (mixer_route_ltor_in)
{
if (cdata.flags & SND_MIXER_FLG_LTOR_IN) if (cdata.flags & SND_MIXER_FLG_LTOR_IN)
cdata.flags &= ~SND_MIXER_FLG_LTOR_IN; cdata.flags &= ~SND_MIXER_FLG_LTOR_IN;
else else
@ -415,7 +448,8 @@ static void mixer_update_cbar(int channel_index)
} }
mixer_route_ltor_in = 0; mixer_route_ltor_in = 0;
if (mixer_route_rtol_in) { if (mixer_route_rtol_in)
{
if (cdata.flags & SND_MIXER_FLG_RTOL_IN) if (cdata.flags & SND_MIXER_FLG_RTOL_IN)
cdata.flags &= ~SND_MIXER_FLG_RTOL_IN; cdata.flags &= ~SND_MIXER_FLG_RTOL_IN;
else else
@ -424,45 +458,28 @@ static void mixer_update_cbar(int channel_index)
mixer_route_rtol_in = 0; mixer_route_rtol_in = 0;
if (snd_mixer_channel_write (mixer_handle, channel_index, &cdata) < 0) if (snd_mixer_channel_write (mixer_handle, channel_index, &cdata) < 0)
mixer_abort(ERR_FCN, "snd_mixer_channel_write"); CHECK_ABORT (ERR_FCN, "snd_mixer_channel_write()");
if (mixer_record_volumes && channel_record_volume &&
snd_mixer_channel_record_write(mixer_handle, channel_index, &crdata) < 0)
mixer_abort(ERR_FCN, "snd_mixer_channel_record_write");
} }
/* first, read values for the numbers to be displayed in /* first, read values for the numbers to be displayed in
* specified EXACT mode * specified EXACT mode
*/ */
if (snd_mixer_channel_read (mixer_handle, channel_index, &cdata) < 0) if (snd_mixer_channel_read (mixer_handle, channel_index, &cdata) < 0)
mixer_abort(ERR_FCN, "snd_mixer_ioctl_channel_read"); CHECK_ABORT (ERR_FCN, "snd_mixer_channel_read()");
if (mixer_record_volumes) {
if (channel_record_volume) {
if (snd_mixer_channel_record_read(mixer_handle, channel_index, &crdata) < 0)
mixer_abort(ERR_FCN, "snd_mixer_channel_record_read");
vleft = crdata.left;
vright = crdata.right;
}
else
vleft = vright = 0;
}
else {
vleft = cdata.left; vleft = cdata.left;
vright = cdata.right; vright = cdata.right;
}
/* then, always use percentage values for the bars. if we don't do /* then, always use percentage values for the bars. if we don't do
* this, we will see aliasing effects on specific circumstances. * this, we will see aliasing effects on specific circumstances.
* (actually they don't really dissapear, but they are transfered * (actually they don't really dissapear, but they are transfered
* to bar<->smaller-scale ambiguities). * to bar<->smaller-scale ambiguities).
*/ */
if (mixer_exact) { if (mixer_exact)
{
i = 0; i = 0;
if (snd_mixer_exact_mode (mixer_handle, 0) < 0) if (snd_mixer_exact_mode (mixer_handle, 0) < 0)
mixer_abort(ERR_FCN, "snd_mixer_exact"); CHECK_ABORT (ERR_FCN, "snd_mixer_exact_mode()");
if (snd_mixer_channel_read (mixer_handle, channel_index, &cdata) < 0) if (snd_mixer_channel_read (mixer_handle, channel_index, &cdata) < 0)
mixer_abort(ERR_FCN, "snd_mixer_channel_read"); CHECK_ABORT (ERR_FCN, "snd_mixer_channel_read()");
if (mixer_record_volumes && channel_record_volume &&
snd_mixer_channel_record_read(mixer_handle, channel_index, &crdata) < 0)
mixer_abort(ERR_FCN, "snd_mixer_channel_record_read");
} }
/* get channel bar position /* get channel bar position
*/ */
@ -473,7 +490,8 @@ static void mixer_update_cbar(int channel_index)
*/ */
mixer_dc (channel_index == mixer_focus_channel ? DC_CBAR_FOCUS_LABEL : DC_CBAR_LABEL); mixer_dc (channel_index == mixer_focus_channel ? DC_CBAR_FOCUS_LABEL : DC_CBAR_LABEL);
cinfo.name[8] = 0; cinfo.name[8] = 0;
for (i = 0; i < 8; i++) { for (i = 0; i < 8; i++)
{
string[i] = ' '; string[i] = ' ';
} }
sprintf (string + (8 - strlen (cinfo.name)) / 2, "%s ", cinfo.name); sprintf (string + (8 - strlen (cinfo.name)) / 2, "%s ", cinfo.name);
@ -505,13 +523,15 @@ static void mixer_update_cbar(int channel_index)
mvaddch (y, x + 4, ACS_HLINE); mvaddch (y, x + 4, ACS_HLINE);
mvaddch (y, x + 5, ACS_LRCORNER); mvaddch (y, x + 5, ACS_LRCORNER);
y--; y--;
for (i = 0; i < mixer_cbar_height; i++) { for (i = 0; i < mixer_cbar_height; i++)
{
mvaddstr (y - i, x, " "); mvaddstr (y - i, x, " ");
mvaddch (y - i, x + 2, ACS_VLINE); mvaddch (y - i, x + 2, ACS_VLINE);
mvaddch (y - i, x + 5, ACS_VLINE); mvaddch (y - i, x + 5, ACS_VLINE);
} }
string[2] = 0; string[2] = 0;
for (i = 0; i < mixer_cbar_height; i++) { for (i = 0; i < mixer_cbar_height; i++)
{
int dc; int dc;
if (i + 1 >= 0.8 * mixer_cbar_height) if (i + 1 >= 0.8 * mixer_cbar_height)
@ -520,8 +540,8 @@ static void mixer_update_cbar(int channel_index)
dc = DC_CBAR_FULL_2; dc = DC_CBAR_FULL_2;
else else
dc = DC_CBAR_FULL_1; dc = DC_CBAR_FULL_1;
mvaddch(y, x + 3, mixer_dc(vleft > i * 100 / mixer_cbar_height ? dc : DC_CBAR_EMPTY)); mvaddch (y, x + 3, mixer_dc (cdata.left > 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)); mvaddch (y, x + 4, mixer_dc (cdata.right > i * 100 / mixer_cbar_height ? dc : DC_CBAR_EMPTY));
y--; y--;
} }
@ -542,38 +562,45 @@ static void mixer_update_cbar(int channel_index)
/* record input? /* record input?
*/ */
if (cdata.flags & SND_MIXER_FLG_RECORD) { if (cdata.flags & SND_MIXER_FLG_RECORD)
{
mixer_dc (DC_CBAR_RECORD); mixer_dc (DC_CBAR_RECORD);
mvaddstr (y, x + 1, "RECORD"); mvaddstr (y, x + 1, "RECORD");
if (cdata.flags & SND_MIXER_FLG_RECORD_LEFT) { if (cdata.flags & SND_MIXER_FLG_RECORD_LEFT)
{
if (cdata.flags & SND_MIXER_FLG_LTOR_IN) if (cdata.flags & SND_MIXER_FLG_LTOR_IN)
mvaddstr (y + 2, x + 6, "L"); mvaddstr (y + 2, x + 6, "L");
else else
mvaddstr (y + 1, x + 1, "L"); mvaddstr (y + 1, x + 1, "L");
} }
if (cdata.flags & SND_MIXER_FLG_RECORD_RIGHT) { if (cdata.flags & SND_MIXER_FLG_RECORD_RIGHT)
{
if (cdata.flags & SND_MIXER_FLG_RTOL_IN) if (cdata.flags & SND_MIXER_FLG_RTOL_IN)
mvaddstr (y + 2, x + 1, "R"); mvaddstr (y + 2, x + 1, "R");
else else
mvaddstr (y + 1, x + 6, "R"); mvaddstr (y + 1, x + 6, "R");
} }
} else if (cinfo.caps & SND_MIXER_CINFO_CAP_RECORD) }
else if (cinfo.caps & SND_MIXER_CINFO_CAP_RECORD)
for (i = 0; i < 6; i++) for (i = 0; i < 6; i++)
mvaddch (y, x + 1 + i, mixer_dc (DC_CBAR_NORECORD)); mvaddch (y, x + 1 + i, mixer_dc (DC_CBAR_NORECORD));
else { else
{
mixer_dc (DC_BACK); mixer_dc (DC_BACK);
mvaddstr (y, x, " "); mvaddstr (y, x, " ");
} }
y--; y--;
} }
static void mixer_update_cbars(void) static void
mixer_update_cbars (void)
{ {
static int o_x = 0; static int o_x = 0;
static int o_y = 0; static int o_y = 0;
int i, x, y; int i, x, y;
if (!mixer_cbar_get_pos(mixer_focus_channel, &x, &y)) { if (!mixer_cbar_get_pos (mixer_focus_channel, &x, &y))
{
if (mixer_focus_channel < mixer_first_vis_channel) if (mixer_focus_channel < mixer_first_vis_channel)
mixer_first_vis_channel = mixer_focus_channel; mixer_first_vis_channel = mixer_focus_channel;
else if (mixer_focus_channel >= mixer_first_vis_channel + mixer_n_vis_channels) else if (mixer_focus_channel >= mixer_first_vis_channel + mixer_n_vis_channels)
@ -585,17 +612,22 @@ static void mixer_update_cbars(void)
/* draw focused cbar /* draw focused cbar
*/ */
if (mixer_have_old_focus)
{
mixer_dc (DC_BACK); mixer_dc (DC_BACK);
mvaddstr (o_y, o_x, " "); mvaddstr (o_y, o_x, " ");
mvaddstr (o_y, o_x + 9, " "); mvaddstr (o_y, o_x + 9, " ");
}
o_x = x - 1; o_x = x - 1;
o_y = y; o_y = y;
mixer_dc (DC_FOCUS); mixer_dc (DC_FOCUS);
mvaddstr (o_y, o_x, "<"); mvaddstr (o_y, o_x, "<");
mvaddstr (o_y, o_x + 9, ">"); mvaddstr (o_y, o_x + 9, ">");
mixer_have_old_focus = 1;
} }
static void mixer_draw_frame(void) static void
mixer_draw_frame (void)
{ {
char string[128]; char string[128];
int i; int i;
@ -603,33 +635,6 @@ static void mixer_draw_frame(void)
mixer_dc (DC_FRAME); 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 /* card name
*/ */
mixer_dc (DC_PROMPT); mixer_dc (DC_PROMPT);
@ -639,7 +644,7 @@ static void mixer_draw_frame(void)
max_len = mixer_max_x - 2 - 6 - 2; max_len = mixer_max_x - 2 - 6 - 2;
if (strlen (string) > max_len) if (strlen (string) > max_len)
string[max_len] = 0; string[max_len] = 0;
mvaddstr(1, 2 + 6, string); addstr (string);
/* device name /* device name
*/ */
@ -650,17 +655,65 @@ static void mixer_draw_frame(void)
max_len = mixer_max_x - 2 - 6 - 2; max_len = mixer_max_x - 2 - 6 - 2;
if (strlen (string) > max_len) if (strlen (string) > max_len)
string[max_len] = 0; string[max_len] = 0;
mvaddstr(2, 2 + 6, string); addstr (string);
if (mixer_record_volumes)
mvaddstr(3, 2, "Record mixer"); /* 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 else
mvaddstr (3, 2, " "); 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);
} }
static void mixer_init(void) /* corners
*/
mixer_dc (DC_PROMPT);
mvaddch (mixer_max_y - 1, mixer_max_x - 1, ACS_LRCORNER);
mvaddch (mixer_max_y - 1, 0, ACS_LLCORNER);
mvaddch (0, 0, ACS_ULCORNER);
mvaddch (0, mixer_max_x - 1, ACS_URCORNER);
/* program title
*/
sprintf (string, "%s %s", PRGNAME_UPPER, VERSION);
max_len = strlen (string);
if (mixer_max_x >= max_len + 4)
{ {
static snd_mixer_info_t mixer_info = mixer_dc (DC_PROMPT);
{0}; 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 void
mixer_init (void)
{
static snd_mixer_info_t mixer_info = { 0, };
static struct snd_ctl_hw_info hw_info; static struct snd_ctl_hw_info hw_info;
void *ctl_handle; void *ctl_handle;
@ -683,39 +736,133 @@ static void mixer_init(void)
mixer_device_name = mixer_info.name; mixer_device_name = mixer_info.name;
} }
static void mixer_iteration_update(void *dummy, int channel) static void
mixer_init_window (void)
{ {
/* initialize ncurses
*/
mixer_window = initscr ();
if (mixer_do_color)
mixer_do_color = has_colors ();
mixer_init_draw_contexts ();
/* react on key presses
* and draw window
*/
keypad (mixer_window, TRUE);
leaveok (mixer_window, TRUE);
cbreak ();
noecho ();
/* init mixer screen
*/
getmaxyx (mixer_window, mixer_max_y, mixer_max_x);
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 ();
}
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,
* clear() segfaults (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_cb (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 #if 0
fprintf (stderr, "*** channel = %i\n", channel); fprintf (stderr, "*** channel = %i\n", channel);
#endif
mixer_update_cbar (channel); mixer_update_cbar (channel);
refresh(); #endif
} }
static int mixer_iteration(void) static int
mixer_iteration (void)
{ {
snd_mixer_callbacks_t callbacks; struct timeval delay = { 0, };
int key; snd_mixer_callbacks_t callbacks = { 0, };
int finished = 0;
int mixer_fd; int mixer_fd;
fd_set in; fd_set rfds;
int finished = 0;
int key = 0;
bzero(&callbacks, sizeof(callbacks)); callbacks.channel_was_changed = mixer_channel_changed_cb;
callbacks.channel_was_changed = mixer_iteration_update;
/* setup for select on stdin and the mixer fd */
mixer_fd = snd_mixer_file_descriptor (mixer_handle); mixer_fd = snd_mixer_file_descriptor (mixer_handle);
while (1) { FD_ZERO (&rfds);
FD_ZERO(&in); FD_SET (fileno (stdin), &rfds);
FD_SET(fileno(stdin), &in); FD_SET (mixer_fd, &rfds);
FD_SET(mixer_fd, &in);
if (select(mixer_fd + 1, &in, NULL, NULL, NULL) <= 0) delay.tv_sec = 0;
return 1; delay.tv_usec = 0 * 100 * 1000;
if (FD_ISSET(mixer_fd, &in))
snd_mixer_read(mixer_handle, &callbacks); finished = select (mixer_fd + 1, &rfds, NULL, NULL, mixer_needs_resize ? &delay : NULL) < 0;
if (FD_ISSET(fileno(stdin), &in))
break; /* 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 (); key = getch ();
switch (key) {
switch (key)
{
case 0:
/* ignore */
break;
case 27: /* Escape */ case 27: /* Escape */
finished = 1; finished = 1;
break; break;
@ -731,19 +878,25 @@ static int mixer_iteration(void)
mixer_focus_channel -= 1; mixer_focus_channel -= 1;
break; break;
case KEY_PPAGE: case KEY_PPAGE:
if (mixer_exact) { if (mixer_exact)
{
mixer_lvolume_delta = 8; mixer_lvolume_delta = 8;
mixer_rvolume_delta = 8; mixer_rvolume_delta = 8;
} else { }
else
{
mixer_lvolume_delta = 10; mixer_lvolume_delta = 10;
mixer_rvolume_delta = 10; mixer_rvolume_delta = 10;
} }
break; break;
case KEY_NPAGE: case KEY_NPAGE:
if (mixer_exact) { if (mixer_exact)
{
mixer_lvolume_delta = -8; mixer_lvolume_delta = -8;
mixer_rvolume_delta = -8; mixer_rvolume_delta = -8;
} else { }
else
{
mixer_lvolume_delta = -10; mixer_lvolume_delta = -10;
mixer_rvolume_delta = -10; mixer_rvolume_delta = -10;
} }
@ -806,7 +959,6 @@ static int mixer_iteration(void)
break; break;
case 'm': case 'm':
case 'M': case 'M':
mixer_record_volumes = 0;
mixer_toggle_mute_left = 1; mixer_toggle_mute_left = 1;
mixer_toggle_mute_right = 1; mixer_toggle_mute_right = 1;
break; break;
@ -817,18 +969,14 @@ static int mixer_iteration(void)
break; break;
case '<': case '<':
case ',': case ',':
mixer_record_volumes = 0;
mixer_toggle_mute_left = 1; mixer_toggle_mute_left = 1;
break; break;
case '>': case '>':
case '.': case '.':
mixer_record_volumes = 0;
mixer_toggle_mute_right = 1; mixer_toggle_mute_right = 1;
break; break;
case 'R': case 'R':
case 'r': case 'r':
mixer_record_volumes = !mixer_record_volumes;
break;
case 'L': case 'L':
case 'l': case 'l':
mixer_clear (); mixer_clear ();
@ -845,11 +993,9 @@ static int mixer_iteration(void)
mixer_toggle_rec_right = 1; mixer_toggle_rec_right = 1;
break; break;
case '1': case '1':
mixer_record_volumes = 0;
mixer_route_rtol_in = 1; mixer_route_rtol_in = 1;
break; break;
case '2': case '2':
mixer_record_volumes = 0;
mixer_route_ltor_in = 1; mixer_route_ltor_in = 1;
break; break;
} }
@ -858,50 +1004,39 @@ static int mixer_iteration(void)
return finished; return finished;
} }
static void mixer_init_screen(void) static void
mixer_winch (void)
{ {
signal(SIGWINCH, (void *) mixer_init_screen); signal (SIGWINCH, (void*) mixer_winch);
getmaxyx(mixer_window, mixer_max_y, mixer_max_x); mixer_needs_resize++;
mixer_clear();
mixer_max_x = MAX(MIXER_MIN_X, mixer_max_x);
mixer_max_y = MAX(MIXER_MIN_Y, mixer_max_y);
mixer_clear();
mixer_ofs_x = 2;
mixer_ofs_y = 2;
mixer_extra_space = 0;
mixer_n_vis_channels = MIN((mixer_max_x - 2 * mixer_ofs_x + 1) / (9 + mixer_extra_space),
mixer_n_channels);
mixer_extra_space = ((mixer_max_x - 2 * mixer_ofs_x - 1 - mixer_n_vis_channels * 9.0) /
(mixer_n_vis_channels - 1));
if (mixer_n_vis_channels < mixer_n_channels) {
/* recalc
*/
mixer_extra_space = MAX(mixer_extra_space, 1);
mixer_n_vis_channels = MIN((mixer_max_x - 2 * mixer_ofs_x + 1) / (9 + mixer_extra_space),
mixer_n_channels);
mixer_extra_space = ((mixer_max_x - 2 * mixer_ofs_x - 1 - mixer_n_vis_channels * 9.0) /
(mixer_n_vis_channels - 1));
}
mixer_first_vis_channel = 0;
mixer_cbar_height = 10 + MAX(0, (mixer_max_y - MIXER_MIN_Y - 1)) / 2;
} }
static void mixer_signal_handler(int signal) static void
mixer_signal_handler (int signal)
{ {
if (signal != SIGSEGV)
mixer_abort (ERR_SIGNAL, sys_siglist[signal]); mixer_abort (ERR_SIGNAL, sys_siglist[signal]);
else
{
fprintf (stderr, "\nSegmentation fault.\n");
_exit (11);
}
} }
int main(int argc, int
main (int argc,
char **argv) char **argv)
{ {
int opt; int opt;
/* parse args /* parse args
*/ */
do { do
{
opt = getopt (argc, argv, "c:m:ehg"); opt = getopt (argc, argv, "c:m:ehg");
switch (opt) { switch (opt)
{
case '?': case '?':
case 'h': case 'h':
fprintf (stderr, "%s %s\n", PRGNAME_UPPER, VERSION); fprintf (stderr, "%s %s\n", PRGNAME_UPPER, VERSION);
@ -940,26 +1075,21 @@ int main(int argc,
/* initialize ncurses /* initialize ncurses
*/ */
mixer_window = initscr(); mixer_init_window ();
if (mixer_do_color)
mixer_do_color = has_colors();
mixer_init_draw_contexts();
mixer_init_screen();
if (mixer_max_x < MIXER_MIN_X || if (mixer_max_x < MIXER_MIN_X ||
mixer_max_y < MIXER_MIN_Y) mixer_max_y < MIXER_MIN_Y)
mixer_abort(ERR_WINSIZE, ""); beep (); // mixer_abort (ERR_WINSIZE, "");
signal (SIGWINCH, (void*) mixer_winch);
/* react on key presses /* react on key presses
* and draw window * and draw window
*/ */
keypad(mixer_window, TRUE); do
leaveok(mixer_window, TRUE); {
cbreak();
noecho();
do {
mixer_update_cbars (); mixer_update_cbars ();
mixer_draw_frame (); mixer_draw_frame ();
refresh(); REFRESH ();
} }
while (!mixer_iteration ()); while (!mixer_iteration ());