/* * xmmix - Motif(tm) Audio Mixer * * Copyright (C) 1994-1996 Ti Kan * E-mail: ti@amb.org * * 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. * */ #ifndef LINT static char *_mixer_c_ident_ = "@(#)mixer.c 3.5 96/11/07"; #endif #include "appenv.h" #include "patchlevel.h" #include "widget.h" #include "mixer.h" #if defined(SOUND_MIXER_LOUD) && defined(SOUND_MIXER_MUTE) && \ defined(SOUND_MIXER_ENHANCE) #if SOUND_MIXER_LOUD == SOUND_MIXER_MUTE /* HACK for new sound drivers */ #undef SOUND_MIXER_LOUD #undef SOUND_MIXER_MUTE #undef SOUND_MIXER_ENHANCE #endif #endif /* Ioctl data direction flags */ #define IOC_DATA_NONE 0 #define IOC_DATA_IN 1 #define IOC_DATA_OUT 2 extern appdata_t app_data; extern widgets_t widgets; extern bool_t exit_flag; extern FILE *errfp; int maxdevs; /* Maximum number of devices */ STATIC int dev_fd = -1, /* Mixer device file descriptor */ fsmode; /* File selection mode */ STATIC char drv_ver[128]; /* Sound driver version */ STATIC ctlinfo_t ctlinfo = { /* Current state of all controls */ 0, 0, 0, 0, 0, 0, FALSE, FALSE, { { 0, 0, TRUE }, /* 0 */ { 50, 50, TRUE }, /* 1 */ { 50, 50, TRUE }, /* 2 */ { 0, 0, TRUE }, /* 3 */ { 0, 0, TRUE }, /* 4 */ { 0, 0, TRUE }, /* 5 */ { 0, 0, TRUE }, /* 6 */ { 0, 0, TRUE }, /* 7 */ { 0, 0, TRUE }, /* 8 */ { 0, 0, TRUE }, /* 9 */ { 0, 0, TRUE }, /* 10 */ { 0, 0, TRUE }, /* 11 */ { 0, 0, TRUE }, /* 12 */ { 0, 0, TRUE }, /* 13 */ { 0, 0, TRUE }, /* 14 */ { 0, 0, TRUE }, /* 15 */ { 0, 0, TRUE }, /* 16 */ { 0, 0, TRUE }, /* 17 */ { 0, 0, TRUE }, /* 18 */ { 0, 0, TRUE }, /* 19 */ { 0, 0, TRUE }, /* 20 */ { 0, 0, TRUE }, /* 21 */ { 0, 0, TRUE }, /* 22 */ { 0, 0, TRUE }, /* 23 */ { 0, 0, TRUE }, /* 24 */ { 0, 0, TRUE }, /* 25 */ { 0, 0, TRUE }, /* 26 */ { 0, 0, TRUE }, /* 27 */ { 0, 0, TRUE }, /* 28 */ { 0, 0, TRUE }, /* 29 */ { 0, 0, TRUE }, /* 30 */ { 0, 0, TRUE } /* 31 */ } }; STATIC ctlinfo_t ctlsav; /* Saved state of all controls */ /*********************** * internal routines * ***********************/ /* * do_ioctl * Perform ioctl command. If file is not yet open or if we * are in demo mode, just quietly return. If an error * status is returned, print an appropriate error message * and exit. * * Args: * cmd - The ioctl command * arg - The ioctl argument * name - The ioctl command string * dir - Data direction * IOC_DATA_IN * IOC_DATA_OUT * IOC_DATA_NONE * * Return: * Nothing */ STATIC void do_ioctl(int cmd, int *arg, char *name, int dir) { int ret; char errmsg[STR_BUF_SZ]; if (dev_fd < 0 || app_data.demo) return; sprintf(errmsg, "\n%s ioctl failed", name); if (app_data.debug) { fprintf(errfp, "%s: cmd=0x%x (%s) ", PROGNAME, cmd, name); if (dir == IOC_DATA_OUT) fprintf(errfp, "*arg=0x%x", *arg); } if ((ret = ioctl(dev_fd, cmd, arg)) < 0) { perror(errmsg); exit_flag = TRUE; } if (app_data.debug) { if (ret == 0 && dir == IOC_DATA_IN) fprintf(errfp, "*arg=0x%x", *arg); fprintf(errfp, "\n"); } } /* * mx_queryhw * Query mixer settings and update current ctlinfo state. * * Args: * m - Pointer to the main widgets structure * * Return: * Nothing */ STATIC void mx_queryhw(widgets_t *m) { int i, level, curmask; char iocstr[STR_BUF_SZ]; static bool_t first = TRUE; if (app_data.demo) { if (first) { /* Save start-up settings */ first = FALSE; ctlsav = ctlinfo; /* Structure copy */ } return; } /* Read record source */ do_ioctl( SOUND_MIXER_READ_RECSRC, &ctlinfo.recsrc, "SOUND_MIXER_READ_RECSRC", IOC_DATA_IN ); /* Read control settings */ for (i = 0; i < maxdevs; i++) { curmask = (1 << i); if (m->sl[i].supp) { sprintf(iocstr, "MIXER_READ[%d]:%s", i, m->sl[i].name); do_ioctl(MIXER_READ(i), &level, iocstr, IOC_DATA_IN); ctlinfo.slinfo[i].left = (level & 0xff); ctlinfo.slinfo[i].right = ((level >> 8) & 0xff); /* Sanity check */ if (ctlinfo.slinfo[i].left > 100) ctlinfo.slinfo[i].left = 100; else if (ctlinfo.slinfo[i].left < 0) ctlinfo.slinfo[i].left = 0; if (ctlinfo.slinfo[i].right > 100) ctlinfo.slinfo[i].right = 100; else if (ctlinfo.slinfo[i].right < 0) ctlinfo.slinfo[i].right = 0; if ((curmask & ctlinfo.stereodevs) == 0) { ctlinfo.slinfo[i].left = ctlinfo.slinfo[i].right = (ctlinfo.slinfo[i].left + ctlinfo.slinfo[i].right) / 2; ctlinfo.slinfo[i].locked = TRUE; } if (ctlinfo.slinfo[i].left != ctlinfo.slinfo[i].right) ctlinfo.slinfo[i].locked = FALSE; } } #ifdef SOUND_ONOFF_MAX /* Additional toggle capabilitites */ for (i = SOUND_ONOFF_MIN; i <= SOUND_ONOFF_MAX; i++) { curmask = (1 << i); if (curmask & ctlinfo.devmask) { sprintf(iocstr, "MIXER_READ[%d]:%s", i, m->sl[i].name); do_ioctl(MIXER_READ(i), &level, iocstr, IOC_DATA_IN); switch (i) { #ifdef SOUND_MIXER_MUTE case SOUND_MIXER_MUTE: ctlinfo.mute = (level != 0); break; #endif /* SOUND_MIXER_MUTE */ #ifdef SOUND_MIXER_LOUD case SOUND_MIXER_LOUD: ctlinfo.loudness = (level != 0); break; #endif /* SOUND_MIXER_LOUD */ #ifdef SOUND_MIXER_ENHANCE case SOUND_MIXER_ENHANCE: /* Hack: The enhance values are hard wired. */ switch (level) { case 80: ctlinfo.enhance = 3; break; case 60: ctlinfo.enhance = 2; break; case 40: ctlinfo.enhance = 1; break; case 0: default: ctlinfo.enhance = 0; break; } break; #endif /* SOUND_MIXER_ENHANCE */ default: /* Unsupported feature: ignore */ break; } } } #endif /* SOUND_ONOFF_MAX */ if (first) { /* Save start-up settings */ first = FALSE; ctlsav = ctlinfo; /* Structure copy */ } } /* * mx_sethw * Set all sound hardware settings to the current ctlinfo state. * * Args: * m - Pointer to the main widgets structure * * Return: * Nothing */ STATIC void mx_sethw(widgets_t *m) { int i; XmScaleCallbackStruct s; XmToggleButtonCallbackStruct t; XmPushButtonCallbackStruct p; /* Fake callbacks */ s.reason = XmCR_VALUE_CHANGED; t.reason = XmCR_VALUE_CHANGED; p.reason = XmCR_ACTIVATE; for (i = 0; i < maxdevs; i++) { if (!m->sl[i].supp) continue; /* Left slider */ s.value = ctlinfo.slinfo[i].left; mx_slider_l(m->sl[i].widget_l, (XtPointer) i, (XtPointer) &s); /* Right slider */ s.value = ctlinfo.slinfo[i].right; mx_slider_r(m->sl[i].widget_r, (XtPointer) i, (XtPointer) &s); /* Lock button */ t.set = (Boolean) ctlinfo.slinfo[i].locked; mx_lock_btn(m->sl[i].widget_lock_btn, (XtPointer) i, (XtPointer) &t); /* Rec button */ if (m->sl[i].recsupp) { #ifdef SOUND_CAP_EXCL_INPUT if ((ctlinfo.recsrc & (1 << i)) || ctlinfo.caps != SOUND_CAP_EXCL_INPUT) #endif { if (ctlinfo.recsrc & (1 << i)) t.set = True; else t.set = False; mx_rec_btn(m->sl[i].widget_rec_btn, (XtPointer) i, (XtPointer) &t); } } } /* Mute button */ if (m->mute_supp) { t.set = (Boolean) ctlinfo.mute; mx_mute_btn(m->mute_btn, NULL, (XtPointer) &t); } /* Loudness button */ if (m->loud_supp) { t.set = (Boolean) ctlinfo.loudness; mx_loud_btn(m->loud_btn, NULL, (XtPointer) &t); } /* Enhance buttons */ if (m->enh_supp) { mx_enhance_btn(m->enh_btn[ctlinfo.enhance], (XtPointer) ctlinfo.enhance, (XtPointer) &p); } } /* * mx_updctl * Update all control positions on screen to the current ctlinfo * state. * * Args: * m - Pointer to the main widgets structure * * Return: * Nothing */ STATIC void mx_updctl(widgets_t *m) { int i; for (i = 0; i < maxdevs; i++) { if (!m->sl[i].supp) continue; /* Left and right sliders */ XmScaleSetValue( m->sl[i].widget_l, ctlinfo.slinfo[i].left ); XmScaleSetValue( m->sl[i].widget_r, ctlinfo.slinfo[i].right ); /* Lock button */ XmToggleButtonSetState( m->sl[i].widget_lock_btn, ctlinfo.slinfo[i].locked, False ); /* Rec button */ if (m->sl[i].recsupp) { if (ctlinfo.recsrc & (1 << i)) { XmToggleButtonSetState( m->sl[i].widget_rec_btn, True, False ); } else { XmToggleButtonSetState( m->sl[i].widget_rec_btn, False, False ); } } } /* Mute button */ if (m->mute_supp) XmToggleButtonSetState(m->mute_btn, ctlinfo.mute, False); /* Loudness button */ if (m->loud_supp) XmToggleButtonSetState(m->loud_btn, ctlinfo.loudness, False); /* Stereo enhance menu */ if (m->enh_supp) { XtVaSetValues(m->enh_opt, XmNmenuHistory, m->enh_btn[ctlinfo.enhance], NULL ); } } /* * mx_readfile * Read mixer settings from file and update control settings. * * Args: * path - File path string * * Return: * TRUE on success, FALSE on failure */ STATIC bool_t mx_readfile(char *path) { FILE *fp; int i, curmask, val1, val2, val3, val4; char *p, buf[STR_BUF_SZ]; if (app_data.debug) fprintf(errfp, "Reading mixer file: %s\n", path); /* Open file for reading */ if ((fp = fopen(path, "r")) == NULL) return FALSE; /* Read first line of mixer settings file */ if (fgets(buf, sizeof(buf), fp) == NULL) { fclose(fp); return FALSE; } /* Mixer settings file signature check */ if (strncmp(buf, "# xmmix ", 8) != 0) { fclose(fp); return FALSE; } /* Read the rest of the mixer settings file */ while (fgets(buf, sizeof(buf), fp) != NULL) { if (buf[0] == '#') /* Comment line */ continue; if ((p = strchr(buf, '=')) == NULL) /* Invalid line */ continue; *p = '\0'; if (sscanf(p+1, "%d,%d,%d,%d\n", &val1, &val2, &val3, &val4) == 4) { for (i = 0; i < maxdevs; i++) { if (strcmp(buf, widgets.sl[i].name) == 0) break; } if (i >= maxdevs) /* Unrecognized keyword */ continue; curmask = (1 << i); if (!(ctlinfo.devmask & curmask)) /* Device not supported */ continue; ctlinfo.slinfo[i].left = val1; ctlinfo.slinfo[i].right = val2; if (val1 == val2) ctlinfo.slinfo[i].locked = (bool_t) (val3 > 0); else ctlinfo.slinfo[i].locked = FALSE; if (val4 > 0 && widgets.sl[i].recsupp && (ctlinfo.recmask & curmask)) ctlinfo.recsrc |= (1 << i); else ctlinfo.recsrc &= ~(1 << i); } #ifdef SOUND_ONOFF_MAX else if (sscanf(p+1, "%d\n", &val1) == 1) { #ifdef SOUND_MIXER_MUTE if (strcmp(buf, "mute") == 0 && ctlinfo.devmask & (1 << SOUND_MIXER_MUTE)) { ctlinfo.mute = (bool_t) (val1 > 0); } #endif /* SOUND_MIXER_MUTE */ #ifdef SOUND_MIXER_ENHANCE if (strcmp(buf, "enhance") == 0 && ctlinfo.devmask & (1 << SOUND_MIXER_ENHANCE)) { ctlinfo.enhance = val1; } #endif /* SOUND_MIXER_ENHANCE */ #ifdef SOUND_MIXER_LOUD if (strcmp(buf, "loudness") == 0 && ctlinfo.devmask & (1 << SOUND_MIXER_LOUD)) { ctlinfo.loudness = (bool_t) (val1 > 0); } #endif /* SOUND_MIXER_LOUD */ } #endif /* SOUND_ONOFF_MAX */ } fclose(fp); /* Update controls to match current status */ mx_updctl(&widgets); /* Set all controls */ mx_sethw(&widgets); return TRUE; } /* * mx_writefile * Write current mixer settings to file. * * Args: * path - File path string * * Return: * TRUE on success, FALSE on failure */ STATIC bool_t mx_writefile(char *path) { FILE *fp; int i; if (app_data.debug) fprintf(errfp, "Writing mixer file: %s\n", path); /* Open file for writing */ if ((fp = fopen(path, "w")) == NULL) return FALSE; /* Write first two lines of mixer settings file */ fprintf(fp, "# xmmix %s Mixer Settings File\n", VERSION); fprintf(fp, "# Copyright (C) 1994-1996 Ti Kan\n#\n"); /* Write all control settings */ for (i = 0; i < MAXDEVS; i++) { if (widgets.sl[i].name == NULL) break; fprintf(fp, "%s=%d,%d,%d,%d\n", widgets.sl[i].name, ctlinfo.slinfo[i].left, ctlinfo.slinfo[i].right, ctlinfo.slinfo[i].locked, (int) ((ctlinfo.recsrc & (1 << i)) > 0)); } #ifdef SOUND_ONOFF_MAX fprintf(fp, "%s=%d\n", "mute", (int) ctlinfo.mute); fprintf(fp, "%s=%d\n", "enhance", ctlinfo.enhance); fprintf(fp, "%s=%d\n", "loudness", (int) ctlinfo.loudness); #endif return (fclose(fp) == 0); } /* * mx_warning * Pop up a warning message dialog box. * * Args: * msg - The message string to display * * Return: * Nothing */ STATIC void mx_warning(char *msg) { XmString xs; xs = XmStringCreateLtoR(msg, XmSTRING_DEFAULT_CHARSET); XtVaSetValues(widgets.warning, XmNmessageString, xs, NULL); XmStringFree(xs); if (!XtIsManaged(widgets.warning)) XtManageChild(widgets.warning); } /*********************** * public routines * ***********************/ /* * mx_init_drv * Sound driver version-specific initialization routine. * Query sound driver version and set appropriate capabilities. * Note: this routine will likely require modification if * future sound driver versions add new mixer features. * * Args: * None * * Return: * Nothing */ void mx_init_drv(void) { FILE *fp; char *p, tmpbuf[128]; if (app_data.debug) { fprintf(errfp, "XMMIX v%s%s PL%d DEBUG MODE\n\n", VERSION, VERSION_EXT, PATCHLEVEL); if (app_data.demo) fprintf(errfp, "DEMO MODE!!!\n"); fprintf(errfp, "Compiled with soundcard.h version %d\n", SOUND_VERSION); fprintf(errfp, "device=%s\n", app_data.device); fprintf(errfp, "helpPath=%s\n", app_data.helppath); fprintf(errfp, "autoLoadOnStartUp=%s\n", app_data.autoload == NULL ? "" : app_data.autoload); fprintf(errfp, "resetOnExit=%s\n", app_data.exitreset ? "True" : "False"); } /* Set maximum number of devices supported */ maxdevs = MAXDEVS_V3; strcpy(drv_ver, "unknown"); /* Query sound driver version */ if ((fp = fopen("/dev/sndstat", "r")) == NULL) { if (!app_data.demo) { fprintf(errfp, "%s: %s: %s\n", PROGNAME, "Cannot open /dev/sndstat", "assuming pre-3.0 sound driver."); maxdevs = MAXDEVS_V2; } return; } if (fgets(tmpbuf, sizeof(tmpbuf), fp) == NULL) { if (!app_data.demo) { fprintf(errfp, "%s: %s: %s\n", PROGNAME, "Cannot read /dev/sndstat", "assuming pre-3.0 sound driver."); maxdevs = MAXDEVS_V2; } fclose(fp); return; } fclose(fp); /* Eat newline */ tmpbuf[strlen(tmpbuf) - 1] = '\0'; p = tmpbuf; if (strncmp(p, "Sound Driver", 12) == 0) { /* Old style output: * Skip forward to the first colon character */ for (; *p != '\0' && *p != ':'; p++) ; if (*p != ':') { if (!app_data.demo) { fprintf(errfp, "%s: %s: %s\n", PROGNAME, "Cannot parse info from /dev/sndstat", "assuming pre-3.0 sound driver."); maxdevs = MAXDEVS_V2; } strcpy(drv_ver, tmpbuf); return; } /* Skip blanks */ p++; while (*p == ' ' || *p == '\t') p++; } strcpy(drv_ver, p); if (!app_data.demo && *p >= '0' && *p < '3') /* Running on old sound driver: set max devices accordingly */ maxdevs = MAXDEVS_V2; if (app_data.debug) { fprintf(errfp, "Sound driver [%s]\n", drv_ver); fprintf(errfp, "Running with maxdevs=%d\n\n", maxdevs); } } /* * mx_init_hw * Pre-realize mixer initialization routine. * Query sound board mixer feature capabilities and current * hardware settings. * * Args: * m - Pointer to the main widgets structure * * Return: * Nothing */ void mx_init_hw(widgets_t *m) { int i, level, curmask; char errmsg[STR_BUF_SZ], iocstr[STR_BUF_SZ]; if (app_data.demo) { /* Fake all capabilities */ for (i = 0; i < maxdevs; i++) { curmask = (1 << i); ctlinfo.devmask |= curmask; ctlinfo.recmask |= curmask; ctlinfo.stereodevs |= curmask; m->sl[i].supp = TRUE; if (m->sl[i].type == CTL_INPUT) m->sl[i].recsupp = TRUE; else m->sl[i].recsupp = FALSE; } #ifdef SOUND_ONOFF_MAX for (i = SOUND_ONOFF_MIN; i <= SOUND_ONOFF_MAX; i++) ctlinfo.devmask |= (1 << i); m->mute_supp = TRUE; m->loud_supp = TRUE; m->enh_supp = TRUE; #endif /* Save start-up settings */ ctlsav = ctlinfo; /* Structure copy */ return; } /* Open device */ sprintf(errmsg, "%s: Cannot open device %s", PROGNAME, app_data.device); if ((dev_fd = open(app_data.device, O_RDONLY)) < 0) { perror(errmsg); exit_flag = TRUE; } /* Read device mask */ do_ioctl( SOUND_MIXER_READ_DEVMASK, &ctlinfo.devmask, "SOUND_MIXER_READ_DEVMASK", IOC_DATA_IN ); /* Read record mask */ do_ioctl( SOUND_MIXER_READ_RECMASK, &ctlinfo.recmask, "SOUND_MIXER_READ_RECMASK", IOC_DATA_IN ); /* Read stereo devices */ do_ioctl( SOUND_MIXER_READ_STEREODEVS, &ctlinfo.stereodevs, "SOUND_MIXER_READ_STEREODEVS", IOC_DATA_IN ); #ifdef SOUND_MIXER_CAPS /* Check mixer capability */ do_ioctl( SOUND_MIXER_READ_CAPS, &ctlinfo.caps, "SOUND_MIXER_READ_CAPS", IOC_DATA_IN ); #endif /* Set flags */ for (i = 0; i < maxdevs; i++) { curmask = (1 << i); if ((curmask & ctlinfo.devmask) == 0) m->sl[i].supp = FALSE; else m->sl[i].supp = TRUE; if (m->sl[i].type == CTL_INPUT && (curmask & ctlinfo.recmask)) m->sl[i].recsupp = TRUE; else m->sl[i].recsupp = FALSE; } #ifdef SOUND_ONOFF_MAX /* Additional toggle capabilitites */ for (i = SOUND_ONOFF_MIN; i <= SOUND_ONOFF_MAX; i++) { curmask = (1 << i); switch (i) { #ifdef SOUND_MIXER_MUTE case SOUND_MIXER_MUTE: if (curmask & ctlinfo.devmask) m->mute_supp = TRUE; else m->mute_supp = FALSE; break; #endif /* SOUND_MIXER_MUTE */ #ifdef SOUND_MIXER_LOUD case SOUND_MIXER_LOUD: if (curmask & ctlinfo.devmask) m->loud_supp = TRUE; else m->loud_supp = FALSE; break; #endif /* SOUND_MIXER_LOUD */ #ifdef SOUND_MIXER_ENHANCE case SOUND_MIXER_ENHANCE: if (curmask & ctlinfo.devmask) m->enh_supp = TRUE; else m->enh_supp = FALSE; break; #endif /* SOUND_MIXER_ENHANCE */ default: /* Unsupported feature: ignore */ break; } } #else m->mute_supp = FALSE; m->loud_supp = FALSE; m->enh_supp = FALSE; #endif /* SOUND_ONOFF_MAX */ } /* * mx_start * Post-realize mixer initialization routine * * Args: * m - Pointer to the main widgets structure * * Return: * Nothing */ void mx_start(widgets_t *m) { char msg[256]; if (app_data.autoload != NULL && app_data.autoload[0] != '\0' && strcmp(app_data.autoload, "/dev/null") != 0) { if (mx_readfile(app_data.autoload)) { /* Successful auto-load */ return; } else { sprintf(msg, "Autoload: Cannot read mixer settings file:\n%s", app_data.autoload); mx_warning(msg); } } /* Update screen controls to match current status */ mx_updctl(m); } /**************** vv Callback routines vv ****************/ /* * mx_slider_l * Left slider callback routine */ /*ARGSUSED*/ void mx_slider_l(Widget w, XtPointer client_data, XtPointer call_data) { int level, i = (int)(void *) client_data; XmScaleCallbackStruct *p = (XmScaleCallbackStruct *)(void *) call_data; char iocstr[STR_BUF_SZ]; ctlinfo.slinfo[i].left = p->value; if (ctlinfo.slinfo[i].locked) { ctlinfo.slinfo[i].right = p->value; XmScaleSetValue(widgets.sl[i].widget_r, p->value); } level = (ctlinfo.slinfo[i].right << 8) | ctlinfo.slinfo[i].left; sprintf(iocstr, "MIXER_WRITE[%d]:%s", i, widgets.sl[i].name); do_ioctl(MIXER_WRITE(i), &level, iocstr, IOC_DATA_OUT); } /* * mx_slider_r * Right slider callback routine */ /*ARGSUSED*/ void mx_slider_r(Widget w, XtPointer client_data, XtPointer call_data) { int level, i = (int)(void *) client_data; XmScaleCallbackStruct *p = (XmScaleCallbackStruct *)(void *) call_data; char iocstr[STR_BUF_SZ]; ctlinfo.slinfo[i].right = p->value; if (ctlinfo.slinfo[i].locked) { ctlinfo.slinfo[i].left = p->value; XmScaleSetValue(widgets.sl[i].widget_l, p->value); } level = (ctlinfo.slinfo[i].right << 8) | ctlinfo.slinfo[i].left; sprintf(iocstr, "MIXER_WRITE[%d]:%s", i, widgets.sl[i].name); do_ioctl(MIXER_WRITE(i), &level, iocstr, IOC_DATA_OUT); } /* * mx_lock_btn * Slider lock button callback routine */ void mx_lock_btn(Widget w, XtPointer client_data, XtPointer call_data) { int level, i = (int)(void *) client_data; XmToggleButtonCallbackStruct *p = (XmToggleButtonCallbackStruct *)(void *) call_data; char iocstr[STR_BUF_SZ]; if (ctlinfo.slinfo[i].locked == p->set) /* No change */ return; if (p->set) { ctlinfo.slinfo[i].locked = TRUE; ctlinfo.slinfo[i].left = ctlinfo.slinfo[i].right = (ctlinfo.slinfo[i].left + ctlinfo.slinfo[i].right) / 2; XmScaleSetValue( widgets.sl[i].widget_l, ctlinfo.slinfo[i].left ); XmScaleSetValue( widgets.sl[i].widget_r, ctlinfo.slinfo[i].right ); level = (ctlinfo.slinfo[i].right << 8) | ctlinfo.slinfo[i].left; sprintf(iocstr, "MIXER_WRITE[%d]:%s", i, widgets.sl[i].name); do_ioctl(MIXER_WRITE(i), &level, iocstr, IOC_DATA_OUT); } else { if ((1 << i) & ctlinfo.stereodevs) ctlinfo.slinfo[i].locked = FALSE; else { XmToggleButtonSetState(w, (Boolean) !p->set, False); XBell(XtDisplay(w), 50); } } } /* * mx_rec_btn * Record source select button callback routine */ void mx_rec_btn(Widget w, XtPointer client_data, XtPointer call_data) { int i = (int)(void *) client_data, j, curmask; XmToggleButtonCallbackStruct *p = (XmToggleButtonCallbackStruct *)(void *) call_data; #ifdef SOUND_CAP_EXCL_INPUT if (ctlinfo.caps == SOUND_CAP_EXCL_INPUT) { if (!p->set) { XmToggleButtonSetState(w, True, False); return; } for (j = 0; j < maxdevs; j++) { if (!widgets.sl[j].supp || i == j) continue; curmask = (1 << j); if ((curmask & ctlinfo.recmask) && (curmask & ctlinfo.recsrc)) { XmToggleButtonSetState( widgets.sl[j].widget_rec_btn, False, False ); } } ctlinfo.recsrc = 0; } #endif if (p->set) ctlinfo.recsrc |= (1 << i); else ctlinfo.recsrc &= ~(1 << i); do_ioctl( SOUND_MIXER_WRITE_RECSRC, &ctlinfo.recsrc, "SOUND_MIXER_WRITE_RECSRC", IOC_DATA_OUT ); } /* * mx_flat_btn * Bass/Treble Flat button callback routine. */ /*ARGSUSED*/ void mx_flat_btn(Widget w, XtPointer client_data, XtPointer call_data) { int level; ctlinfo.slinfo[SOUND_MIXER_BASS].left = 50; ctlinfo.slinfo[SOUND_MIXER_BASS].right = 50; ctlinfo.slinfo[SOUND_MIXER_TREBLE].left = 50; ctlinfo.slinfo[SOUND_MIXER_TREBLE].right = 50; XmScaleSetValue(widgets.sl[SOUND_MIXER_BASS].widget_l, 50); XmScaleSetValue(widgets.sl[SOUND_MIXER_BASS].widget_r, 50); XmScaleSetValue(widgets.sl[SOUND_MIXER_TREBLE].widget_l, 50); XmScaleSetValue(widgets.sl[SOUND_MIXER_TREBLE].widget_r, 50); level = (50 << 8) | 50; do_ioctl( SOUND_MIXER_WRITE_BASS, &level, "SOUND_MIXER_WRITE_BASS", IOC_DATA_OUT ); do_ioctl( SOUND_MIXER_WRITE_TREBLE, &level, "SOUND_MIXER_WRITE_TREBLE", IOC_DATA_OUT ); } /* * mx_mute_btn * Mute button callback routine. */ /*ARGSUSED*/ void mx_mute_btn(Widget w, XtPointer client_data, XtPointer call_data) { int level; XmToggleButtonCallbackStruct *p = (XmToggleButtonCallbackStruct *)(void *) call_data; if (ctlinfo.mute == p->set) /* No change */ return; ctlinfo.mute = p->set; level = (int) p->set; #ifdef SOUND_MIXER_MUTE do_ioctl( SOUND_MIXER_WRITE_MUTE, &level, "SOUND_MIXER_WRITE_MUTE", IOC_DATA_OUT ); #endif } /* * mx_loud_btn * Loudness button callback routine. */ /*ARGSUSED*/ void mx_loud_btn(Widget w, XtPointer client_data, XtPointer call_data) { int level; XmToggleButtonCallbackStruct *p = (XmToggleButtonCallbackStruct *)(void *) call_data; if (ctlinfo.loudness == p->set) /* No change */ return; ctlinfo.loudness = p->set; level = (int) p->set; #ifdef SOUND_MIXER_LOUD do_ioctl( SOUND_MIXER_WRITE_LOUD, &level, "SOUND_MIXER_WRITE_LOUD", IOC_DATA_OUT ); #endif } /* * mx_enhance_btn * Stereo enhance button callback routine. */ /*ARGSUSED*/ void mx_enhance_btn(Widget w, XtPointer client_data, XtPointer call_data) { int level, i = (int)(void *) client_data; if (ctlinfo.enhance == i) /* No change */ return; ctlinfo.enhance = i; /* Hack: The enhance values are hard wired */ switch (i) { case 0: level = 0; break; case 1: level = 40; break; case 2: level = 60; break; case 3: level = 80; break; default: return; } #ifdef SOUND_MIXER_ENHANCE do_ioctl( SOUND_MIXER_WRITE_ENHANCE, &level, "SOUND_MIXER_WRITE_ENHANCE", IOC_DATA_OUT ); #endif } /* * mx_load * Load button callback routine. */ /*ARGSUSED*/ void mx_load(Widget w, XtPointer client_data, XtPointer call_data) { XmString xs; /* Pop up file selection box window */ if (!XtIsManaged(widgets.fsform)) { fsmode = FS_LOAD; xs = XmStringCreateSimple("Load Mixer Settings"); XtVaSetValues(widgets.fsform, XmNdialogTitle, xs, NULL); XtManageChild(widgets.fsform); XmStringFree(xs); } } /* * mx_save * Save button callback routine. */ /*ARGSUSED*/ void mx_save(Widget w, XtPointer client_data, XtPointer call_data) { XmString xs; /* Pop up file selection box window */ if (!XtIsManaged(widgets.fsform)) { fsmode = FS_SAVE; xs = XmStringCreateSimple("Save Mixer Settings"); XtVaSetValues(widgets.fsform, XmNdialogTitle, xs, NULL); XtManageChild(widgets.fsform); XmStringFree(xs); } } /* * mx_exit * Exit button callback routine. */ /*ARGSUSED*/ void mx_exit(Widget w, XtPointer client_data, XtPointer call_data) { if (app_data.exitreset) mx_reset(w, client_data, call_data); exit_flag = TRUE; } /* * mx_reset * Reset button callback routine. */ /*ARGSUSED*/ void mx_reset(Widget w, XtPointer client_data, XtPointer call_data) { if (app_data.debug) fprintf(errfp, "Resetting mixer\n"); /* Restore start-up settings */ ctlinfo = ctlsav; /* Structure copy */ /* Update controls to match current status */ mx_updctl(&widgets); /* Set all controls */ mx_sethw(&widgets); } /* * mx_manpg * Man Page button callback routine. */ /*ARGSUSED*/ void mx_manpg(Widget w, XtPointer client_data, XtPointer call_data) { FILE *fp; char *helptext = NULL, buf[STR_BUF_SZ * 2]; if (XtIsManaged(widgets.helpform)) return; if ((fp = fopen(app_data.helppath, "r")) == NULL) { /* Can't read help file on this widget */ sprintf(buf, "Cannot open help file: %s", app_data.helppath); XmTextSetString(widgets.helptxt, buf); XtManageChild(widgets.helpform); return; } while (fgets(buf, sizeof(buf), fp) != NULL) { if (buf[0] == '#') /* Comment */ continue; if (helptext == NULL) { helptext = (char *) XtMalloc(strlen(buf) + 1); if (helptext != NULL) *helptext = '\0'; } else { helptext = (char *) XtRealloc( helptext, strlen(helptext) + strlen(buf) + 1 ); } if (helptext == NULL) { fprintf(errfp, "%s: Out of memory\n", PROGNAME); exit_flag = TRUE; } strcat(helptext, buf); } fclose(fp); XmTextSetString(widgets.helptxt, helptext); XtFree(helptext); XtManageChild(widgets.helpform); } /* * mx_about * About button callback routine. */ /*ARGSUSED*/ void mx_about(Widget w, XtPointer client_data, XtPointer call_data) { char txt[512]; XmString xs, xs_progname, xs_desc, xs_info, xs_tmp; if (XtIsManaged(widgets.about)) return; xs_progname = XmStringCreateLtoR(PROGNAME, CHSET1); sprintf(txt, " v%s%s PL%d\n%s\n%s\n%s\n\n", VERSION, VERSION_EXT, PATCHLEVEL, "Motif(tm) Audio Mixer", "Copyright (C) 1994-1996 Ti Kan", "E-mail: ti@amb.org"); xs_desc = XmStringCreateLtoR(txt, CHSET2); sprintf(txt, "%s %d\n\nDevice: %s\nDriver: %s\n\n%s\n%s\n", "Compiled with Unix Sound System soundcard.h version", SOUND_VERSION, app_data.demo ? "DEMO MODE - does not operate sound hardware" : app_data.device, drv_ver, "This is free software and comes with no warranty.", "See the GNU General Public License for details."); xs_info = XmStringCreateLtoR(txt, CHSET3); /* Set the dialog box message */ xs_tmp = XmStringConcat(xs_progname, xs_desc); xs = XmStringConcat(xs_tmp, xs_info); XtVaSetValues(widgets.about, XmNmessageString, xs, NULL); XmStringFree(xs_progname); XmStringFree(xs_desc); XmStringFree(xs_info); XmStringFree(xs_tmp); XmStringFree(xs); /* Pop up the about dialog box */ XtManageChild(widgets.about); } /* * mx_fsok_btn * File selection box OK button callback routine. */ /*ARGSUSED*/ void mx_fsok_btn(Widget w, XtPointer client_data, XtPointer call_data) { XmFileSelectionBoxCallbackStruct *p = (XmFileSelectionBoxCallbackStruct *)(void *) call_data; struct stat stbuf; char *file, msg[256]; if (!XmStringGetLtoR(p->value, XmSTRING_DEFAULT_CHARSET, &file) || file == NULL) { XBell(XtDisplay(w), 50); return; } if (stat(file, &stbuf) < 0) { if (errno != ENOENT) { sprintf(msg, "Invalid mixer settings file:\n%s", file); mx_warning(msg); return; } } else { switch (stbuf.st_mode & S_IFMT) { case S_IFREG: break; default: sprintf(msg, "Invalid mixer settings file:\n%s", file); mx_warning(msg); return; } } switch (fsmode) { case FS_LOAD: if (!mx_readfile(file)) { sprintf(msg, "Cannot read mixer settings file:\n%s", file); mx_warning(msg); return; } break; case FS_SAVE: if (!mx_writefile(file)) { sprintf(msg, "Cannot write mixer settings file:\n%s", file); mx_warning(msg); return; } break; default: XBell(XtDisplay(w), 50); return; } /* Pop down file selection box window */ if (XtIsManaged(widgets.fsform)) XtUnmanageChild(widgets.fsform); } /* * mx_fscancel_btn * File selection box Cancel button callback routine. */ /*ARGSUSED*/ void mx_fscancel_btn(Widget w, XtPointer client_data, XtPointer call_data) { /* Pop down file selection box window */ if (XtIsManaged(widgets.fsform)) XtUnmanageChild(widgets.fsform); } /* * mx_focuschg * Main window focus change callback routine. */ /*ARGSUSED*/ void mx_focuschg(Widget w, XtPointer client_data, XtPointer call_data) { XmAnyCallbackStruct *p = (XmAnyCallbackStruct *)(void *) call_data; Widget form = (Widget) client_data; if (p->reason != XmCR_FOCUS || form == (Widget) NULL) return; /* Query hardware mixer settings */ mx_queryhw(&widgets); /* Update screen controls */ mx_updctl(&widgets); } /**************** ^^ Callback routines ^^ ****************/