/* * ALSA mixer console for Echoaudio soundcards. * Copyright (C) 2003 Giuliano Pochini * * 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; version 2 of the License. * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define EM_VERSION "%s Echomixer v" VERSION /******* Remove the "//" if you want to compile Echomixer in reverse mode. *******/ //#define REVERSE /******* Constants marked with *M* can be modified to customize the interface. *******/ #define BORDER 6 // *M* Inner border of GTK containers #define SPACING 8 // *M* Spacing of control sections // Graphic mixer constants #define GM_BARWIDTH 5 // *M* Width of meters bars #define XCELLBORDER 2 // *M* Space between the grid lines and the content of the cell #define YCELLBORDER 2 // *M* #define XCELLDIM 20 // *M* Width of the cell #define YCELLDIM 32 // Height of the cell #define XCELLTOT (1+XCELLBORDER*2+XCELLDIM) // line + left border + cell + right border #define YCELLTOT (1+YCELLBORDER*2+YCELLDIM) #define XVOLUME (1+XCELLBORDER+3) // Position of the volume slider #define XMETER (1+XCELLBORDER-GM_BARWIDTH/2+13) // Position of the VU bar // VU-meter constants #define VU_XGRAF 30 // Left margin of the graphic #define VU_YGRAF 20 // Top margin #define VU_BARWIDTH 6 // *M* Width of VU-meters bars #define VU_BARSEP 2 // *M* Space between bars #define SHORTSTEP 1 // *M* 1dB (when the users moves a slider with cursor keys) #define LONGSTEP 6 // *M* 6dB (with Page up/down or clicking the background) #define DIGITAL_MODES 16 // Max number of digital modes #define ECHO_CLOCKS 8 // Max number of clock sources #define INPUT 0 #define OUTPUT 1 #define ECHO_MAXAUDIO_IOS 32 // The maximum number of inputs + outputs #define ECHO_MAXAUDIOINPUTS 32 // Max audio input channels #define ECHO_MAXAUDIOOUTPUTS 32 // Max audio output channels #define ECHOGAIN_MUTED (-128) // Minimum possible gain #define ECHOGAIN_MINOUT (-128) // Min output gain (unit is 1dB) #define ECHOGAIN_MAXOUT 6 // Max output gain (unit is 1dB) #define ECHOGAIN_MININP (-50) // Min input gain (unit is 0.5dB) #define ECHOGAIN_MAXINP 50 // Max input gain (unit is 0.5dB) // GTK+ adjustment widgets have the mininum value at top and maximum at bottom, // position, but we need the opposite. This function puts the scale upside-down. #define INVERT(x) (ECHOGAIN_MINOUT+ECHOGAIN_MAXOUT-(x)) #define IN_INVERT(x) (ECHOGAIN_MININP+ECHOGAIN_MAXINP-(x)) // REAL is for debugging only. #define REAL //#define CTLID_DEBUG(x) printf x #define CTLID_DEBUG(x) #define UI_DEBUG(x) //#define UI_DEBUG(x) printf x #include #include #include #include #include #include char card[64], cardId[16]; char dmodeName[DIGITAL_MODES][64], clocksrcName[DIGITAL_MODES][64], spdifmodeName[DIGITAL_MODES][64]; int nLOut, nIn, fdIn, fdOut, nPOut, ClockMask; int ndmodes, nclocksrc, nspdifmodes; int GMixerRow, GMixerColumn, Gang, AutoClock; int lineinId, pcmoutId, lineoutId, mixerId, vmixerId, p4InId, p4OutId, dmodeId; int clocksrcId, spdifmodeId, vuswitchId, vumetersId, channelsId, phantomId, automuteId; int metersStreams, metersNumber, metersTypes; int outvolCount; int mouseY, mouseButton; int dmodeVal, clocksrcVal, spdifmodeVal; int VUwidth, VUheight, Mixwidth, Mixheight; #define DONT_DRAW (ECHOGAIN_MUTED-1) #define DONT_CHANGE (1<<31) #define NOPOS 999999 struct geometry { int st; // window status: 0 = hidden ; 1 = visible ; NOPOS = no stored setting GtkWidget *toggler; // The toggle button that controls this window int x, y; int w, h; } Mainw_geom, Miscw_geom, PVw_geom, LVw_geom, Mixerw_geom, Vmixerw_geom, VUw_geom, GMw_geom; // This structure contains the first and the last row of each section of the graphic mixer window struct { int VmixerFirst, VmixerLast; // Rows int LineOut; // There is only one row int Inputs; // Rows int Outputs; // Columns } GMixerSection; struct mixel { int id; int Gain; }; snd_ctl_t *ctlhandle; struct mixerControl_s { int input, inputs; int output, outputs; int id; GtkWidget *window; GtkWidget *volume[ECHO_MAXAUDIOOUTPUTS]; GtkWidget *label[ECHO_MAXAUDIOOUTPUTS]; GtkObject *adj[ECHO_MAXAUDIOOUTPUTS]; GtkWidget *outsel[ECHO_MAXAUDIOOUTPUTS]; GtkWidget *inpsel[ECHO_MAXAUDIOINPUTS]; GtkWidget *vchsel[ECHO_MAXAUDIOOUTPUTS]; struct mixel mixer[ECHO_MAXAUDIOOUTPUTS][ECHO_MAXAUDIOOUTPUTS]; } mixerControl, vmixerControl; struct VolumeControl_s { int input, output; // Currently selected channels int inputs, outputs; int id; GtkWidget *window; GtkWidget *volume[ECHO_MAXAUDIOOUTPUTS]; GtkWidget *label[ECHO_MAXAUDIOOUTPUTS]; GtkObject *adj[ECHO_MAXAUDIOOUTPUTS]; int Gain[ECHO_MAXAUDIOOUTPUTS]; } lineinControl, lineoutControl, pcmoutControl; struct NominalLevelControl_s { int id; int Channels; char Level[ECHO_MAXAUDIOINPUTS]; GtkWidget *Button[ECHO_MAXAUDIOINPUTS]; } NominalIn, NominalOut; struct SwitchControl_s { int id; int value; GtkWidget *Button; } PhantomPower, Automute; GtkWidget *clocksrc_menuitem[ECHO_CLOCKS]; GtkWidget *dmodeOpt, *clocksrcOpt, *spdifmodeOpt, *phantomChkbutton, *autoclockChkbutton; GtkWidget *window, *Mainwindow, *Miscwindow, *LVwindow, *VUwindow, *GMwindow; GtkWidget *VUdarea, *Mixdarea; gint VUtimer, Mixtimer, clocksrctimer; GdkGC *gc=0; static GdkPixmap *VUpixmap = NULL; static GdkPixmap *Mixpixmap = NULL; GdkFont *fnt; void Clock_source_activate(GtkWidget *widget, gpointer clk); int CountBits(int n) { int c; c=0; while (n) { c++; n&=(n-1); } return(c); } void ClampOutputVolume(int *v) { if (*v>ECHOGAIN_MAXOUT) *v=ECHOGAIN_MAXOUT; else if (*vECHOGAIN_MAXINP) *v=ECHOGAIN_MAXINP; else if (*v=fdIn+2) mixerControl.input=0; } #else // REVERSE // Enable/disable widgets that control ADAT digital channels void SetSensitivity(int enable) { int i; for (i=fdIn+2; i=fdOut+2) mixerControl.output=0; if (vmixerId && !enable && vmixerControl.output>=fdOut+2) vmixerControl.output=0; } #endif // REVERSE // Read current control settings. int ReadControl(int *vol, int channels, int numid, int iface) { snd_ctl_elem_id_t *id; snd_ctl_elem_value_t *control; int err, ch; snd_ctl_elem_id_alloca(&id); snd_ctl_elem_value_alloca(&control); snd_ctl_elem_id_set_interface(id, iface); snd_ctl_elem_id_set_numid(id, numid); snd_ctl_elem_value_set_id(control, id); if ((err=snd_ctl_elem_read(ctlhandle, control))<0) { printf("Control %s element read error: %s\n", card, snd_strerror(err)); return(err); } for (ch=0; chid); snd_ctl_elem_value_set_id(control, id); if ((err=snd_ctl_elem_read(ctlhandle, control))<0) printf("Control %s element read error: %s\n", card, snd_strerror(err)); for (i=0; iChannels; i++) NominalLevel->Level[i]=snd_ctl_elem_value_get_integer(control, i); } int SetMixerGain(struct mixel *mxl, int Gain) { snd_ctl_elem_id_t *id; snd_ctl_elem_value_t *control; int err; snd_ctl_elem_id_alloca(&id); snd_ctl_elem_value_alloca(&control); snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_id_set_numid(id, mxl->id); snd_ctl_elem_value_set_id(control, id); snd_ctl_elem_value_set_integer(control, 0, Gain); if ((err = snd_ctl_elem_write(ctlhandle, control)) < 0) { printf("Control %s element write error: %s\n", card, snd_strerror(err)); return(err); } return(0); } // Read current (v)mixer settings. void ReadMixer(struct mixerControl_s *mixer) { int err, in, out; snd_ctl_elem_id_t *id; snd_ctl_elem_value_t *control; #ifndef REAL return; #endif snd_ctl_elem_id_alloca(&id); snd_ctl_elem_value_alloca(&control); snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER); for (out=0; outoutputs; out++) { for (in=0; ininputs; in++) { snd_ctl_elem_id_set_numid(id, mixer->mixer[out][in].id); snd_ctl_elem_value_set_id(control, id); if ((err=snd_ctl_elem_read(ctlhandle, control))<0) printf("InitMixer - Control %s element read error: %s\n", card, snd_strerror(err)); mixer->mixer[out][in].Gain=snd_ctl_elem_value_get_integer(control, 0); } } } void GetChannels(void) { snd_ctl_elem_id_t *id; snd_ctl_elem_value_t *control; int err; snd_ctl_elem_id_alloca(&id); snd_ctl_elem_value_alloca(&control); snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_HWDEP); snd_ctl_elem_id_set_numid(id, channelsId); snd_ctl_elem_value_set_id(control, id); if ((err = snd_ctl_elem_read(ctlhandle, control)) < 0) { printf("GetChannels() read error: %s\n", snd_strerror(err)); exit(1); } if (!nIn) { // Only read the first time (mainly for debugging, see #define REAL) nIn=snd_ctl_elem_value_get_integer(control, 0); // Number of input channels fdIn=snd_ctl_elem_value_get_integer(control, 1); // First digital in (= number of analog input channels) nLOut=snd_ctl_elem_value_get_integer(control, 2); // Number of output channels fdOut=snd_ctl_elem_value_get_integer(control, 3); // First digital out nPOut=snd_ctl_elem_value_get_integer(control, 4); // Number of virtual output channels (==nLOut on non-vmixer cards) mixerControl.outputs = nLOut; mixerControl.inputs = nIn; if (vmixerId) { vmixerControl.outputs = nLOut; vmixerControl.inputs = nPOut; /* For outputs and inputs. */ metersStreams = 2; } else { /* For outputs, inputs and system outputs. */ metersStreams = 3; } /* For the number of channels. */ metersNumber = 16; /* For each of levels and peaks. */ metersTypes = 2; } ClockMask=snd_ctl_elem_value_get_integer(control, 5); // Bitmask of available input clocks } // Read what input clocks are valid and (de)activate the pop-down menu items accordingly gint CheckInputs(gpointer unused) { int clk, valid, source; GetChannels(); source=-1; // Switch to internal if the source is not available if (AutoClock>=0 && !(ClockMask & (1<=0 && source!=clocksrcVal) { // Set the clock source, but do not change the value of AutoClock Clock_source_activate(clocksrc_menuitem[source], (gpointer)(long)(source|DONT_CHANGE)); gtk_option_menu_set_history(GTK_OPTION_MENU(clocksrcOpt), clocksrcVal); } return(TRUE); } void DrawBar(int x, int y, int level, int peak, int gain) { GdkColor Bars={0x00FF00, 0, 0, 0}; GdkColor Bars1={0x000000, 0, 0, 0}; GdkColor Peak={0x1BABFF, 0, 0, 0}; GdkColor Level={0xC0B000, 0, 0, 0}; int db; x=XMETER+XCELLTOT*x; y=YCELLTOT*y+YCELLBORDER; if (level>ECHOGAIN_MUTED) { // Draw the "integer" part of the bar db=level>>2; gdk_gc_set_foreground(gc, &Bars); gdk_draw_rectangle(Mixpixmap, gc, TRUE, x, y-db, GM_BARWIDTH, YCELLDIM+db); // Draw the antialiased part Bars1.pixel=(level&3) << (6 + 8); // 4 levels (256/4==64==2^6) of green (2^8) if (Bars1.pixel) { gdk_gc_set_foreground(gc, &Bars1); gdk_draw_rectangle(Mixpixmap, gc, TRUE, x, y-db-1, GM_BARWIDTH, 1); } } // Draw the peak if (peak>ECHOGAIN_MUTED) { db=peak>>2; gdk_gc_set_foreground(gc, &Peak); gdk_draw_rectangle(Mixpixmap, gc, TRUE, x, y-db, GM_BARWIDTH, 1); } // Draw the mixer gain if (gain>=ECHOGAIN_MUTED) { db=gain>>2; gdk_gc_set_foreground(gc, &Level); gdk_draw_rectangle(Mixpixmap, gc, TRUE, x-XMETER+XVOLUME, y, 1, YCELLDIM); gdk_draw_rectangle(Mixpixmap, gc, TRUE, x-XMETER+XVOLUME-2, y-db, 5, 1); } } // Draw the matrix mixer gint DrawMixer(gpointer unused) { GdkRectangle update_rect; int InLevel[ECHO_MAXAUDIOINPUTS]; int InPeak[ECHO_MAXAUDIOINPUTS]; int OutLevel[ECHO_MAXAUDIOOUTPUTS]; int OutPeak[ECHO_MAXAUDIOOUTPUTS]; int VirLevel[ECHO_MAXAUDIOOUTPUTS]; int VirPeak[ECHO_MAXAUDIOOUTPUTS]; static int InClip[ECHO_MAXAUDIOINPUTS]; static int OutClip[ECHO_MAXAUDIOOUTPUTS]; char str[8]; int i, o, dB; GdkColor Grid={0x787878, 0, 0, 0}; GdkColor Labels={0x9694C4, 0, 0, 0}; GdkColor Hilight={0x000078, 0, 0, 0}; GdkColor Hilight2={0x600000, 0, 0, 0}; if (!Mixpixmap) return(TRUE); update_rect.x = 0; update_rect.y = 0; update_rect.width = Mixwidth; update_rect.height = Mixheight; GetVUmeters(InLevel, InPeak, OutLevel, OutPeak, VirLevel, VirPeak); if (!gc) { gc=gdk_gc_new(gtk_widget_get_parent_window(Mixdarea)); for (i=0; istyle->black_gc, TRUE, 0, 0, Mixwidth, Mixheight); // Highlight gdk_gc_set_foreground(gc, &Hilight); gdk_draw_rectangle(Mixpixmap, gc, TRUE, 0, YCELLTOT*mixerControl.input, XCELLTOT*(mixerControl.output+1), YCELLTOT); gdk_draw_rectangle(Mixpixmap, gc, TRUE, XCELLTOT*(mixerControl.output+1), YCELLTOT*mixerControl.input, XCELLTOT, Mixheight); if (vmixerId) { gdk_gc_set_foreground(gc, &Hilight2); gdk_draw_rectangle(Mixpixmap, gc, TRUE, 0, YCELLTOT*(GMixerSection.VmixerFirst+vmixerControl.input), XCELLTOT*(vmixerControl.output+1), YCELLTOT); gdk_draw_rectangle(Mixpixmap, gc, TRUE, XCELLTOT*(vmixerControl.output+1), YCELLTOT*(GMixerSection.VmixerFirst+vmixerControl.input), XCELLTOT, Mixheight); } // Draw the grid gdk_gc_set_font(gc, fnt); // Horizontal lines and input channel labels for (i=0; istyle->black_gc, TRUE, 0, 0, VUwidth, VUheight); // Draw the dB scale and the grid gdk_gc_set_font(gc, fnt); gdk_gc_set_foreground(gc, &Peak); gdk_draw_string(VUpixmap, fnt, gc, 2, VU_YGRAF-12+4, " dB"); for (i=0; i<=120; i+=12) { sprintf(str, "%4d", -i); gdk_gc_set_foreground(gc, &dBValues); gdk_draw_string(VUpixmap, fnt, gc, 2, VU_YGRAF+i+4, str); gdk_gc_set_foreground(gc, &Grid); gdk_draw_rectangle(VUpixmap, gc, TRUE, VU_XGRAF, VU_YGRAF+i, VUwidth-VU_XGRAF, 1); } gdk_gc_set_foreground(gc, &Grid2); gdk_draw_rectangle(VUpixmap, gc, TRUE, VU_XGRAF, VU_YGRAF+128, VUwidth-VU_XGRAF, 1); x=VU_XGRAF+VU_BARSEP; // Draw inputs for (i=0; iy/YCELLTOT; GMixerColumn=(int)event->x/XCELLTOT-1; if (GMixerColumn<0 || GMixerColumn>=GMixerSection.Outputs) return TRUE; /* grab_focus must follow set_active because the latter causes Vmixer_*_selector_clicked() to be called and, in turn, Vmixer_volume_changed() which changes mixerControl.input (or .output in non-reverse mode). grab_focus then causes Vmixer_volume_clicked() to be called and that event handler finally sets the correct value of mixerControl.input */ if (GMixerRow=GMixerSection.VmixerFirst && GMixerRow<=GMixerSection.VmixerLast) { if (GMixerColumn!=vmixerControl.output) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(vmixerControl.outsel[GMixerColumn]), TRUE); if (GMixerRow!=vmixerControl.input) gtk_widget_grab_focus(GTK_WIDGET(vmixerControl.volume[GMixerRow-GMixerSection.VmixerFirst])); } if (event->button==1) { mouseY=event->y; mouseButton=1; } return(TRUE); } #else //REVERSE static gint Gmixer_button_press(GtkWidget *widget, GdkEventButton *event) { GMixerRow=(int)event->y/YCELLTOT; GMixerColumn=(int)event->x/XCELLTOT-1; if (GMixerColumn<0 || GMixerColumn>=GMixerSection.Outputs) return TRUE; // See the note above if (GMixerRow=GMixerSection.VmixerFirst && GMixerRow<=GMixerSection.VmixerLast) { if (GMixerRow!=vmixerControl.input+GMixerSection.VmixerFirst) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(vmixerControl.vchsel[GMixerRow-GMixerSection.VmixerFirst]), TRUE); if (GMixerColumn!=vmixerControl.output) gtk_widget_grab_focus(GTK_WIDGET(vmixerControl.volume[GMixerColumn])); } if (event->button==1) { mouseY=event->y; mouseButton=1; } return(TRUE); } #endif //REVERSE static gint Gmixer_button_release(GtkWidget *widget, GdkEventButton *event) { if (event->state & GDK_BUTTON1_MASK) mouseButton=0; return TRUE; } // Gets how many pixels the mouse pointer was moved of and updates // the currently active mixer/vmixer/line_out control. static gint Gmixer_motion_notify(GtkWidget *widget, GdkEventMotion *event) { int x, y; GdkModifierType state; float val; if (event->is_hint) gdk_window_get_pointer(event->window, &x, &y, &state); else { x=event->x; y=event->y; state=event->state; } // Check if the button is still pressed because the release event can // fall in another window, so we may miss it. Ignore the event if there // isn't a backing pixmap or the user clicked an invalid cell. We also // have to check mouseButton because the button_press event may arrive // after the respective motion_notify event. if (!(state & GDK_BUTTON1_MASK) || !mouseButton || !Mixpixmap || GMixerColumn<0 || GMixerColumn>=GMixerSection.Outputs) { mouseButton=0; return(TRUE); } if (GMixerRow=GMixerSection.VmixerFirst && GMixerRow<=GMixerSection.VmixerLast) { val=INVERT(vmixerControl.mixer[vmixerControl.output][vmixerControl.input].Gain); val+=y-mouseY; mouseY=y; #ifdef REVERSE gtk_adjustment_set_value(GTK_ADJUSTMENT(vmixerControl.adj[vmixerControl.input]), (gfloat)val); #else gtk_adjustment_set_value(GTK_ADJUSTMENT(vmixerControl.adj[vmixerControl.output]), (gfloat)val); #endif } else if (GMixerRow==GMixerSection.LineOut) { val=INVERT(lineoutControl.Gain[GMixerColumn]); val+=y-mouseY; mouseY=y; gtk_adjustment_set_value(GTK_ADJUSTMENT(lineoutControl.adj[GMixerColumn]), (gfloat)val); } return(TRUE); } void Monitor_volume_changed(GtkWidget *widget, gpointer cnl) { int val, rval, ch; int i, o; char str[16]; UI_DEBUG(("Monitor_volume_changed() %d %d\n",mixerControl.input,mixerControl.output)); val=rval=INVERT((int)GTK_ADJUSTMENT(widget)->value); ch=(int)(long)cnl; #ifdef REVERSE i=ch; o=mixerControl.output; #else i=mixerControl.input; o=ch; #endif // Emulate the line-out volume if this card can't do it in hw. if (!lineoutId) { rval=Add_dB(val, lineoutControl.Gain[o]); ClampOutputVolume(&rval); } SetMixerGain(&mixerControl.mixer[o][i], rval); //@ we should restore the old adj position on error mixerControl.mixer[o][i].Gain=val; if (Gang) { SetMixerGain(&mixerControl.mixer[o^1][i^1], rval); mixerControl.mixer[o^1][i^1].Gain=val; } gtk_label_set_text(GTK_LABEL(mixerControl.label[ch]), strOutGain(str, val)); } void Monitor_volume_clicked(GtkWidget *widget, gpointer ch) { #ifdef REVERSE mixerControl.input=(int)(long)ch; #else mixerControl.output=(int)(long)ch; #endif } void Gang_button_toggled(GtkWidget *widget, gpointer unused) { Gang=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); } void PCM_volume_changed(GtkWidget *widget, gpointer ch) { snd_ctl_elem_id_t *id; snd_ctl_elem_value_t *control; char str[16]; int err, channel, val, rval; struct VolumeControl_s *vol; snd_ctl_elem_id_alloca(&id); snd_ctl_elem_value_alloca(&control); if ((int)(long)chvalue); sprintf(str, "%+4.1f", 0.5*val); } else { // Output channel=(int)(long)ch-ECHO_MAXAUDIOINPUTS; vol=&pcmoutControl; val=rval=INVERT((int)GTK_ADJUSTMENT(widget)->value); pcmoutControl.Gain[channel]=val; // Emulate the line-out volume if this card can't do it in hw. if (!lineoutId) { rval=Add_dB(val, lineoutControl.Gain[channel]); ClampOutputVolume(&rval); } strOutGain(str, val); } gtk_label_set_text(GTK_LABEL(vol->label[channel]), str); snd_ctl_elem_id_set_numid(id, vol->id); snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_id(control, id); if ((err=snd_ctl_elem_read(ctlhandle, control))<0) { printf("Control %s element read error: %s\n", card, snd_strerror(err)); return; } snd_ctl_elem_value_set_integer(control, channel, rval); if ((err=snd_ctl_elem_write(ctlhandle, control))<0) { printf("Control %s element write error: %s\n", card, snd_strerror(err)); } else { vol->Gain[channel]=val; } if (Gang) gtk_adjustment_set_value(GTK_ADJUSTMENT(vol->adj[channel^1]), (gfloat)GTK_ADJUSTMENT(widget)->value); } // Changes the PCM volume according to the current Line-out volume for non-vmixer cards void UpdatePCMVolume(int outchannel) { snd_ctl_elem_id_t *id; snd_ctl_elem_value_t *control; int err, val; snd_ctl_elem_id_alloca(&id); snd_ctl_elem_value_alloca(&control); snd_ctl_elem_id_set_numid(id, pcmoutId); snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_id(control, id); if ((err=snd_ctl_elem_read(ctlhandle, control))<0) printf("Control %s element read error: %s\n", card, snd_strerror(err)); val=Add_dB(pcmoutControl.Gain[outchannel], lineoutControl.Gain[outchannel]); ClampOutputVolume(&val); snd_ctl_elem_value_set_integer(control, outchannel, val); if ((err=snd_ctl_elem_write(ctlhandle, control))<0) printf("Control %s element write error: %s\n", card, snd_strerror(err)); } // Changes the monitor mixer volume according to the current Line-out volume for non-vmixer cards. void UpdateMixerVolume(int outchannel) { snd_ctl_elem_id_t *id; snd_ctl_elem_value_t *control; int err, val, ch; snd_ctl_elem_id_alloca(&id); snd_ctl_elem_value_alloca(&control); snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER); for (ch=0; chvalue); lineoutControl.Gain[channel]=val; gtk_label_set_text(GTK_LABEL(lineoutControl.label[channel]), strOutGain(str, val)); if (lineoutId) { // If this card has the line-out control, use it snd_ctl_elem_id_t *id; snd_ctl_elem_value_t *control; snd_ctl_elem_id_alloca(&id); snd_ctl_elem_value_alloca(&control); snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_id_set_numid(id, lineoutId); snd_ctl_elem_value_set_id(control, id); if ((err=snd_ctl_elem_read(ctlhandle, control))<0) printf("Control %s element read error: %s\n", card, snd_strerror(err)); snd_ctl_elem_value_set_integer(control, channel, val); if ((err=snd_ctl_elem_write(ctlhandle, control))<0) printf("Control %s element write error: %s\n", card, snd_strerror(err)); } else if (vmixerId) { UpdateVMixerVolume(channel); UpdateMixerVolume(channel); } else { // Otherwise we have to emulate it. UpdatePCMVolume(channel); UpdateMixerVolume(channel); } if (Gang) gtk_adjustment_set_value(GTK_ADJUSTMENT(lineoutControl.adj[channel^1]), (gfloat)GTK_ADJUSTMENT(widget)->value); } void Vmixer_volume_changed(GtkWidget *widget, gpointer ch) { char str[16]; int val, rval, channel; int o, v; channel=(int)(long)ch; val=rval=INVERT((int)GTK_ADJUSTMENT(widget)->value); #ifdef REVERSE v=channel; o=vmixerControl.output; #else v=vmixerControl.input; o=channel; #endif // Emulate the line-out volume if this card can't do it in hw. if (!lineoutId) { rval=Add_dB(val, lineoutControl.Gain[o]); ClampOutputVolume(&rval); } SetMixerGain(&vmixerControl.mixer[o][v], rval); vmixerControl.mixer[o][v].Gain=val; if (Gang) { SetMixerGain(&vmixerControl.mixer[o^1][v^1], rval); vmixerControl.mixer[o^1][v^1].Gain=val; } gtk_label_set_text(GTK_LABEL(vmixerControl.label[channel]), strOutGain(str, val)); } void Vmixer_volume_clicked(GtkWidget *widget, gpointer ch) { #ifdef REVERSE vmixerControl.input=(int)(long)ch; UI_DEBUG(("Vmixer_volume_clicked vch=%d\n",vmixerControl.input)); #else vmixerControl.output=(int)(long)ch; UI_DEBUG(("Vmixer_volume_clicked out=%d\n",vmixerControl.output)); #endif } #ifdef REVERSE void Vmixer_output_selector_clicked(GtkWidget *widget, gpointer ch) { int c, val; snd_ctl_elem_id_t *id; snd_ctl_elem_value_t *control; if (vmixerControl.output==(int)ch) return; vmixerControl.output=(int)ch; UI_DEBUG(("Vmixer_selector_clicked out=%d\n",vmixerControl.output)); snd_ctl_elem_id_alloca(&id); snd_ctl_elem_value_alloca(&control); snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER); for (c=vmixerControl.inputs-1; c>=0; c--) { val=INVERT(vmixerControl.mixer[vmixerControl.output][c].Gain); gtk_adjustment_set_value(GTK_ADJUSTMENT(vmixerControl.adj[c]), (gfloat)val); } } #else void Vmixer_vchannel_selector_clicked(GtkWidget *widget, gpointer ch) { int c, val; snd_ctl_elem_id_t *id; snd_ctl_elem_value_t *control; if (vmixerControl.input==(int)(long)ch) return; vmixerControl.input=(int)(long)ch; UI_DEBUG(("Vmixer_selector_clicked vch=%d\n",vmixerControl.input)); snd_ctl_elem_id_alloca(&id); snd_ctl_elem_value_alloca(&control); snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER); for (c=vmixerControl.outputs-1; c>=0; c--) { val=INVERT(vmixerControl.mixer[c][vmixerControl.input].Gain); gtk_adjustment_set_value(GTK_ADJUSTMENT(vmixerControl.adj[c]), (gfloat)val); } } #endif void Nominal_level_toggled(GtkWidget *widget, gpointer ch) { snd_ctl_elem_id_t *id; snd_ctl_elem_value_t *control; int err, val, channel; struct NominalLevelControl_s *NominalLevel; snd_ctl_elem_id_alloca(&id); snd_ctl_elem_value_alloca(&control); snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER); val=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); if ((int)(long)chLevel[channel]=!val; snd_ctl_elem_id_set_numid(id, NominalLevel->id); snd_ctl_elem_value_set_id(control, id); if ((err=snd_ctl_elem_read(ctlhandle, control))<0) printf("Control %s element read error: %s\n", card, snd_strerror(err)); snd_ctl_elem_value_set_integer(control, channel, !val); // FALSE is +4 if ((err=snd_ctl_elem_write(ctlhandle, control))<0) printf("Control %s element write error: %s\n", card, snd_strerror(err)); if (Gang) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(NominalLevel->Button[channel^1]), val); } void Switch_toggled(GtkWidget *widget, gpointer Ctl) { snd_ctl_elem_id_t *id; snd_ctl_elem_value_t *control; int err; struct SwitchControl_s *Switch=(struct SwitchControl_s *)Ctl; snd_ctl_elem_id_alloca(&id); snd_ctl_elem_value_alloca(&control); Switch->value=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_CARD); snd_ctl_elem_id_set_numid(id, Switch->id); snd_ctl_elem_value_set_id(control, id); snd_ctl_elem_value_set_integer(control, 0, Switch->value); if ((err=snd_ctl_elem_write(ctlhandle, control))<0) printf("Control %s element write error: %s\n", card, snd_strerror(err)); } void AutoClock_toggled(GtkWidget *widget, gpointer unused) { char str[32]; if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) { AutoClock=clocksrcVal; snprintf(str, 31, "Autoclock [%s]", clocksrcName[AutoClock]); str[31]=0; gtk_label_set_text(GTK_LABEL(GTK_BIN(widget)->child), str); } else { AutoClock=-1; gtk_label_set_text(GTK_LABEL(GTK_BIN(widget)->child), "Autoclock"); } } void Digital_mode_activate(GtkWidget *widget, gpointer mode) { int adat; if (SetEnum(dmodeId, (int)(long)mode)<0) { // Restore old value if it failed gtk_option_menu_set_history(GTK_OPTION_MENU(dmodeOpt), dmodeVal); return; } dmodeVal=(int)(long)mode; // When I change the digital mode, the clock source can change too clocksrcVal=GetEnum(clocksrcId); gtk_option_menu_set_history(GTK_OPTION_MENU(clocksrcOpt), clocksrcVal); adat=!memcmp(dmodeName[dmodeVal], "ADAT", 4); SetSensitivity(adat); if (adat) { GMixerSection.Inputs=nIn; GMixerSection.Outputs=nLOut; } else { GMixerSection.Inputs=fdIn+2; // S/PDIF has only 2 channels GMixerSection.Outputs=fdOut+2; } } void Clock_source_activate(GtkWidget *widget, gpointer clk) { unsigned int source; source=(unsigned int)(long)clk & 0xff; if (SetEnum(clocksrcId, source)<0) { gtk_option_menu_set_history(GTK_OPTION_MENU(clocksrcOpt), clocksrcVal); } else { clocksrcVal=(int)(long)clk & 0xff; // Change only when the user triggers it if (((int)(long)clk & DONT_CHANGE)==0 && AutoClock>=0) { AutoClock=clocksrcVal; AutoClock_toggled(autoclockChkbutton, NULL); } } } void SPDIF_mode_activate(GtkWidget *widget, gpointer mode) { SetEnum(spdifmodeId, (int)(long)mode); // This one should never fail spdifmodeVal=(int)(long)mode; } // Create a new backing pixmap of the appropriate size static gint VU_configure_event(GtkWidget *widget, GdkEventConfigure *event) { if (VUpixmap) gdk_pixmap_unref(VUpixmap); VUpixmap=gdk_pixmap_new(widget->window, widget->allocation.width, widget->allocation.height, -1); gdk_draw_rectangle(VUpixmap, widget->style->black_gc, TRUE, 0, 0, widget->allocation.width, widget->allocation.height); return(TRUE); } // Redraw the screen from the backing pixmap static gint VU_expose(GtkWidget *widget, GdkEventExpose *event) { if (VUpixmap) gdk_draw_pixmap(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)], VUpixmap, event->area.x, event->area.y, event->area.x, event->area.y, event->area.width, event->area.height); return(FALSE); } // Create a new backing pixmap of the appropriate size static gint Gmixer_configure_event(GtkWidget *widget, GdkEventConfigure *event) { if (Mixpixmap) gdk_pixmap_unref(Mixpixmap); Mixpixmap=gdk_pixmap_new(widget->window, widget->allocation.width, widget->allocation.height, -1); gdk_draw_rectangle(Mixpixmap, widget->style->black_gc, TRUE, 0, 0, widget->allocation.width, widget->allocation.height); return(TRUE); } // Redraw the screen from the backing pixmap static gint Gmixer_expose(GtkWidget *widget, GdkEventExpose *event) { if (Mixpixmap) gdk_draw_pixmap(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)], Mixpixmap, event->area.x, event->area.y, event->area.x, event->area.y, event->area.width, event->area.height); return(FALSE); } gint CloseWindow(GtkWidget *widget, GdkEvent *event, gpointer geom) { struct geometry *g=geom; gdk_window_get_root_origin(widget->window, &g->x, &g->y); gdk_window_get_size(widget->window, &g->w, &g->h); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->toggler), FALSE); // This hides the window //gtk_widget_set_uposition(widget, g->x, g->y); return(TRUE); // Do not destroy it } gint Mainwindow_delete(GtkWidget *widget, GdkEvent *event, gpointer geom) { struct geometry *g=geom; if (VUwindow) { gdk_window_get_root_origin(VUwindow->window, &VUw_geom.x, &VUw_geom.y); gtk_widget_destroy(VUwindow); } if (GMwindow) { gdk_window_get_root_origin(GMwindow->window, &GMw_geom.x, &GMw_geom.y); gtk_widget_destroy(GMwindow); } gdk_window_get_root_origin(Mainwindow->window, &g->x, &g->y); gtk_main_quit(); return(FALSE); } gint VUwindow_delete(GtkWidget *widget, GdkEvent *event, gpointer geom) { struct geometry *g=geom; gdk_window_get_root_origin(widget->window, &g->x, &g->y); g->st=0; gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->toggler), FALSE); return(FALSE); } gint VUwindow_destroy(GtkWidget *widget, gpointer unused) { SetVUmeters(0); gtk_timeout_remove(VUtimer); //@@@del gc and fnt VUwindow=0; return(TRUE); } gint GMwindow_delete(GtkWidget *widget, GdkEvent *event, gpointer geom) { struct geometry *g=geom; gdk_window_get_root_origin(widget->window, &g->x, &g->y); g->st=0; gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->toggler), FALSE); return(FALSE); } gint GMwindow_destroy(GtkWidget *widget, gpointer unused) { SetVUmeters(0); gtk_timeout_remove(Mixtimer); //@@@del gc and fnt GMwindow=0; return(TRUE); } void VUmeters_button_click(GtkWidget *widget, gpointer unused) { char str[64]; if (VUwindow && !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) { VUw_geom.st=0; gtk_widget_destroy(VUwindow); } else if (!VUwindow && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) { // Create VU-meter window VUwidth=VU_XGRAF+(VU_BARWIDTH+VU_BARSEP)*(nIn+nLOut+1)+VU_BARSEP; VUheight=160; SetVUmeters(1); VUwindow=gtk_window_new(GTK_WINDOW_TOPLEVEL); sprintf(str, "%s VU-meters", cardId); gtk_window_set_title (GTK_WINDOW (VUwindow), str); gtk_window_set_wmclass(GTK_WINDOW(VUwindow), "vumeters", "Emixer"); gtk_signal_connect(GTK_OBJECT(VUwindow), "destroy", GTK_SIGNAL_FUNC(VUwindow_destroy), NULL); gtk_signal_connect(GTK_OBJECT(VUwindow), "delete_event", GTK_SIGNAL_FUNC(VUwindow_delete), (gpointer)&VUw_geom); gtk_window_set_policy(GTK_WINDOW(VUwindow), FALSE, FALSE, TRUE); if (VUw_geom.st!=NOPOS) gtk_widget_set_uposition(VUwindow, VUw_geom.x, VUw_geom.y); gtk_widget_show(VUwindow); VUdarea=gtk_drawing_area_new(); gtk_widget_set_events(VUdarea, GDK_EXPOSURE_MASK); gtk_drawing_area_size(GTK_DRAWING_AREA(VUdarea), VUwidth, VUheight); gtk_container_add(GTK_CONTAINER(VUwindow), VUdarea); gtk_widget_show(VUdarea); gtk_signal_connect(GTK_OBJECT(VUdarea), "expose_event", (GtkSignalFunc)VU_expose, NULL); gtk_signal_connect(GTK_OBJECT(VUdarea), "configure_event", (GtkSignalFunc)VU_configure_event, NULL); VUtimer=gtk_timeout_add(30, DrawVUmeters, 0); // The hw updates the meters about 30 times/s gdk_window_clear_area(VUdarea->window, 0, 0, VUwidth, VUheight); VUw_geom.st=1; } } void GMixer_button_click(GtkWidget *widget, gpointer unused) { char str[64]; if (GMwindow && !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) { GMw_geom.st=0; gtk_widget_destroy(GMwindow); } else if (!GMwindow && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) { // Create graphic mixer window Mixwidth=XCELLTOT*(nLOut+1); Mixheight=YCELLTOT*(GMixerSection.LineOut+1)+9; SetVUmeters(1); GMwindow=gtk_window_new(GTK_WINDOW_TOPLEVEL); sprintf(str, "%s Mixer", cardId); gtk_window_set_title (GTK_WINDOW (GMwindow), str); gtk_window_set_wmclass(GTK_WINDOW(GMwindow), "gridmixer", "Emixer"); gtk_signal_connect(GTK_OBJECT(GMwindow), "destroy", GTK_SIGNAL_FUNC(GMwindow_destroy), NULL); gtk_signal_connect(GTK_OBJECT(GMwindow), "delete_event", GTK_SIGNAL_FUNC(GMwindow_delete), (gpointer)&GMw_geom); gtk_window_set_policy(GTK_WINDOW(GMwindow), FALSE, FALSE, TRUE); if (GMw_geom.st!=NOPOS) gtk_widget_set_uposition(GMwindow, GMw_geom.x, GMw_geom.y); gtk_widget_show(GMwindow); Mixdarea=gtk_drawing_area_new(); gtk_widget_set_events(Mixdarea, GDK_EXPOSURE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK); gtk_drawing_area_size(GTK_DRAWING_AREA(Mixdarea), Mixwidth, Mixheight); gtk_container_add(GTK_CONTAINER(GMwindow), Mixdarea); gtk_widget_show(Mixdarea); gtk_signal_connect(GTK_OBJECT(Mixdarea), "expose_event", (GtkSignalFunc)Gmixer_expose, NULL); gtk_signal_connect(GTK_OBJECT(Mixdarea), "configure_event", (GtkSignalFunc)Gmixer_configure_event, NULL); gtk_signal_connect(GTK_OBJECT(Mixdarea), "motion_notify_event", (GtkSignalFunc)Gmixer_motion_notify, NULL); gtk_signal_connect(GTK_OBJECT(Mixdarea), "button_press_event", (GtkSignalFunc)Gmixer_button_press, NULL); gtk_signal_connect(GTK_OBJECT(Mixdarea), "button_release_event", (GtkSignalFunc)Gmixer_button_release, NULL); Mixtimer=gtk_timeout_add(30, DrawMixer, 0); // The hw updates the meters about 30 times/s gdk_window_clear_area(Mixdarea->window, 0, 0, Mixwidth, Mixheight); GMw_geom.st=1; } } void ToggleWindow(GtkWidget *widget, gpointer window) { if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) gtk_widget_show(GTK_WIDGET(window)); else gtk_widget_hide(GTK_WIDGET(window)); } // Scan all controls and sets up the structures needed to access them. int OpenControls(const char *card, const char *cardname) { int err, i, o; int numid, count, items, item; snd_hctl_t *handle; snd_hctl_elem_t *elem; snd_ctl_elem_id_t *id; snd_ctl_elem_info_t *info; pcmoutId=lineoutId=vmixerId=p4InId=p4OutId=dmodeId=clocksrcId=spdifmodeId=vuswitchId=vumetersId=mixerId=0; memset(&vmixerControl, 0, sizeof(vmixerControl)); memset(&mixerControl, 0, sizeof(mixerControl)); memset(&lineoutControl, 0, sizeof(struct VolumeControl_s)); memset(&lineinControl, 0, sizeof(struct VolumeControl_s)); memset(&pcmoutControl, 0, sizeof(struct VolumeControl_s)); ndmodes=nclocksrc=nspdifmodes=0; snd_ctl_elem_id_alloca(&id); snd_ctl_elem_info_alloca(&info); if ((err=snd_hctl_open(&handle, card, 0))<0) { printf("Control %s open error: %s", card, snd_strerror(err)); return err; } if ((err=snd_hctl_load(handle))<0) { printf("Control %s local error: %s\n", card, snd_strerror(err)); return err; } for (elem=snd_hctl_first_elem(handle); elem; elem=snd_hctl_elem_next(elem)) { if ((err=snd_hctl_elem_info(elem, info))<0) { printf("Control %s snd_hctl_elem_info error: %s\n", card, snd_strerror(err)); return err; } if (snd_ctl_elem_info_is_inactive(info)) continue; snd_hctl_elem_get_id(elem, id); numid=snd_ctl_elem_id_get_numid(id); count=snd_ctl_elem_info_get_count(info); if (!strcmp("Monitor Mixer Volume", snd_ctl_elem_id_get_name(id))) { if (!mixerId) { mixerId=numid; CTLID_DEBUG(("First Mixer id=%d\n", mixerId)); } } else if (!strcmp("VMixer Volume", snd_ctl_elem_id_get_name(id))) { if (!vmixerId) { vmixerId=vmixerControl.id=numid; CTLID_DEBUG(("First Vmixer id=%d\n", vmixerId)); } } else if (!strcmp("PCM Playback Volume", snd_ctl_elem_id_get_name(id))) { pcmoutId=pcmoutControl.id=numid; CTLID_DEBUG(("PCM Playback Volume id=%d [%d]\n", pcmoutId, count)); } else if (!strcmp("Line Playback Volume", snd_ctl_elem_id_get_name(id))) { lineoutId=numid; CTLID_DEBUG(("Line Volume id=%d\n", lineoutId)); } else if (!strcmp("Line Capture Volume", snd_ctl_elem_id_get_name(id))) { lineinId=lineinControl.id=numid; CTLID_DEBUG(("Capture Volume id=%d [%d]\n", lineinId, count)); } else if (!strcmp("Line Playback Switch (-10dBV)", snd_ctl_elem_id_get_name(id))) { p4OutId=NominalOut.id=numid; CTLID_DEBUG(("Playback nominal id=%d [%d]\n", p4OutId, count)); } else if (!strcmp("Line Capture Switch (-10dBV)", snd_ctl_elem_id_get_name(id))) { p4InId=NominalIn.id=numid; CTLID_DEBUG(("Capture nominal id=%d [%d]\n", p4InId, count)); } else if (!strcmp("Digital mode Switch", snd_ctl_elem_id_get_name(id))) { dmodeId=numid; items=snd_ctl_elem_info_get_items(info); ndmodes=items; for (item=0; item1) cardnum=atoi(argv[1])-1; while (snd_card_next(&cardnum)>=0 && cardnum>=0) { sprintf(hwname, "hw:%d", cardnum); if ((err=snd_ctl_open(&ctlhandle, hwname, 0))<0) { printf("snd_ctl_open(%s) Error: %s\n", hwname, snd_strerror(err)); continue; } if ((err=snd_ctl_card_info(ctlhandle, hw_info))>=0) { if (!strncmp(snd_ctl_card_info_get_driver(hw_info), "Echo_", 5)) { strncpy(card, hwname, 7); hwname[7]=0; strncpy(cardname, snd_ctl_card_info_get_name(hw_info), 31); cardname[31]=0; strncpy(cardId, snd_ctl_card_info_get_name(hw_info), 15); cardId[15]=0; CTLID_DEBUG(("Card found: %s (%s)\n", snd_ctl_card_info_get_longname(hw_info), hwname)); /*printf("card = %d\n", snd_ctl_card_info_get_card(hw_info)); printf("id = %s\n", snd_ctl_card_info_get_id(hw_info)); printf("driver = %s\n", snd_ctl_card_info_get_driver(hw_info)); printf("name = %s\n", snd_ctl_card_info_get_name(hw_info)); printf("longname = %s\n", snd_ctl_card_info_get_longname(hw_info)); printf("mixername = %s\n", snd_ctl_card_info_get_mixername(hw_info)); printf("components = %s\n", snd_ctl_card_info_get_components(hw_info));*/ break; } } else { printf("snd_ctl_card_info(%s) Error: %s\n", hwname, snd_strerror(err)); } snd_ctl_close(ctlhandle); ctlhandle=0; } if (!ctlhandle) { printf("No Echoaudio cards found, sorry.\n"); return(0); } // Reads available controls if (OpenControls(card, cardname)) exit(1); mouseButton=0; Gang=0; // Set the gang button off, because has annoying side effects during initialization Mainw_geom.st=NOPOS; PVw_geom.st=NOPOS; LVw_geom.st=NOPOS; VUw_geom.st=NOPOS; Mixerw_geom.st=NOPOS; Vmixerw_geom.st=NOPOS; VUwindow=GMwindow=0; GMixerSection.Inputs=nIn; // The correct value is set by Digital_mode_activate() GMixerSection.Outputs=nLOut; GMixerSection.VmixerFirst=nIn; GMixerSection.VmixerLast=nIn+vmixerControl.inputs-1; GMixerSection.LineOut=GMixerSection.VmixerLast+1; // Read current mixer setting. if (mixerId) ReadMixer(&mixerControl); if (vmixerId) ReadMixer(&vmixerControl); if (pcmoutId) ReadControl(pcmoutControl.Gain, nPOut, pcmoutControl.id, SND_CTL_ELEM_IFACE_MIXER); if (lineinId) ReadControl(lineinControl.Gain, nIn, lineinControl.id, SND_CTL_ELEM_IFACE_MIXER); if (lineoutId) ReadControl(lineoutControl.Gain, nLOut, lineoutId, SND_CTL_ELEM_IFACE_MIXER); if (p4InId) ReadNominalLevels(&NominalIn); if (p4OutId) ReadNominalLevels(&NominalOut); //@@ check the values if (load) { FILE *f; snprintf(str, 255, "%s/.Emixer_%s", getenv("HOME"), cardId); str[255]=0; if ((f=fopen(str, "r"))) { str[255]=0; while (fgets(str, 255, f)) { if (!strncmp("LineOut ", str, 8)) { sscanf(str+8, "%d %d", &o, &n); if (o>=0 && o=0 && i=0 && o=0 && o=0 && i=0 && o=0 && i=0 && o=0 && i1) { // Digital mode switch frame=gtk_frame_new("Digital mode"); gtk_widget_show(frame); gtk_box_pack_start(GTK_BOX(mainbox), frame, TRUE, FALSE, 0); hbox=gtk_hbox_new(FALSE, 0); gtk_widget_show(hbox); gtk_container_add(GTK_CONTAINER(frame), hbox); dmodeOpt=gtk_option_menu_new(); gtk_widget_show(dmodeOpt); menu=gtk_menu_new(); gtk_widget_show(menu); for (i=0; i1) { // Clock source switch frame=gtk_frame_new("Clock source"); gtk_widget_show(frame); gtk_box_pack_start(GTK_BOX(mainbox), frame, TRUE, FALSE, 0); hbox=gtk_hbox_new(FALSE, 0); gtk_widget_show(hbox); gtk_container_add(GTK_CONTAINER(frame), hbox); clocksrcOpt=gtk_option_menu_new(); gtk_widget_show(clocksrcOpt); menu=gtk_menu_new(); gtk_widget_show(menu); for (i=0; i1) { // S/PDIF mode switch frame=gtk_frame_new("S/PDIF mode"); gtk_widget_show(frame); gtk_box_pack_start(GTK_BOX(mainbox), frame, TRUE, FALSE, 0); hbox=gtk_hbox_new(FALSE, 0); gtk_widget_show(hbox); gtk_container_add(GTK_CONTAINER(frame), hbox); spdifmodeOpt=gtk_option_menu_new(); gtk_widget_show(spdifmodeOpt); menu=gtk_menu_new(); gtk_widget_show(menu); for (i=0; iwindow, Mixerw_geom.x, Mixerw_geom.y, Mixerw_geom.w, Mixerw_geom.h); /* gtk_widget_set_usize(mixerControl.window, Mixerw_geom.w, Mixerw_geom.h); gtk_widget_set_usize(mixerControl.window, -1, -1);*/ } mainbox=gtk_hbox_new(FALSE, SPACING); gtk_widget_show(mainbox); gtk_container_add(GTK_CONTAINER(mixerControl.window), mainbox); #ifdef REVERSE // Mixer volume widgets frame=gtk_frame_new("Mixer input levels"); gtk_widget_show(frame); gtk_box_pack_start(GTK_BOX(mainbox), frame, TRUE, TRUE, 0); hbox=gtk_hbox_new(TRUE, 1); gtk_widget_show(hbox); gtk_container_add(GTK_CONTAINER(frame), hbox); for (i=0; i1) || (clocksrcId && nclocksrc>1) || (spdifmodeId && nspdifmodes>1)) { button=gtk_toggle_button_new_with_label("Misc"); gtk_widget_show(button); gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 1); gtk_signal_connect(GTK_OBJECT(button), "toggled", ToggleWindow, (gpointer)Miscwindow); Miscw_geom.toggler=button; if (Miscw_geom.st==1) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); } if (mixerId) { // Graphical mixer button button=gtk_toggle_button_new_with_label("GrMix"); gtk_widget_show(button); gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 1); gtk_signal_connect(GTK_OBJECT(button), "toggled", GMixer_button_click, 0); GMw_geom.toggler=button; if (GMw_geom.st==1) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); // Mixer button button=gtk_toggle_button_new_with_label("Mixer"); gtk_widget_show(button); gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 1); gtk_signal_connect(GTK_OBJECT(button), "toggled", ToggleWindow, (gpointer)mixerControl.window); Mixerw_geom.toggler=button; if (Mixerw_geom.st==1) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); } if (vmixerId) { // Vmixer button button=gtk_toggle_button_new_with_label("Vmixer"); gtk_widget_show(button); gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 1); gtk_signal_connect(GTK_OBJECT(button), "toggled", ToggleWindow, (gpointer)vmixerControl.window); Vmixerw_geom.toggler=button; if (Vmixerw_geom.st==1) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); } if (pcmoutId) { // PCM volume button button=gtk_toggle_button_new_with_label("PCM"); gtk_widget_show(button); gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 1); gtk_signal_connect(GTK_OBJECT(button), "toggled", ToggleWindow, (gpointer)pcmoutControl.window); PVw_geom.toggler=button; if (PVw_geom.st==1) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE); } /* ********** GTK-main ********** */ Gang=1; if (dmodeId) Digital_mode_activate(dmodeOpt, (gpointer)(long)dmodeVal); // Also calls SetSensitivity() gtk_widget_show(Mainwindow); gtk_main(); if (save) { FILE *f; if (snprintf(str, 255, "%s/.Emixer_%s", getenv("HOME"), cardId)>0) { str[255]=0; if ((f=fopen(str, "w"))) { fprintf(f, "-- LineOut \n"); for (i=0; i \n"); for (i=0; i \n"); for (i=0; i \n"); for (i=0; i \n"); for (o=0; o \n"); for (o=0; o \n"); for (o=0; o \n"); fprintf(f, "MainWindow %d %d %d %d\n", Mainw_geom.x, Mainw_geom.y, Mainw_geom.w, Mainw_geom.h); if (VUwindow) gdk_window_get_root_origin(VUwindow->window, &VUw_geom.x, &VUw_geom.y); fprintf(f, "VUmetersWindow %d %d %d\n", VUw_geom.x, VUw_geom.y, VUw_geom.st); if (GMwindow) gdk_window_get_root_origin(GMwindow->window, &VUw_geom.x, &VUw_geom.y); fprintf(f, "GfxMixerWindow %d %d %d\n", GMw_geom.x, GMw_geom.y, GMw_geom.st); if (pcmoutId) { if (pcmoutControl.window->window) { gdk_window_get_root_origin(pcmoutControl.window->window, &PVw_geom.x, &PVw_geom.y); gdk_window_get_size(pcmoutControl.window->window, &PVw_geom.w, &PVw_geom.h); } fprintf(f, "PcmVolumeWindow %d %d %d %d %d\n", PVw_geom.x, PVw_geom.y, PVw_geom.w, PVw_geom.h, !!GTK_WIDGET_VISIBLE(pcmoutControl.window)); } if (LVwindow->window) { gdk_window_get_root_origin(LVwindow->window, &LVw_geom.x, &LVw_geom.y); gdk_window_get_size(LVwindow->window, &LVw_geom.w, &LVw_geom.h); } fprintf(f, "LineVolumeWindow %d %d %d %d %d\n", LVw_geom.x, LVw_geom.y, LVw_geom.w, LVw_geom.h, !!GTK_WIDGET_VISIBLE(LVwindow)); if (Miscwindow->window) { gdk_window_get_root_origin(Miscwindow->window, &Miscw_geom.x, &Miscw_geom.y); gdk_window_get_size(Miscwindow->window, &Miscw_geom.w, &Miscw_geom.h); } fprintf(f, "MiscControlsWindow %d %d %d %d %d\n", Miscw_geom.x, Miscw_geom.y, Miscw_geom.w, Miscw_geom.h, !!GTK_WIDGET_VISIBLE(Miscwindow)); if (mixerId) { if (mixerControl.window->window) { gdk_window_get_root_origin(mixerControl.window->window, &Mixerw_geom.x, &Mixerw_geom.y); gdk_window_get_size(mixerControl.window->window, &Mixerw_geom.w, &Mixerw_geom.h); } fprintf(f, "MixerWindow %d %d %d %d %d\n", Mixerw_geom.x, Mixerw_geom.y, Mixerw_geom.w, Mixerw_geom.h, !!GTK_WIDGET_VISIBLE(mixerControl.window)); } if (vmixerId) { if (vmixerControl.window->window) { gdk_window_get_root_origin(vmixerControl.window->window, &Vmixerw_geom.x, &Vmixerw_geom.y); gdk_window_get_size(vmixerControl.window->window, &Vmixerw_geom.w, &Vmixerw_geom.h); } fprintf(f, "VmixerWindow %d %d %d %d %d\n", Vmixerw_geom.x, Vmixerw_geom.y, Vmixerw_geom.w, Vmixerw_geom.h, !!GTK_WIDGET_VISIBLE(vmixerControl.window)); } fprintf(f, "\n"); fclose(f); } } } if (VUwindow) { SetVUmeters(0); gtk_timeout_remove(VUtimer); } if (GMwindow) { SetVUmeters(0); gtk_timeout_remove(Mixtimer); } snd_ctl_close(ctlhandle); return(0); }