📄 alsamixer.c
字号:
/* AlsaMixer - Commandline mixer for the ALSA project Copyright (C) 1998, * 1999 Tim Janik <timj@gtk.org> and Jaroslav Kysela <perex@perex.cz> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU 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: * * Wed Feb 14 13:08:17 CET 2001 Jaroslav Kysela <perex@perex.cz> * * * ported to the latest mixer 0.9.x API (function based) * * Fri Jun 23 14:10:00 MEST 2000 Jaroslav Kysela <perex@perex.cz> * * * ported to new mixer 0.9.x API (simple control) * * improved error handling (mixer_abort) * * Thu Mar 9 22:54:16 MET 2000 Takashi iwai <iwai@ww.uni-erlangen.de> * * * a group is split into front, rear, center and woofer elements. * * Mon Jan 3 23:33:42 MET 2000 Jaroslav Kysela <perex@perex.cz> * * * version 1.00 * * * ported to new mixer API (scontrol control) * * Sun Feb 21 19:55:01 1999 Tim Janik <timj@gtk.org> * * * 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 capture 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 <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 capture volume changes since they broke ExactMode display. * * * composed ChangeLog entries. * * 1998/11/04 19:43:45 perex * * * Stereo capture 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!). */#define _GNU_SOURCE#include <stdio.h>#include <unistd.h>#include <fcntl.h>#include <sys/ioctl.h>#include <errno.h>#include <string.h>#include <stdlib.h>#include <unistd.h>#include <sys/signal.h>#include <sys/time.h>#include <locale.h>#ifndef CURSESINC#include <ncurses.h>#else#include CURSESINC#endif#include <time.h>#include <alsa/asoundlib.h>#include "aconfig.h"/* example compilation commandline: * clear; gcc -Wall -pipe -O2 alsamixer.c -o alsamixer -lasound -lncurses *//* --- defines --- */#define PRGNAME "alsamixer"#define PRGNAME_UPPER "AlsaMixer"#define CHECK_ABORT(e,s,n) ({ if ((n) != -EINTR) mixer_abort ((e), (s), (n)); })#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_CBAR_STD_HGT (10)#define MIXER_MIN_Y (MIXER_TEXT_Y + 6) /* abs minimum: 16 */#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_PLAYBACK, VIEW_CAPTURE, 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_PLAYBACK;static int mixer_view_saved = VIEW_PLAYBACK;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 mixer_text_y = MIXER_TEXT_Y;static char card_id[64] = "default";static snd_mixer_t *mixer_handle;static char mixer_card_name[128];static char mixer_device_name[128];static int mixer_level = 0;static struct snd_mixer_selem_regopt mixer_options;/* mixer bar channel : left or right */#define MIXER_CHN_LEFT 0#define MIXER_CHN_RIGHT 1/* mask for toggle mute and capture */#define MIXER_MASK_LEFT (1 << 0)#define MIXER_MASK_RIGHT (1 << 1)#define MIXER_MASK_STEREO (MIXER_MASK_LEFT|MIXER_MASK_RIGHT)/* mixer split types */enum { MIXER_ELEM_FRONT, MIXER_ELEM_REAR, MIXER_ELEM_CENTER, MIXER_ELEM_WOOFER, MIXER_ELEM_SIDE, MIXER_ELEM_CAPTURE, MIXER_ELEM_ENUM, MIXER_ELEM_CAPTURE_ENUM, MIXER_ELEM_END};#define MIXER_ELEM_TYPE_MASK 0xff#define MIXER_ELEM_CAPTURE_SWITCH 0x100 /* bit */#define MIXER_ELEM_MUTE_SWITCH 0x200 /* bit */#define MIXER_ELEM_CAPTURE_SUFFIX 0x400#define MIXER_ELEM_HAS_VOLUME 0x800/* left and right channels for each type */static const snd_mixer_selem_channel_id_t mixer_elem_chn[][2] = { { SND_MIXER_SCHN_FRONT_LEFT, SND_MIXER_SCHN_FRONT_RIGHT }, { SND_MIXER_SCHN_REAR_LEFT, SND_MIXER_SCHN_REAR_RIGHT }, { SND_MIXER_SCHN_FRONT_CENTER, SND_MIXER_SCHN_UNKNOWN }, { SND_MIXER_SCHN_WOOFER, SND_MIXER_SCHN_UNKNOWN }, { SND_MIXER_SCHN_SIDE_LEFT, SND_MIXER_SCHN_SIDE_RIGHT }, { SND_MIXER_SCHN_FRONT_LEFT, SND_MIXER_SCHN_FRONT_RIGHT },};static void *mixer_sid = NULL;static int mixer_n_selems = 0;static int mixer_changed_state = 1;/* split scontrols */static int mixer_n_elems = 0;static int mixer_n_view_elems = 0;static int mixer_n_vis_elems = 0;static int mixer_first_vis_elem = 0;static int mixer_focus_elem = 0;static int mixer_have_old_focus = 0;static int *mixer_grpidx;static int *mixer_type;static int mixer_volume_delta[2]; /* left/right volume delta in % */static int mixer_volume_absolute = -1; /* absolute volume settings in % */static int mixer_balance_volumes = 0; /* boolean */static unsigned mixer_toggle_mute = 0; /* left/right mask */static unsigned mixer_toggle_capture = 0; /* left/right mask */static int mixer_hscroll_delta = 0;static int mixer_vscroll_delta = 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 =( " Esc exit alsamixer\n" " F1 ? show Help screen\n" " F2 / show /proc info screen\n" " F3 show Playback controls only\n" " F4 show Capture controls only\n" " F5 show all controls\n" " Tab toggle view mode\n" " Return return to main screen\n" " Space toggle Capture facility\n" " m M toggle mute on both channels\n" " < > toggle mute on 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 <timj@gtk.org> and Jaroslav Kysela <perex@perex.cz>.\n" );/* --- draw contexts --- */enum { DC_DEFAULT, DC_BACK, DC_TEXT, DC_PROMPT, DC_CBAR_FRAME, DC_CBAR_MUTE, DC_CBAR_NOMUTE, DC_CBAR_CAPTURE, DC_CBAR_NOCAPTURE, 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 voidmixer_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 intmixer_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 voidmixer_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 ('.', DC_CBAR_FRAME, MIXER_CYAN, MIXER_BLACK, A_BOLD); mixer_init_dc ('M', DC_CBAR_MUTE, MIXER_DARK_CYAN, MIXER_BLACK, A_NORMAL); mixer_init_dc ('O', DC_CBAR_NOMUTE, MIXER_WHITE, MIXER_GREEN, A_BOLD); mixer_init_dc ('x', DC_CBAR_CAPTURE, MIXER_DARK_RED, MIXER_BLACK, A_BOLD); mixer_init_dc ('-', DC_CBAR_NOCAPTURE, 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 (ACS_CKBOARD, DC_ANY_1, MIXER_WHITE, MIXER_WHITE, A_BOLD); mixer_init_dc (ACS_CKBOARD, DC_ANY_2, MIXER_GREEN, MIXER_GREEN, A_BOLD); mixer_init_dc (ACS_CKBOARD, DC_ANY_3, MIXER_RED, MIXER_RED, A_BOLD); mixer_init_dc ('.', DC_ANY_4, MIXER_WHITE, MIXER_BLUE, A_BOLD);}#define DC_FRAME (DC_PROMPT)/* --- error types --- */typedef enum{ ERR_NONE, ERR_OPEN, ERR_FCN, ERR_SIGNAL, ERR_WINSIZE,} ErrType;/* --- prototypes --- */static voidmixer_abort (ErrType error, const char *err_string, int xerrno) __attribute__((noreturn));/* --- functions --- */static voidmixer_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 voidmixer_abort (ErrType error, const char *err_string, int xerrno){ 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 ": function %s failed for %s: %s\n", err_string, card_id, snd_strerror (xerrno)); break; case ERR_FCN: fprintf (stderr, PRGNAME ": function %s failed: %s\n", err_string, snd_strerror (xerrno)); 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 intmixer_cbar_get_pos (int elem_index, int *x_p, int *y_p){ int x; int y; if (elem_index < mixer_first_vis_elem || elem_index - mixer_first_vis_elem >= mixer_n_vis_elems) return FALSE; elem_index -= mixer_first_vis_elem; x = mixer_ofs_x; x += (3 + 2 + 3 + 1) * elem_index + mixer_extra_space * (elem_index + 1); if (mixer_text_y + MIXER_CBAR_STD_HGT < mixer_max_y) y = (mixer_text_y + mixer_cbar_height) / 2 - 1 + mixer_max_y / 2; else y = mixer_text_y - 1 + mixer_cbar_height; if (y >= mixer_max_y - 1) y = mixer_max_y - 2; if (x_p) *x_p = x; if (y_p) *y_p = y; return TRUE;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -