/* rexima 1.4 - a curses-based (and command-line) mixer for Linux. * Copyright (C) 1996-2003 Russell Marks. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #define REXIMA_VER "1.4" #define DEV_Y_START 3 #define DEV_X_START 4 #define DEV_X_DEVEND (DEV_X_START+9) #define DEV_X_BAR (DEV_X_DEVEND+3) #define DEV_X_PCNT (DEV_X_BAR+55) #define DEV_X_REC (DEV_X_PCNT+5) char dev_labels[SOUND_MIXER_NRDEVICES][80]=SOUND_DEVICE_LABELS; char dev_names [SOUND_MIXER_NRDEVICES][80]=SOUND_DEVICE_NAMES; int dev_line[SOUND_MIXER_NRDEVICES]; char *mixerdev="/dev/mixer"; /* equivalents of optopt, opterr, optind, and optarg */ int optnopt=0,optnerr=0,optnind=1; char *optnarg=NULL; /* holds offset in current argv[] value */ static int optnpos=1; /* This routine assumes that the caller is pretty sane and doesn't * try passing an invalid 'optstring' or varying argc/argv. */ int getoptn(int argc,char *argv[],char *optstring) { char *ptr; /* check for end of arg list */ if(optnind==argc || *(argv[optnind])!='-' || strlen(argv[optnind])<=1) return(-1); if((ptr=strchr(optstring,argv[optnind][optnpos]))==NULL) return('?'); /* error: unknown option */ else { optnopt=*ptr; if(ptr[1]==':') { if(optnind==argc-1) return(':'); /* error: missing option */ optnarg=argv[optnind+1]; optnpos=1; optnind+=2; return(optnopt); /* return early, avoiding the normal increment */ } } /* now increment position ready for next time. * no checking is done for the end of args yet - this is done on * the next call. */ optnpos++; if(optnpos>strlen(argv[optnind])) { optnpos=1; optnind++; } return(optnopt); /* return the found option */ } void die(char *str) { fprintf(stderr,"rexima: couldn't %s.\n",str); exit(1); } void init(int *mixfd,int *existmask,int *canrecmask,int *isrecmask, int *stereomask) { if(((*mixfd)=open(mixerdev,O_RDWR))<0) die("open mixer device"); if(ioctl(*mixfd,SOUND_MIXER_READ_DEVMASK,existmask)==-1) die("ioctl"); if(ioctl(*mixfd,SOUND_MIXER_READ_RECMASK,canrecmask)==-1) die("ioctl"); if(ioctl(*mixfd,SOUND_MIXER_READ_RECSRC,isrecmask)==-1) die("ioctl"); /* this looks like a `recent' addition [in 1996 maybe ;-)] so be lenient */ if(ioctl(*mixfd,SOUND_MIXER_READ_STEREODEVS,stereomask)==-1) *stereomask=0; } void init_term() { initscr(); cbreak(); noecho(); keypad(stdscr,TRUE); } void uninit(int mixfd) { clear(); refresh(); echo(); nocbreak(); endwin(); putchar('\n'); close(mixfd); } void drawsel(int new,int old) { if(new!=old) { if(old>=0) { mvaddstr(DEV_Y_START+dev_line[old],DEV_X_START-3," "); mvaddstr(DEV_Y_START+dev_line[old],DEV_X_DEVEND," "); } if(new>=0) { mvaddstr(DEV_Y_START+dev_line[new],DEV_X_START-3,"->"); mvaddstr(DEV_Y_START+dev_line[new],DEV_X_DEVEND,"<-"); } } /* this'll be the last thing before a refresh, so... */ move(DEV_Y_START+dev_line[new],0); } int mixer_getlevel_stereo(int mixfd,int dev) { int level=0; ioctl(mixfd,MIXER_READ(dev),&level); return(level); } int mixer_getlevel(int mixfd,int dev) { return(mixer_getlevel_stereo(mixfd,dev)&255); } void mixer_setlevel_stereo(int mixfd,int dev,int left,int right) { left+=256*right; ioctl(mixfd,MIXER_WRITE(dev),&left); } void mixer_setlevel(int mixfd,int dev,int level) { mixer_setlevel_stereo(mixfd,dev,level,level); } void mixer_change(int mixfd,int dev,int add) { int level=mixer_getlevel(mixfd,dev)+add; if(level<0) level=0; if(level>100) level=100; mixer_setlevel(mixfd,dev,level); } void mixer_rectoggle(int mixfd,int dev,int *isrecmask) { (*isrecmask)^=(1<100) level=100; memset(buf,'=',51); buf[51]=0; buf[level/2]='|'; for(f=level/2+1;f<51;f++) buf[f]='-'; mvaddstr(DEV_Y_START+dev_line[dev],DEV_X_BAR+1,buf); sprintf(buf,"%3d%%",level); mvaddstr(DEV_Y_START+dev_line[dev],DEV_X_PCNT,buf); } void drawrec(int dev,int on) { mvaddch(DEV_Y_START+dev_line[dev],DEV_X_REC+1,on?'R':' '); } void setupframe(int existmask,int canrecmask,int *firstdevp,int *lastdevp) { int f; int offset=0; mvaddstr(0,36,"rexima"); mvaddstr(DEV_Y_START-1,DEV_X_BAR+1, "min . . . . : . . . . max"); /* we know existmask is non-zero by now (see main()) */ for(f=0;f"); } void usage_help(int existmask) { int f,count; puts( "rexima " REXIMA_VER " - copyright (c) 1996-2003 Russell Marks.\n" "\n" "usage: rexima [-hv] [-d mixer_device_file]\n" "\t\t [device >\n" "\t\t [device ...]]\n" "\n" " -d specify mixer device file to use (ordinarily /dev/mixer).\n" " -h give this usage help.\n" " -v show current mixer settings.\n"); printf( " device a device to set the level of. %s\n",mixerdev); printf("\t\twill allow the levels of these devices to be set:\n\n\t\t"); count=0; for(f=0;f=7) printf("\n\t\t"),count=0; } puts( "\n\n" " level level to set specified device to.\n" " offset amount to change level by (e.g. `-3', `+12').\n" " left,right set (stereo) device's level with independent left/right values.\n" " rec | norec `rec' makes device a recording source, `norec' makes it, well,\n" " not a recording source. :-)\n" "\n" "If invoked without any args (with the exception of `-d'), rexima runs\n" "interactively."); } /* on entry, we know argc>=2 */ void cmdline_main(int argc,char *argv[]) { int mixfd; int existmask,canrecmask,isrecmask,stereomask; int f,tmp,l,r; int found; char *ptr; int done=0,want_usage=0,want_levels=0; do switch(getoptn(argc,argv,"d:hvV")) { case 'd': /* mixer device */ if((mixerdev=malloc(strlen(optnarg)+1))==NULL) die("allocate memory"); strcpy(mixerdev,optnarg); break; case 'h': want_usage=1; break; case 'v': case 'V': /* show levels */ want_levels=1; break; case '?': switch(optnopt) { case 'd': fprintf(stderr,"rexima: " "the `-d' option needs a mixer device to be specified.\n"); break; default: fprintf(stderr,"rexima: option `%c' not recognised.\n",optnopt); } exit(1); case -1: done=1; } while(!done); if(want_usage) { init(&mixfd,&existmask,&canrecmask,&isrecmask,&stereomask); usage_help(existmask); exit(0); } if(want_levels) { init(&mixfd,&existmask,&canrecmask,&isrecmask,&stereomask); for(f=0;f>8)&255); printf("%s\t%3d",dev_names[f],l); if(stereomask&(1<=argc) return; /* otherwise, init - we have one or more pairs to deal with. */ init(&mixfd,&existmask,&canrecmask,&isrecmask,&stereomask); while(optnind1) cmdline_main(argc,argv); /* returns (without init) if we're interactive */ init(&mixfd,&existmask,&canrecmask,&isrecmask,&stereomask); /* later things assume existmask is non-zero, so... */ if(!existmask) fprintf(stderr,"rexima: mixer has no devices!\n"),exit(1); init_term(); setupframe(existmask,canrecmask,&firstdev,&lastdev); cursel=firstdev; for(f=0;f=firstdev && !(existmask&(1<lastdev) cursel=firstdev; break; case 'h': case 'H': case KEY_LEFT: case '-': if(existmask&(1<