--- /dev/null
+/*
+ * ALSA mixer console for Echoaudio soundcards.
+ * Copyright (C) 2003 Giuliano Pochini <pochini@shiny.it>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define EM_VERSION "%s Echomixer v1.0.2"
+
+
+/*******
+ 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 <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+#include <alsa/asoundlib.h>
+
+
+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;
+int lineinId, pcmoutId, lineoutId, mixerId, vmixerId, p4InId, p4OutId, dmodeId, clocksrcId, spdifmodeId, vuswitchId, vumetersId, channelsId, phantomId;
+int metersStreams, metersNumber, metersTypes;
+int outvolCount;
+int mouseY, mouseButton;
+int dmodeVal, clocksrcVal, spdifmodeVal;
+int VUwidth, VUheight, Mixwidth, Mixheight;
+
+#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 Monitor; // The first is always 0
+ int VmixerFirst, VmixerLast;
+ int LineOut; // There is only one row
+} GMixerSection;
+
+struct mixel {
+ int id;
+ int Gain;
+};
+
+snd_ctl_t *ctlhandle;
+
+
+#if __GNUC__ == 3 // gcc 2.x doesn't like unnamed unions inside structures
+
+struct mixerControl_s {
+ union { // Currently selected channels
+ int vchannel;
+ int input;
+ };
+ union { // Number of channels
+ int vchannels;
+ int inputs;
+ };
+ int output, outputs;
+ int id;
+ GtkWidget *window;
+ GtkWidget *volume[ECHO_MAXAUDIOOUTPUTS];
+ GtkWidget *label[ECHO_MAXAUDIOOUTPUTS];
+ GtkObject *adj[ECHO_MAXAUDIOOUTPUTS];
+ GtkWidget *outsel[ECHO_MAXAUDIOOUTPUTS];
+ union {
+ GtkWidget *inpsel[ECHO_MAXAUDIOINPUTS];
+ GtkWidget *vchsel[ECHO_MAXAUDIOOUTPUTS];
+ };
+ struct mixel mixer[ECHO_MAXAUDIOOUTPUTS][ECHO_MAXAUDIOOUTPUTS];
+} mixerControl, vmixerControl;
+
+#else
+
+struct mixerControl_s {
+ int vchannel;
+ int input;
+ int vchannels;
+ int 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;
+
+#endif
+
+
+struct VolumeControl {
+ 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;
+
+GtkWidget *p4dbuOut[ECHO_MAXAUDIOOUTPUTS], *p4dbuIn[ECHO_MAXAUDIOINPUTS]; // +4dBu/-10dBV toggles
+GtkWidget *clocksrc_menuitem[ECHO_CLOCKS];
+GtkWidget *dmodeOpt, *clocksrcOpt, *spdifmodeOpt, *phantomToggle;
+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;
+
+
+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 (*v<ECHOGAIN_MINOUT)
+ *v=ECHOGAIN_MINOUT;
+}
+
+
+
+void ClampInputVolume(int *v) {
+
+ if (*v>ECHOGAIN_MAXINP)
+ *v=ECHOGAIN_MAXINP;
+ else if (*v<ECHOGAIN_MININP)
+ *v=ECHOGAIN_MININP;
+}
+
+
+
+// -128 dB means muted, that is -infinite dB
+int Add_dB (int a, int b) {
+
+ if (a==ECHOGAIN_MINOUT || b==ECHOGAIN_MINOUT)
+ return(ECHOGAIN_MINOUT);
+ a+=b;
+ if (a<ECHOGAIN_MINOUT)
+ return(ECHOGAIN_MINOUT);
+ return(a);
+}
+
+
+
+char *strOutGain(char *s, int g) {
+
+ if (g==ECHOGAIN_MINOUT)
+ strcpy(s, "mute");
+ else
+ sprintf(s, "%+d", g);
+ return(s);
+}
+
+
+
+int ADATmode() {
+ return(!memcmp(dmodeName[dmodeVal], "ADAT", 4));
+}
+
+
+
+// Write an enumerated ALSA control
+int SetEnum(int numid, int val) {
+ int err;
+ 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, numid==clocksrcId ? SND_CTL_ELEM_IFACE_PCM : SND_CTL_ELEM_IFACE_CARD);
+ snd_ctl_elem_id_set_numid(id, numid);
+ snd_ctl_elem_value_set_id(control, id);
+ snd_ctl_elem_value_set_enumerated(control, 0, val);
+ if ((err=snd_ctl_elem_write(ctlhandle, control)) < 0)
+ printf("Control %s element write error: %s\n", card, snd_strerror(err));
+ return(err);
+}
+
+
+
+// Read an enumerated ALSA control
+int GetEnum(int numid) {
+ int err, val;
+ 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, numid==clocksrcId ? SND_CTL_ELEM_IFACE_PCM : SND_CTL_ELEM_IFACE_CARD);
+ 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));
+ val=snd_ctl_elem_value_get_enumerated(control, 0);
+ return(val);
+}
+
+
+
+// Turn VU-meters on/off
+void SetVUmeters(int onoff) {
+ static signed char oncount=0;
+ int err;
+ snd_ctl_elem_id_t *id;
+ snd_ctl_elem_value_t *control;
+
+ if (onoff)
+ oncount++;
+ else
+ if (--oncount<0)
+ oncount=0;
+ snd_ctl_elem_id_alloca(&id);
+ snd_ctl_elem_value_alloca(&control);
+ snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_CARD);
+ snd_ctl_elem_id_set_numid(id, vuswitchId);
+ snd_ctl_elem_value_set_id(control, id);
+ snd_ctl_elem_value_set_integer(control, 0, !!oncount);
+ if ((err=snd_ctl_elem_write(ctlhandle, control)) < 0) {
+ printf("Control %s element write error: %s\n", card, snd_strerror(err));
+ }
+}
+
+
+
+void GetVUmeters(int *InLevel, int *InPeak, int *OutLevel, int *OutPeak, int *VirLevel, int *VirPeak) {
+ int err, i, m;
+ 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, vumetersId);
+ 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;
+ }
+
+ m=0;
+ for (i=0; i<nLOut; i++) {
+ OutLevel[i]=snd_ctl_elem_value_get_integer(control, m++);
+ OutPeak[i]=snd_ctl_elem_value_get_integer(control, m++);
+ }
+
+ m=1*metersNumber*metersTypes;
+ for (i=0; i<nIn; i++) {
+ InLevel[i]=snd_ctl_elem_value_get_integer(control, m++);
+ InPeak[i]=snd_ctl_elem_value_get_integer(control, m++);
+ }
+
+ if (metersStreams==3) { // Has PCM levels (Mia only) ?
+ m=2*metersNumber*metersTypes;
+#ifdef REAL
+ for (i=0; i<nPOut; i++) {
+ VirLevel[i]=snd_ctl_elem_value_get_integer(control, m++);
+ VirPeak[i]=snd_ctl_elem_value_get_integer(control, m++);
+ }
+#else
+ for (i=0; i<nPOut; i++) {
+ VirLevel[i]=-20;
+ VirPeak[i]=-10;
+ }
+#endif
+ }
+}
+
+
+
+#ifdef REVERSE
+
+// Enable/disable widgets that control ADAT digital channels
+void SetSensitivity(int enable) {
+ int i;
+
+ for (i=fdOut+2; i<nLOut; i++) {
+ if (!enable && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(mixerControl.outsel[i])))
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mixerControl.outsel[0]), TRUE);
+ if (mixerId)
+ gtk_widget_set_sensitive(mixerControl.outsel[i], enable);
+ if (vmixerId) {
+ if (!enable && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(vmixerControl.outsel[i])))
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(vmixerControl.outsel[0]), TRUE);
+ gtk_widget_set_sensitive(vmixerControl.outsel[i], enable);
+ }
+ if (pcmoutId) {
+ gtk_widget_set_sensitive(pcmoutControl.label[i], enable);
+ gtk_widget_set_sensitive(pcmoutControl.volume[i], enable);
+ }
+ // Line-out control is always present
+ gtk_widget_set_sensitive(lineoutControl.label[i], enable);
+ gtk_widget_set_sensitive(lineoutControl.volume[i], enable);
+ }
+ for (i=fdIn+2; i<nIn; i++) {
+ gtk_widget_set_sensitive(mixerControl.label[i], enable);
+ gtk_widget_set_sensitive(mixerControl.volume[i], enable);
+ }
+ if (!enable && mixerControl.input>=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<nIn; i++) {
+ if (!enable && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(mixerControl.inpsel[i])))
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mixerControl.inpsel[0]), TRUE);
+ gtk_widget_set_sensitive(mixerControl.inpsel[i], enable);
+ }
+ for (i=fdOut+2; i<nLOut; i++) {
+ if (mixerId) {
+ gtk_widget_set_sensitive(mixerControl.label[i], enable);
+ gtk_widget_set_sensitive(mixerControl.volume[i], enable);
+ }
+ if (vmixerId) {
+ gtk_widget_set_sensitive(vmixerControl.label[i], enable);
+ gtk_widget_set_sensitive(vmixerControl.volume[i], enable);
+ }
+ if (pcmoutId) {
+ gtk_widget_set_sensitive(pcmoutControl.label[i], enable);
+ gtk_widget_set_sensitive(pcmoutControl.volume[i], enable);
+ }
+ // Line-out control is always present
+ gtk_widget_set_sensitive(lineoutControl.label[i], enable);
+ gtk_widget_set_sensitive(lineoutControl.volume[i], enable);
+ }
+ if (!enable && mixerControl.output>=fdOut+2)
+ mixerControl.output=0;
+ if (vmixerId && !enable && vmixerControl.output>=fdOut+2)
+ vmixerControl.output=0;
+}
+
+#endif // REVERSE
+
+
+// At startup this functions reads the current nominal levels and sets the switches accordingly.
+void InitNominalLevelGUI(int numid) {
+ snd_ctl_elem_id_t *id;
+ snd_ctl_elem_value_t *control;
+ int err, i, n;
+ GtkWidget **w;
+
+ snd_ctl_elem_id_alloca(&id);
+ snd_ctl_elem_value_alloca(&control);
+
+ snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
+ if (numid==p4InId) {
+ snd_ctl_elem_id_set_numid(id, p4InId);
+ n=fdIn;
+ w=p4dbuIn;
+ } else {
+ snd_ctl_elem_id_set_numid(id, p4OutId);
+ n=fdOut;
+ w=p4dbuOut;
+ }
+ 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; i<n; i++)
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w[i]), !snd_ctl_elem_value_get_integer(control, i)); // FALSE is +4 here
+}
+
+
+
+// At startup this functions reads if the dithering is enabled sets the button accordingly.
+void InitPhantomPowerGUI(int numid) {
+ 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_CARD);
+ snd_ctl_elem_id_set_numid(id, phantomId);
+ 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));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(phantomToggle), snd_ctl_elem_value_get_integer(control, 0));
+}
+
+
+
+// Read current control settings.
+void ReadControl(int *vol, int channels, int volId) {
+ 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, SND_CTL_ELEM_IFACE_MIXER);
+ snd_ctl_elem_id_set_numid(id, volId);
+ 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;
+ }
+
+ for (ch=0; ch<channels; ch++)
+ vol[ch]=snd_ctl_elem_value_get_integer(control, ch);
+}
+
+
+
+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; out<mixer->outputs; out++) {
+ for (in=0; in<mixer->inputs; 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)
+ }
+ ClockMask=snd_ctl_elem_value_get_integer(control, 5); // Bitmask of available input clocks
+}
+
+
+
+// Read what input clocks are valid and sets the pop-down menu accordingly
+gint CheckInputs(gpointer unused) {
+ int i;
+
+ GetChannels();
+ for (i=0; i<nclocksrc; i++)
+ gtk_widget_set_sensitive(clocksrc_menuitem[i], !!(ClockMask & (1<<i)));
+ return(TRUE);
+}
+
+
+
+// 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];//maxpipes
+ int VirPeak[ECHO_MAXAUDIOOUTPUTS];
+ static int InClip[ECHO_MAXAUDIOINPUTS];
+ static int OutClip[ECHO_MAXAUDIOOUTPUTS];
+ char str[8];
+ int i, o, y, dB, db, inchannels, outchannels;
+ GdkColor Grid={0x787878, 0, 0, 0};
+ GdkColor Labels={0x9694C4, 0, 0, 0};
+ GdkColor Bars={0x00FF00, 0, 0, 0};
+ GdkColor Bars1={0x000000, 0, 0, 0};
+ GdkColor Peak={0x1BABFF, 0, 0, 0};
+ GdkColor Level={0xC0B000, 0, 0, 0};
+ GdkColor Hilight={0x000078, 0, 0, 0};
+ GdkColor Hilight2={0x600000, 0, 0, 0};
+
+ if (ADATmode()) {
+ inchannels=nIn;
+ outchannels=nLOut;
+ } else {
+ inchannels=fdIn+2;
+ outchannels=fdOut+2;
+ }
+
+ 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; i<nIn; i++)
+ InClip[i]=0;
+ for (i=0; i<nLOut; i++)
+ OutClip[i]=0;
+ }
+
+ gdk_draw_rectangle(Mixpixmap, Mixdarea->style->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.vchannel), XCELLTOT*(vmixerControl.output+1), YCELLTOT);
+ gdk_draw_rectangle(Mixpixmap, gc, TRUE, XCELLTOT*(vmixerControl.output+1), YCELLTOT*(GMixerSection.VmixerFirst+vmixerControl.vchannel), XCELLTOT, Mixheight);
+ }
+
+ // Draw the grid
+ gdk_gc_set_font(gc, fnt);
+ // Horizontal lines and input channel labels
+ for (i=0; i<GMixerSection.LineOut; i++) {
+ gdk_gc_set_foreground(gc, &Grid);
+ gdk_draw_rectangle(Mixpixmap, gc, TRUE, 0, YCELLTOT*(i+1)-1, Mixwidth, 1);
+ if (i<fdIn)
+ sprintf(str, "A%d", i); // Analog
+ else if (i<nIn)
+ sprintf(str, "D%d", i-fdIn); // Digital
+ else
+ sprintf(str, "V%d", i-nIn); // Virtual
+ gdk_gc_set_foreground(gc, &Labels);
+ gdk_draw_string(Mixpixmap, fnt, gc, 1, YCELLTOT*i+(YCELLTOT/2)+4, str);
+ }
+ // Vertical lines and output channel labels
+ for (o=0; o<nLOut; o++) {
+ gdk_gc_set_foreground(gc, &Grid);
+ gdk_draw_rectangle(Mixpixmap, gc, TRUE, XCELLTOT*(o+1), 0, 1, Mixheight);
+ if (o<fdOut)
+ sprintf(str, "A%d", o);
+ else
+ sprintf(str, "D%d", o-fdOut);
+ gdk_gc_set_foreground(gc, &Labels);
+ gdk_draw_string(Mixpixmap, fnt, gc, XCELLTOT*(o+1)+(XCELLTOT/2)-6, YCELLTOT*GMixerSection.LineOut+YCELLTOT+8, str);
+ }
+ gdk_draw_string(Mixpixmap, fnt, gc, 1, 8, "In");
+ gdk_draw_string(Mixpixmap, fnt, gc, 1, YCELLTOT*GMixerSection.LineOut+YCELLTOT+8, "Out");
+ gdk_gc_set_foreground(gc, &Grid);
+ gdk_draw_rectangle(Mixpixmap, gc, TRUE, 0, YCELLTOT*(GMixerSection.LineOut+1)-1, Mixwidth, 1);
+
+ // Draw input levels and peaks
+ for (i=0; i<inchannels; i++) {
+ y=YCELLTOT*i+YCELLBORDER;
+ dB=InLevel[i];
+ db=dB>>2;
+ gdk_gc_set_foreground(gc, &Bars);
+ gdk_draw_rectangle(Mixpixmap, gc, TRUE, XMETER, y-db, GM_BARWIDTH, YCELLDIM+db);
+ if ((Bars1.pixel=(((dB&3)*0x40)<<8))) {
+ gdk_gc_set_foreground(gc, &Bars1);
+ gdk_draw_rectangle(Mixpixmap, gc, TRUE, XMETER, y-db-1, GM_BARWIDTH, 1);
+ }
+ }
+ gdk_gc_set_foreground(gc, &Peak);
+ for (i=0; i<inchannels; i++) {
+ db=InPeak[i]>>2;
+ gdk_draw_rectangle(Mixpixmap, gc, TRUE, XMETER, YCELLTOT*i+YCELLBORDER-db, GM_BARWIDTH, 1);
+ }
+
+ // Draw vchannels levels and peaks (Vmixer cards only)
+ if (vmixerId) {
+ for (i=0; i<vmixerControl.vchannels; i++) {
+ y=YCELLTOT*(i+GMixerSection.VmixerFirst)+YCELLBORDER;
+ dB=VirLevel[i];
+ db=dB>>2;
+ gdk_gc_set_foreground(gc, &Bars);
+ gdk_draw_rectangle(Mixpixmap, gc, TRUE, XMETER, y-db, GM_BARWIDTH, YCELLDIM+db);
+ if ((Bars1.pixel=(((dB&3)*0x40)<<8))) {
+ gdk_gc_set_foreground(gc, &Bars1);
+ gdk_draw_rectangle(Mixpixmap, gc, TRUE, XMETER, y-db-1, GM_BARWIDTH, 1);
+ }
+ }
+ gdk_gc_set_foreground(gc, &Peak);
+ for (i=0; i<vmixerControl.vchannels; i++) {
+ db=VirPeak[i]>>2;
+ gdk_draw_rectangle(Mixpixmap, gc, TRUE, XMETER, YCELLTOT*(i+GMixerSection.VmixerFirst)+YCELLBORDER-db, GM_BARWIDTH, 1);
+ }
+ }
+
+ // Draw output levels, peaks and volumes
+ y=YCELLTOT*GMixerSection.LineOut+YCELLBORDER;
+ for (o=0; o<outchannels; o++) {
+ dB=OutLevel[o];
+ db=dB>>2;
+ gdk_gc_set_foreground(gc, &Bars);
+ gdk_draw_rectangle(Mixpixmap, gc, TRUE, XMETER+XCELLTOT*(o+1), y-db, GM_BARWIDTH, YCELLDIM+db);
+ if ((Bars1.pixel=(((dB&3)*0x40)<<8))) {
+ gdk_gc_set_foreground(gc, &Bars1);
+ gdk_draw_rectangle(Mixpixmap, gc, TRUE, XMETER+XCELLTOT*(o+1), y-db-1, GM_BARWIDTH, 1);
+ }
+ db=OutPeak[o]>>2;
+ gdk_gc_set_foreground(gc, &Peak);
+ gdk_draw_rectangle(Mixpixmap, gc, TRUE, XMETER+XCELLTOT*(o+1), y-db, GM_BARWIDTH, 1);
+ gdk_gc_set_foreground(gc, &Level);
+ gdk_draw_rectangle(Mixpixmap, gc, TRUE, XVOLUME+XCELLTOT*(o+1), y, 1, YCELLDIM);
+ db=lineoutControl.Gain[o]>>2;
+ gdk_draw_rectangle(Mixpixmap, gc, TRUE, XVOLUME-2+XCELLTOT*(o+1), y-db, 5, 1);
+ }
+
+ // Draw monitor mixer elements
+ for (o=0; o<outchannels; o++) {
+ for (i=0; i<inchannels; i++) {
+ y=YCELLTOT*i+YCELLBORDER;
+ dB=Add_dB(mixerControl.mixer[o][i].Gain, InLevel[i]);
+ db=dB>>2;
+ if (db<-YCELLDIM)
+ db=-YCELLDIM;
+ gdk_gc_set_foreground(gc, &Bars);
+ gdk_draw_rectangle(Mixpixmap, gc, TRUE, XMETER+XCELLTOT*(o+1), y-db, GM_BARWIDTH, YCELLDIM+db);
+ if (dB>-(YCELLDIM<<2))
+ if ((Bars1.pixel=(((dB&3)*0x40)<<8))) {
+ gdk_gc_set_foreground(gc, &Bars1);
+ gdk_draw_rectangle(Mixpixmap, gc, TRUE, XMETER+XCELLTOT*(o+1), y-db-1, GM_BARWIDTH, 1);
+ }
+ gdk_gc_set_foreground(gc, &Level);
+ gdk_draw_rectangle(Mixpixmap, gc, TRUE, XVOLUME+XCELLTOT*(o+1), y, 1, YCELLDIM);
+ db=mixerControl.mixer[o][i].Gain>>2;
+ gdk_draw_rectangle(Mixpixmap, gc, TRUE, XVOLUME-2+XCELLTOT*(o+1), y-db, 5, 1);
+ }
+ }
+
+ // Draw vmixer elements
+ if (vmixerId) {
+ for (o=0; o<outchannels; o++) {
+ for (i=0; i<vmixerControl.vchannels; i++) {
+ y=YCELLTOT*(i+GMixerSection.VmixerFirst)+YCELLBORDER;
+ dB=Add_dB(vmixerControl.mixer[o][i].Gain, VirLevel[i]);
+ db=dB>>2;
+ if (db<-YCELLDIM)
+ db=-YCELLDIM;
+ gdk_gc_set_foreground(gc, &Bars);
+ gdk_draw_rectangle(Mixpixmap, gc, TRUE, XMETER+XCELLTOT*(o+1), y-db, GM_BARWIDTH, YCELLDIM+db);
+ if (dB>-(YCELLDIM<<2))
+ if ((Bars1.pixel=(((dB&3)*0x40)<<8))) {
+ gdk_gc_set_foreground(gc, &Bars1);
+ gdk_draw_rectangle(Mixpixmap, gc, TRUE, XMETER+XCELLTOT*(o+1), y-db-1, GM_BARWIDTH, 1);
+ }
+ gdk_gc_set_foreground(gc, &Level);
+ gdk_draw_rectangle(Mixpixmap, gc, TRUE, XVOLUME+XCELLTOT*(o+1), y, 1, YCELLDIM);
+ db=vmixerControl.mixer[o][i].Gain>>2;
+ gdk_draw_rectangle(Mixpixmap, gc, TRUE, XVOLUME-2+XCELLTOT*(o+1), y-db, 5, 1);
+ }
+ }
+ }
+
+ gtk_widget_draw(Mixdarea, &update_rect);
+ return(TRUE);
+}
+
+
+
+// Draw the VU-meter
+gint DrawVUmeters(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];//maxpipes
+ int VirPeak[ECHO_MAXAUDIOOUTPUTS];
+ static int InClip[ECHO_MAXAUDIOINPUTS];
+ static int OutClip[ECHO_MAXAUDIOOUTPUTS];
+ int i, x, dB;
+ char str[16];
+ GdkColor Selected={0xC86060, 0, 0, 0};
+ GdkColor Grid={0x9694C4, 0, 0, 0};
+ GdkColor Grid2={0x646383, 0, 0, 0};
+ GdkColor dBValues={0x00B000, 0, 0, 0};
+ GdkColor AnBars={0x00E0B8, 0, 0, 0};
+ GdkColor DiBars={0x98E000, 0, 0, 0};
+ GdkColor ClipPeak={0, 0, 0, 0};
+ GdkColor Peak={0x00FF00, 0, 0, 0};
+
+ if (!VUpixmap)
+ return(TRUE);
+
+ update_rect.x = 0;
+ update_rect.y = 0;
+ update_rect.width = VUwidth;
+ update_rect.height = VUheight;
+ GetVUmeters(InLevel, InPeak, OutLevel, OutPeak, VirLevel, VirPeak);
+
+ if (!gc) {
+ gc=gdk_gc_new(gtk_widget_get_parent_window(VUdarea));
+ for (i=0; i<nIn; i++)
+ InClip[i]=0;
+ for (i=0; i<nLOut; i++)
+ OutClip[i]=0;
+ }
+
+ // Clear the image
+ gdk_draw_rectangle(VUpixmap, VUdarea->style->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; i<nIn; i++) {
+ if (i<fdIn)
+ gdk_gc_set_foreground(gc, &AnBars);
+ else
+ gdk_gc_set_foreground(gc, &DiBars);
+ dB=InLevel[i];
+ gdk_draw_rectangle(VUpixmap, gc, TRUE, x, VU_YGRAF-dB, VU_BARWIDTH, 129+VU_YGRAF-(VU_YGRAF-dB));
+
+ dB=InPeak[i];
+ if (dB==0)
+ InClip[i]=64;
+ if (InClip[i]) {
+ InClip[i]--;
+ ClipPeak.pixel=(InClip[i]<<18)+((255-(InClip[i]*3))<<8);
+ gdk_gc_set_foreground(gc, &ClipPeak);
+ } else {
+ gdk_gc_set_foreground(gc, &Peak);
+ }
+ gdk_draw_rectangle(VUpixmap, gc, TRUE, x, VU_YGRAF-dB, VU_BARWIDTH, 1);
+ if (mixerControl.input==i) {
+ gdk_gc_set_foreground(gc, &Selected);
+ gdk_draw_rectangle(VUpixmap, gc, TRUE, x+1, VU_YGRAF+128+3, VU_BARWIDTH-2, 1);
+ gdk_draw_rectangle(VUpixmap, gc, TRUE, x, VU_YGRAF+128+4, VU_BARWIDTH, 1);
+ }
+ x+=VU_BARWIDTH+VU_BARSEP;
+ }
+
+ // Draw outputs
+ x+=VU_BARWIDTH+VU_BARSEP;
+ for (i=0; i<nLOut; i++) {
+ if (i<fdOut)
+ gdk_gc_set_foreground(gc, &AnBars);
+ else
+ gdk_gc_set_foreground(gc, &DiBars);
+ dB=OutLevel[i];
+ gdk_draw_rectangle(VUpixmap, gc, TRUE, x, VU_YGRAF-dB, VU_BARWIDTH, 129+VU_YGRAF-(VU_YGRAF-dB));
+
+ dB=OutPeak[i];
+ if (dB==0)
+ OutClip[i]=64;
+ if (OutClip[i]) {
+ OutClip[i]--;
+ ClipPeak.pixel=(OutClip[i]<<18)+((255-(OutClip[i]*3))<<8);
+ gdk_gc_set_foreground(gc, &ClipPeak);
+ } else {
+ gdk_gc_set_foreground(gc, &Peak);
+ }
+ gdk_draw_rectangle(VUpixmap, gc, TRUE, x, VU_YGRAF-dB, VU_BARWIDTH, 1);
+ if (mixerControl.output==i) {
+ gdk_gc_set_foreground(gc, &Selected);
+ gdk_draw_rectangle(VUpixmap, gc, TRUE, x+1, VU_YGRAF+128+3, VU_BARWIDTH-2, 1);
+ gdk_draw_rectangle(VUpixmap, gc, TRUE, x, VU_YGRAF+128+4, VU_BARWIDTH, 1);
+ }
+ x+=VU_BARWIDTH+VU_BARSEP;
+ }
+
+ gtk_widget_draw(VUdarea, &update_rect);
+
+ return(TRUE);
+}
+
+
+
+///////////////////// GUI events
+
+
+#ifdef REVERSE
+
+void Mixer_Output_selector_clicked(GtkWidget *widget, gpointer och) {
+ int ich, val;
+ snd_ctl_elem_id_t *id;
+ snd_ctl_elem_value_t *control;
+
+ if (mixerControl.output==(int)och)
+ return;
+
+ mixerControl.output=(int)och;
+ 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 (ich=0; ich<nIn; ich++) {
+ val=INVERT(mixerControl.mixer[mixerControl.output][ich].Gain);
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(mixerControl.adj[ich]), (gfloat)val);
+ }
+}
+
+#else // REVERSE
+
+void Mixer_Input_selector_clicked(GtkWidget *widget, gpointer ich) {
+ int och, val;
+ snd_ctl_elem_id_t *id;
+ snd_ctl_elem_value_t *control;
+
+ if (mixerControl.input==(int)ich)
+ return;
+
+ mixerControl.input=(int)ich;
+ 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 (och=0; och<nLOut; och++) {
+ val=INVERT(mixerControl.mixer[och][mixerControl.input].Gain);
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(mixerControl.adj[och]), (gfloat)val);
+ }
+}
+
+#endif // REVERSE
+
+
+#ifdef REVERSE
+
+static gint Gmixer_button_press(GtkWidget *widget, GdkEventButton *event) {
+
+ GMixerRow=(int)event->y/YCELLTOT;
+ GMixerColumn=(int)event->x/XCELLTOT-1;
+
+ /* 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.Monitor) {
+ if (GMixerColumn!=mixerControl.output && GMixerColumn>=0) {
+ if (GMixerColumn<fdOut+2 || (ADATmode() && GMixerColumn<nLOut))
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mixerControl.outsel[GMixerColumn]), TRUE);
+ }
+ if (GMixerRow!=mixerControl.input)
+ gtk_widget_grab_focus(GTK_WIDGET(mixerControl.volume[GMixerRow]));
+ } else if (GMixerRow>=GMixerSection.VmixerFirst && GMixerRow<=GMixerSection.VmixerLast) {
+ if (GMixerColumn!=vmixerControl.output && GMixerColumn>=0) {
+ if (GMixerColumn<fdOut+2 || (ADATmode() && GMixerColumn<vmixerControl.outputs))
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(vmixerControl.outsel[GMixerColumn]), TRUE);
+ }
+ if (GMixerRow!=vmixerControl.vchannel)
+ 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;
+
+ // See the note above
+ if (GMixerRow<=GMixerSection.Monitor) {
+ if (GMixerRow!=mixerControl.input)
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mixerControl.inpsel[GMixerRow]), TRUE);
+ if (GMixerColumn!=mixerControl.output && GMixerColumn>=0) {
+ if (GMixerColumn<fdOut+2 || (ADATmode() && GMixerColumn<nLOut))
+ gtk_widget_grab_focus(GTK_WIDGET(mixerControl.volume[GMixerColumn]));
+ }
+ } else if (GMixerRow>=GMixerSection.VmixerFirst && GMixerRow<=GMixerSection.VmixerLast) {
+ if (GMixerRow!=vmixerControl.vchannel)
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(vmixerControl.vchsel[GMixerRow-GMixerSection.VmixerFirst]), TRUE);
+ if (GMixerColumn!=vmixerControl.output && GMixerColumn>=0) {
+ if (GMixerColumn<fdOut+2 || (ADATmode() && GMixerColumn<vmixerControl.outputs))
+ 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;
+}
+
+
+
+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.
+ if (!(state & GDK_BUTTON1_MASK))
+ mouseButton=0;
+
+ if (GMixerRow<=GMixerSection.Monitor) {
+ if (mouseButton && Mixpixmap != NULL) {
+ val=INVERT(mixerControl.mixer[mixerControl.output][mixerControl.input].Gain);
+ val+=y-mouseY;
+ mouseY=y;
+ // Gtk already limits the range of "val"
+#ifdef REVERSE
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(mixerControl.adj[mixerControl.input]), (gfloat)val);
+#else
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(mixerControl.adj[mixerControl.output]), (gfloat)val);
+#endif
+ }
+ } else if (GMixerRow>=GMixerSection.VmixerFirst && GMixerRow<=GMixerSection.VmixerLast) {
+ if (mouseButton && Mixpixmap != NULL) {
+ val=INVERT(vmixerControl.mixer[vmixerControl.output][vmixerControl.vchannel].Gain);
+ val+=y-mouseY;
+ mouseY=y;
+ // Gtk already limits the range of "val"
+#ifdef REVERSE
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(vmixerControl.adj[vmixerControl.vchannel]), (gfloat)val);
+#else
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(vmixerControl.adj[vmixerControl.output]), (gfloat)val);
+#endif
+ }
+ } else if (GMixerRow==GMixerSection.LineOut) {
+ if (mouseButton && Mixpixmap != NULL) {
+ val=INVERT(lineoutControl.Gain[GMixerColumn]);
+ val+=y-mouseY;
+ mouseY=y;
+ // Gtk already limits the range of "val"
+ 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)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)ch;
+#else
+ mixerControl.output=(int)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 *vol;
+
+ snd_ctl_elem_id_alloca(&id);
+ snd_ctl_elem_value_alloca(&control);
+
+ if ((int)ch<ECHO_MAXAUDIOINPUTS) {
+ // Input
+ channel=(int)ch;
+ vol=&lineinControl;
+ rval=val=IN_INVERT((int)GTK_ADJUSTMENT(widget)->value);
+ sprintf(str, "%+4.1f", 0.5*val);
+ } else {
+ // Output
+ channel=(int)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; ch<nIn; ch++) {
+ val=Add_dB(mixerControl.mixer[outchannel][ch].Gain, lineoutControl.Gain[outchannel]);
+ ClampOutputVolume(&val);
+ snd_ctl_elem_id_set_numid(id, mixerControl.mixer[outchannel][ch].id);
+ snd_ctl_elem_value_set_id(control, id);
+ snd_ctl_elem_value_set_integer(control, 0, val);
+ if ((err = snd_ctl_elem_write(ctlhandle, control)) < 0)
+ printf("Control %s element write error: %s\n", card, snd_strerror(err));
+ }
+}
+
+
+
+void LineOut_volume_changed(GtkWidget *widget, gpointer ch) {
+ char str[16];
+ int err, channel, val;
+
+ channel=(int)ch;
+
+ val=INVERT((int)GTK_ADJUSTMENT(widget)->value);
+ 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 { // 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, channel;
+ int o, v;
+
+ channel=(int)ch;
+
+#ifdef REVERSE
+ v=channel;
+ o=vmixerControl.output;
+#else
+ v=vmixerControl.vchannel;
+ o=channel;
+#endif
+
+ val=INVERT((int)GTK_ADJUSTMENT(widget)->value);
+
+ SetMixerGain(&vmixerControl.mixer[o][v], val);
+ vmixerControl.mixer[o][v].Gain=val;
+
+ if (Gang) {
+ SetMixerGain(&vmixerControl.mixer[o^1][v^1], val);
+ 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.vchannel=(int)ch;
+ UI_DEBUG(("Vmixer_volume_clicked vch=%d\n",vmixerControl.vchannel));
+#else
+ vmixerControl.output=(int)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.vchannels-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.vchannel==(int)ch)
+ return;
+ vmixerControl.vchannel=(int)ch;
+
+ UI_DEBUG(("Vmixer_selector_clicked vch=%d\n",vmixerControl.vchannel));
+ 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.vchannel].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;
+ GtkWidget **button;
+ int err, val, channel;
+
+ snd_ctl_elem_id_alloca(&id);
+ snd_ctl_elem_value_alloca(&control);
+
+ val=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
+
+ snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
+ if ((int)ch<ECHO_MAXAUDIOINPUTS) {
+ channel=(int)ch;
+ button=p4dbuIn;
+ snd_ctl_elem_id_set_numid(id, p4InId);
+ } else {
+ channel=(int)ch-ECHO_MAXAUDIOINPUTS;
+ button=p4dbuOut;
+ snd_ctl_elem_id_set_numid(id, p4OutId);
+ }
+ 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(button[channel^1]), val);
+}
+
+
+
+void PhantomPower_toggled(GtkWidget *widget, gpointer unused) {
+ 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);
+
+ val=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, phantomId);
+ snd_ctl_elem_value_set_id(control, id);
+ snd_ctl_elem_value_set_integer(control, 0, val);
+ if ((err=snd_ctl_elem_write(ctlhandle, control))<0) {
+ printf("Control %s element write error: %s\n", card, snd_strerror(err));
+ }
+}
+
+
+
+void Digital_mode_activate(GtkWidget *widget, gpointer mode) {
+ int adat;
+
+ if (SetEnum(dmodeId, (int)mode)<0)
+ // Restore old value if it failed
+ gtk_option_menu_set_history(GTK_OPTION_MENU(dmodeOpt), dmodeVal);
+ else {
+ dmodeVal=(int)mode;
+ // When I change the digital mode, the clock source can change too
+ gtk_option_menu_set_history(GTK_OPTION_MENU(clocksrcOpt), clocksrcVal=GetEnum(clocksrcId));
+ }
+ adat=ADATmode();
+ SetSensitivity(adat);
+ if (adat)
+ GMixerSection.Monitor=nIn-1;
+ else
+ GMixerSection.Monitor=fdIn+2-1; // S/PDIF has only 2 channels
+}
+
+
+
+void Clock_source_activate(GtkWidget *widget, gpointer clk) {
+
+ if (SetEnum(clocksrcId, (int)clk)<0)
+ gtk_option_menu_set_history(GTK_OPTION_MENU(clocksrcOpt), clocksrcVal);
+ else
+ clocksrcVal=(int)clk;
+}
+
+
+
+void SPDIF_mode_activate(GtkWidget *widget, gpointer mode) {
+
+ SetEnum(spdifmodeId, (int)mode); // This one should never fail
+ spdifmodeVal=(int)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));
+}
+
+
+
+/*
+int LockControls(snd_ctl_elem_id_t *id, int first_id, int num) {
+ int i, err;
+
+ for (i=0; i<num; i++) {
+ snd_ctl_elem_id_set_numid(id, first_id+i);
+ err=snd_ctl_elem_lock(ctlhandle, id);
+ if (err)
+ return(err);
+ }
+ return(0);
+}
+
+int LockControls(int first_id, int num) {
+ int i, err;
+ snd_ctl_elem_id_t *id;
+
+ snd_ctl_elem_id_alloca(&id);
+ snd_ctl_elem_id_set_interface(id, first_id==clocksrcId ? SND_CTL_ELEM_IFACE_PCM : SND_CTL_ELEM_IFACE_CARD);
+
+ for (i=0; i<num; i++) {
+ snd_ctl_elem_id_set_numid(id, first_id+i);
+ err=snd_ctl_elem_lock(ctlhandle, id);
+ if (err)
+ return(err);
+ }
+ return(0);
+}
+*/
+
+
+
+// Scan all controls and store their ID's for later use.
+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));
+ memset(&lineinControl, 0, sizeof(struct VolumeControl));
+ memset(&pcmoutControl, 0, sizeof(struct VolumeControl));
+ 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));
+ mixerControl.outputs=snd_ctl_elem_info_get_dimension(info, 0);
+ mixerControl.inputs=snd_ctl_elem_info_get_dimension(info, 1);
+ }
+ } 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));
+ vmixerControl.outputs=snd_ctl_elem_info_get_dimension(info, 0);
+ vmixerControl.vchannels=snd_ctl_elem_info_get_dimension(info, 1);
+ }
+ } else if (!strcmp("PCM Playback Volume", snd_ctl_elem_id_get_name(id))) {
+ pcmoutId=pcmoutControl.id=numid;
+//printf("*** %d\n", LockControls(id, numid, 1));
+//#warning ************* usare id giร pronto ? controllo errori, eccc.
+ 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=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=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; item<items; item++) {
+ snd_ctl_elem_info_set_item(info, item);
+ if ((err=snd_hctl_elem_info(elem, info)) < 0) {
+ printf("Control %s element info error: %s\n", card, snd_strerror(err));
+ exit(err);
+ }
+ strncpy(dmodeName[item], snd_ctl_elem_info_get_item_name(info), 63);
+ dmodeName[item][63]=0;
+ CTLID_DEBUG(("Digital Mode id=%d item #%u '%s'\n", numid, item, snd_ctl_elem_info_get_item_name(info)));
+ }
+ } else if (!strcmp("Sample Clock Source", snd_ctl_elem_id_get_name(id))) {
+ clocksrcId=numid;
+ items=snd_ctl_elem_info_get_items(info);
+ nclocksrc=items;
+ for (item=0; item<items; item++) {
+ snd_ctl_elem_info_set_item(info, item);
+ if ((err=snd_hctl_elem_info(elem, info))<0) {
+ printf("Control %s element info error: %s\n", card, snd_strerror(err));
+ exit(err);
+ }
+ strncpy(clocksrcName[item], snd_ctl_elem_info_get_item_name(info), 63);
+ clocksrcName[item][63]=0;
+ CTLID_DEBUG(("Clock source id=%d item #%u '%s'\n", numid, item, snd_ctl_elem_info_get_item_name(info)));
+ }
+ } else if (!strcmp("S/PDIF mode Switch", snd_ctl_elem_id_get_name(id))) {
+ spdifmodeId=numid;
+ items=snd_ctl_elem_info_get_items(info);
+ nspdifmodes=items;
+ for (item=0; item<items; item++) {
+ snd_ctl_elem_info_set_item(info, item);
+ if ((err=snd_hctl_elem_info(elem, info)) < 0) {
+ printf("Control %s element info error: %s\n", card, snd_strerror(err));
+ }
+ strncpy(spdifmodeName[item], snd_ctl_elem_info_get_item_name(info), 63);
+ spdifmodeName[item][63]=0;
+ CTLID_DEBUG(("S/PDIF Mode id=%d item #%u '%s'\n", numid, item, snd_ctl_elem_info_get_item_name(info)));
+ }
+ } else if (!strcmp("Phantom power Switch", snd_ctl_elem_id_get_name(id))) {
+ phantomId=numid;
+ CTLID_DEBUG(("Phantom power Switch id=%d\n", numid));
+ } else if (!strcmp("VU-meters Switch", snd_ctl_elem_id_get_name(id))) {
+ vuswitchId=numid;
+ CTLID_DEBUG(("VU-meter switch id=%d\n", numid));
+ } else if (!strcmp("VU-meters", snd_ctl_elem_id_get_name(id))) {
+ vumetersId=numid;
+ metersStreams=snd_ctl_elem_info_get_dimension(info, 0); // 2 or 3: output, input and (vmixer cards only) pcm
+ metersNumber=snd_ctl_elem_info_get_dimension(info, 1); // Number of channels
+ metersTypes=snd_ctl_elem_info_get_dimension(info, 2); // 2: level and peak
+ CTLID_DEBUG(("VU-meters id=%d\n", numid));
+ } else if (!strcmp("Channels info", snd_ctl_elem_id_get_name(id))) {
+ channelsId=numid;
+ CTLID_DEBUG(("Channels info id=%d\n", numid));
+ }
+ }
+
+ GetChannels();
+ CTLID_DEBUG(("Input channels = %d (analog=%d digital=%d)\n", nIn, fdIn, nIn-fdIn));
+ CTLID_DEBUG(("Output channels = %d (analog=%d digital=%d)\n", nLOut, fdOut, nLOut-fdOut));
+ CTLID_DEBUG(("PCM channels out = %d\n", nPOut));
+
+#ifndef REAL
+vmixerId=1000;
+vmixerControl.vchannels=12;
+vmixerControl.outputs=mixerControl.outputs=10;
+metersStreams=3;
+metersNumber=16;
+metersTypes=2;
+nPOut=12;
+nLOut=10;
+fdOut=2;
+nIn=10;
+fdIn=2;
+printf("nIn=%d fdIn=%d nLOut=%d nPOut=%d fdOut=%d\n", nIn,fdIn,nLOut,nPOut, fdOut);
+#endif
+
+ if (mixerId && (mixerControl.inputs!=nIn || mixerControl.outputs!=nLOut)) {
+ printf("** Error - Mixer/channels mismatch !! nIn=%d mnIn=%d nLOut=%d mnLOut=%d\n", nIn, mixerControl.inputs, nLOut, mixerControl.outputs);
+ return(1);
+ }
+ if (lineoutId && !vmixerId)
+ printf("** Warning - Vmixer cards without LineOut volume control are not supported !\n");
+
+ if (vmixerId) {
+ if (vmixerControl.vchannels!=nPOut || vmixerControl.outputs!=nLOut) {
+ printf("** Error - vmixer/channels mismatch: vmp=%d npo=%d vmo=%d nlo=%d !!\n", vmixerControl.vchannels, nPOut, vmixerControl.outputs, nLOut);
+ return(1);
+ }
+ }
+
+ //@ Assumes all mixer and vmixer controls are contiguous
+ if (mixerId)
+ for (o=0, numid=mixerId; o<nLOut; o++) {
+ for (i=0; i<nIn; i++) {
+ mixerControl.mixer[o][i].id=numid++;
+ }
+ }
+
+ if (vmixerId)
+ for (o=0, numid=vmixerId; o<vmixerControl.outputs; o++) {
+ for (i=0; i<vmixerControl.vchannels; i++) {
+ vmixerControl.mixer[o][i].id=numid++;
+ }
+ }
+
+ snd_hctl_close(handle);
+ return(0);
+}
+
+
+
+int main(int argc, char *argv[]) {
+ gchar str[256];
+ GtkWidget *hbox, *vbox;
+ GtkWidget *mainbox;
+ GtkWidget *vbsel, *frame, *button;
+ GtkWidget *label, *menu, *menuitem;
+ GSList *bgroup;
+ int err, i, o, n, cardnum;
+ char hwname[8], cardname[32], load, save;
+ snd_ctl_card_info_t *hw_info;
+
+ load=save=1;
+
+ // Scans all installed cards
+ snd_ctl_card_info_alloca(&hw_info);
+ cardnum=-1;
+ ctlhandle=0;
+
+ if (argc>1)
+ 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), "Echoaudio", 9)) {
+ 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.Monitor=fdIn+2-1; // The correct value is set by Digital_mode_activate()
+ GMixerSection.VmixerFirst=nIn;
+ GMixerSection.VmixerLast=nIn+vmixerControl.vchannels-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);
+ if (lineinId)
+ ReadControl(lineinControl.Gain, nIn, lineinControl.id);
+ if (lineoutId)
+ ReadControl(lineoutControl.Gain, nLOut, lineoutId);
+
+ //@@ 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<nLOut)
+ lineoutControl.Gain[o]=n;
+ } else if (!strncmp("LineIn ", str, 7)) {
+ sscanf(str+7, "%d %d", &i, &n);
+ if (i>=0 && i<nIn)
+ lineinControl.Gain[i]=n;
+ } else if (!strncmp("PcmOut ", str, 7)) {
+ sscanf(str+7, "%d %d", &o, &n);
+ if (o>=0 && o<nPOut)
+ pcmoutControl.Gain[o]=n;
+ } else if (!strncmp("Mixer ", str, 6)) {
+ sscanf(str+6, "%d %d %d", &o, &i, &n);
+ if (o>=0 && o<nLOut && i>=0 && i<nIn)
+ mixerControl.mixer[o][i].Gain=n;
+ } else if (!strncmp("Vmixer ", str, 7)) {
+ sscanf(str+7, "%d %d %d", &o, &i, &n);
+ if (o>=0 && o<nLOut && i>=0 && i<nPOut)
+ vmixerControl.mixer[o][i].Gain=n;
+ } else if (!strncmp("MainWindow ", str, 11)) {
+ sscanf(str+11, "%d %d %d %d", &Mainw_geom.x, &Mainw_geom.y, &Mainw_geom.w, &Mainw_geom.h);
+ } else if (!strncmp("VUmetersWindow ", str, 15)) {
+ sscanf(str+15, "%d %d %d", &VUw_geom.x, &VUw_geom.y, &VUw_geom.st);
+ } else if (!strncmp("GfxMixerWindow ", str, 15)) {
+ sscanf(str+15, "%d %d %d", &GMw_geom.x, &GMw_geom.y, &GMw_geom.st);
+ } else if (!strncmp("PcmVolumeWindow ", str, 16)) {
+ sscanf(str+16, "%d %d %d %d %d", &PVw_geom.x, &PVw_geom.y, &PVw_geom.w, &PVw_geom.h, &PVw_geom.st);
+ } else if (!strncmp("LineVolumeWindow ", str, 17)) {
+ sscanf(str+17, "%d %d %d %d %d", &LVw_geom.x, &LVw_geom.y, &LVw_geom.w, &LVw_geom.h, &LVw_geom.st);
+ } else if (!strncmp("MixerWindow ", str, 12)) {
+ sscanf(str+12, "%d %d %d %d %d", &Mixerw_geom.x, &Mixerw_geom.y, &Mixerw_geom.w, &Mixerw_geom.h, &Mixerw_geom.st);
+ } else if (!strncmp("VmixerWindow ", str, 13)) {
+ sscanf(str+13, "%d %d %d %d %d", &Vmixerw_geom.x, &Vmixerw_geom.y, &Vmixerw_geom.w, &Vmixerw_geom.h, &Vmixerw_geom.st);
+ } else if (!strncmp("MiscControlsWindow ", str, 19)) {
+ sscanf(str+19, "%d %d %d %d %d", &Miscw_geom.x, &Miscw_geom.y, &Miscw_geom.w, &Miscw_geom.h, &Miscw_geom.st);
+ }
+ }
+ }
+ }
+ gtk_init(&argc, &argv);
+ fnt=gdk_font_load("-misc-fixed-medium-r-*-*-10-*-*-*-*-*-*-*");
+ if (!fnt) {
+ printf("Cannot find the font\n");
+ exit(1);
+ }
+
+ /* Now assemble the control windows */
+
+
+ /* ********** Misc controls window ********** */
+
+ Miscwindow=gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ sprintf(str, "%s Misc controls", cardId);
+ gtk_window_set_title(GTK_WINDOW(Miscwindow), str);
+ gtk_window_set_wmclass(GTK_WINDOW(Miscwindow), "misc", "Emixer");
+ gtk_signal_connect(GTK_OBJECT(Miscwindow), "delete_event", GTK_SIGNAL_FUNC(CloseWindow), (gpointer)&Miscw_geom);
+ gtk_container_set_border_width(GTK_CONTAINER(Miscwindow), BORDER);
+ if (Miscw_geom.st!=NOPOS) {
+ gtk_widget_set_uposition(Miscwindow, Miscw_geom.x, Miscw_geom.y);
+ gtk_window_set_default_size(GTK_WINDOW(Miscwindow), Miscw_geom.w, Miscw_geom.h);
+ }
+
+ mainbox=gtk_vbox_new(FALSE, SPACING);
+ gtk_widget_show(mainbox);
+ gtk_container_add(GTK_CONTAINER(Miscwindow), mainbox);
+
+
+ if (p4InId) {
+ // Consumer/professional analog input switches
+ frame=gtk_frame_new("Input +4dBu");
+ 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);
+
+ for (i=0; i<fdIn; i++) {
+ sprintf(str, "%d", i);
+ p4dbuIn[i]=gtk_toggle_button_new_with_label(str);
+ gtk_box_pack_start(GTK_BOX(hbox), p4dbuIn[i], TRUE, FALSE, 1);
+ gtk_widget_show(p4dbuIn[i]);
+ gtk_signal_connect(GTK_OBJECT(p4dbuIn[i]), "toggled", GTK_SIGNAL_FUNC(Nominal_level_toggled), (gpointer)i);
+ }
+ InitNominalLevelGUI(p4InId);
+ }
+
+
+ if (p4OutId) {
+ // Consumer/professional analog output switches
+ frame=gtk_frame_new("Output +4dBu");
+ 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);
+
+ for (i=0; i<fdOut; i++) {
+ sprintf(str, "%d", i);
+ p4dbuOut[i]=gtk_toggle_button_new_with_label(str);
+ gtk_box_pack_start(GTK_BOX(hbox), p4dbuOut[i], TRUE, FALSE, 1);
+ gtk_widget_show(p4dbuOut[i]);
+ gtk_signal_connect(GTK_OBJECT(p4dbuOut[i]), "toggled", GTK_SIGNAL_FUNC(Nominal_level_toggled), (gpointer)(i+ECHO_MAXAUDIOINPUTS));
+ }
+ InitNominalLevelGUI(p4OutId);
+ }
+
+
+ if (dmodeId && ndmodes>1) {
+ // 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; i<ndmodes; i++) {
+ menuitem=gtk_menu_item_new_with_label(dmodeName[i]);
+ gtk_widget_show(menuitem);
+ gtk_signal_connect(GTK_OBJECT(menuitem), "activate", Digital_mode_activate, (gpointer)i);
+ gtk_menu_append(GTK_MENU(menu), menuitem);
+ }
+ gtk_option_menu_set_menu(GTK_OPTION_MENU(dmodeOpt), menu);
+ gtk_box_pack_start(GTK_BOX(hbox), dmodeOpt, TRUE, TRUE, 0);
+ gtk_option_menu_set_history(GTK_OPTION_MENU(dmodeOpt), dmodeVal=GetEnum(dmodeId));
+ }
+
+
+ if (clocksrcId && nclocksrc>1) {
+ // 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; i<nclocksrc; i++) {
+ clocksrc_menuitem[i]=gtk_menu_item_new_with_label(clocksrcName[i]);
+ gtk_widget_show(clocksrc_menuitem[i]);
+ gtk_widget_set_sensitive(clocksrc_menuitem[i], FALSE);
+ gtk_signal_connect(GTK_OBJECT(clocksrc_menuitem[i]), "activate", Clock_source_activate, (gpointer)i);
+ gtk_menu_append(GTK_MENU(menu), clocksrc_menuitem[i]);
+ }
+ gtk_option_menu_set_menu(GTK_OPTION_MENU(clocksrcOpt), menu);
+ gtk_box_pack_start(GTK_BOX(hbox), clocksrcOpt, TRUE, TRUE, 0);
+ gtk_option_menu_set_history(GTK_OPTION_MENU(clocksrcOpt), clocksrcVal=GetEnum(clocksrcId));
+ clocksrctimer=gtk_timeout_add(2000, CheckInputs, 0);
+ }
+
+
+ if (spdifmodeId && nspdifmodes>1) {
+ // 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; i<nspdifmodes; i++) {
+ menuitem=gtk_menu_item_new_with_label(spdifmodeName[i]);
+ gtk_widget_show(menuitem);
+ gtk_signal_connect(GTK_OBJECT(menuitem), "activate", SPDIF_mode_activate, (gpointer)i);
+ gtk_menu_append(GTK_MENU(menu), menuitem);
+ }
+ gtk_option_menu_set_menu(GTK_OPTION_MENU(spdifmodeOpt), menu);
+ gtk_box_pack_start(GTK_BOX(hbox), spdifmodeOpt, TRUE, TRUE, 0);
+ gtk_option_menu_set_history(GTK_OPTION_MENU(spdifmodeOpt), spdifmodeVal=GetEnum(spdifmodeId));
+ }
+
+
+ if (phantomId) {
+ // Phantom power switch
+ frame=gtk_frame_new("Phantom power");
+ 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);
+
+ phantomToggle=gtk_toggle_button_new_with_label("On");
+ gtk_widget_show(phantomToggle);
+ gtk_box_pack_start(GTK_BOX(hbox), phantomToggle, TRUE, FALSE, 0);
+ gtk_signal_connect(GTK_OBJECT(phantomToggle), "toggled", PhantomPower_toggled, (gpointer)0);
+ InitPhantomPowerGUI(phantomId);
+ }
+
+
+/* ********** PCM volume window ********** */
+
+ pcmoutControl.window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ sprintf(str, "%s PCM volume", cardId);
+ gtk_window_set_title(GTK_WINDOW (pcmoutControl.window), str);
+ gtk_window_set_wmclass(GTK_WINDOW(pcmoutControl.window), "pcm", "Emixer");
+ gtk_signal_connect(GTK_OBJECT(pcmoutControl.window), "delete_event", GTK_SIGNAL_FUNC(CloseWindow), (gpointer)&PVw_geom);
+ gtk_container_set_border_width(GTK_CONTAINER(pcmoutControl.window), BORDER);
+ if (PVw_geom.st!=NOPOS) {
+ gtk_widget_set_uposition(pcmoutControl.window, PVw_geom.x, PVw_geom.y);
+ gtk_window_set_default_size(GTK_WINDOW(pcmoutControl.window), PVw_geom.w, PVw_geom.h);
+ }
+
+ mainbox=gtk_hbox_new(FALSE, SPACING);
+ gtk_widget_show(mainbox);
+ gtk_container_add(GTK_CONTAINER(pcmoutControl.window), mainbox);
+
+
+ if (pcmoutId) {
+ // PCM Output volume widgets
+ frame=gtk_frame_new("PCM Output volume");
+ 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; i<nPOut; i++) {
+ vbox=gtk_vbox_new(FALSE, 0);
+ gtk_widget_show(vbox);
+ gtk_container_add(GTK_CONTAINER(hbox), vbox);
+ // Channel label
+ if (i<fdOut)
+ sprintf(str, "A%d", i);
+ else
+ sprintf(str, "D%d", i-fdOut);
+ label=gtk_label_new(str);
+ gtk_widget_show(label);
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+ // Volume
+ pcmoutControl.adj[i]=gtk_adjustment_new(999, ECHOGAIN_MINOUT, ECHOGAIN_MAXOUT, SHORTSTEP, LONGSTEP, 0);
+ pcmoutControl.volume[i]=gtk_vscale_new(GTK_ADJUSTMENT(pcmoutControl.adj[i]));
+ gtk_widget_show(pcmoutControl.volume[i]);
+ gtk_box_pack_start(GTK_BOX(vbox), pcmoutControl.volume[i], TRUE, TRUE, 0);
+ gtk_scale_set_draw_value(GTK_SCALE(pcmoutControl.volume[i]), 0);
+ gtk_signal_connect(GTK_OBJECT(pcmoutControl.adj[i]), "value_changed", GTK_SIGNAL_FUNC(PCM_volume_changed), (gpointer)(i+ECHO_MAXAUDIOINPUTS));
+ // Value label
+ pcmoutControl.label[i]=gtk_label_new("xxx");
+ gtk_widget_show(pcmoutControl.label[i]);
+ gtk_box_pack_start(GTK_BOX(vbox), pcmoutControl.label[i], FALSE, FALSE, 0);
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(pcmoutControl.adj[i]), INVERT(pcmoutControl.Gain[i]));
+ }
+ gtk_widget_set_usize(GTK_WIDGET(pcmoutControl.volume[0]), 0, 170); // Set minimum y size
+ }
+
+
+/* ********** Line volume window ********** */
+
+ LVwindow=gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ sprintf(str, "%s Line volume", cardId);
+ gtk_window_set_title(GTK_WINDOW (LVwindow), str);
+ gtk_window_set_wmclass(GTK_WINDOW(LVwindow), "line", "Emixer");
+ gtk_signal_connect(GTK_OBJECT(LVwindow), "delete_event", GTK_SIGNAL_FUNC(CloseWindow), (gpointer)&LVw_geom);
+ gtk_container_set_border_width(GTK_CONTAINER(LVwindow), BORDER);
+ if (LVw_geom.st!=NOPOS) {
+ gtk_widget_set_uposition(LVwindow, LVw_geom.x, LVw_geom.y);
+ gtk_window_set_default_size(GTK_WINDOW(LVwindow), LVw_geom.w, LVw_geom.h);
+ }
+
+ mainbox=gtk_hbox_new(FALSE, SPACING);
+ gtk_widget_show(mainbox);
+ gtk_container_add(GTK_CONTAINER(LVwindow), mainbox);
+
+ // Line input volume widgets
+ if (lineinId) {
+ frame=gtk_frame_new("Analog input volume");
+ 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; i<fdIn; i++) {
+ vbox=gtk_vbox_new(FALSE, 0);
+ gtk_widget_show(vbox);
+ gtk_container_add(GTK_CONTAINER(hbox), vbox);
+ // Channel label
+ sprintf(str, "%d", i);
+ label=gtk_label_new(str);
+ gtk_widget_show(label);
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+ // Volume (resolution is 0.5 dB)
+ lineinControl.adj[i]=gtk_adjustment_new(999, ECHOGAIN_MININP, ECHOGAIN_MAXINP, SHORTSTEP, LONGSTEP*2, 0);
+ lineinControl.volume[i]=gtk_vscale_new(GTK_ADJUSTMENT(lineinControl.adj[i]));
+ gtk_widget_show(lineinControl.volume[i]);
+ gtk_box_pack_start(GTK_BOX(vbox), lineinControl.volume[i], TRUE, TRUE, 0);
+ gtk_scale_set_draw_value(GTK_SCALE(lineinControl.volume[i]), 0);
+ gtk_signal_connect(GTK_OBJECT(lineinControl.adj[i]), "value_changed", GTK_SIGNAL_FUNC(PCM_volume_changed), (gpointer)i);
+ // Value label
+ lineinControl.label[i]=gtk_label_new("xxx");
+ gtk_widget_show(lineinControl.label[i]);
+ gtk_box_pack_start(GTK_BOX(vbox), lineinControl.label[i], FALSE, FALSE, 0);
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(lineinControl.adj[i]), IN_INVERT(lineinControl.Gain[i]));
+ }
+ gtk_widget_set_usize(GTK_WIDGET(lineinControl.volume[0]), 0, 170); // Set minimum y size
+ }
+
+
+ // Line output volume widgets
+ if (1) {
+ frame=gtk_frame_new("Line output volume");
+ 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; i<nLOut; i++) {
+ vbox=gtk_vbox_new(FALSE, 0);
+ gtk_widget_show(vbox);
+ gtk_container_add(GTK_CONTAINER(hbox), vbox);
+ // Channel label
+ if (i<fdOut)
+ sprintf(str, "A%d", i);
+ else
+ sprintf(str, "D%d", i-fdOut);
+ label=gtk_label_new(str);
+ gtk_widget_show(label);
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+ // Volume
+ lineoutControl.adj[i]=gtk_adjustment_new(0, ECHOGAIN_MINOUT, ECHOGAIN_MAXOUT, SHORTSTEP, LONGSTEP, 0);
+ lineoutControl.volume[i]=gtk_vscale_new(GTK_ADJUSTMENT(lineoutControl.adj[i]));
+ gtk_widget_show(lineoutControl.volume[i]);
+ gtk_box_pack_start(GTK_BOX(vbox), lineoutControl.volume[i], TRUE, TRUE, 0);
+ gtk_scale_set_draw_value(GTK_SCALE(lineoutControl.volume[i]), 0);
+ gtk_signal_connect(GTK_OBJECT(lineoutControl.adj[i]), "value_changed", GTK_SIGNAL_FUNC(LineOut_volume_changed), (gpointer)i);
+ // Value label
+ lineoutControl.label[i]=gtk_label_new("xxx");
+ gtk_widget_show(lineoutControl.label[i]);
+ gtk_box_pack_start(GTK_BOX(vbox), lineoutControl.label[i], FALSE, FALSE, 0);
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(lineoutControl.adj[i]), INVERT(lineoutControl.Gain[i]));
+ }
+ gtk_widget_set_usize(GTK_WIDGET(lineoutControl.volume[0]), 0, 170); // Set minimum y size
+ }
+
+
+
+/* ********** Mixer window ********** */
+
+ if (mixerId) {
+ mixerControl.window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ sprintf(str, "%s Monitor mixer", cardId);
+ gtk_window_set_title(GTK_WINDOW(mixerControl.window), str);
+ gtk_window_set_wmclass(GTK_WINDOW(mixerControl.window), "mixer", "Emixer");
+ gtk_signal_connect(GTK_OBJECT(mixerControl.window), "delete_event", GTK_SIGNAL_FUNC(CloseWindow), (gpointer)&Mixerw_geom);
+ gtk_container_set_border_width(GTK_CONTAINER(mixerControl.window), BORDER);
+ if (Mixerw_geom.st!=NOPOS) {
+ gtk_widget_set_uposition(mixerControl.window, Mixerw_geom.x, Mixerw_geom.y);
+ gtk_window_set_default_size(GTK_WINDOW(mixerControl.window), Mixerw_geom.w, Mixerw_geom.h);
+// gdk_window_move_resize(mixerControl.window->window, 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; i<nIn; i++) {
+ vbox=gtk_vbox_new(FALSE, 0);
+ gtk_widget_show(vbox);
+ gtk_container_add(GTK_CONTAINER(hbox), vbox);
+ // Channel label
+ if (i<fdIn)
+ sprintf(str, "A%d", i);
+ else
+ sprintf(str, "D%d", i-fdIn);
+ label=gtk_label_new(str);
+ gtk_widget_show(label);
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+ // Volume
+ mixerControl.adj[i]=gtk_adjustment_new(0, ECHOGAIN_MINOUT, ECHOGAIN_MAXOUT, SHORTSTEP, LONGSTEP, 0);
+ mixerControl.volume[i]=gtk_vscale_new(GTK_ADJUSTMENT(mixerControl.adj[i]));
+ gtk_widget_show(mixerControl.volume[i]);
+ gtk_box_pack_start(GTK_BOX(vbox), mixerControl.volume[i], TRUE, TRUE, 0);
+ gtk_scale_set_draw_value(GTK_SCALE(mixerControl.volume[i]), 0);
+ gtk_signal_connect(GTK_OBJECT(mixerControl.volume[i]), "grab_focus", GTK_SIGNAL_FUNC(Monitor_volume_clicked), (gpointer)i);
+ gtk_signal_connect(GTK_OBJECT(mixerControl.adj[i]), "value_changed", GTK_SIGNAL_FUNC(Monitor_volume_changed), (gpointer)i);
+ // Value label
+ mixerControl.label[i]=gtk_label_new("xxx");
+ gtk_widget_show(mixerControl.label[i]);
+ gtk_box_pack_start(GTK_BOX(vbox), mixerControl.label[i], FALSE, FALSE, 0);
+ }
+ gtk_widget_set_usize(GTK_WIDGET(mixerControl.volume[0]), 0, 170); // Set minimum y size
+
+ // Output channel selectors
+ frame=gtk_frame_new("Mixer output");
+ gtk_widget_show(frame);
+ vbsel=gtk_vbox_new(FALSE, 2);
+ gtk_box_pack_start(GTK_BOX(mainbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show(vbsel);
+ gtk_container_add(GTK_CONTAINER(frame), vbsel);
+
+ bgroup=0;
+ for (i=n=0; i<nLOut; i++) {
+ if (i<fdOut)
+ sprintf(str, "An-%d", i);
+ else
+ sprintf(str, "Di-%d", i-fdOut);
+ if (i)
+ bgroup=gtk_radio_button_group(GTK_RADIO_BUTTON(mixerControl.outsel[i-1]));
+ mixerControl.outsel[i]=gtk_radio_button_new_with_label(bgroup, str);
+ gtk_widget_show(mixerControl.outsel[i]);
+ gtk_box_pack_start(GTK_BOX(vbsel), mixerControl.outsel[i], FALSE, FALSE, 0);
+ gtk_signal_connect(GTK_OBJECT(mixerControl.outsel[i]), "toggled", GTK_SIGNAL_FUNC(Mixer_Output_selector_clicked), (gpointer)i);
+ }
+ mixerControl.input=0;
+ mixerControl.output=-1;
+ Mixer_Output_selector_clicked(0, 0);
+
+#else // REVERSE
+
+ // Input channel selectors
+ frame=gtk_frame_new("Mixer input");
+ gtk_widget_show(frame);
+ gtk_box_pack_start(GTK_BOX(mainbox), frame, FALSE, FALSE, 0);
+ vbsel=gtk_vbox_new(FALSE, 2);
+ gtk_widget_show(vbsel);
+ gtk_container_add(GTK_CONTAINER(frame), vbsel);
+
+ bgroup=0;
+ for (i=n=0; i<nIn; i++) {
+ if (i<fdIn)
+ sprintf(str, "An-%d", i);
+ else
+ sprintf(str, "Di-%d", i-fdIn);
+ if (i)
+ bgroup=gtk_radio_button_group(GTK_RADIO_BUTTON(mixerControl.inpsel[i-1]));
+ mixerControl.inpsel[i]=gtk_radio_button_new_with_label(bgroup, str);
+ gtk_widget_show(mixerControl.inpsel[i]);
+ gtk_box_pack_start(GTK_BOX(vbsel), mixerControl.inpsel[i], FALSE, FALSE, 0);
+ gtk_signal_connect(GTK_OBJECT(mixerControl.inpsel[i]), "toggled", GTK_SIGNAL_FUNC(Mixer_Input_selector_clicked), (gpointer)i);
+ }
+
+ // Mixer volume widgets
+ frame=gtk_frame_new("Mixer output 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; i<nLOut; i++) {
+ vbox=gtk_vbox_new(FALSE, 0);
+ gtk_widget_show(vbox);
+ gtk_container_add(GTK_CONTAINER(hbox), vbox);
+ // Channel label
+ if (i<fdOut)
+ sprintf(str, "A%d", i);
+ else
+ sprintf(str, "D%d", i-fdOut);
+ label=gtk_label_new(str);
+ gtk_widget_show(label);
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+ // Volume
+ mixerControl.adj[i]=gtk_adjustment_new(0, ECHOGAIN_MINOUT, ECHOGAIN_MAXOUT, SHORTSTEP, LONGSTEP, 0);
+ mixerControl.volume[i]=gtk_vscale_new(GTK_ADJUSTMENT(mixerControl.adj[i]));
+ gtk_widget_show(mixerControl.volume[i]);
+ gtk_box_pack_start(GTK_BOX(vbox), mixerControl.volume[i], TRUE, TRUE, 0);
+ gtk_scale_set_draw_value(GTK_SCALE(mixerControl.volume[i]), 0);
+ gtk_signal_connect(GTK_OBJECT(mixerControl.volume[i]), "grab_focus", GTK_SIGNAL_FUNC(Monitor_volume_clicked), (gpointer)i);
+ gtk_signal_connect(GTK_OBJECT(mixerControl.adj[i]), "value_changed", GTK_SIGNAL_FUNC(Monitor_volume_changed), (gpointer)i);
+ // Value label
+ mixerControl.label[i]=gtk_label_new("xxx");
+ gtk_widget_show(mixerControl.label[i]);
+ gtk_box_pack_start(GTK_BOX(vbox), mixerControl.label[i], FALSE, FALSE, 0);
+ }
+ gtk_widget_set_usize(GTK_WIDGET(mixerControl.volume[0]), 0, 170); // Set minimum y size
+ mixerControl.input=-1;
+ mixerControl.output=0;
+ Mixer_Input_selector_clicked(0, 0);
+#endif
+ }
+
+
+/* ********** Vmixer window ********** */
+
+ if (vmixerId) {
+ vmixerControl.window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ sprintf(str, "%s Vmixer", cardId);
+ gtk_window_set_title(GTK_WINDOW(vmixerControl.window), str);
+ gtk_window_set_wmclass(GTK_WINDOW(vmixerControl.window), "vmixer", "Emixer");
+ gtk_signal_connect(GTK_OBJECT(vmixerControl.window), "delete_event", GTK_SIGNAL_FUNC(CloseWindow), (gpointer)&Vmixerw_geom);
+ gtk_container_set_border_width(GTK_CONTAINER(vmixerControl.window), BORDER);
+ if (Vmixerw_geom.st!=NOPOS) {
+ gtk_widget_set_uposition(vmixerControl.window, Vmixerw_geom.x, Vmixerw_geom.y);
+ gtk_window_set_default_size(GTK_WINDOW(vmixerControl.window), Vmixerw_geom.w, Vmixerw_geom.h);
+ }
+
+ mainbox=gtk_hbox_new(FALSE, SPACING);
+ gtk_widget_show(mainbox);
+ gtk_container_add(GTK_CONTAINER(vmixerControl.window), mainbox);
+
+#ifdef REVERSE
+
+ // Vmixer volume widgets
+ frame=gtk_frame_new("Vmixer vchannels 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; i<vmixerControl.vchannels; i++) {
+ vbox=gtk_vbox_new(FALSE, 0);
+ gtk_widget_show(vbox);
+ gtk_container_add(GTK_CONTAINER(hbox), vbox);
+ // Channel label
+ sprintf(str, "V%d", i);
+ label=gtk_label_new(str);
+ gtk_widget_show(label);
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+ // Volume
+ vmixerControl.adj[i]=gtk_adjustment_new(0, ECHOGAIN_MINOUT, ECHOGAIN_MAXOUT, SHORTSTEP, LONGSTEP, 0);
+ vmixerControl.volume[i]=gtk_vscale_new(GTK_ADJUSTMENT(vmixerControl.adj[i]));
+ gtk_widget_show(vmixerControl.volume[i]);
+ gtk_box_pack_start(GTK_BOX(vbox), vmixerControl.volume[i], TRUE, TRUE, 0);
+ gtk_scale_set_draw_value(GTK_SCALE(vmixerControl.volume[i]), 0);
+ gtk_signal_connect(GTK_OBJECT(vmixerControl.volume[i]), "grab_focus", GTK_SIGNAL_FUNC(Vmixer_volume_clicked), (gpointer)i);
+ gtk_signal_connect(GTK_OBJECT(vmixerControl.adj[i]), "value_changed", GTK_SIGNAL_FUNC(Vmixer_volume_changed), (gpointer)i);
+ // Value label
+ vmixerControl.label[i]=gtk_label_new("xxx");
+ gtk_widget_show(vmixerControl.label[i]);
+ gtk_box_pack_start(GTK_BOX(vbox), vmixerControl.label[i], FALSE, FALSE, 0);
+ }
+ gtk_widget_set_usize(GTK_WIDGET(vmixerControl.volume[0]), 0, 170); // Set minimum y size
+
+ // Input channel selectors
+ frame=gtk_frame_new("Output");
+ gtk_widget_show(frame);
+ gtk_box_pack_start(GTK_BOX(mainbox), frame, FALSE, FALSE, 0);
+ vbsel=gtk_vbox_new(FALSE, 2);
+ gtk_widget_show(vbsel);
+ gtk_container_add(GTK_CONTAINER(frame), vbsel);
+
+ bgroup=0;
+ for (i=0; i<vmixerControl.outputs; i++) {
+ if (i<fdOut)
+ sprintf(str, "A%d", i);
+ else
+ sprintf(str, "D%d", i);
+ if (i)
+ bgroup=gtk_radio_button_group(GTK_RADIO_BUTTON(vmixerControl.outsel[i-1]));
+ vmixerControl.outsel[i]=gtk_radio_button_new_with_label(bgroup, str);
+ gtk_widget_show(vmixerControl.outsel[i]);
+ gtk_box_pack_start(GTK_BOX(vbsel), vmixerControl.outsel[i], FALSE, FALSE, 0);
+ gtk_signal_connect(GTK_OBJECT(vmixerControl.outsel[i]), "toggled", GTK_SIGNAL_FUNC(Vmixer_output_selector_clicked), (gpointer)i);
+ }
+ vmixerControl.output=-1;
+ Vmixer_output_selector_clicked(0, 0);
+
+#else // REVERSE
+
+ // Input channel selectors
+ frame=gtk_frame_new("Vchannel");
+ gtk_widget_show(frame);
+ gtk_box_pack_start(GTK_BOX(mainbox), frame, FALSE, FALSE, 0);
+ vbsel=gtk_vbox_new(FALSE, 2);
+ gtk_widget_show(vbsel);
+ gtk_container_add(GTK_CONTAINER(frame), vbsel);
+
+ bgroup=0;
+ for (i=0; i<vmixerControl.vchannels; i++) {
+ sprintf(str, "V%d", i);
+ if (i)
+ bgroup=gtk_radio_button_group(GTK_RADIO_BUTTON(vmixerControl.vchsel[i-1]));
+ vmixerControl.vchsel[i]=gtk_radio_button_new_with_label(bgroup, str);
+ gtk_widget_show(vmixerControl.vchsel[i]);
+ gtk_box_pack_start(GTK_BOX(vbsel), vmixerControl.vchsel[i], FALSE, FALSE, 0);
+ gtk_signal_connect(GTK_OBJECT(vmixerControl.vchsel[i]), "toggled", GTK_SIGNAL_FUNC(Vmixer_vchannel_selector_clicked), (gpointer)i);
+ }
+
+ // Vmixer volume widgets
+ frame=gtk_frame_new("Vmixer output 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; i<vmixerControl.outputs; i++) {
+ vbox=gtk_vbox_new(FALSE, 0);
+ gtk_widget_show(vbox);
+ gtk_container_add(GTK_CONTAINER(hbox), vbox);
+ // Channel label
+ if (i<fdOut)
+ sprintf(str, "A%d", i);
+ else
+ sprintf(str, "D%d", i-fdOut);
+ label=gtk_label_new(str);
+ gtk_widget_show(label);
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+ // Volume
+ vmixerControl.adj[i]=gtk_adjustment_new(0, ECHOGAIN_MINOUT, ECHOGAIN_MAXOUT, SHORTSTEP, LONGSTEP, 0);
+ vmixerControl.volume[i]=gtk_vscale_new(GTK_ADJUSTMENT(vmixerControl.adj[i]));
+ gtk_widget_show(vmixerControl.volume[i]);
+ gtk_box_pack_start(GTK_BOX(vbox), vmixerControl.volume[i], TRUE, TRUE, 0);
+ gtk_scale_set_draw_value(GTK_SCALE(vmixerControl.volume[i]), 0);
+ gtk_signal_connect(GTK_OBJECT(vmixerControl.volume[i]), "grab_focus", GTK_SIGNAL_FUNC(Vmixer_volume_clicked), (gpointer)i);
+ gtk_signal_connect(GTK_OBJECT(vmixerControl.adj[i]), "value_changed", GTK_SIGNAL_FUNC(Vmixer_volume_changed), (gpointer)i);
+ // Value label
+ vmixerControl.label[i]=gtk_label_new("xxx");
+ gtk_widget_show(vmixerControl.label[i]);
+ gtk_box_pack_start(GTK_BOX(vbox), vmixerControl.label[i], FALSE, FALSE, 0);
+ }
+ gtk_widget_set_usize(GTK_WIDGET(vmixerControl.volume[0]), 0, 170); // Set minimum y size
+ vmixerControl.vchannel=-1;
+ Vmixer_vchannel_selector_clicked(0, 0);
+#endif
+ }
+
+
+/* ********** Main window ********** */
+
+ Mainwindow=gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ sprintf(str, EM_VERSION, cardId);
+ gtk_window_set_title(GTK_WINDOW(Mainwindow), str);
+ gtk_window_set_wmclass(GTK_WINDOW(Mainwindow), "emixer", "Emixer");
+ gtk_signal_connect(GTK_OBJECT(Mainwindow), "delete_event", GTK_SIGNAL_FUNC(Mainwindow_delete), (gpointer)&Mainw_geom);
+ gtk_container_set_border_width(GTK_CONTAINER(Mainwindow), BORDER);
+ gtk_widget_show(Mainwindow);
+ if (Mainw_geom.x!=NOPOS) {
+ gtk_widget_set_uposition(Mainwindow, Mainw_geom.x, Mainw_geom.y);
+ gtk_window_set_default_size(GTK_WINDOW(Mainwindow), Mainw_geom.w, Mainw_geom.h);
+ }
+
+ mainbox=gtk_hbox_new(FALSE, SPACING);
+ gtk_widget_show(mainbox);
+ gtk_container_add(GTK_CONTAINER(Mainwindow), mainbox);
+
+
+ // Gang button and its frame
+ frame=gtk_frame_new("Gang");
+ gtk_widget_show(frame);
+ gtk_box_pack_start(GTK_BOX(mainbox), frame, FALSE, FALSE, 0);
+ hbox=gtk_hbox_new(FALSE, 0);
+ gtk_widget_show(hbox);
+ gtk_container_add(GTK_CONTAINER(frame), hbox);
+ button=gtk_toggle_button_new_with_label("On");
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), 1);
+ gtk_widget_show(button);
+ gtk_signal_connect(GTK_OBJECT(button), "toggled", Gang_button_toggled, 0);
+
+ // Controls frame
+ frame=gtk_frame_new("Controls");
+ gtk_widget_show(frame);
+ gtk_box_pack_end(GTK_BOX(mainbox), frame, FALSE, FALSE, 0);
+ hbox=gtk_hbox_new(FALSE, 0);
+ gtk_widget_show(hbox);
+ gtk_container_add(GTK_CONTAINER(frame), hbox);
+
+ // VUmeters button
+ if (vumetersId && vuswitchId) {
+ button=gtk_toggle_button_new_with_label("VU");
+ gtk_widget_show(button);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 1);
+ gtk_signal_connect(GTK_OBJECT(button), "toggled", VUmeters_button_click, 0);
+ VUw_geom.toggler=button;
+ if (VUw_geom.st==1)
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+ }
+
+ // Line volume button
+ button=gtk_toggle_button_new_with_label("Line");
+ gtk_widget_show(button);
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 1);
+ gtk_signal_connect(GTK_OBJECT(button), "toggled", ToggleWindow, (gpointer)LVwindow);
+ LVw_geom.toggler=button;
+ if (LVw_geom.st==1)
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
+
+ // Misc controls button
+ 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)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 <channel> <gain>\n");
+ for (i=0; i<nLOut; i++)
+ fprintf(f, "LineOut %2d %d\n", i, lineoutControl.Gain[i]);
+ fprintf(f, "-- LineIn <channel> <gain>\n");
+ for (i=0; i<nIn; i++)
+ fprintf(f, "LineIn %2d %d\n", i, lineinControl.Gain[i]);
+ fprintf(f, "-- PcmOut <channel> <gain>\n");
+ for (i=0; i<nPOut; i++)
+ fprintf(f, "PcmOut %2d %d\n", i, pcmoutControl.Gain[i]);
+ fprintf(f, "-- Mixer <output> <input> <gain>\n");
+ for (o=0; o<nLOut; o++)
+ for (i=0; i<nIn; i++)
+ fprintf(f, "Mixer %2d %2d %d\n", o, i, mixerControl.mixer[o][i].Gain);
+ fprintf(f, "-- Vmixer <output> <vchannel> <gain>\n");
+ if (vmixerId)
+ for (o=0; o<nLOut; o++)
+ for (i=0; i<nPOut; i++)
+ fprintf(f, "Vmixer %2d %2d %d\n", o, i, vmixerControl.mixer[o][i].Gain);
+ fprintf(f, "-- xxWindow <x> <y> <width> <height> <visible>\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);
+}
+
+/*
+TODO:
+non controlla se c'รจ mixerId e forse altri
+controllare tutorial su GdkColor e tracciamento
+*/
+
+