/* * $Id: umix_common.c,v 1.6 2003/08/30 15:21:36 sakari Exp $ * * Common functions for Umix. All UIs have to be able to call * these, and errors are handled by the caller. * * Copyright (C) 2002 Sakari Lehtonen * * 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., * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include "umix_common.h" /* internal function prototypes */ static void parseglobal(char *buf); static void parsemixer(char *buf); static int slidevol(float *left, float *right, const char *optarg); /* Parses one line of channel options. * * Format is the following : * * vol|left:right[-|+]p|r * * where 'name' is the name of the channel. 'vol' is either * volume for both channels, or individual left and right values * separated with a ':'. the last char sets the recording * state of the channel to play 'p' or record source 'r'. * * OR * * fromvol,tovol,noofsteps,delaytime p|r * * If you want to raise/decrease the volume with 'noofsteps' as the * number of steps from 'fromvol' to 'tovol', where 'delaytime' is the * delay between two steps. * * TODO: Okay, this starts to suck. Multiple sscanf etc, rewrite perhaps. * */ int parsechanopt(const char *optarg) { volume vb; int recstate; int retval; int change; int slide; size_t n; float left; float right; change = 0; slide = 0; retval = 0; /* skip whitespace */ while (*optarg == ' ' || *optarg == '\t') optarg++; /* check for increment or decrement */ n = strlen(optarg)-1; if (optarg[n] == '+') change = 1; else if (optarg[n] == '-') change = -1; #ifdef UMIX_DEBUG err_msg("parsechanopt: mixer=%d ch=%d optarg=%s change=%d", mixer_get_curr(), chan_get_curr(), optarg, change); #endif /* if the first char is not a number, we might be setting only * record sources */ if (isdigit(optarg[0])) { /* normal volume setting */ if (strchr(optarg, ',') == NULL) { /* if no ':' found, use same value for both */ if (strchr(optarg, ':') == NULL) { left = atof(optarg); right = left; } else { sscanf(optarg, "%f:%f", &left, &right); /* if channel is not stereo, use same * for both left and right */ if (chan_is_stereo() == 0) right = left; } } /* volume sliding */ else { slide = 1; if ((retval = slidevol(&left, &right, optarg)) != 0) return retval; else retval = 0; } } else { /* check for garbage */ recstate = tolower(optarg[0]); if (recstate != 'r' && recstate != 'p') return UMIX_EOPTINVALID; } /* check for setting of record source or play */ n = strcspn(optarg, "rRpP"); recstate = tolower(optarg[n]); if ((recstate == 'r' || recstate == 'p')) { if (chan_is_record()) { if (recstate == 'r') recstate = CHAN_RECSRC; else recstate = CHAN_PLAY; chan_set_rec(recstate); } else retval = UMIX_EOPTNOTREC; /* P or R is the only argument for this channel, return */ if (isdigit(optarg[0]) == 0) return retval; } /* check for overflowing */ if (left > VOL_MAX || left < 0 || right > VOL_MAX || right < 0) return UMIX_EOPTLIMIT; /* add or decrement from the old values ? if not sliding the vol */ if (change != 0 && slide == 0) { /* change is either 1 or -1 */ left = chan_get_lr(CHAN_LEFT) + (change * left); right = chan_get_lr(CHAN_RIGHT) + (change * right); } lr_to_vb(left, right, &vb.volume, &vb.balance); chan_set_vb(vb); #ifdef UMIX_DEBUG err_msg("parsechanopt: left=%d right=%d parsedleft=%f parsedright=%f", chan_get_lr(CHAN_LEFT), chan_get_lr(CHAN_RIGHT), left, right); #endif return retval; } /* increment the volume in steps * the format is this : fromvol,destvol,steps,delay * left and right are handled as pointers because we set the final volume * in the parsechanopt() function. */ static int slidevol(float *left, float *right, const char *optarg) { int i; float rinc; float linc; float ldir; float rdir; float rdiff; float ldiff; float fleft; float fright; volume fvb; int steps; long int delay; steps = 20; delay = 20; /* no different left:right values specified, use same for both */ if (strchr(optarg, ':') == NULL) { sscanf(optarg, "%f,%f,%d,%ld", &fleft, left, &steps, &delay); fright = fleft; *right = *left; } else { sscanf(optarg, "%f:%f,%f:%f,%d,%ld", &fleft, &fright, left, right, &steps, &delay); } /* we do not support steps going over the wanted * left or right, because then increment would be * zero */ steps = CLAMP(steps, 0, VOL_MAX); ldiff = ABS(*left - fleft); rdiff = ABS(*right - fright); if (steps > ldiff && steps > rdiff) steps = MAX(ldiff, rdiff); /* check limits */ if( (ldiff == 0 && rdiff == 0) || (ldiff > VOL_MAX && rdiff > VOL_MAX) ||steps == 0 ) return UMIX_EOPTLIMIT; /* which way to slide with left */ linc = *left - fleft; if (linc < 0) { ldir = -1; linc = ABS(linc); } else ldir = 1; linc /= steps; /* which way to slide with right */ rinc = *right - fright; if (rinc < 0) { rdir = -1; rinc = ABS(rinc); } else rdir = 1; rinc /= steps; /* milliseconds, maximum is 5000 (5 seconds) */ delay = CLAMP(delay, 0, 5000); delay *= 1000; /* correct direction */ linc *= ldir; rinc *= rdir; #ifdef UMIX_DEBUG err_msg("slidevol: ldiff=%3f rdiff=%3f steps=%d delay=%d", ldiff, rdiff, steps, delay); #endif for (i=0; i